| //===--- SILGenLValue.cpp - Constructs logical lvalues for SILGen ---------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Emission of l-value expressions and basic operations on them. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ASTVisitor.h" |
| #include "ArgumentScope.h" |
| #include "ArgumentSource.h" |
| #include "Conversion.h" |
| #include "Initialization.h" |
| #include "LValue.h" |
| #include "RValue.h" |
| #include "SILGen.h" |
| #include "Scope.h" |
| #include "swift/AST/DiagnosticsCommon.h" |
| #include "swift/AST/DiagnosticsSIL.h" |
| #include "swift/AST/GenericEnvironment.h" |
| #include "swift/SIL/InstructionUtils.h" |
| #include "swift/SIL/MemAccessUtils.h" |
| #include "swift/SIL/PrettyStackTrace.h" |
| #include "swift/SIL/SILArgument.h" |
| #include "swift/SIL/SILUndef.h" |
| #include "swift/SIL/TypeLowering.h" |
| #include "llvm/Support/raw_ostream.h" |
| using namespace swift; |
| using namespace Lowering; |
| |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| struct LValueWritebackCleanup : Cleanup { |
| FormalEvaluationContext::stable_iterator Depth; |
| |
| LValueWritebackCleanup() : Depth() {} |
| |
| void emit(SILGenFunction &SGF, CleanupLocation loc, |
| ForUnwind_t forUnwind) override { |
| FullExpr scope(SGF.Cleanups, loc); |
| |
| // TODO: honor forUnwind! |
| auto &evaluation = getEvaluation(SGF); |
| evaluation.performWriteback(SGF, /*isFinal*/ false); |
| } |
| |
| void dump(SILGenFunction &) const override { |
| #ifndef NDEBUG |
| llvm::errs() << "LValueWritebackCleanup\n" |
| << "State: " << getState() << " Depth: " << Depth.getDepth() |
| << "\n"; |
| #endif |
| } |
| |
| private: |
| ExclusiveBorrowFormalAccess &getEvaluation(SILGenFunction &SGF) { |
| auto &evaluation = *SGF.FormalEvalContext.find(Depth); |
| assert(evaluation.getKind() == FormalAccess::Exclusive); |
| return static_cast<ExclusiveBorrowFormalAccess &>(evaluation); |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| /// Push a writeback onto the current LValueWriteback stack. |
| static void pushWriteback(SILGenFunction &SGF, |
| SILLocation loc, |
| std::unique_ptr<LogicalPathComponent> &&comp, |
| ManagedValue base, |
| MaterializedLValue materialized) { |
| assert(SGF.isInFormalEvaluationScope()); |
| |
| // Push a cleanup to execute the writeback consistently. |
| auto &context = SGF.FormalEvalContext; |
| LValueWritebackCleanup &cleanup = |
| SGF.Cleanups.pushCleanup<LValueWritebackCleanup>(); |
| CleanupHandle handle = SGF.Cleanups.getTopCleanup(); |
| |
| context.push<ExclusiveBorrowFormalAccess>(loc, std::move(comp), base, |
| materialized, handle); |
| cleanup.Depth = context.stable_begin(); |
| } |
| |
| static bool areCertainlyEqualIndices(const Expr *e1, const Expr *e2); |
| |
| void ExclusiveBorrowFormalAccess::diagnoseConflict( |
| const ExclusiveBorrowFormalAccess &rhs, |
| SILGenFunction &SGF) const { |
| // If the two writebacks we're comparing are of different kinds (e.g. |
| // ownership conversion vs a computed property) then they aren't the |
| // same and thus cannot conflict. |
| if (component->getKind() != rhs.component->getKind()) |
| return; |
| |
| // If the lvalues don't have the same base value (possibly null), then |
| // they aren't the same. Note that this is the primary source of false |
| // negative for this diagnostic. |
| SILValue lhsBaseValue = base.getValue(), rhsBaseValue = rhs.base.getValue(); |
| if (lhsBaseValue != rhsBaseValue && |
| (!lhsBaseValue || !rhsBaseValue || |
| !RValue::areObviouslySameValue(lhsBaseValue, rhsBaseValue))) { |
| return; |
| } |
| |
| auto lhsStorage = component->getAccessedStorage(); |
| if (!lhsStorage) return; |
| |
| auto rhsStorage = rhs.component->getAccessedStorage(); |
| if (!rhsStorage) return; |
| |
| // If the decls match, then this could conflict. |
| if (lhsStorage->Storage != rhsStorage->Storage || |
| !lhsStorage->Storage || |
| lhsStorage->IsSuper != rhsStorage->IsSuper) |
| return; |
| |
| assert((lhsStorage->Indices != nullptr) == (rhsStorage->Indices != nullptr)); |
| |
| auto storage = lhsStorage->Storage; |
| |
| // If the decl is monomorphically a stored property, allow aliases. |
| // It could be overridden by a computed property in a subclass, but |
| // that's not likely enough to be worth the strictness here. |
| auto impl = storage->getImplInfo(); |
| // TODO: Stored properties with didSet accessors that don't look at the |
| // oldValue could also be addressed. |
| if ((impl.getReadImpl() == ReadImplKind::Stored || |
| impl.getReadImpl() == ReadImplKind::Address) && |
| (impl.getWriteImpl() == WriteImplKind::Immutable || |
| impl.getWriteImpl() == WriteImplKind::Stored || |
| impl.getWriteImpl() == WriteImplKind::MutableAddress)) { |
| return; |
| } |
| |
| // If the property is a generic requirement, allow aliases, because |
| // it may be conformed to using a stored property. |
| if (isa<ProtocolDecl>(storage->getDeclContext())) |
| return; |
| |
| // If this is a simple property access, then we must have a conflict. |
| if (!lhsStorage->Indices) { |
| assert(isa<VarDecl>(storage)); |
| SGF.SGM.diagnose(loc, diag::writeback_overlap_property, |
| storage->getBaseName().getIdentifier()) |
| .highlight(loc.getSourceRange()); |
| SGF.SGM.diagnose(rhs.loc, diag::writebackoverlap_note) |
| .highlight(rhs.loc.getSourceRange()); |
| return; |
| } |
| |
| // Otherwise, it is a subscript, check the index values. |
| |
| // If the indices are literally identical SILValue's, then there is |
| // clearly a conflict. |
| if (!lhsStorage->Indices->isObviouslyEqual(*rhsStorage->Indices)) { |
| // If the index value doesn't lower to literally the same SILValue's, |
| // do some fuzzy matching to catch the common case. |
| if (!lhsStorage->IndexExprForDiagnostics || |
| !rhsStorage->IndexExprForDiagnostics || |
| !areCertainlyEqualIndices(lhsStorage->IndexExprForDiagnostics, |
| rhsStorage->IndexExprForDiagnostics)) |
| return; |
| } |
| |
| // The locations for the subscripts are almost certainly SubscriptExprs. |
| // If so, dig into them to produce better location info in the |
| // diagnostics and be able to do more precise analysis. |
| auto expr1 = loc.getAsASTNode<SubscriptExpr>(); |
| auto expr2 = rhs.loc.getAsASTNode<SubscriptExpr>(); |
| |
| if (expr1 && expr2) { |
| SGF.SGM.diagnose(loc, diag::writeback_overlap_subscript) |
| .highlight(expr1->getBase()->getSourceRange()); |
| |
| SGF.SGM.diagnose(rhs.loc, diag::writebackoverlap_note) |
| .highlight(expr2->getBase()->getSourceRange()); |
| |
| } else { |
| SGF.SGM.diagnose(loc, diag::writeback_overlap_subscript) |
| .highlight(loc.getSourceRange()); |
| SGF.SGM.diagnose(rhs.loc, diag::writebackoverlap_note) |
| .highlight(rhs.loc.getSourceRange()); |
| } |
| } |
| |
| //===----------------------------------------------------------------------===// |
| |
| static CanType getSubstFormalRValueType(Expr *expr) { |
| return expr->getType()->getRValueType()->getCanonicalType(); |
| } |
| |
| static LValueTypeData getAbstractedTypeData(SILGenModule &SGM, |
| SGFAccessKind accessKind, |
| AbstractionPattern origFormalType, |
| CanType substFormalType) { |
| return { |
| accessKind, |
| origFormalType, |
| substFormalType, |
| SGM.Types.getLoweredRValueType(origFormalType, substFormalType) |
| }; |
| } |
| |
| static LValueTypeData getLogicalStorageTypeData(SILGenModule &SGM, |
| SGFAccessKind accessKind, |
| CanType substFormalType) { |
| assert(!isa<ReferenceStorageType>(substFormalType)); |
| AbstractionPattern origFormalType( |
| substFormalType.getReferenceStorageReferent()); |
| return getAbstractedTypeData(SGM, accessKind, origFormalType, substFormalType); |
| } |
| |
| static LValueTypeData getPhysicalStorageTypeData(SILGenModule &SGM, |
| SGFAccessKind accessKind, |
| AbstractStorageDecl *storage, |
| CanType substFormalType) { |
| assert(!isa<ReferenceStorageType>(substFormalType)); |
| auto origFormalType = SGM.Types.getAbstractionPattern(storage) |
| .getReferenceStorageReferentType(); |
| return getAbstractedTypeData(SGM, accessKind, origFormalType, substFormalType); |
| } |
| |
| static bool shouldUseUnsafeEnforcement(VarDecl *var) { |
| if (var->isDebuggerVar()) |
| return true; |
| |
| // TODO: Check for the explicit "unsafe" attribute. |
| return false; |
| } |
| |
| Optional<SILAccessEnforcement> |
| SILGenFunction::getStaticEnforcement(VarDecl *var) { |
| if (var && shouldUseUnsafeEnforcement(var)) |
| return SILAccessEnforcement::Unsafe; |
| |
| return SILAccessEnforcement::Static; |
| } |
| |
| Optional<SILAccessEnforcement> |
| SILGenFunction::getDynamicEnforcement(VarDecl *var) { |
| if (getOptions().EnforceExclusivityDynamic) { |
| if (var && shouldUseUnsafeEnforcement(var)) |
| return SILAccessEnforcement::Unsafe; |
| return SILAccessEnforcement::Dynamic; |
| } |
| return None; |
| } |
| |
| Optional<SILAccessEnforcement> |
| SILGenFunction::getUnknownEnforcement(VarDecl *var) { |
| if (var && shouldUseUnsafeEnforcement(var)) |
| return SILAccessEnforcement::Unsafe; |
| |
| return SILAccessEnforcement::Unknown; |
| } |
| |
| /// SILGenLValue - An ASTVisitor for building logical lvalues. |
| class LLVM_LIBRARY_VISIBILITY SILGenLValue |
| : public Lowering::ExprVisitor<SILGenLValue, LValue, |
| SGFAccessKind, LValueOptions> |
| { |
| |
| public: |
| SILGenFunction &SGF; |
| SILGenLValue(SILGenFunction &SGF) : SGF(SGF) {} |
| |
| LValue visitRec(Expr *e, SGFAccessKind accessKind, LValueOptions options, |
| AbstractionPattern orig = AbstractionPattern::getInvalid()); |
| |
| /// Dummy handler to log unimplemented nodes. |
| LValue visitExpr(Expr *e, SGFAccessKind accessKind, LValueOptions options); |
| |
| // Nodes that form the root of lvalue paths |
| LValue visitDiscardAssignmentExpr(DiscardAssignmentExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options); |
| LValue visitDeclRefExpr(DeclRefExpr *e, SGFAccessKind accessKind, |
| LValueOptions options); |
| LValue visitOpaqueValueExpr(OpaqueValueExpr *e, SGFAccessKind accessKind, |
| LValueOptions options); |
| |
| // Nodes that make up components of lvalue paths |
| |
| LValue visitMemberRefExpr(MemberRefExpr *e, SGFAccessKind accessKind, |
| LValueOptions options); |
| LValue visitSubscriptExpr(SubscriptExpr *e, SGFAccessKind accessKind, |
| LValueOptions options); |
| LValue visitTupleElementExpr(TupleElementExpr *e, SGFAccessKind accessKind, |
| LValueOptions options); |
| LValue visitForceValueExpr(ForceValueExpr *e, SGFAccessKind accessKind, |
| LValueOptions options); |
| LValue visitBindOptionalExpr(BindOptionalExpr *e, SGFAccessKind accessKind, |
| LValueOptions options); |
| LValue visitOpenExistentialExpr(OpenExistentialExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options); |
| LValue visitKeyPathApplicationExpr(KeyPathApplicationExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options); |
| |
| // Expressions that wrap lvalues |
| |
| LValue visitInOutExpr(InOutExpr *e, SGFAccessKind accessKind, |
| LValueOptions options); |
| LValue visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options); |
| }; |
| |
| /// Read this component. |
| ManagedValue |
| LogicalPathComponent::projectForRead(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, |
| SGFAccessKind accessKind) && { |
| assert(isReadAccess(accessKind)); |
| |
| const TypeLowering &RValueTL = SGF.getTypeLowering(getTypeOfRValue()); |
| |
| // If the access doesn't require us to make an owned address, don't |
| // force a materialization. |
| if (!isReadAccessResultAddress(accessKind)) { |
| auto rvalue = std::move(*this).get(SGF, loc, base, SGFContext()); |
| return std::move(rvalue).getAsSingleValue(SGF, loc); |
| } |
| |
| TemporaryInitializationPtr tempInit; |
| RValue rvalue; |
| |
| // If the RValue type has an openedExistential, then the RValue must be |
| // materialized before allocating a temporary for the RValue type. In that |
| // case, the RValue cannot be emitted directly into the temporary. |
| if (getTypeOfRValue().hasOpenedExistential()) { |
| // Emit a 'get'. |
| rvalue = std::move(*this).get(SGF, loc, base, SGFContext()); |
| |
| // Create a temporary, whose type may depend on the 'get'. |
| tempInit = SGF.emitFormalAccessTemporary(loc, RValueTL); |
| } else { |
| // Create a temporary for a static (non-dependent) RValue type. |
| tempInit = SGF.emitFormalAccessTemporary(loc, RValueTL); |
| |
| // Emit a 'get' directly into the temporary. |
| rvalue = std::move(*this).get(SGF, loc, base, SGFContext(tempInit.get())); |
| } |
| // `this` is now dead. |
| |
| // Force `value` into a temporary if is wasn't emitted there. |
| if (!rvalue.isInContext()) |
| std::move(rvalue).forwardInto(SGF, loc, tempInit.get()); |
| |
| return tempInit->getManagedAddress(); |
| } |
| |
| ManagedValue LogicalPathComponent::project(SILGenFunction &SGF, |
| SILLocation loc, |
| ManagedValue base) && { |
| auto accessKind = getAccessKind(); |
| if (isReadAccess(accessKind)) |
| return std::move(*this).projectForRead(SGF, loc, base, accessKind); |
| |
| // AccessKind is Write or ReadWrite. We need to emit a get and set. |
| assert(SGF.isInFormalEvaluationScope() && |
| "materializing l-value for modification without writeback scope"); |
| |
| // Clone anything else about the component that we might need in the |
| // writeback. |
| auto clonedComponent = clone(SGF, loc); |
| |
| ManagedValue temp = |
| std::move(*this).projectForRead(SGF, loc, base, |
| SGFAccessKind::OwnedAddressRead); |
| |
| if (SGF.getOptions().VerifyExclusivity) { |
| // Begin an access of the temporary. It is unenforced because enforcement |
| // isn't required for RValues. |
| SILValue accessAddress = UnenforcedFormalAccess::enter( |
| SGF, loc, temp.getValue(), SILAccessKind::Modify); |
| temp = std::move(temp).transform(accessAddress); |
| } |
| // Push a writeback for the temporary. |
| pushWriteback(SGF, loc, std::move(clonedComponent), base, |
| MaterializedLValue(temp)); |
| return ManagedValue::forLValue(temp.getValue()); |
| } |
| |
| void LogicalPathComponent::writeback(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, |
| MaterializedLValue materialized, |
| bool isFinal) { |
| assert(!materialized.callback && |
| "unexpected materialized lvalue with callback!"); |
| |
| // Load the value from the temporary unless the type is address-only |
| // and this is the final use, in which case we can just consume the |
| // value as-is. |
| auto temporary = materialized.temporary; |
| |
| assert(temporary.getType().isAddress()); |
| auto &tempTL = SGF.getTypeLowering(temporary.getType()); |
| if (!tempTL.isAddressOnly() || !isFinal || |
| !SGF.silConv.useLoweredAddresses()) { |
| if (isFinal) temporary.forward(SGF); |
| temporary = SGF.emitLoad(loc, temporary.getValue(), tempTL, |
| SGFContext(), IsTake_t(isFinal)); |
| } |
| RValue rvalue(SGF, loc, getSubstFormalType(), temporary); |
| |
| // Don't consume cleanups on the base if this isn't final. |
| if (base && !isFinal) { base = ManagedValue::forUnmanaged(base.getValue()); } |
| |
| // Clone the component if this isn't final. |
| std::unique_ptr<LogicalPathComponent> clonedComponent = |
| (isFinal ? nullptr : clone(SGF, loc)); |
| LogicalPathComponent *component = (isFinal ? this : &*clonedComponent); |
| std::move(*component).set(SGF, loc, ArgumentSource(loc, std::move(rvalue)), |
| base); |
| } |
| |
| InOutConversionScope::InOutConversionScope(SILGenFunction &SGF) |
| : SGF(SGF) |
| { |
| assert(SGF.isInFormalEvaluationScope() |
| && "inout conversions should happen in writeback scopes"); |
| assert(!SGF.InInOutConversionScope |
| && "inout conversions should not be nested"); |
| SGF.InInOutConversionScope = true; |
| } |
| |
| InOutConversionScope::~InOutConversionScope() { |
| assert(SGF.InInOutConversionScope && "already exited conversion scope?!"); |
| SGF.InInOutConversionScope = false; |
| } |
| |
| void PathComponent::_anchor() {} |
| void PhysicalPathComponent::_anchor() {} |
| |
| void PathComponent::dump() const { |
| dump(llvm::errs()); |
| } |
| |
| /// Return the LValueTypeData for a SIL value with the given AST formal type. |
| static LValueTypeData getValueTypeData(SGFAccessKind accessKind, |
| CanType formalType, SILValue value) { |
| return { |
| accessKind, |
| AbstractionPattern(formalType), |
| formalType, |
| value->getType().getASTType(), |
| }; |
| } |
| static LValueTypeData getValueTypeData(SILGenFunction &SGF, |
| SGFAccessKind accessKind, Expr *e) { |
| CanType formalType = getSubstFormalRValueType(e); |
| CanType loweredType = SGF.getLoweredType(formalType).getASTType(); |
| |
| return { |
| accessKind, |
| AbstractionPattern(formalType), |
| formalType, |
| loweredType |
| }; |
| } |
| |
| /// Given the address of an optional value, unsafely project out the |
| /// address of the value. |
| static ManagedValue getAddressOfOptionalValue(SILGenFunction &SGF, |
| SILLocation loc, |
| ManagedValue optAddr, |
| const LValueTypeData &valueTypeData) { |
| // Project out the 'Some' payload. |
| EnumElementDecl *someDecl = SGF.getASTContext().getOptionalSomeDecl(); |
| |
| // If the base is +1, we want to forward the cleanup. |
| bool hadCleanup = optAddr.hasCleanup(); |
| |
| // UncheckedTakeEnumDataAddr is safe to apply to Optional, because it is |
| // a single-payload enum. There will (currently) never be spare bits |
| // embedded in the payload. |
| SILValue valueAddr = |
| SGF.B.createUncheckedTakeEnumDataAddr(loc, optAddr.forward(SGF), someDecl, |
| SILType::getPrimitiveAddressType( |
| valueTypeData.TypeOfRValue)); |
| |
| // Return the value as +1 if the optional was +1. |
| if (hadCleanup) { |
| return SGF.emitManagedBufferWithCleanup(valueAddr); |
| } else { |
| return ManagedValue::forLValue(valueAddr); |
| } |
| } |
| |
| namespace { |
| /// A helper class for creating writebacks associated with l-value |
| /// components that don't really need them. |
| class WritebackPseudoComponent : public LogicalPathComponent { |
| protected: |
| WritebackPseudoComponent(const LValueTypeData &typeData) |
| : LogicalPathComponent(typeData, WritebackPseudoKind) {} |
| |
| public: |
| std::unique_ptr<LogicalPathComponent> |
| clone(SILGenFunction &SGF, SILLocation l) const override { |
| llvm_unreachable("called clone on pseudo-component"); |
| } |
| |
| RValue get(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SGFContext c) && override { |
| llvm_unreachable("called get on a pseudo-component"); |
| } |
| void set(SILGenFunction &SGF, SILLocation loc, |
| ArgumentSource &&value, ManagedValue base) && override { |
| llvm_unreachable("called set on a pseudo-component"); |
| } |
| ManagedValue project(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base) && override { |
| llvm_unreachable("called project on a pseudo-component"); |
| } |
| |
| Optional<AccessedStorage> getAccessedStorage() const override { |
| return None; |
| } |
| }; |
| |
| class EndAccessPseudoComponent : public WritebackPseudoComponent { |
| public: |
| EndAccessPseudoComponent(const LValueTypeData &typeData) |
| : WritebackPseudoComponent(typeData) {} |
| |
| private: |
| void writeback(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, |
| MaterializedLValue materialized, |
| bool isFinal) override { |
| loc.markAutoGenerated(); |
| |
| assert(base.isLValue()); |
| SGF.B.createEndAccess(loc, base.getValue(), /*abort*/ false); |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "EndAccessPseudoComponent\n"; |
| } |
| }; |
| } // end anonymous namespace |
| |
| static SILValue enterAccessScope(SILGenFunction &SGF, SILLocation loc, |
| SILValue addr, LValueTypeData typeData, |
| SGFAccessKind accessKind, |
| SILAccessEnforcement enforcement) { |
| auto silAccessKind = isReadAccess(accessKind) ? SILAccessKind::Read |
| : SILAccessKind::Modify; |
| |
| assert(SGF.isInFormalEvaluationScope() && |
| "tried to enter access scope without a writeback scope!"); |
| |
| // Enter the access. |
| addr = SGF.B.createBeginAccess(loc, addr, silAccessKind, enforcement, |
| /*hasNoNestedConflict=*/false, |
| /*fromBuiltin=*/false); |
| |
| // Push a writeback to end it. |
| auto accessedMV = ManagedValue::forLValue(addr); |
| std::unique_ptr<LogicalPathComponent> |
| component(new EndAccessPseudoComponent(typeData)); |
| pushWriteback(SGF, loc, std::move(component), accessedMV, |
| MaterializedLValue()); |
| |
| return addr; |
| } |
| |
| static ManagedValue enterAccessScope(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue addr, LValueTypeData typeData, |
| SGFAccessKind accessKind, |
| SILAccessEnforcement enforcement) { |
| return ManagedValue::forLValue( |
| enterAccessScope(SGF, loc, addr.getLValueAddress(), typeData, |
| accessKind, enforcement)); |
| } |
| |
| // Find the base of the formal access at `address`. If the base requires an |
| // access marker, then create a begin_access on `address`. Return the |
| // address to be used for the access. |
| // |
| // FIXME: In order to generate more consistent and verifiable SIL patterns, or |
| // subobject projections, create the access on the base address and recreate the |
| // projection. |
| SILValue UnenforcedAccess::beginAccess(SILGenFunction &SGF, SILLocation loc, |
| SILValue address, SILAccessKind kind) { |
| if (!SGF.getOptions().VerifyExclusivity) |
| return address; |
| |
| const AccessedStorage &storage = findAccessedStorage(address); |
| // Unsafe access may have invalid storage (e.g. a RawPointer). |
| if (storage && !isPossibleFormalAccessBase(storage, &SGF.F)) |
| return address; |
| |
| auto BAI = |
| SGF.B.createBeginAccess(loc, address, kind, SILAccessEnforcement::Unsafe, |
| /*hasNoNestedConflict=*/false, |
| /*fromBuiltin=*/false); |
| beginAccessPtr = BeginAccessPtr(BAI, DeleterCheck()); |
| |
| return BAI; |
| } |
| |
| void UnenforcedAccess::endAccess(SILGenFunction &SGF) { |
| emitEndAccess(SGF); |
| beginAccessPtr.release(); |
| } |
| |
| void UnenforcedAccess::emitEndAccess(SILGenFunction &SGF) { |
| if (!beginAccessPtr) |
| return; |
| |
| SGF.B.createEndAccess(beginAccessPtr->getLoc(), beginAccessPtr.get(), |
| /*abort*/ false); |
| } |
| |
| // Emit an end_access marker when executing a cleanup (on a side branch). |
| void UnenforcedFormalAccess::emitEndAccess(SILGenFunction &SGF) { |
| access.emitEndAccess(SGF); |
| } |
| |
| // End the access when existing the FormalEvaluationScope. |
| void UnenforcedFormalAccess::finishImpl(SILGenFunction &SGF) { |
| access.endAccess(SGF); |
| } |
| |
| namespace { |
| struct UnenforcedAccessCleanup : Cleanup { |
| FormalEvaluationContext::stable_iterator Depth; |
| |
| UnenforcedAccessCleanup() : Depth() {} |
| |
| void emit(SILGenFunction &SGF, CleanupLocation loc, |
| ForUnwind_t forUnwind) override { |
| auto &evaluation = *SGF.FormalEvalContext.find(Depth); |
| assert(evaluation.getKind() == FormalAccess::Unenforced); |
| auto &formalAccess = static_cast<UnenforcedFormalAccess &>(evaluation); |
| formalAccess.emitEndAccess(SGF); |
| } |
| |
| void dump(SILGenFunction &) const override { |
| #ifndef NDEBUG |
| llvm::errs() << "UnenforcedAccessCleanup\n" |
| << "State: " << getState() << " Depth: " << Depth.getDepth() |
| << "\n"; |
| #endif |
| } |
| }; |
| } // end anonymous namespace |
| |
| SILValue UnenforcedFormalAccess::enter(SILGenFunction &SGF, SILLocation loc, |
| SILValue address, SILAccessKind kind) { |
| assert(SGF.isInFormalEvaluationScope()); |
| |
| UnenforcedAccess access; |
| SILValue accessAddress = access.beginAccess(SGF, loc, address, kind); |
| if (!access.beginAccessPtr) |
| return address; |
| |
| auto &cleanup = SGF.Cleanups.pushCleanup<UnenforcedAccessCleanup>(); |
| CleanupHandle handle = SGF.Cleanups.getTopCleanup(); |
| auto &context = SGF.FormalEvalContext; |
| context.push<UnenforcedFormalAccess>(loc, std::move(access), handle); |
| cleanup.Depth = context.stable_begin(); |
| |
| return accessAddress; |
| } |
| |
| static void copyBorrowedYieldsIntoTemporary(SILGenFunction &SGF, |
| SILLocation loc, |
| ArrayRef<ManagedValue> &yields, |
| AbstractionPattern origFormalType, |
| CanType substFormalType, |
| Initialization *init) { |
| if (!origFormalType.isTuple()) { |
| auto value = yields.front(); |
| yields = yields.drop_front(); |
| init->copyOrInitValueInto(SGF, loc, value, /*isInit*/ false); |
| init->finishInitialization(SGF); |
| return; |
| } |
| |
| assert(init->canSplitIntoTupleElements()); |
| SmallVector<InitializationPtr, 4> scratch; |
| auto eltInits = |
| init->splitIntoTupleElements(SGF, loc, substFormalType, scratch); |
| for (size_t i : indices(eltInits)) { |
| auto origEltType = origFormalType.getTupleElementType(i); |
| auto substEltType = cast<TupleType>(substFormalType).getElementType(i); |
| copyBorrowedYieldsIntoTemporary(SGF, loc, yields, origEltType, |
| substEltType, eltInits[i].get()); |
| } |
| init->finishInitialization(SGF); |
| } |
| |
| namespace { |
| class RefElementComponent : public PhysicalPathComponent { |
| VarDecl *Field; |
| SILType SubstFieldType; |
| bool IsNonAccessing; |
| public: |
| RefElementComponent(VarDecl *field, LValueOptions options, |
| SILType substFieldType, LValueTypeData typeData) |
| : PhysicalPathComponent(typeData, RefElementKind), |
| Field(field), SubstFieldType(substFieldType), |
| IsNonAccessing(options.IsNonAccessing) {} |
| |
| virtual bool isLoadingPure() const override { return true; } |
| |
| ManagedValue project(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base) && override { |
| assert(base.getType().hasReferenceSemantics() && |
| "base for ref element component must be a reference type"); |
| |
| // Borrow the ref element addr using formal access. If we need the ref |
| // element addr, we will load it in this expression. |
| if (base.getType().isAddress()) { |
| base = SGF.B.createFormalAccessLoadBorrow(loc, base); |
| } else { |
| base = base.formalAccessBorrow(SGF, loc); |
| } |
| SILValue result = |
| SGF.B.createRefElementAddr(loc, base.getUnmanagedValue(), |
| Field, SubstFieldType); |
| |
| // Avoid emitting access markers completely for non-accesses or immutable |
| // declarations. Access marker verification is aware of these cases. |
| if (!IsNonAccessing && !Field->isLet()) { |
| if (auto enforcement = SGF.getDynamicEnforcement(Field)) { |
| result = enterAccessScope(SGF, loc, result, getTypeData(), |
| getAccessKind(), *enforcement); |
| } |
| } |
| |
| return ManagedValue::forLValue(result); |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "RefElementComponent(" << Field->getName() << ")\n"; |
| } |
| }; |
| |
| class TupleElementComponent : public PhysicalPathComponent { |
| unsigned ElementIndex; |
| public: |
| TupleElementComponent(unsigned elementIndex, LValueTypeData typeData) |
| : PhysicalPathComponent(typeData, TupleElementKind), |
| ElementIndex(elementIndex) {} |
| |
| virtual bool isLoadingPure() const override { return true; } |
| |
| ManagedValue project(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base) && override { |
| assert(base && "invalid value for element base"); |
| if (base.getType().isObject()) { |
| return SGF.B.createTupleExtract(loc, base, ElementIndex); |
| } |
| |
| // TODO: if the base is +1, break apart its cleanup. |
| auto Res = SGF.B.createTupleElementAddr(loc, base.getValue(), |
| ElementIndex, |
| getTypeOfRValue().getAddressType()); |
| return ManagedValue::forLValue(Res); |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "TupleElementComponent(" << ElementIndex << ")\n"; |
| } |
| }; |
| |
| class StructElementComponent : public PhysicalPathComponent { |
| VarDecl *Field; |
| SILType SubstFieldType; |
| public: |
| StructElementComponent(VarDecl *field, SILType substFieldType, |
| LValueTypeData typeData) |
| : PhysicalPathComponent(typeData, StructElementKind), |
| Field(field), SubstFieldType(substFieldType) {} |
| |
| virtual bool isLoadingPure() const override { return true; } |
| |
| ManagedValue project(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base) && override { |
| assert(base && "invalid value for element base"); |
| if (base.getType().isObject()) { |
| return SGF.B.createStructExtract(loc, base, Field); |
| } |
| |
| // TODO: if the base is +1, break apart its cleanup. |
| auto Res = SGF.B.createStructElementAddr(loc, base.getValue(), |
| Field, SubstFieldType); |
| return ManagedValue::forLValue(Res); |
| } |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "StructElementComponent(" |
| << Field->getName() << ")\n"; |
| } |
| }; |
| |
| /// A physical path component which force-projects the address of |
| /// the value of an optional l-value. |
| class ForceOptionalObjectComponent : public PhysicalPathComponent { |
| bool isImplicitUnwrap; |
| public: |
| ForceOptionalObjectComponent(LValueTypeData typeData, |
| bool isImplicitUnwrap) |
| : PhysicalPathComponent(typeData, OptionalObjectKind), |
| isImplicitUnwrap(isImplicitUnwrap) {} |
| |
| ManagedValue project(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base) && override { |
| // Assert that the optional value is present and return the projected out |
| // payload. |
| return SGF.emitPreconditionOptionalHasValue(loc, base, isImplicitUnwrap); |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "ForceOptionalObjectComponent(" << isImplicitUnwrap << ")\n"; |
| } |
| }; |
| |
| /// A physical path component which projects out an opened archetype |
| /// from an existential. |
| class OpenOpaqueExistentialComponent : public PhysicalPathComponent { |
| public: |
| OpenOpaqueExistentialComponent(CanArchetypeType openedArchetype, |
| LValueTypeData typeData) |
| : PhysicalPathComponent(typeData, OpenOpaqueExistentialKind) { |
| assert(getSubstFormalType() == openedArchetype); |
| } |
| |
| virtual bool isLoadingPure() const override { return true; } |
| |
| ManagedValue project(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base) && override { |
| assert(base.getType().isExistentialType() && |
| "base for open existential component must be an existential"); |
| assert((base.getType().isAddress() || |
| base.getType().getPreferredExistentialRepresentation(SGF.SGM.M) == |
| ExistentialRepresentation::Boxed) && |
| "base value of open-existential component was not an address or a " |
| "boxed existential?"); |
| SILValue addr; |
| |
| auto rep = base.getType().getPreferredExistentialRepresentation(SGF.SGM.M); |
| switch (rep) { |
| case ExistentialRepresentation::Opaque: |
| addr = SGF.B.createOpenExistentialAddr( |
| loc, base.getValue(), getTypeOfRValue().getAddressType(), |
| getOpenedExistentialAccessFor(getFormalAccessKind(getAccessKind()))); |
| break; |
| case ExistentialRepresentation::Boxed: { |
| ManagedValue error; |
| if (base.getType().isObject()) { |
| error = base; |
| } else { |
| auto &TL = SGF.getTypeLowering(base.getType()); |
| error = SGF.emitLoad(loc, base.getValue(), TL, |
| SGFContext(), IsNotTake); |
| } |
| addr = SGF.B.createOpenExistentialBox( |
| loc, error.getValue(), getTypeOfRValue().getAddressType()); |
| break; |
| } |
| default: |
| llvm_unreachable("Bad existential representation for address-only type"); |
| } |
| |
| return ManagedValue::forLValue(addr); |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "OpenOpaqueExistentialComponent(" |
| << getSubstFormalType() << ")\n"; |
| } |
| }; |
| |
| /// A local path component for the payload of a class or metatype existential. |
| /// |
| /// TODO: Could be physical if we had a way to project out the |
| /// payload. |
| class OpenNonOpaqueExistentialComponent : public LogicalPathComponent { |
| CanArchetypeType OpenedArchetype; |
| public: |
| OpenNonOpaqueExistentialComponent(CanArchetypeType openedArchetype, |
| LValueTypeData typeData) |
| : LogicalPathComponent(typeData, OpenNonOpaqueExistentialKind), |
| OpenedArchetype(openedArchetype) {} |
| |
| virtual bool isLoadingPure() const override { return true; } |
| |
| Optional<AccessedStorage> getAccessedStorage() const override { |
| return None; |
| } |
| |
| RValue get(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SGFContext c) && override { |
| auto refType = base.getType().getObjectType(); |
| auto &TL = SGF.getTypeLowering(refType); |
| |
| // Load the original value if necessary. |
| auto result = base.getType().isAddress() |
| ? SGF.emitLoad(loc, base.getValue(), TL, |
| SGFContext(), IsNotTake) |
| : base; |
| |
| assert(refType.isAnyExistentialType() && |
| "base for open existential component must be an existential"); |
| ManagedValue ref; |
| if (refType.is<ExistentialMetatypeType>()) { |
| assert(refType.getPreferredExistentialRepresentation(SGF.SGM.M) |
| == ExistentialRepresentation::Metatype); |
| ref = ManagedValue::forUnmanaged( |
| SGF.B.createOpenExistentialMetatype(loc, |
| result.getUnmanagedValue(), |
| getTypeOfRValue())); |
| } else { |
| assert(refType.getPreferredExistentialRepresentation(SGF.SGM.M) |
| == ExistentialRepresentation::Class); |
| ref = SGF.B.createOpenExistentialRef(loc, result, getTypeOfRValue()); |
| } |
| |
| return RValue(SGF, loc, getSubstFormalType(), ref); |
| } |
| |
| void set(SILGenFunction &SGF, SILLocation loc, |
| ArgumentSource &&value, ManagedValue base) && override { |
| auto payload = std::move(value).getAsSingleValue(SGF).forward(SGF); |
| |
| SmallVector<ProtocolConformanceRef, 2> conformances; |
| for (auto proto : OpenedArchetype->getConformsTo()) |
| conformances.push_back(ProtocolConformanceRef(proto)); |
| |
| SILValue ref; |
| if (base.getType().is<ExistentialMetatypeType>()) { |
| ref = SGF.B.createInitExistentialMetatype( |
| loc, |
| payload, |
| base.getType().getObjectType(), |
| SGF.getASTContext().AllocateCopy(conformances)); |
| } else { |
| assert(getSubstFormalType()->isBridgeableObjectType()); |
| ref = SGF.B.createInitExistentialRef( |
| loc, |
| base.getType().getObjectType(), |
| getSubstFormalType(), |
| payload, |
| SGF.getASTContext().AllocateCopy(conformances)); |
| } |
| |
| auto &TL = SGF.getTypeLowering(base.getType()); |
| SGF.emitSemanticStore(loc, ref, |
| base.getValue(), TL, IsNotInitialization); |
| } |
| |
| std::unique_ptr<LogicalPathComponent> |
| clone(SILGenFunction &SGF, SILLocation loc) const override { |
| LogicalPathComponent *clone = |
| new OpenNonOpaqueExistentialComponent(OpenedArchetype, getTypeData()); |
| return std::unique_ptr<LogicalPathComponent>(clone); |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "OpenNonOpaqueExistentialComponent(" |
| << OpenedArchetype << ", ...)\n"; |
| } |
| }; |
| |
| /// A physical path component which returns a literal address. |
| class ValueComponent : public PhysicalPathComponent { |
| ManagedValue Value; |
| Optional<SILAccessEnforcement> Enforcement; |
| bool IsRValue; |
| public: |
| ValueComponent(ManagedValue value, |
| Optional<SILAccessEnforcement> enforcement, |
| LValueTypeData typeData, |
| bool isRValue = false) : |
| PhysicalPathComponent(typeData, ValueKind), |
| Value(value), |
| Enforcement(enforcement), |
| IsRValue(isRValue) { |
| assert(IsRValue || value.getType().isAddress()); |
| } |
| |
| virtual bool isLoadingPure() const override { return true; } |
| |
| ManagedValue project(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base) && override { |
| assert(!base && "value component must be root of lvalue path"); |
| |
| if (!Enforcement) |
| return Value; |
| |
| SILValue addr = Value.getLValueAddress(); |
| addr = enterAccessScope(SGF, loc, addr, getTypeData(), |
| getAccessKind(), *Enforcement); |
| |
| return ManagedValue::forLValue(addr); |
| } |
| |
| bool isRValue() const override { |
| return IsRValue; |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS << "ValueComponent("; |
| if (IsRValue) OS << "rvalue, "; |
| if (Enforcement) { |
| OS << getSILAccessEnforcementName(*Enforcement); |
| } else { |
| OS << "unenforced"; |
| } |
| OS << "):\n"; |
| Value.dump(OS, indent + 2); |
| } |
| }; |
| } // end anonymous namespace |
| |
| static bool isReadNoneFunction(const Expr *e) { |
| // If this is a curried call to an integer literal conversion operations, then |
| // we can "safely" assume it is readnone (btw, yes this is totally gross). |
| // This is better to be attribute driven, a la rdar://15587352. |
| if (auto *dre = dyn_cast<DeclRefExpr>(e)) { |
| DeclName name = dre->getDecl()->getFullName(); |
| return (name.getArgumentNames().size() == 1 && |
| name.getBaseName() == DeclBaseName::createConstructor() && |
| !name.getArgumentNames()[0].empty() && |
| (name.getArgumentNames()[0].str() == "integerLiteral" || |
| name.getArgumentNames()[0].str() == "_builtinIntegerLiteral")); |
| } |
| |
| // Look through DotSyntaxCallExpr, since the literal functions are curried. |
| if (auto *CRCE = dyn_cast<ConstructorRefCallExpr>(e)) |
| return isReadNoneFunction(CRCE->getFn()); |
| |
| return false; |
| } |
| |
| |
| /// Given two expressions used as indexes to the same SubscriptDecl (and thus |
| /// are guaranteed to have the same AST type) check to see if they are going to |
| /// produce the same value. |
| static bool areCertainlyEqualIndices(const Expr *e1, const Expr *e2) { |
| if (e1->getKind() != e2->getKind()) return false; |
| |
| // Look through ParenExpr's. |
| if (auto *pe1 = dyn_cast<ParenExpr>(e1)) { |
| auto *pe2 = cast<ParenExpr>(e2); |
| return areCertainlyEqualIndices(pe1->getSubExpr(), pe2->getSubExpr()); |
| } |
| |
| // Calls are identical if the callee and operands are identical and we know |
| // that the call is something that is "readnone". |
| if (auto *ae1 = dyn_cast<ApplyExpr>(e1)) { |
| auto *ae2 = cast<ApplyExpr>(e2); |
| return areCertainlyEqualIndices(ae1->getFn(), ae2->getFn()) && |
| areCertainlyEqualIndices(ae1->getArg(), ae2->getArg()) && |
| isReadNoneFunction(ae1->getFn()); |
| } |
| |
| // TypeExpr's that produce the same metatype type are identical. |
| if (isa<TypeExpr>(e1)) |
| return true; |
| |
| if (auto *dre1 = dyn_cast<DeclRefExpr>(e1)) { |
| auto *dre2 = cast<DeclRefExpr>(e2); |
| return dre1->getDecl() == dre2->getDecl() && |
| dre1->getType()->isEqual(dre2->getType()); |
| } |
| |
| // Compare a variety of literals. |
| if (auto *il1 = dyn_cast<IntegerLiteralExpr>(e1)) { |
| const auto &val1 = il1->getValue(); |
| const auto &val2 = cast<IntegerLiteralExpr>(e2)->getValue(); |
| // If the integers are arbitrary-precision, their bit-widths may differ, |
| // but only if they represent different values. |
| return val1.getBitWidth() == val2.getBitWidth() && val1 == val2; |
| } |
| if (auto *il1 = dyn_cast<FloatLiteralExpr>(e1)) |
| return il1->getValue().bitwiseIsEqual( |
| cast<FloatLiteralExpr>(e2)->getValue()); |
| if (auto *bl1 = dyn_cast<BooleanLiteralExpr>(e1)) |
| return bl1->getValue() == cast<BooleanLiteralExpr>(e2)->getValue(); |
| if (auto *sl1 = dyn_cast<StringLiteralExpr>(e1)) |
| return sl1->getValue() == cast<StringLiteralExpr>(e2)->getValue(); |
| |
| // Compare tuple expressions. |
| if (auto *te1 = dyn_cast<TupleExpr>(e1)) { |
| auto *te2 = cast<TupleExpr>(e2); |
| |
| // Easy checks: # of elements, trailing closures, element names. |
| if (te1->getNumElements() != te2->getNumElements() || |
| te1->hasTrailingClosure() != te2->hasTrailingClosure() || |
| te1->getElementNames() != te2->getElementNames()) { |
| return false; |
| } |
| |
| for (unsigned i = 0, n = te1->getNumElements(); i != n; ++i) { |
| if (!areCertainlyEqualIndices(te1->getElement(i), te2->getElement(i))) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Otherwise, we have no idea if they are identical. |
| return false; |
| } |
| |
| static LValueOptions getBaseOptions(LValueOptions options, |
| AccessStrategy strategy) { |
| return (strategy.getKind() == AccessStrategy::Storage |
| ? options.forProjectedBaseLValue() |
| : options.forComputedBaseLValue()); |
| } |
| |
| static ArgumentSource emitBaseValueForAccessor(SILGenFunction &SGF, |
| SILLocation loc, LValue &&dest, |
| CanType baseFormalType, |
| SILDeclRef accessor); |
| |
| static SGFAccessKind getBaseAccessKind(SILGenModule &SGM, |
| AbstractStorageDecl *member, |
| SGFAccessKind accessKind, |
| AccessStrategy strategy, |
| CanType baseFormalType); |
| |
| namespace { |
| /// A helper class for implementing components that involve accessing |
| /// storage. |
| template <class Base> |
| class AccessComponent : public Base { |
| protected: |
| // The VarDecl or SubscriptDecl being get/set. |
| AbstractStorageDecl *Storage; |
| |
| /// The subscript index expression. Useless |
| Expr *IndexExprForDiagnostics; |
| PreparedArguments Indices; |
| |
| /// AST type of the base expression, in case the accessor call |
| /// requires re-abstraction. |
| CanType BaseFormalType; |
| |
| struct AccessorArgs { |
| ArgumentSource base; |
| PreparedArguments Indices; |
| }; |
| |
| /// Returns a tuple of RValues holding the accessor value, base (retained if |
| /// necessary), and subscript arguments, in that order. |
| AccessorArgs |
| prepareAccessorArgs(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SILDeclRef accessor) && |
| { |
| AccessorArgs result; |
| if (base) { |
| // Borrow the base, because we may need it again to invoke other |
| // accessors. |
| result.base = SGF.prepareAccessorBaseArg(loc, |
| base.formalAccessBorrow(SGF, loc), |
| BaseFormalType, |
| accessor); |
| } |
| |
| if (!Indices.isNull()) |
| result.Indices = std::move(Indices); |
| |
| return result; |
| } |
| |
| AccessComponent(PathComponent::KindTy kind, |
| AbstractStorageDecl *storage, |
| CanType baseFormalType, |
| LValueTypeData typeData, |
| Expr *indexExprForDiagnostics, |
| PreparedArguments &&indices) |
| : Base(typeData, kind), Storage(storage), |
| IndexExprForDiagnostics(indexExprForDiagnostics), |
| Indices(std::move(indices)), |
| BaseFormalType(baseFormalType) |
| { |
| } |
| |
| AccessComponent(const AccessComponent &copied, |
| SILGenFunction &SGF, |
| SILLocation loc) |
| : Base(copied.getTypeData(), copied.getKind()), |
| Storage(copied.Storage), |
| IndexExprForDiagnostics(copied.IndexExprForDiagnostics), |
| Indices(copied.Indices.copy(SGF, loc)) , |
| BaseFormalType(copied.BaseFormalType) {} |
| |
| bool doesAccessorMutateSelf(SILGenFunction &SGF, |
| SILDeclRef accessor) const { |
| auto accessorSelf = SGF.SGM.Types.getConstantSelfParameter(accessor); |
| return accessorSelf.getType() && accessorSelf.isIndirectMutating(); |
| } |
| |
| void printBase(raw_ostream &OS, unsigned indent, StringRef name) const { |
| OS.indent(indent) << name << "(" << Storage->getBaseName() << ")"; |
| if (IndexExprForDiagnostics) { |
| OS << " subscript_index:\n"; |
| IndexExprForDiagnostics->dump(OS, 2); |
| } |
| OS << '\n'; |
| } |
| }; |
| |
| /// A helper class for implementing a component that involves |
| /// calling accessors. |
| template <class Base> |
| class AccessorBasedComponent : public AccessComponent<Base> { |
| using super = AccessComponent<Base>; |
| |
| protected: |
| SILDeclRef Accessor; |
| bool IsSuper; |
| bool IsDirectAccessorUse; |
| bool IsOnSelfParameter; |
| SubstitutionMap Substitutions; |
| |
| public: |
| AccessorBasedComponent(PathComponent::KindTy kind, |
| AbstractStorageDecl *decl, SILDeclRef accessor, |
| bool isSuper, bool isDirectAccessorUse, |
| SubstitutionMap substitutions, |
| CanType baseFormalType, LValueTypeData typeData, |
| Expr *indexExprForDiagnostics, |
| PreparedArguments &&indices, |
| bool isOnSelfParameter = false) |
| : super(kind, decl, baseFormalType, typeData, indexExprForDiagnostics, |
| std::move(indices)), |
| Accessor(accessor), IsSuper(isSuper), |
| IsDirectAccessorUse(isDirectAccessorUse), |
| IsOnSelfParameter(isOnSelfParameter), Substitutions(substitutions) {} |
| |
| AccessorBasedComponent(const AccessorBasedComponent &copied, |
| SILGenFunction &SGF, |
| SILLocation loc) |
| : super(copied, SGF, loc), |
| Accessor(copied.Accessor), |
| IsSuper(copied.IsSuper), |
| IsDirectAccessorUse(copied.IsDirectAccessorUse), |
| IsOnSelfParameter(copied.IsOnSelfParameter), |
| Substitutions(copied.Substitutions) {} |
| |
| AccessorDecl *getAccessorDecl() const { |
| return cast<AccessorDecl>(Accessor.getFuncDecl()); |
| } |
| }; |
| |
| class GetterSetterComponent |
| : public AccessorBasedComponent<LogicalPathComponent> { |
| public: |
| |
| GetterSetterComponent(AbstractStorageDecl *decl, |
| SILDeclRef accessor, |
| bool isSuper, bool isDirectAccessorUse, |
| SubstitutionMap substitutions, |
| CanType baseFormalType, |
| LValueTypeData typeData, |
| Expr *subscriptIndexExpr, |
| PreparedArguments &&indices, |
| bool isOnSelfParameter) |
| : AccessorBasedComponent(GetterSetterKind, decl, accessor, isSuper, |
| isDirectAccessorUse, substitutions, |
| baseFormalType, typeData, subscriptIndexExpr, |
| std::move(indices), isOnSelfParameter) |
| { |
| assert(getAccessorDecl()->isGetterOrSetter()); |
| } |
| |
| GetterSetterComponent(const GetterSetterComponent &copied, |
| SILGenFunction &SGF, |
| SILLocation loc) |
| : AccessorBasedComponent(copied, SGF, loc) |
| { |
| } |
| |
| void emitAssignWithSetter(SILGenFunction &SGF, SILLocation loc, |
| LValue &&dest, ArgumentSource &&value) { |
| assert(getAccessorDecl()->isSetter()); |
| SILDeclRef setter = Accessor; |
| |
| // Pull everything out of this that we'll need, because we're |
| // about to modify the LValue and delete this component. |
| auto subs = this->Substitutions; |
| bool isSuper = this->IsSuper; |
| bool isDirectAccessorUse = this->IsDirectAccessorUse; |
| auto indices = std::move(this->Indices); |
| auto baseFormalType = this->BaseFormalType; |
| bool isOnSelfParameter = this->IsOnSelfParameter; |
| |
| // Drop this component from the l-value. |
| dest.dropLastComponent(*this); |
| |
| return emitAssignWithSetter(SGF, loc, std::move(dest), baseFormalType, |
| isSuper, setter, isDirectAccessorUse, subs, |
| std::move(indices), std::move(value), |
| isOnSelfParameter); |
| } |
| |
| static void emitAssignWithSetter(SILGenFunction &SGF, SILLocation loc, |
| LValue &&baseLV, CanType baseFormalType, |
| bool isSuper, SILDeclRef setter, |
| bool isDirectAccessorUse, |
| SubstitutionMap subs, |
| PreparedArguments &&indices, |
| ArgumentSource &&value, |
| bool isSelfParameter) { |
| ArgumentSource self = [&] { |
| if (!baseLV.isValid()) { |
| return ArgumentSource(); |
| } else if (computeSelfParam(cast<FuncDecl>(setter.getDecl())) |
| .getParameterFlags().isInOut()) { |
| return ArgumentSource(loc, std::move(baseLV)); |
| } else { |
| return emitBaseValueForAccessor(SGF, loc, std::move(baseLV), |
| baseFormalType, setter); |
| } |
| }(); |
| |
| return SGF.emitSetAccessor(loc, setter, subs, std::move(self), isSuper, |
| isDirectAccessorUse, std::move(indices), |
| std::move(value), isSelfParameter); |
| } |
| |
| void set(SILGenFunction &SGF, SILLocation loc, |
| ArgumentSource &&value, ManagedValue base) && override { |
| assert(getAccessorDecl()->isSetter()); |
| SILDeclRef setter = Accessor; |
| |
| FormalEvaluationScope scope(SGF); |
| // Pass in just the setter. |
| auto args = |
| std::move(*this).prepareAccessorArgs(SGF, loc, base, setter); |
| |
| return SGF.emitSetAccessor(loc, setter, Substitutions, |
| std::move(args.base), IsSuper, |
| IsDirectAccessorUse, std::move(args.Indices), |
| std::move(value), IsOnSelfParameter); |
| } |
| |
| ManagedValue project(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base) && override { |
| assert(isReadAccess(getAccessKind()) && |
| "shouldn't be using this path to call modify"); |
| return std::move(*this).LogicalPathComponent::project(SGF, loc, base); |
| } |
| |
| RValue get(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SGFContext c) && override { |
| assert(getAccessorDecl()->isGetter()); |
| SILDeclRef getter = Accessor; |
| |
| FormalEvaluationScope scope(SGF); |
| |
| auto args = |
| std::move(*this).prepareAccessorArgs(SGF, loc, base, getter); |
| |
| return SGF.emitGetAccessor( |
| loc, getter, Substitutions, std::move(args.base), IsSuper, |
| IsDirectAccessorUse, std::move(args.Indices), c, IsOnSelfParameter); |
| } |
| |
| std::unique_ptr<LogicalPathComponent> |
| clone(SILGenFunction &SGF, SILLocation loc) const override { |
| LogicalPathComponent *clone = new GetterSetterComponent(*this, SGF, loc); |
| return std::unique_ptr<LogicalPathComponent>(clone); |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| printBase(OS, indent, "GetterSetterComponent"); |
| } |
| |
| /// Compare 'this' lvalue and the 'rhs' lvalue (which is guaranteed to have |
| /// the same dynamic PathComponent type as the receiver) to see if they are |
| /// identical. If so, there is a conflicting writeback happening, so emit a |
| /// diagnostic. |
| Optional<AccessedStorage> getAccessedStorage() const override { |
| return AccessedStorage{Storage, IsSuper, |
| Indices.isNull() ? nullptr : &Indices, |
| IndexExprForDiagnostics }; |
| } |
| }; |
| |
| class MaterializeToTemporaryComponent final |
| : public AccessComponent<LogicalPathComponent> { |
| SubstitutionMap Substitutions; |
| AccessStrategy ReadStrategy; |
| AccessStrategy WriteStrategy; |
| LValueOptions Options; |
| bool IsSuper; |
| bool IsOnSelfParameter; |
| |
| public: |
| MaterializeToTemporaryComponent(AbstractStorageDecl *storage, |
| bool isSuper, SubstitutionMap subs, |
| LValueOptions options, |
| AccessStrategy readStrategy, |
| AccessStrategy writeStrategy, |
| CanType baseFormalType, |
| LValueTypeData typeData, |
| Expr *indexExprForDiagnostics, |
| PreparedArguments &&indices, |
| bool isOnSelfParameter) |
| : AccessComponent(MaterializeToTemporaryKind, storage, baseFormalType, |
| typeData, indexExprForDiagnostics, std::move(indices)), |
| Substitutions(subs), |
| ReadStrategy(readStrategy), WriteStrategy(writeStrategy), |
| Options(options), IsSuper(isSuper), IsOnSelfParameter(isOnSelfParameter) |
| {} |
| |
| |
| std::unique_ptr<LogicalPathComponent> |
| clone(SILGenFunction &SGF, SILLocation loc) const override { |
| PreparedArguments clonedIndices = Indices.copy(SGF, loc); |
| |
| LogicalPathComponent *clone = |
| new MaterializeToTemporaryComponent(Storage, IsSuper, Substitutions, |
| Options, |
| ReadStrategy, WriteStrategy, |
| BaseFormalType, getTypeData(), |
| IndexExprForDiagnostics, |
| std::move(clonedIndices), |
| IsOnSelfParameter); |
| return std::unique_ptr<LogicalPathComponent>(clone); |
| } |
| |
| RValue get(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SGFContext C) && override { |
| LValue lv = std::move(*this).prepareLValue(SGF, loc, base, |
| SGFAccessKind::OwnedObjectRead, |
| ReadStrategy); |
| return SGF.emitLoadOfLValue(loc, std::move(lv), C); |
| } |
| |
| void set(SILGenFunction &SGF, SILLocation loc, |
| ArgumentSource &&value, ManagedValue base) && override { |
| LValue lv = std::move(*this).prepareLValue(SGF, loc, base, |
| SGFAccessKind::Write, |
| WriteStrategy); |
| return SGF.emitAssignToLValue(loc, std::move(value), std::move(lv)); |
| } |
| |
| Optional<AccessedStorage> getAccessedStorage() const override { |
| return AccessedStorage{Storage, IsSuper, |
| Indices.isNull() ? nullptr : &Indices, |
| IndexExprForDiagnostics}; |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "MaterializeToTemporaryComponent"; |
| } |
| |
| private: |
| LValue prepareLValue(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SGFAccessKind accessKind, |
| AccessStrategy strategy) && { |
| LValue lv = [&] { |
| if (!base) return LValue(); |
| auto baseAccessKind = |
| getBaseAccessKind(SGF.SGM, Storage, accessKind, strategy, |
| BaseFormalType); |
| return LValue::forValue(baseAccessKind, base, BaseFormalType); |
| }(); |
| |
| if (auto subscript = dyn_cast<SubscriptDecl>(Storage)) { |
| lv.addMemberSubscriptComponent(SGF, loc, subscript, Substitutions, |
| Options, IsSuper, accessKind, strategy, |
| getSubstFormalType(), |
| std::move(Indices), |
| IndexExprForDiagnostics); |
| } else { |
| auto var = cast<VarDecl>(Storage); |
| if (base) { |
| lv.addMemberVarComponent(SGF, loc, var, Substitutions, Options, |
| IsSuper, accessKind, strategy, |
| getSubstFormalType()); |
| } else { |
| lv.addNonMemberVarComponent(SGF, loc, var, Substitutions, Options, |
| accessKind, strategy, |
| getSubstFormalType()); |
| } |
| } |
| |
| return lv; |
| } |
| }; |
| |
| /// A physical component which involves calling addressors. |
| class AddressorComponent |
| : public AccessorBasedComponent<PhysicalPathComponent> { |
| SILType SubstFieldType; |
| public: |
| AddressorComponent(AbstractStorageDecl *decl, SILDeclRef accessor, |
| bool isSuper, bool isDirectAccessorUse, |
| SubstitutionMap substitutions, |
| CanType baseFormalType, LValueTypeData typeData, |
| SILType substFieldType, |
| Expr *indexExprForDiagnostics, |
| PreparedArguments &&indices, bool isOnSelfParameter) |
| : AccessorBasedComponent(AddressorKind, decl, accessor, isSuper, |
| isDirectAccessorUse, substitutions, |
| baseFormalType, typeData, |
| indexExprForDiagnostics, std::move(indices), |
| isOnSelfParameter), |
| SubstFieldType(substFieldType) |
| { |
| assert(getAccessorDecl()->isAnyAddressor()); |
| } |
| |
| ManagedValue project(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base) && override { |
| assert(SGF.isInFormalEvaluationScope() && |
| "offsetting l-value for modification without writeback scope"); |
| |
| ManagedValue addr; |
| { |
| FormalEvaluationScope scope(SGF); |
| |
| auto args = |
| std::move(*this).prepareAccessorArgs(SGF, loc, base, Accessor); |
| addr = SGF.emitAddressorAccessor( |
| loc, Accessor, Substitutions, std::move(args.base), IsSuper, |
| IsDirectAccessorUse, std::move(args.Indices), SubstFieldType, |
| IsOnSelfParameter); |
| } |
| |
| // Enter an unsafe access scope for the access. |
| addr = enterAccessScope(SGF, loc, addr, getTypeData(), getAccessKind(), |
| SILAccessEnforcement::Unsafe); |
| |
| return addr; |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| printBase(OS, indent, "AddressorComponent"); |
| } |
| }; |
| |
| class EndApplyPseudoComponent : public WritebackPseudoComponent { |
| CleanupHandle EndApplyHandle; |
| AbstractStorageDecl *Storage; |
| bool IsSuper; |
| PreparedArguments PeekedIndices; |
| Expr *IndexExprForDiagnostics; |
| public: |
| EndApplyPseudoComponent(const LValueTypeData &typeData, |
| CleanupHandle endApplyHandle, |
| AbstractStorageDecl *storage, |
| bool isSuper, |
| PreparedArguments &&peekedIndices, |
| Expr *indexExprForDiagnostics) |
| : WritebackPseudoComponent(typeData), |
| EndApplyHandle(endApplyHandle), |
| Storage(storage), IsSuper(isSuper), |
| PeekedIndices(std::move(peekedIndices)), |
| IndexExprForDiagnostics(indexExprForDiagnostics) {} |
| |
| private: |
| void writeback(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, |
| MaterializedLValue materialized, |
| bool isFinal) override { |
| // Just let the cleanup get emitted normally if the writeback is for |
| // an unwind. |
| if (!isFinal) return; |
| |
| SGF.Cleanups.popAndEmitCleanup(EndApplyHandle, CleanupLocation::get(loc), |
| NotForUnwind); |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "EndApplyPseudoComponent"; |
| } |
| |
| Optional<AccessedStorage> getAccessedStorage() const override { |
| return AccessedStorage{Storage, IsSuper, |
| PeekedIndices.isNull() ? nullptr : &PeekedIndices, |
| IndexExprForDiagnostics}; |
| } |
| }; |
| } |
| |
| static void pushEndApplyWriteback(SILGenFunction &SGF, SILLocation loc, |
| CleanupHandle endApplyHandle, |
| LValueTypeData typeData, |
| ManagedValue base = ManagedValue(), |
| AbstractStorageDecl *storage = nullptr, |
| bool isSuper = false, |
| PreparedArguments &&indices |
| = PreparedArguments(), |
| Expr *indexExprForDiagnostics = nullptr) { |
| std::unique_ptr<LogicalPathComponent> |
| component(new EndApplyPseudoComponent(typeData, endApplyHandle, |
| storage, isSuper, std::move(indices), |
| indexExprForDiagnostics)); |
| pushWriteback(SGF, loc, std::move(component), /*for diagnostics*/ base, |
| MaterializedLValue()); |
| } |
| |
| namespace { |
| |
| /// A physical component which involves calling coroutine accessors. |
| class CoroutineAccessorComponent |
| : public AccessorBasedComponent<PhysicalPathComponent> { |
| public: |
| CoroutineAccessorComponent(AbstractStorageDecl *decl, SILDeclRef accessor, |
| bool isSuper, bool isDirectAccessorUse, |
| SubstitutionMap substitutions, |
| CanType baseFormalType, LValueTypeData typeData, |
| Expr *indexExprForDiagnostics, |
| PreparedArguments &&indices, |
| bool isOnSelfParameter) |
| : AccessorBasedComponent( |
| CoroutineAccessorKind, decl, accessor, isSuper, |
| isDirectAccessorUse, substitutions, baseFormalType, typeData, |
| indexExprForDiagnostics, std::move(indices), isOnSelfParameter) {} |
| |
| using AccessorBasedComponent::AccessorBasedComponent; |
| |
| ManagedValue project(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base) && override { |
| assert(SGF.isInFormalEvaluationScope() && |
| "offsetting l-value for modification without writeback scope"); |
| |
| ManagedValue result; |
| |
| auto args = |
| std::move(*this).prepareAccessorArgs(SGF, loc, base, Accessor); |
| auto peekedIndices = args.Indices.copyForDiagnostics(); |
| SmallVector<ManagedValue, 4> yields; |
| auto endApplyHandle = SGF.emitCoroutineAccessor( |
| loc, Accessor, Substitutions, std::move(args.base), IsSuper, |
| IsDirectAccessorUse, std::move(args.Indices), yields, |
| IsOnSelfParameter); |
| |
| // Push a writeback that ends the access. |
| pushEndApplyWriteback(SGF, loc, endApplyHandle, getTypeData(), |
| base, Storage, IsSuper, std::move(peekedIndices), |
| IndexExprForDiagnostics); |
| |
| auto decl = cast<AccessorDecl>(Accessor.getFuncDecl()); |
| |
| // 'modify' always returns an address of the right type. |
| if (decl->getAccessorKind() == AccessorKind::Modify) { |
| assert(yields.size() == 1); |
| return yields[0]; |
| } |
| |
| // 'read' returns a borrowed r-value, which might or might not be |
| // an address of the right type. |
| |
| // Use the yield value directly if it's the right type. |
| if (!getOrigFormalType().isTuple()) { |
| assert(yields.size() == 1); |
| auto value = yields[0]; |
| if (value.getType().isAddress() || |
| !isReadAccessResultAddress(getAccessKind())) |
| return value; |
| } |
| |
| // Otherwise, we need to make a temporary. |
| // TODO: build a scalar tuple if possible. |
| auto temporary = |
| SGF.emitTemporary(loc, SGF.getTypeLowering(getTypeOfRValue())); |
| auto yieldsAsArray = llvm::makeArrayRef(yields); |
| copyBorrowedYieldsIntoTemporary(SGF, loc, yieldsAsArray, |
| getOrigFormalType(), getSubstFormalType(), |
| temporary.get()); |
| assert(yieldsAsArray.empty() && "didn't claim all yields"); |
| return temporary->getManagedAddress(); |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| printBase(OS, indent, "CoroutineAccessorComponent"); |
| } |
| }; |
| } |
| |
| static ManagedValue |
| makeBaseConsumableMaterializedRValue(SILGenFunction &SGF, |
| SILLocation loc, ManagedValue base) { |
| if (base.isLValue()) { |
| auto tmp = SGF.emitTemporaryAllocation(loc, base.getType()); |
| SGF.B.createCopyAddr(loc, base.getValue(), tmp, |
| IsNotTake, IsInitialization); |
| return SGF.emitManagedBufferWithCleanup(tmp); |
| } |
| |
| bool isBorrowed = base.isPlusZeroRValueOrTrivial() |
| && !base.getType().isTrivial(SGF.F); |
| if (!base.getType().isAddress() || isBorrowed) { |
| auto tmp = SGF.emitTemporaryAllocation(loc, base.getType()); |
| if (isBorrowed) |
| base.copyInto(SGF, tmp, loc); |
| else |
| base.forwardInto(SGF, loc, tmp); |
| return SGF.emitManagedBufferWithCleanup(tmp); |
| } |
| |
| return base; |
| } |
| |
| static ManagedValue |
| emitUpcastToKeyPath(SILGenFunction &SGF, SILLocation loc, |
| KeyPathTypeKind typeKind, ManagedValue keyPath) { |
| if (typeKind == KPTK_KeyPath) return keyPath; |
| assert(typeKind == KPTK_WritableKeyPath || |
| typeKind == KPTK_ReferenceWritableKeyPath); |
| |
| auto derivedKeyPathTy = keyPath.getType().castTo<BoundGenericType>(); |
| auto baseKeyPathTy = |
| BoundGenericType::get(SGF.getASTContext().getKeyPathDecl(), |
| Type(), derivedKeyPathTy->getGenericArgs()) |
| ->getCanonicalType(); |
| return SGF.B.createUpcast(loc, keyPath, |
| SILType::getPrimitiveObjectType(baseKeyPathTy)); |
| } |
| |
| namespace { |
| class LogicalKeyPathApplicationComponent final |
| : public LogicalPathComponent { |
| KeyPathTypeKind TypeKind; |
| ManagedValue KeyPath; |
| Type BaseFormalType; |
| public: |
| LogicalKeyPathApplicationComponent(LValueTypeData typeData, |
| KeyPathTypeKind typeKind, |
| ManagedValue keyPath, |
| Type baseFormalType) |
| : LogicalPathComponent(typeData, LogicalKeyPathApplicationKind), |
| TypeKind(typeKind), KeyPath(keyPath), BaseFormalType(baseFormalType) { |
| assert(isReadAccess(getAccessKind()) || |
| typeKind == KPTK_WritableKeyPath || |
| typeKind == KPTK_ReferenceWritableKeyPath); |
| } |
| |
| RValue get(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SGFContext C) && override { |
| assert(isReadAccess(getAccessKind())); |
| FuncDecl *projectFn; |
| |
| auto keyPathValue = KeyPath; |
| |
| SmallVector<Type, 2> typeArgs; |
| typeArgs.push_back(BaseFormalType); |
| if (TypeKind == KPTK_AnyKeyPath) { |
| projectFn = SGF.getASTContext().getGetAtAnyKeyPath(); |
| } else if (TypeKind == KPTK_PartialKeyPath) { |
| projectFn = SGF.getASTContext().getGetAtPartialKeyPath(); |
| } else if (TypeKind == KPTK_KeyPath || |
| TypeKind == KPTK_WritableKeyPath || |
| TypeKind == KPTK_ReferenceWritableKeyPath) { |
| projectFn = SGF.getASTContext().getGetAtKeyPath(); |
| |
| auto keyPathTy = keyPathValue.getType().castTo<BoundGenericType>(); |
| assert(keyPathTy->getGenericArgs().size() == 2); |
| assert(keyPathTy->getGenericArgs()[0]->getCanonicalType() == |
| BaseFormalType->getCanonicalType()); |
| typeArgs.push_back(keyPathTy->getGenericArgs()[1]); |
| |
| keyPathValue = emitUpcastToKeyPath(SGF, loc, TypeKind, keyPathValue); |
| } else { |
| llvm_unreachable("bad key path kind for this component"); |
| } |
| |
| auto subs = SubstitutionMap::get(projectFn->getGenericSignature(), |
| ArrayRef<Type>(typeArgs), |
| ArrayRef<ProtocolConformanceRef>()); |
| |
| base = makeBaseConsumableMaterializedRValue(SGF, loc, base); |
| |
| return SGF.emitApplyOfLibraryIntrinsic(loc, projectFn, subs, |
| {base, keyPathValue}, C); |
| } |
| |
| void set(SILGenFunction &SGF, SILLocation loc, |
| ArgumentSource &&value, ManagedValue base) && override { |
| assert(!isReadAccess(getAccessKind())); |
| |
| auto keyPathValue = KeyPath; |
| FuncDecl *setFn; |
| if (TypeKind == KPTK_WritableKeyPath) { |
| setFn = SGF.getASTContext().getSetAtWritableKeyPath(); |
| assert(base.isLValue()); |
| } else if (TypeKind == KPTK_ReferenceWritableKeyPath) { |
| setFn = SGF.getASTContext().getSetAtReferenceWritableKeyPath(); |
| base = makeBaseConsumableMaterializedRValue(SGF, loc, base); |
| } else { |
| llvm_unreachable("bad writable type kind"); |
| } |
| |
| auto keyPathTy = keyPathValue.getType().castTo<BoundGenericType>(); |
| auto subs = SubstitutionMap::get(setFn->getGenericSignature(), |
| keyPathTy->getGenericArgs(), |
| ArrayRef<ProtocolConformanceRef>()); |
| |
| auto setValue = |
| std::move(value).getAsSingleValue(SGF, AbstractionPattern::getOpaque()); |
| if (!setValue.getType().isAddress()) { |
| setValue = setValue.materialize(SGF, loc); |
| } |
| |
| SGF.emitApplyOfLibraryIntrinsic(loc, setFn, subs, |
| {base, keyPathValue, setValue}, |
| SGFContext()); |
| } |
| |
| Optional<AccessedStorage> getAccessedStorage() const override { |
| return None; |
| } |
| |
| std::unique_ptr<LogicalPathComponent> |
| clone(SILGenFunction &SGF, SILLocation l) const override { |
| llvm_unreachable("can't be cloned"); |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "LogicalKeyPathApplicationComponent\n"; |
| } |
| }; |
| |
| /// A physical component which involves applying a key path. |
| class PhysicalKeyPathApplicationComponent final |
| : public PhysicalPathComponent { |
| KeyPathTypeKind TypeKind; |
| ManagedValue KeyPath; |
| public: |
| PhysicalKeyPathApplicationComponent(LValueTypeData typeData, |
| KeyPathTypeKind typeKind, |
| ManagedValue keyPath) |
| : PhysicalPathComponent(typeData, PhysicalKeyPathApplicationKind), |
| TypeKind(typeKind), KeyPath(keyPath) { |
| assert(typeKind == KPTK_KeyPath || |
| typeKind == KPTK_WritableKeyPath || |
| typeKind == KPTK_ReferenceWritableKeyPath); |
| assert(typeKind != KPTK_KeyPath || isReadAccess(getAccessKind())); |
| } |
| |
| ManagedValue project(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base) && override { |
| assert(SGF.isInFormalEvaluationScope() && |
| "offsetting l-value for modification without writeback scope"); |
| |
| bool isRead = isReadAccess(getAccessKind()); |
| |
| // Set up the base and key path values correctly. |
| auto keyPathValue = KeyPath; |
| if (isRead) { |
| keyPathValue = emitUpcastToKeyPath(SGF, loc, TypeKind, keyPathValue); |
| base = makeBaseConsumableMaterializedRValue(SGF, loc, base); |
| } else if (TypeKind == KPTK_WritableKeyPath) { |
| // nothing to do |
| } else if (TypeKind == KPTK_ReferenceWritableKeyPath) { |
| base = makeBaseConsumableMaterializedRValue(SGF, loc, base); |
| } else { |
| llvm_unreachable("bad combination"); |
| } |
| |
| SILFunction *projectFn = |
| SGF.SGM.getKeyPathProjectionCoroutine(isRead, TypeKind); |
| auto projectFnRef = SGF.B.createManagedFunctionRef(loc, projectFn); |
| auto projectFnType = projectFn->getLoweredFunctionType(); |
| |
| auto keyPathTy = keyPathValue.getType().castTo<BoundGenericType>(); |
| auto subs = SubstitutionMap::get(projectFnType->getGenericSignature(), |
| keyPathTy->getGenericArgs(), {}); |
| |
| auto substFnType = projectFnType->substGenericArgs(SGF.SGM.M, subs); |
| |
| // Perform the begin_apply. |
| SmallVector<ManagedValue, 1> yields; |
| auto cleanup = |
| SGF.emitBeginApply(loc, projectFnRef, subs, { base, keyPathValue }, |
| substFnType, ApplyOptions(), yields); |
| |
| // Push an operation to do the end_apply. |
| pushEndApplyWriteback(SGF, loc, cleanup, getTypeData()); |
| |
| assert(yields.size() == 1); |
| return yields[0]; |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "PhysicalKeyPathApplicationComponent\n"; |
| } |
| }; |
| } // end anonymous namespace |
| |
| RValue |
| TranslationPathComponent::get(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SGFContext c) && { |
| // Inline constructor. |
| RValue baseVal = [&]() -> RValue { |
| // If our base is an object, just put it into an RValue and return. |
| if (base.getType().isObject()) { |
| return RValue(SGF, loc, getSubstFormalType(), base); |
| } |
| |
| // Otherwise, load the value and put it into an RValue. |
| return RValue(SGF, loc, getSubstFormalType(), |
| SGF.emitLoad(loc, base.getValue(), |
| SGF.getTypeLowering(base.getType()), |
| SGFContext(), IsNotTake)); |
| }(); |
| |
| // Map the base value to its substituted representation. |
| return std::move(*this).translate(SGF, loc, std::move(baseVal), c); |
| } |
| |
| void TranslationPathComponent::set(SILGenFunction &SGF, SILLocation loc, |
| ArgumentSource &&valueSource, |
| ManagedValue base) && { |
| assert(base.getType().isAddress() && |
| "Only support setting bases that have addresses"); |
| RValue value = std::move(valueSource).getAsRValue(SGF); |
| |
| // Map the value to the original pattern. |
| RValue newValue = std::move(*this).untranslate(SGF, loc, std::move(value)); |
| |
| // Store to the base. |
| std::move(newValue).assignInto(SGF, loc, base.getValue()); |
| } |
| |
| namespace { |
| /// Remap an lvalue referencing a generic type to an lvalue of its |
| /// substituted type in a concrete context. |
| class OrigToSubstComponent : public TranslationPathComponent { |
| AbstractionPattern OrigType; |
| |
| public: |
| OrigToSubstComponent(const LValueTypeData &typeData, |
| AbstractionPattern origType) |
| : TranslationPathComponent(typeData, OrigToSubstKind), |
| OrigType(origType) |
| {} |
| |
| virtual bool isLoadingPure() const override { return true; } |
| |
| RValue untranslate(SILGenFunction &SGF, SILLocation loc, |
| RValue &&rv, SGFContext c) && override { |
| return SGF.emitSubstToOrigValue(loc, std::move(rv), OrigType, |
| getSubstFormalType(), c); |
| } |
| |
| RValue translate(SILGenFunction &SGF, SILLocation loc, |
| RValue &&rv, SGFContext c) && override { |
| return SGF.emitOrigToSubstValue(loc, std::move(rv), OrigType, |
| getSubstFormalType(), c); |
| } |
| |
| std::unique_ptr<LogicalPathComponent> |
| clone(SILGenFunction &SGF, SILLocation loc) const override { |
| LogicalPathComponent *clone |
| = new OrigToSubstComponent(getTypeData(), OrigType); |
| return std::unique_ptr<LogicalPathComponent>(clone); |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "OrigToSubstComponent(" |
| << getOrigFormalType() << ", " |
| << getSubstFormalType() << ", " |
| << getTypeOfRValue() << ")\n"; |
| } |
| }; |
| |
| /// Remap an lvalue referencing a concrete type to an lvalue of a |
| /// generically-reabstracted type. |
| class SubstToOrigComponent : public TranslationPathComponent { |
| public: |
| SubstToOrigComponent(const LValueTypeData &typeData) |
| : TranslationPathComponent(typeData, SubstToOrigKind) |
| {} |
| |
| virtual bool isLoadingPure() const override { return true; } |
| |
| RValue untranslate(SILGenFunction &SGF, SILLocation loc, |
| RValue &&rv, SGFContext c) && override { |
| return SGF.emitOrigToSubstValue(loc, std::move(rv), getOrigFormalType(), |
| getSubstFormalType(), c); |
| } |
| |
| RValue translate(SILGenFunction &SGF, SILLocation loc, |
| RValue &&rv, SGFContext c) && override { |
| return SGF.emitSubstToOrigValue(loc, std::move(rv), getOrigFormalType(), |
| getSubstFormalType(), c); |
| } |
| |
| std::unique_ptr<LogicalPathComponent> |
| clone(SILGenFunction &SGF, SILLocation loc) const override { |
| LogicalPathComponent *clone |
| = new SubstToOrigComponent(getTypeData()); |
| return std::unique_ptr<LogicalPathComponent>(clone); |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "SubstToOrigComponent(" |
| << getOrigFormalType() << ", " |
| << getSubstFormalType() << ", " |
| << getTypeOfRValue() << ")\n"; |
| } |
| }; |
| |
| /// Remap a weak value to Optional<T>*, or unowned pointer to T*. |
| class OwnershipComponent : public LogicalPathComponent { |
| public: |
| OwnershipComponent(LValueTypeData typeData) |
| : LogicalPathComponent(typeData, OwnershipKind) { |
| } |
| |
| virtual bool isLoadingPure() const override { return true; } |
| |
| Optional<AccessedStorage> getAccessedStorage() const override { |
| return None; |
| } |
| |
| RValue get(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SGFContext c) && override { |
| assert(base && "ownership component must not be root of lvalue path"); |
| |
| auto &TL = SGF.getTypeLowering(getTypeOfRValue()); |
| |
| ManagedValue result; |
| if (base.getType().isObject()) { |
| result = SGF.emitConversionToSemanticRValue(loc, base, TL); |
| } else { |
| result = SGF.emitLoad(loc, base.getValue(), TL, SGFContext(), |
| IsNotTake); |
| } |
| |
| return RValue(SGF, loc, getSubstFormalType(), result); |
| } |
| |
| void set(SILGenFunction &SGF, SILLocation loc, |
| ArgumentSource &&valueSource, ManagedValue base) && override { |
| assert(base && "ownership component must not be root of lvalue path"); |
| auto &TL = SGF.getTypeLowering(base.getType()); |
| |
| auto value = std::move(valueSource).getAsSingleValue(SGF).forward(SGF); |
| SGF.emitSemanticStore(loc, value, base.getValue(), TL, |
| IsNotInitialization); |
| } |
| |
| std::unique_ptr<LogicalPathComponent> |
| clone(SILGenFunction &SGF, SILLocation loc) const override { |
| LogicalPathComponent *clone = new OwnershipComponent(getTypeData()); |
| return std::unique_ptr<LogicalPathComponent>(clone); |
| } |
| |
| void dump(raw_ostream &OS, unsigned indent) const override { |
| OS.indent(indent) << "OwnershipComponent(...)\n"; |
| } |
| }; |
| } // end anonymous namespace |
| |
| LValue LValue::forValue(SGFAccessKind accessKind, ManagedValue value, |
| CanType substFormalType) { |
| if (value.getType().isObject()) { |
| LValueTypeData typeData = getValueTypeData(accessKind, substFormalType, |
| value.getValue()); |
| |
| LValue lv; |
| lv.add<ValueComponent>(value, None, typeData, /*isRValue=*/true); |
| return lv; |
| } else { |
| // Treat an address-only value as an lvalue we only read from. |
| if (!value.isLValue()) |
| value = ManagedValue::forLValue(value.getValue()); |
| return forAddress(accessKind, value, None, |
| AbstractionPattern(substFormalType), substFormalType); |
| } |
| } |
| |
| LValue LValue::forAddress(SGFAccessKind accessKind, ManagedValue address, |
| Optional<SILAccessEnforcement> enforcement, |
| AbstractionPattern origFormalType, |
| CanType substFormalType) { |
| assert(address.isLValue()); |
| LValueTypeData typeData = { |
| accessKind, origFormalType, substFormalType, |
| address.getType().getASTType() |
| }; |
| |
| LValue lv; |
| lv.add<ValueComponent>(address, enforcement, typeData); |
| return lv; |
| } |
| |
| void LValue::addMemberComponent(SILGenFunction &SGF, SILLocation loc, |
| AbstractStorageDecl *storage, |
| SubstitutionMap subs, |
| LValueOptions options, |
| bool isSuper, |
| SGFAccessKind accessKind, |
| AccessStrategy accessStrategy, |
| CanType formalRValueType, |
| PreparedArguments &&indices, |
| Expr *indexExprForDiagnostics) { |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| assert(indices.isNull()); |
| addMemberVarComponent(SGF, loc, var, subs, options, isSuper, |
| accessKind, accessStrategy, formalRValueType); |
| } else { |
| auto subscript = cast<SubscriptDecl>(storage); |
| addMemberSubscriptComponent(SGF, loc, subscript, subs, options, isSuper, |
| accessKind, accessStrategy, formalRValueType, |
| std::move(indices), indexExprForDiagnostics); |
| } |
| } |
| |
| void LValue::addOrigToSubstComponent(SILType loweredSubstType) { |
| loweredSubstType = loweredSubstType.getObjectType(); |
| assert(getTypeOfRValue() != loweredSubstType && |
| "reabstraction component is unnecessary!"); |
| |
| // Peephole away complementary reabstractions. |
| assert(!Path.empty() && "adding translation component to empty l-value"); |
| if (Path.back()->getKind() == PathComponent::SubstToOrigKind) { |
| // But only if the lowered type matches exactly. |
| if (Path[Path.size()-2]->getTypeOfRValue() == loweredSubstType) { |
| Path.pop_back(); |
| return; |
| } |
| // TODO: combine reabstractions; this doesn't matter all that much |
| // for most things, but it can be dramatically better for function |
| // reabstraction. |
| } |
| |
| auto substFormalType = getSubstFormalType(); |
| LValueTypeData typeData = { |
| getAccessKind(), |
| AbstractionPattern(substFormalType), |
| substFormalType, |
| loweredSubstType.getASTType() |
| }; |
| add<OrigToSubstComponent>(typeData, getOrigFormalType()); |
| } |
| |
| void LValue::addSubstToOrigComponent(AbstractionPattern origType, |
| SILType loweredSubstType) { |
| loweredSubstType = loweredSubstType.getObjectType(); |
| assert(getTypeOfRValue() != loweredSubstType && |
| "reabstraction component is unnecessary!"); |
| |
| // Peephole away complementary reabstractions. |
| assert(!Path.empty() && "adding translation component to empty l-value"); |
| if (Path.back()->getKind() == PathComponent::OrigToSubstKind) { |
| // But only if the lowered type matches exactly. |
| if (Path[Path.size()-2]->getTypeOfRValue() == loweredSubstType) { |
| Path.pop_back(); |
| return; |
| } |
| // TODO: combine reabstractions; this doesn't matter all that much |
| // for most things, but it can be dramatically better for function |
| // reabstraction. |
| } |
| |
| LValueTypeData typeData = { |
| getAccessKind(), |
| origType, |
| getSubstFormalType(), |
| loweredSubstType.getASTType() |
| }; |
| add<SubstToOrigComponent>(typeData); |
| } |
| |
| void LValue::dump() const { |
| dump(llvm::errs()); |
| } |
| |
| void LValue::dump(raw_ostream &OS, unsigned indent) const { |
| for (const auto &component : *this) { |
| component->dump(OS, indent); |
| } |
| } |
| |
| LValue SILGenFunction::emitLValue(Expr *e, SGFAccessKind accessKind, |
| LValueOptions options) { |
| // Some lvalue nodes (namely BindOptionalExprs) require immediate evaluation |
| // of their subexpression, so we must have a writeback scope open while |
| // building an lvalue. |
| assert(isInFormalEvaluationScope() && "must be in a formal evaluation scope"); |
| |
| LValue r = SILGenLValue(*this).visit(e, accessKind, options); |
| // If the final component has an abstraction change, introduce a |
| // reabstraction component. |
| auto substFormalType = r.getSubstFormalType(); |
| auto loweredSubstType = getLoweredType(substFormalType); |
| if (r.getTypeOfRValue() != loweredSubstType.getObjectType()) { |
| // Logical components always re-abstract back to the substituted |
| // type. |
| assert(r.isLastComponentPhysical()); |
| r.addOrigToSubstComponent(loweredSubstType); |
| } |
| return r; |
| } |
| |
| static LValue visitRecInOut(SILGenLValue &SGL, Expr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options, AbstractionPattern orig) { |
| auto lv = SGL.visit(e, accessKind, options); |
| // If necessary, handle reabstraction with a SubstToOrigComponent that handles |
| // writeback in the original representation. |
| if (orig.isValid()) { |
| auto &origTL = SGL.SGF.getTypeLowering(orig, e->getType()->getRValueType()); |
| if (lv.getTypeOfRValue() != origTL.getLoweredType().getObjectType()) |
| lv.addSubstToOrigComponent(orig, origTL.getLoweredType().getObjectType()); |
| } |
| |
| return lv; |
| } |
| |
| // Otherwise we have a non-lvalue type (references, values, metatypes, |
| // etc). These act as the root of a logical lvalue. |
| static ManagedValue visitRecNonInOutBase(SILGenLValue &SGL, Expr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options, |
| AbstractionPattern orig) { |
| auto &SGF = SGL.SGF; |
| |
| // For an rvalue base, apply the reabstraction (if any) eagerly, since |
| // there's no need for writeback. |
| if (orig.isValid()) { |
| return SGF.emitRValueAsOrig( |
| e, orig, SGF.getTypeLowering(orig, e->getType()->getRValueType())); |
| } |
| |
| // Ok, at this point we know that re-abstraction is not required. |
| |
| SGFContext ctx; |
| |
| if (auto *dre = dyn_cast<DeclRefExpr>(e)) { |
| // Any reference to "self" can be done at +0 so long as it is a direct |
| // access, since we know it is guaranteed. |
| // |
| // TODO: it would be great to factor this even lower into SILGen to the |
| // point where we can see that the parameter is +0 guaranteed. Note that |
| // this handles the case in initializers where there is actually a stack |
| // allocation for it as well. |
| if (isa<ParamDecl>(dre->getDecl()) && |
| dre->getDecl()->getFullName() == SGF.getASTContext().Id_self && |
| dre->getDecl()->isImplicit()) { |
| ctx = SGFContext::AllowGuaranteedPlusZero; |
| if (SGF.SelfInitDelegationState != SILGenFunction::NormalSelf) { |
| // This needs to be inlined since there is a Formal Evaluation Scope |
| // in emitRValueForDecl that causing any borrow for this LValue to be |
| // popped too soon. |
| auto *vd = cast<ParamDecl>(dre->getDecl()); |
| CanType formalRValueType = dre->getType()->getCanonicalType(); |
| ManagedValue selfLValue = |
| SGF.emitAddressOfLocalVarDecl(dre, vd, formalRValueType, |
| SGFAccessKind::OwnedObjectRead); |
| selfLValue = SGF.emitFormalEvaluationRValueForSelfInDelegationInit( |
| e, formalRValueType, |
| selfLValue.getLValueAddress(), ctx) |
| .getAsSingleValue(SGF, e); |
| |
| return selfLValue; |
| } |
| } |
| |
| if (auto *VD = dyn_cast<VarDecl>(dre->getDecl())) { |
| // All let values are guaranteed to be held alive across their lifetime, |
| // and won't change once initialized. Any loaded value is good for the |
| // duration of this expression evaluation. |
| if (VD->isLet()) { |
| ctx = SGFContext::AllowGuaranteedPlusZero; |
| } |
| } |
| } |
| |
| if (SGF.SGM.Types.isIndirectPlusZeroSelfParameter(e->getType())) { |
| ctx = SGFContext::AllowGuaranteedPlusZero; |
| } |
| |
| ManagedValue mv = SGF.emitRValueAsSingleValue(e, ctx); |
| if (mv.isPlusZeroRValueOrTrivial()) |
| return mv; |
| |
| // Any temporaries needed to materialize the lvalue must be destroyed when |
| // at the end of the lvalue's formal evaluation scope. |
| // e.g. for foo(self.bar) |
| // %self = load [copy] %ptr_self |
| // %rvalue = barGetter(%self) |
| // destroy_value %self // self must be released before calling foo. |
| // foo(%rvalue) |
| SILValue value = mv.forward(SGF); |
| return SGF.emitFormalAccessManagedRValueWithCleanup(CleanupLocation(e), |
| value); |
| } |
| |
| LValue SILGenLValue::visitRec(Expr *e, SGFAccessKind accessKind, |
| LValueOptions options, AbstractionPattern orig) { |
| // First see if we have an lvalue type. If we do, then quickly handle that and |
| // return. |
| if (e->getType()->is<LValueType>() || e->isSemanticallyInOutExpr()) { |
| return visitRecInOut(*this, e, accessKind, options, orig); |
| } |
| |
| // Otherwise we have a non-lvalue type (references, values, metatypes, |
| // etc). These act as the root of a logical lvalue. Compute the root value, |
| // wrap it in a ValueComponent, and return it for our caller. |
| ManagedValue rv = visitRecNonInOutBase(*this, e, accessKind, options, orig); |
| CanType formalType = getSubstFormalRValueType(e); |
| auto typeData = getValueTypeData(accessKind, formalType, rv.getValue()); |
| LValue lv; |
| lv.add<ValueComponent>(rv, None, typeData, /*isRValue=*/true); |
| return lv; |
| } |
| |
| LValue SILGenLValue::visitExpr(Expr *e, SGFAccessKind accessKind, |
| LValueOptions options) { |
| e->dump(llvm::errs()); |
| llvm::errs() << "\n"; |
| llvm_unreachable("unimplemented lvalue expr"); |
| } |
| |
| namespace { |
| /// A CRTP class for emitting accesses. |
| template <class Impl, class StorageType> |
| struct AccessEmitter { |
| SILGenFunction &SGF; |
| StorageType *Storage; |
| CanType FormalRValueType; |
| SGFAccessKind AccessKind; |
| |
| Impl &asImpl() { return static_cast<Impl&>(*this); } |
| |
| AccessEmitter(SILGenFunction &SGF, StorageType *storage, |
| SGFAccessKind accessKind, CanType formalRValueType) |
| : SGF(SGF), Storage(storage), FormalRValueType(formalRValueType), |
| AccessKind(accessKind) {} |
| |
| void emitUsingStrategy(AccessStrategy strategy) { |
| switch (strategy.getKind()) { |
| case AccessStrategy::Storage: { |
| auto typeData = getPhysicalStorageTypeData(SGF.SGM, AccessKind, Storage, |
| FormalRValueType); |
| return asImpl().emitUsingStorage(typeData); |
| } |
| |
| case AccessStrategy::DirectToAccessor: |
| return asImpl().emitUsingAccessor(strategy.getAccessor(), true); |
| |
| case AccessStrategy::DispatchToAccessor: |
| return asImpl().emitUsingAccessor(strategy.getAccessor(), false); |
| |
| case AccessStrategy::MaterializeToTemporary: { |
| auto typeData = |
| getLogicalStorageTypeData(SGF.SGM, AccessKind, FormalRValueType); |
| return asImpl().emitUsingMaterialization(strategy.getReadStrategy(), |
| strategy.getWriteStrategy(), |
| typeData); |
| } |
| } |
| llvm_unreachable("unknown kind"); |
| } |
| |
| void emitUsingAccessor(AccessorKind accessorKind, bool isDirect) { |
| auto accessor = |
| SGF.SGM.getAccessorDeclRef(Storage->getAccessor(accessorKind)); |
| |
| switch (accessorKind) { |
| case AccessorKind::Get: |
| case AccessorKind::Set: { |
| auto typeData = |
| getLogicalStorageTypeData(SGF.SGM, AccessKind, FormalRValueType); |
| return asImpl().emitUsingGetterSetter(accessor, isDirect, typeData); |
| } |
| |
| case AccessorKind::Address: |
| case AccessorKind::MutableAddress: { |
| auto typeData = getPhysicalStorageTypeData(SGF.SGM, AccessKind, Storage, |
| FormalRValueType); |
| return asImpl().emitUsingAddressor(accessor, isDirect, typeData); |
| } |
| |
| case AccessorKind::Read: |
| case AccessorKind::Modify: { |
| auto typeData = getPhysicalStorageTypeData(SGF.SGM, AccessKind, Storage, |
| FormalRValueType); |
| return asImpl().emitUsingCoroutineAccessor(accessor, isDirect, |
| typeData); |
| } |
| |
| case AccessorKind::WillSet: |
| case AccessorKind::DidSet: |
| llvm_unreachable("cannot use accessor directly to perform an access"); |
| } |
| llvm_unreachable("bad kind"); |
| } |
| }; |
| } |
| |
| SubstitutionMap |
| SILGenModule::getNonMemberVarDeclSubstitutions(VarDecl *var) { |
| auto *dc = var->getDeclContext(); |
| if (auto *genericEnv = dc->getGenericEnvironmentOfContext()) |
| return genericEnv->getForwardingSubstitutionMap(); |
| |
| return SubstitutionMap(); |
| } |
| |
| static LValue emitLValueForNonMemberVarDecl(SILGenFunction &SGF, |
| SILLocation loc, VarDecl *var, |
| CanType formalRValueType, |
| SGFAccessKind accessKind, |
| LValueOptions options, |
| AccessSemantics semantics) { |
| LValue lv; |
| |
| auto access = getFormalAccessKind(accessKind); |
| auto strategy = var->getAccessStrategy(semantics, access, |
| SGF.SGM.M.getSwiftModule(), |
| SGF.F.getResilienceExpansion()); |
| |
| lv.addNonMemberVarComponent(SGF, loc, var, /*be lazy*/ None, |
| options, accessKind, strategy, formalRValueType); |
| |
| return lv; |
| } |
| |
| void LValue::addNonMemberVarComponent(SILGenFunction &SGF, SILLocation loc, |
| VarDecl *var, |
| Optional<SubstitutionMap> subs, |
| LValueOptions options, |
| SGFAccessKind accessKind, |
| AccessStrategy strategy, |
| CanType formalRValueType) { |
| struct NonMemberVarAccessEmitter : |
| AccessEmitter<NonMemberVarAccessEmitter, VarDecl> { |
| LValue &LV; |
| SILLocation Loc; |
| Optional<SubstitutionMap> Subs; |
| LValueOptions Options; |
| |
| SubstitutionMap getSubs() { |
| if (Subs) return *Subs; |
| Subs = SGF.SGM.getNonMemberVarDeclSubstitutions(Storage); |
| return *Subs; |
| } |
| |
| NonMemberVarAccessEmitter(SILGenFunction &SGF, SILLocation loc, |
| VarDecl *var, Optional<SubstitutionMap> subs, |
| SGFAccessKind accessKind, |
| CanType formalRValueType, |
| LValueOptions options, LValue &lv) |
| : AccessEmitter(SGF, var, accessKind, formalRValueType), |
| LV(lv), Loc(loc), Subs(subs), Options(options) {} |
| |
| void emitUsingAddressor(SILDeclRef addressor, bool isDirect, |
| LValueTypeData typeData) { |
| SILType storageType = |
| SGF.getLoweredType(Storage->getType()).getAddressType(); |
| LV.add<AddressorComponent>(Storage, addressor, |
| /*isSuper=*/false, isDirect, getSubs(), |
| CanType(), typeData, storageType, nullptr, |
| PreparedArguments(), |
| /* isOnSelfParameter */ false); |
| } |
| |
| void emitUsingCoroutineAccessor(SILDeclRef accessor, bool isDirect, |
| LValueTypeData typeData) { |
| LV.add<CoroutineAccessorComponent>( |
| Storage, accessor, |
| /*isSuper*/ false, isDirect, getSubs(), CanType(), typeData, nullptr, |
| PreparedArguments(), /*isOnSelfParameter*/ false); |
| } |
| |
| void emitUsingGetterSetter(SILDeclRef accessor, bool isDirect, |
| LValueTypeData typeData) { |
| LV.add<GetterSetterComponent>( |
| Storage, accessor, |
| /*isSuper=*/false, isDirect, getSubs(), CanType(), typeData, nullptr, |
| PreparedArguments(), /* isOnSelfParameter */ false); |
| } |
| |
| void emitUsingMaterialization(AccessStrategy readStrategy, |
| AccessStrategy writeStrategy, |
| LValueTypeData typeData) { |
| LV.add<MaterializeToTemporaryComponent>( |
| Storage, /*super*/ false, getSubs(), Options, readStrategy, |
| writeStrategy, |
| /*base type*/ CanType(), typeData, nullptr, PreparedArguments(), |
| /* isOnSelfParameter */ false); |
| } |
| |
| void emitUsingStorage(LValueTypeData typeData) { |
| // If it's a physical value (e.g. a local variable in memory), push its |
| // address. |
| |
| // Check for a local (possibly captured) variable. |
| auto address = SGF.maybeEmitValueOfLocalVarDecl(Storage); |
| |
| // The only other case that should get here is a global variable. |
| if (!address) { |
| address = SGF.emitGlobalVariableRef(Loc, Storage); |
| } |
| assert(address.isLValue() && |
| "physical lvalue decl ref must evaluate to an address"); |
| |
| Optional<SILAccessEnforcement> enforcement; |
| if (!Storage->isLet()) { |
| if (Options.IsNonAccessing) { |
| enforcement = None; |
| } else if (Storage->getDeclContext()->isLocalContext()) { |
| enforcement = SGF.getUnknownEnforcement(Storage); |
| } else if (Storage->getDeclContext()->isModuleScopeContext()) { |
| enforcement = SGF.getDynamicEnforcement(Storage); |
| } else { |
| assert(Storage->getDeclContext()->isTypeContext() && |
| !Storage->isInstanceMember()); |
| enforcement = SGF.getDynamicEnforcement(Storage); |
| } |
| } |
| |
| LV.add<ValueComponent>(address, enforcement, typeData); |
| |
| if (address.getType().is<ReferenceStorageType>()) |
| LV.add<OwnershipComponent>(typeData); |
| } |
| } emitter(SGF, loc, var, subs, accessKind, formalRValueType, options, *this); |
| |
| emitter.emitUsingStrategy(strategy); |
| } |
| |
| ManagedValue |
| SILGenFunction::maybeEmitValueOfLocalVarDecl(VarDecl *var) { |
| // For local decls, use the address we allocated or the value if we have it. |
| auto It = VarLocs.find(var); |
| if (It != VarLocs.end()) { |
| // If this has an address, return it. By-value let's have no address. |
| SILValue ptr = It->second.value; |
| if (ptr->getType().isAddress()) |
| return ManagedValue::forLValue(ptr); |
| |
| // Otherwise, it is an RValue let. Uses of it are borrows, but we don't |
| // want to proactively emit a borrow here. |
| // TODO: integrate this with how callers want these values so we can do |
| // something more semantic than just forUnmanaged. |
| return ManagedValue::forUnmanaged(ptr); |
| } |
| |
| // Otherwise, it's non-local or not stored. |
| return ManagedValue(); |
| } |
| |
| ManagedValue |
| SILGenFunction::emitAddressOfLocalVarDecl(SILLocation loc, VarDecl *var, |
| CanType formalRValueType, |
| SGFAccessKind accessKind) { |
| assert(var->getDeclContext()->isLocalContext()); |
| assert(var->getImplInfo().isSimpleStored()); |
| auto address = maybeEmitValueOfLocalVarDecl(var); |
| assert(address); |
| assert(address.isLValue()); |
| return address; |
| } |
| |
| RValue SILGenFunction::emitRValueForNonMemberVarDecl(SILLocation loc, |
| VarDecl *var, |
| CanType formalRValueType, |
| AccessSemantics semantics, |
| SGFContext C) { |
| // Any writebacks for this access are tightly scoped. |
| FormalEvaluationScope scope(*this); |
| |
| auto localValue = maybeEmitValueOfLocalVarDecl(var); |
| |
| // If this VarDecl is represented as an address, emit it as an lvalue, then |
| // perform a load to get the rvalue. |
| if (localValue && localValue.isLValue()) { |
| bool guaranteedValid = false; |
| IsTake_t shouldTake = IsNotTake; |
| |
| // We should only end up in this path for local and global variables, |
| // i.e. ones whose lifetime is assured for the duration of the evaluation. |
| // Therefore, if the variable is a constant, the value is guaranteed |
| // valid as well. |
| if (var->isLet()) |
| guaranteedValid = true; |
| |
| // Protect the lvalue read with access markers. The !is<LValueType> assert |
| // above ensures that the "LValue" is actually immutable, so we use an |
| // unenforced access marker. |
| SILValue destAddr = localValue.getLValueAddress(); |
| SILValue accessAddr = UnenforcedFormalAccess::enter(*this, loc, destAddr, |
| SILAccessKind::Read); |
| auto propagateRValuePastAccess = [&](RValue &&rvalue) { |
| // Check if a new begin_access was emitted and returned as the |
| // RValue. This means that the load did not actually load. If so, then |
| // fix the rvalue to begin_access operand. The end_access cleanup |
| // doesn't change. FIXME: this can't happen with sil-opaque-values. |
| if (accessAddr != destAddr && rvalue.isComplete() |
| && rvalue.isPlusZero(*this) && !isa<TupleType>(rvalue.getType())) { |
| auto mv = std::move(rvalue).getScalarValue(); |
| if (mv.getValue() == accessAddr) |
| mv = std::move(mv).transform( |
| cast<BeginAccessInst>(accessAddr)->getOperand()); |
| return RValue(*this, loc, formalRValueType, mv); |
| } |
| return std::move(rvalue); |
| }; |
| // If we have self, see if we are in an 'init' delegation sequence. If so, |
| // call out to the special delegation init routine. Otherwise, use the |
| // normal RValue emission logic. |
| if (var->getName() == getASTContext().Id_self && |
| SelfInitDelegationState != NormalSelf) { |
| auto rvalue = |
| emitRValueForSelfInDelegationInit(loc, formalRValueType, accessAddr, C); |
| return propagateRValuePastAccess(std::move(rvalue)); |
| } |
| |
| // Avoid computing an abstraction pattern for local variables. |
| // This is a slight compile-time optimization, but more importantly |
| // it avoids problems where locals don't always have interface types. |
| if (var->getDeclContext()->isLocalContext()) { |
| auto &rvalueTL = getTypeLowering(formalRValueType); |
| auto rvalue = RValue(*this, loc, formalRValueType, |
| emitLoad(loc, accessAddr, rvalueTL, |
| C, shouldTake, guaranteedValid)); |
| |
| return propagateRValuePastAccess(std::move(rvalue)); |
| } |
| |
| // Otherwise, do the full thing where we potentially bridge and |
| // reabstract the declaration. |
| auto origFormalType = SGM.Types.getAbstractionPattern(var); |
| auto rvalue = RValue(*this, loc, formalRValueType, |
| emitLoad(loc, accessAddr, origFormalType, |
| formalRValueType, |
| getTypeLowering(formalRValueType), |
| C, shouldTake, guaranteedValid)); |
| return propagateRValuePastAccess(std::move(rvalue)); |
| } |
| |
| // For local decls, use the address we allocated or the value if we have it. |
| if (localValue) { |
| // Mutable lvalue and address-only 'let's are LValues. |
| assert(!localValue.getType().isAddress() && |
| "LValue cases should be handled above"); |
| |
| SILValue Scalar = localValue.getUnmanagedValue(); |
| |
| // For weak and unowned types, convert the reference to the right |
| // pointer. |
| if (Scalar->getType().is<ReferenceStorageType>()) { |
| Scalar = emitConversionToSemanticRValue(loc, Scalar, |
| getTypeLowering(formalRValueType)); |
| // emitConversionToSemanticRValue always produces a +1 strong result. |
| return RValue(*this, loc, formalRValueType, |
| emitManagedRValueWithCleanup(Scalar)); |
| } |
| |
| // This is a let, so we can make guarantees, so begin the borrow scope. |
| ManagedValue Result = emitManagedBeginBorrow(loc, Scalar); |
| |
| // If the client can't handle a +0 result, retain it to get a +1. |
| // This is a 'let', so we can make guarantees. |
| return RValue(*this, loc, formalRValueType, |
| C.isGuaranteedPlusZeroOk() |
| ? Result : Result.copyUnmanaged(*this, loc)); |
| } |
| |
| LValue lv = emitLValueForNonMemberVarDecl(*this, loc, var, formalRValueType, |
| SGFAccessKind::OwnedObjectRead, |
| LValueOptions(), semantics); |
| return emitLoadOfLValue(loc, std::move(lv), C); |
| } |
| |
| LValue SILGenLValue::visitDiscardAssignmentExpr(DiscardAssignmentExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options) { |
| LValueTypeData typeData = getValueTypeData(SGF, accessKind, e); |
| |
| SILValue address = SGF.emitTemporaryAllocation(e, |
| SILType::getPrimitiveObjectType( |
| typeData.TypeOfRValue)); |
| address = SGF.B.createMarkUninitialized(e, address, |
| MarkUninitializedInst::Var); |
| LValue lv; |
| lv.add<ValueComponent>(SGF.emitManagedBufferWithCleanup(address), |
| None, typeData); |
| return lv; |
| } |
| |
| |
| LValue SILGenLValue::visitDeclRefExpr(DeclRefExpr *e, SGFAccessKind accessKind, |
| LValueOptions options) { |
| // The only non-member decl that can be an lvalue is VarDecl. |
| return emitLValueForNonMemberVarDecl(SGF, e, cast<VarDecl>(e->getDecl()), |
| getSubstFormalRValueType(e), |
| accessKind, options, |
| e->getAccessSemantics()); |
| } |
| |
| LValue SILGenLValue::visitOpaqueValueExpr(OpaqueValueExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options) { |
| // Handle an opaque lvalue that refers to an opened existential. |
| auto known = SGF.OpaqueValueExprs.find(e); |
| if (known != SGF.OpaqueValueExprs.end()) { |
| // Dig the open-existential expression out of the list. |
| OpenExistentialExpr *opened = known->second; |
| SGF.OpaqueValueExprs.erase(known); |
| |
| // Do formal evaluation of the underlying existential lvalue. |
| auto lv = visitRec(opened->getExistentialValue(), accessKind, options); |
| lv = SGF.emitOpenExistentialLValue( |
| opened, std::move(lv), |
| CanArchetypeType(opened->getOpenedArchetype()), |
| e->getType()->getWithoutSpecifierType()->getCanonicalType(), |
| accessKind); |
| return lv; |
| } |
| |
| assert(SGF.OpaqueValues.count(e) && "Didn't bind OpaqueValueExpr"); |
| auto value = SGF.OpaqueValues[e]; |
| |
| RegularLocation loc(e); |
| LValue lv; |
| lv.add<ValueComponent>(value.formalAccessBorrow(SGF, loc), None, |
| getValueTypeData(SGF, accessKind, e)); |
| return lv; |
| } |
| |
| LValue SILGenLValue::visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options) { |
| SGF.emitIgnoredExpr(e->getLHS()); |
| return visitRec(e->getRHS(), accessKind, options); |
| } |
| |
| static SGFAccessKind getBaseAccessKindForAccessor(SILGenModule &SGM, |
| AccessorDecl *accessor, |
| CanType baseFormalType) { |
| if (accessor->isMutating()) { |
| return SGFAccessKind::ReadWrite; |
| } else if (SGM.shouldEmitSelfAsRValue(accessor, baseFormalType)) { |
| return SGM.isNonMutatingSelfIndirect(SGM.getAccessorDeclRef(accessor)) |
| ? SGFAccessKind::OwnedAddressRead |
| : SGFAccessKind::OwnedObjectRead; |
| } else { |
| return SGM.isNonMutatingSelfIndirect(SGM.getAccessorDeclRef(accessor)) |
| ? SGFAccessKind::BorrowedAddressRead |
| : SGFAccessKind::BorrowedObjectRead; |
| } |
| } |
| |
| static SGFAccessKind getBaseAccessKindForStorage(SGFAccessKind accessKind) { |
| // Assume that the member only partially projects the enclosing value, |
| // so a write to the member is a read/write of the base. |
| return (accessKind == SGFAccessKind::Write |
| ? SGFAccessKind::ReadWrite : accessKind); |
| } |
| |
| /// Return the appropriate access kind for the base l-value of a |
| /// particular member, which is being accessed in a particular way. |
| static SGFAccessKind getBaseAccessKind(SILGenModule &SGM, |
| AbstractStorageDecl *member, |
| SGFAccessKind accessKind, |
| AccessStrategy strategy, |
| CanType baseFormalType) { |
| switch (strategy.getKind()) { |
| case AccessStrategy::Storage: |
| return getBaseAccessKindForStorage(accessKind); |
| |
| case AccessStrategy::MaterializeToTemporary: { |
| assert(accessKind == SGFAccessKind::ReadWrite); |
| auto writeBaseKind = getBaseAccessKind(SGM, member, SGFAccessKind::Write, |
| strategy.getWriteStrategy(), |
| baseFormalType); |
| |
| // Fast path for the common case that the write will need to mutate |
| // the base. |
| if (writeBaseKind == SGFAccessKind::ReadWrite) |
| return writeBaseKind; |
| |
| auto readBaseKind = getBaseAccessKind(SGM, member, |
| SGFAccessKind::OwnedAddressRead, |
| strategy.getReadStrategy(), |
| baseFormalType); |
| |
| // If they're the same kind, just use that. |
| if (readBaseKind == writeBaseKind) |
| return readBaseKind; |
| |
| // If either access is mutating, the total access is a read-write. |
| if (!isReadAccess(readBaseKind) || !isReadAccess(writeBaseKind)) |
| return SGFAccessKind::ReadWrite; |
| |
| // Okay, we have two different kinds of read somehow for different |
| // accessors of the same storage? |
| return SGFAccessKind::OwnedObjectRead; |
| } |
| |
| case AccessStrategy::DirectToAccessor: |
| case AccessStrategy::DispatchToAccessor: { |
| auto accessor = member->getAccessor(strategy.getAccessor()); |
| return getBaseAccessKindForAccessor(SGM, accessor, baseFormalType); |
| } |
| |
| } |
| llvm_unreachable("bad access strategy"); |
| } |
| |
| static CanType getBaseFormalType(Expr *baseExpr) { |
| return baseExpr->getType()->getWithoutSpecifierType()->getCanonicalType(); |
| } |
| |
| bool isCallToReplacedInDynamicReplacement(SILGenFunction &SGF, |
| AbstractFunctionDecl *afd, |
| bool &isObjCReplacementSelfCall); |
| |
| static bool isCallToSelfOfCurrentFunction(SILGenFunction &SGF, LookupExpr *e) { |
| return SGF.FunctionDC->getAsDecl() && |
| isa<AbstractFunctionDecl>(SGF.FunctionDC->getAsDecl()) && |
| e->getBase()->isSelfExprOf( |
| cast<AbstractFunctionDecl>(SGF.FunctionDC->getAsDecl()), false); |
| } |
| |
| static bool isCurrentFunctionReadAccess(SILGenFunction &SGF) { |
| auto *contextAccessorDecl = |
| dyn_cast_or_null<AccessorDecl>(SGF.FunctionDC->getAsDecl()); |
| return contextAccessorDecl && |
| contextAccessorDecl->getAccessorKind() == AccessorKind::Read; |
| } |
| |
| LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options) { |
| // MemberRefExpr can refer to type and function members, but the only case |
| // that can be an lvalue is a VarDecl. |
| VarDecl *var = cast<VarDecl>(e->getMember().getDecl()); |
| |
| auto accessSemantics = e->getAccessSemantics(); |
| AccessStrategy strategy = |
| var->getAccessStrategy(accessSemantics, |
| getFormalAccessKind(accessKind), |
| SGF.SGM.M.getSwiftModule(), |
| SGF.F.getResilienceExpansion()); |
| |
| bool isOnSelfParameter = isCallToSelfOfCurrentFunction(SGF, e); |
| |
| bool isContextRead = isCurrentFunctionReadAccess(SGF); |
| |
| // If we are inside _read, calling self.get, and the _read we are inside of is |
| // the same as the as self's variable and the current function is a |
| // dynamic replacement directly call the implementation. |
| if (isContextRead && isOnSelfParameter && strategy.hasAccessor() && |
| strategy.getAccessor() == AccessorKind::Get && |
| var->getAccessor(AccessorKind::Read)) { |
| bool isObjC = false; |
| auto readAccessor = |
| SGF.SGM.getAccessorDeclRef(var->getAccessor(AccessorKind::Read)); |
| if (isCallToReplacedInDynamicReplacement( |
| SGF, readAccessor.getAbstractFunctionDecl(), isObjC)) { |
| accessSemantics = AccessSemantics::DirectToImplementation; |
| strategy = var->getAccessStrategy( |
| accessSemantics, |
| getFormalAccessKind(accessKind), |
| SGF.SGM.M.getSwiftModule(), |
| SGF.F.getResilienceExpansion()); |
| } |
| } |
| |
| LValue lv = visitRec(e->getBase(), |
| getBaseAccessKind(SGF.SGM, var, accessKind, strategy, |
| getBaseFormalType(e->getBase())), |
| getBaseOptions(options, strategy)); |
| assert(lv.isValid()); |
| |
| CanType substFormalRValueType = getSubstFormalRValueType(e); |
| lv.addMemberVarComponent(SGF, e, var, e->getMember().getSubstitutions(), |
| options, e->isSuper(), accessKind, strategy, |
| substFormalRValueType, isOnSelfParameter); |
| return lv; |
| } |
| |
| namespace { |
| |
| /// A CRTP class for emitting member accesses. |
| template <class Impl, class StorageType> |
| struct MemberStorageAccessEmitter : AccessEmitter<Impl, StorageType> { |
| using super = AccessEmitter<Impl, StorageType>; |
| using super::SGF; |
| using super::Storage; |
| using super::FormalRValueType; |
| LValue &LV; |
| LValueOptions Options; |
| SILLocation Loc; |
| bool IsSuper; |
| bool IsOnSelfParameter; // Is self the self parameter in context. |
| CanType BaseFormalType; |
| SubstitutionMap Subs; |
| Expr *IndexExprForDiagnostics; |
| PreparedArguments Indices; |
| |
| MemberStorageAccessEmitter(SILGenFunction &SGF, SILLocation loc, |
| StorageType *storage, SubstitutionMap subs, |
| bool isSuper, SGFAccessKind accessKind, |
| CanType formalRValueType, LValueOptions options, |
| LValue &lv, Expr *indexExprForDiagnostics, |
| PreparedArguments &&indices, bool isSelf = false) |
| : super(SGF, storage, accessKind, formalRValueType), LV(lv), |
| Options(options), Loc(loc), IsSuper(isSuper), IsOnSelfParameter(isSelf), |
| BaseFormalType(lv.getSubstFormalType()), Subs(subs), |
| IndexExprForDiagnostics(indexExprForDiagnostics), |
| Indices(std::move(indices)) {} |
| |
| void emitUsingAddressor(SILDeclRef addressor, bool isDirect, |
| LValueTypeData typeData) { |
| SILType varStorageType = |
| SGF.SGM.Types.getSubstitutedStorageType(Storage, FormalRValueType); |
| |
| LV.add<AddressorComponent>(Storage, addressor, IsSuper, isDirect, Subs, |
| BaseFormalType, typeData, varStorageType, |
| IndexExprForDiagnostics, std::move(Indices), |
| IsOnSelfParameter); |
| } |
| |
| void emitUsingCoroutineAccessor(SILDeclRef accessor, bool isDirect, |
| LValueTypeData typeData) { |
| LV.add<CoroutineAccessorComponent>( |
| Storage, accessor, IsSuper, isDirect, Subs, BaseFormalType, typeData, |
| IndexExprForDiagnostics, std::move(Indices), IsOnSelfParameter); |
| } |
| |
| void emitUsingGetterSetter(SILDeclRef accessor, bool isDirect, |
| LValueTypeData typeData) { |
| LV.add<GetterSetterComponent>( |
| Storage, accessor, IsSuper, isDirect, Subs, BaseFormalType, typeData, |
| IndexExprForDiagnostics, std::move(Indices), IsOnSelfParameter); |
| } |
| |
| void emitUsingMaterialization(AccessStrategy readStrategy, |
| AccessStrategy writeStrategy, |
| LValueTypeData typeData) { |
| LV.add<MaterializeToTemporaryComponent>( |
| Storage, IsSuper, Subs, Options, readStrategy, writeStrategy, |
| BaseFormalType, typeData, IndexExprForDiagnostics, std::move(Indices), |
| IsOnSelfParameter); |
| } |
| }; |
| } // end anonymous namespace |
| |
| void LValue::addMemberVarComponent(SILGenFunction &SGF, SILLocation loc, |
| VarDecl *var, |
| SubstitutionMap subs, |
| LValueOptions options, |
| bool isSuper, |
| SGFAccessKind accessKind, |
| AccessStrategy strategy, |
| CanType formalRValueType, |
| bool isOnSelfParameter) { |
| struct MemberVarAccessEmitter |
| : MemberStorageAccessEmitter<MemberVarAccessEmitter, VarDecl> { |
| using MemberStorageAccessEmitter::MemberStorageAccessEmitter; |
| |
| void emitUsingStorage(LValueTypeData typeData) { |
| // For static variables, emit a reference to the global variable backing |
| // them. |
| // FIXME: This has to be dynamically looked up for classes, and |
| // dynamically instantiated for generics. |
| if (Storage->isStatic()) { |
| // FIXME: this implicitly drops the earlier components, but maybe |
| // we ought to evaluate them for side-effects even during the |
| // formal access? |
| LV.Path.clear(); |
| LV.addNonMemberVarComponent(SGF, Loc, Storage, Subs, Options, |
| typeData.getAccessKind(), |
| AccessStrategy::getStorage(), |
| FormalRValueType); |
| return; |
| } |
| |
| // Otherwise, it's a physical member. |
| SILType varStorageType = |
| SGF.SGM.Types.getSubstitutedStorageType(Storage, FormalRValueType); |
| |
| if (BaseFormalType->mayHaveSuperclass()) { |
| LV.add<RefElementComponent>(Storage, Options, varStorageType, typeData); |
| } else { |
| assert(BaseFormalType->getStructOrBoundGenericStruct()); |
| LV.add<StructElementComponent>(Storage, varStorageType, typeData); |
| } |
| |
| // If the member has weak or unowned storage, convert it away. |
| if (varStorageType.is<ReferenceStorageType>()) { |
| LV.add<OwnershipComponent>(typeData); |
| } |
| } |
| } emitter(SGF, loc, var, subs, isSuper, accessKind, |
| formalRValueType, options, *this, |
| /*indices for diags*/ nullptr, /*indices*/ PreparedArguments(), |
| isOnSelfParameter); |
| |
| emitter.emitUsingStrategy(strategy); |
| } |
| |
| LValue SILGenLValue::visitSubscriptExpr(SubscriptExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options) { |
| auto decl = cast<SubscriptDecl>(e->getDecl().getDecl()); |
| auto subs = e->getDecl().getSubstitutions(); |
| |
| |
| auto accessSemantics = e->getAccessSemantics(); |
| auto strategy = |
| decl->getAccessStrategy(accessSemantics, |
| getFormalAccessKind(accessKind), |
| SGF.SGM.M.getSwiftModule(), |
| SGF.F.getResilienceExpansion()); |
| |
| bool isOnSelfParameter = isCallToSelfOfCurrentFunction(SGF, e); |
| bool isContextRead = isCurrentFunctionReadAccess(SGF); |
| |
| // If we are inside _read, calling self.get, and the _read we are inside of is |
| // the same as the as self's variable and the current function is a |
| // dynamic replacement directly call the implementation. |
| if (isContextRead && isOnSelfParameter && strategy.hasAccessor() && |
| strategy.getAccessor() == AccessorKind::Get && |
| decl->getAccessor(AccessorKind::Read)) { |
| bool isObjC = false; |
| auto readAccessor = |
| SGF.SGM.getAccessorDeclRef(decl->getAccessor(AccessorKind::Read)); |
| if (isCallToReplacedInDynamicReplacement( |
| SGF, readAccessor.getAbstractFunctionDecl(), isObjC)) { |
| accessSemantics = AccessSemantics::DirectToImplementation; |
| strategy = decl->getAccessStrategy( |
| accessSemantics, |
| getFormalAccessKind(accessKind), |
| SGF.SGM.M.getSwiftModule(), |
| SGF.F.getResilienceExpansion()); |
| } |
| } |
| |
| LValue lv = visitRec(e->getBase(), |
| getBaseAccessKind(SGF.SGM, decl, accessKind, strategy, |
| getBaseFormalType(e->getBase())), |
| getBaseOptions(options, strategy)); |
| assert(lv.isValid()); |
| |
| Expr *indexExpr = e->getIndex(); |
| auto indices = SGF.prepareSubscriptIndices(decl, subs, strategy, indexExpr); |
| |
| CanType formalRValueType = getSubstFormalRValueType(e); |
| lv.addMemberSubscriptComponent(SGF, e, decl, subs, |
| options, e->isSuper(), accessKind, strategy, |
| formalRValueType, std::move(indices), |
| indexExpr, isOnSelfParameter); |
| return lv; |
| } |
| |
| LValue SILGenLValue::visitKeyPathApplicationExpr(KeyPathApplicationExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options) { |
| auto keyPathExpr = e->getKeyPath(); |
| auto keyPathKind = |
| *keyPathExpr->getType()->getAnyNominal()->getKeyPathTypeKind(); |
| |
| // Determine the base access strategy based on the strategy of this access. |
| SGFAccessKind subAccess; |
| if (!isReadAccess(accessKind)) { |
| // The only read-write accesses we allow are with WritableKeyPath and |
| // ReferenceWritableKeyPath. |
| assert(keyPathKind == KPTK_WritableKeyPath || |
| keyPathKind == KPTK_ReferenceWritableKeyPath); |
| subAccess = (keyPathKind == KPTK_ReferenceWritableKeyPath |
| ? SGFAccessKind::BorrowedAddressRead |
| : SGFAccessKind::ReadWrite); |
| } else { |
| // For all the other kinds, we want the emit the base as an address |
| // r-value; we don't support key paths for storage with mutating read |
| // operations. |
| subAccess = SGFAccessKind::BorrowedAddressRead; |
| } |
| |
| // For now, just ignore any options we were given. |
| LValueOptions subOptions = LValueOptions(); |
| |
| // Do formal evaluation of the base l-value. |
| // The base should be reabstracted to the maximal abstraction pattern. |
| LValue lv = visitRec(e->getBase(), subAccess, subOptions, |
| AbstractionPattern::getOpaque()); |
| |
| // Emit the key path r-value. |
| auto keyPath = SGF.emitRValueAsSingleValue(keyPathExpr); |
| |
| // The result will end up projected at the maximal abstraction level too. |
| auto substFormalType = e->getType()->getRValueType()->getCanonicalType(); |
| |
| bool useLogical = [&] { |
| switch (accessKind) { |
| // Use the physical 'read' pattern for these unless we're working with |
| // AnyKeyPath or PartialKeyPath, which only have getters. |
| case SGFAccessKind::BorrowedAddressRead: |
| case SGFAccessKind::BorrowedObjectRead: |
| return (keyPathKind == KPTK_AnyKeyPath || |
| keyPathKind == KPTK_PartialKeyPath); |
| |
| case SGFAccessKind::OwnedObjectRead: |
| case SGFAccessKind::OwnedAddressRead: |
| case SGFAccessKind::IgnoredRead: // should we use physical for this? |
| case SGFAccessKind::Write: |
| return true; |
| |
| case SGFAccessKind::ReadWrite: |
| return false; |
| } |
| llvm_unreachable("bad access kind"); |
| }(); |
| |
| if (useLogical) { |
| auto typeData = getLogicalStorageTypeData(SGF.SGM, accessKind, |
| substFormalType); |
| |
| Type baseFormalType = e->getBase()->getType()->getRValueType(); |
| lv.add<LogicalKeyPathApplicationComponent>(typeData, keyPathKind, keyPath, |
| baseFormalType); |
| |
| // TODO: make LogicalKeyPathApplicationComponent expect/produce values |
| // in the opaque AbstractionPattern and push an OrigToSubstComponent here |
| // so it can be peepholed. |
| } else { |
| auto typeData = getAbstractedTypeData(SGF.SGM, accessKind, |
| AbstractionPattern::getOpaque(), |
| substFormalType); |
| |
| lv.add<PhysicalKeyPathApplicationComponent>(typeData, keyPathKind, keyPath); |
| |
| // Reabstract to the substituted abstraction level if necessary. |
| auto substResultSILTy = SGF.getLoweredType(substFormalType); |
| if (typeData.TypeOfRValue != substResultSILTy.getASTType()) { |
| lv.addOrigToSubstComponent(substResultSILTy); |
| } |
| } |
| |
| return lv; |
| } |
| |
| void LValue::addMemberSubscriptComponent(SILGenFunction &SGF, SILLocation loc, |
| SubscriptDecl *decl, |
| SubstitutionMap subs, |
| LValueOptions options, |
| bool isSuper, |
| SGFAccessKind accessKind, |
| AccessStrategy strategy, |
| CanType formalRValueType, |
| PreparedArguments &&indices, |
| Expr *indexExprForDiagnostics, |
| bool isOnSelfParameter) { |
| struct MemberSubscriptAccessEmitter |
| : MemberStorageAccessEmitter<MemberSubscriptAccessEmitter, |
| SubscriptDecl> { |
| using MemberStorageAccessEmitter::MemberStorageAccessEmitter; |
| |
| void emitUsingStorage(LValueTypeData typeData) { |
| llvm_unreachable("subscripts never have storage"); |
| } |
| } emitter(SGF, loc, decl, subs, isSuper, accessKind, formalRValueType, |
| options, *this, indexExprForDiagnostics, std::move(indices), |
| isOnSelfParameter); |
| |
| emitter.emitUsingStrategy(strategy); |
| } |
| |
| bool LValue::isObviouslyNonConflicting(const LValue &other, |
| SGFAccessKind selfAccess, |
| SGFAccessKind otherAccess) { |
| // Reads never conflict with reads. |
| if (isReadAccess(selfAccess) && isReadAccess(otherAccess)) |
| return true; |
| |
| // We can cover more cases here. |
| return false; |
| } |
| |
| LValue SILGenLValue::visitTupleElementExpr(TupleElementExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options) { |
| unsigned index = e->getFieldNumber(); |
| LValue lv = visitRec(e->getBase(), |
| getBaseAccessKindForStorage(accessKind), |
| options.forProjectedBaseLValue()); |
| |
| auto baseTypeData = lv.getTypeData(); |
| LValueTypeData typeData = { |
| accessKind, |
| baseTypeData.OrigFormalType.getTupleElementType(index), |
| cast<TupleType>(baseTypeData.SubstFormalType).getElementType(index), |
| cast<TupleType>(baseTypeData.TypeOfRValue).getElementType(index) |
| }; |
| |
| lv.add<TupleElementComponent>(index, typeData); |
| return lv; |
| } |
| |
| LValue SILGenLValue::visitOpenExistentialExpr(OpenExistentialExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options) { |
| // If the opaque value is not an lvalue, open the existential immediately. |
| if (!e->getOpaqueValue()->getType()->is<LValueType>()) { |
| return SGF.emitOpenExistentialExpr<LValue>(e, |
| [&](Expr *subExpr) -> LValue { |
| return visitRec(subExpr, |
| accessKind, |
| options); |
| }); |
| } |
| |
| // Record the fact that we're opening this existential. The actual |
| // opening operation will occur when we see the OpaqueValueExpr. |
| bool inserted = SGF.OpaqueValueExprs.insert({e->getOpaqueValue(), e}).second; |
| (void)inserted; |
| assert(inserted && "already have this opened existential?"); |
| |
| // Visit the subexpression. |
| LValue lv = visitRec(e->getSubExpr(), accessKind, options); |
| |
| // Sanity check that we did see the OpaqueValueExpr. |
| assert(SGF.OpaqueValueExprs.count(e->getOpaqueValue()) == 0 && |
| "opened existential not removed?"); |
| return lv; |
| } |
| |
| static LValueTypeData |
| getOptionalObjectTypeData(SILGenFunction &SGF, SGFAccessKind accessKind, |
| const LValueTypeData &baseTypeData) { |
| return { |
| accessKind, |
| baseTypeData.OrigFormalType.getOptionalObjectType(), |
| baseTypeData.SubstFormalType.getOptionalObjectType(), |
| baseTypeData.TypeOfRValue.getOptionalObjectType(), |
| }; |
| } |
| |
| LValue SILGenLValue::visitForceValueExpr(ForceValueExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options) { |
| // Like BindOptional, this is a read even if we only write to the result. |
| // (But it's unnecessary to use a force this way!) |
| LValue lv = visitRec(e->getSubExpr(), |
| getBaseAccessKindForStorage(accessKind), |
| options.forComputedBaseLValue()); |
| LValueTypeData typeData = |
| getOptionalObjectTypeData(SGF, accessKind, lv.getTypeData()); |
| bool isImplicitUnwrap = e->isImplicit() && |
| e->isForceOfImplicitlyUnwrappedOptional(); |
| lv.add<ForceOptionalObjectComponent>(typeData, isImplicitUnwrap); |
| return lv; |
| } |
| |
| LValue SILGenLValue::visitBindOptionalExpr(BindOptionalExpr *e, |
| SGFAccessKind accessKind, |
| LValueOptions options) { |
| // Binding reads the base even if we then only write to the result. |
| auto baseAccessKind = getBaseAccessKindForStorage(accessKind); |
| |
| // We're going to take the address of the base. |
| // TODO: deal more efficiently with an object-preferring access. |
| baseAccessKind = getAddressAccessKind(baseAccessKind); |
| |
| // Do formal evaluation of the base l-value. |
| LValue optLV = visitRec(e->getSubExpr(), baseAccessKind, |
| options.forComputedBaseLValue()); |
| |
| LValueTypeData optTypeData = optLV.getTypeData(); |
| LValueTypeData valueTypeData = |
| getOptionalObjectTypeData(SGF, accessKind, optTypeData); |
| |
| // The chaining operator immediately begins a formal access to the |
| // base l-value. In concrete terms, this means we can immediately |
| // evaluate the base down to an address. |
| ManagedValue optAddr = SGF.emitAddressOfLValue(e, std::move(optLV)); |
| |
| // Bind the value, branching to the destination address if there's no |
| // value there. |
| SGF.emitBindOptionalAddress(e, optAddr, e->getDepth()); |
| |
| // Project out the payload on the success branch. We can just use a |
| // naked ValueComponent here; this is effectively a separate l-value. |
| ManagedValue valueAddr = |
| getAddressOfOptionalValue(SGF, e, optAddr, valueTypeData); |
| LValue valueLV; |
| valueLV.add<ValueComponent>(valueAddr, None, valueTypeData); |
| return valueLV; |
| } |
| |
| LValue SILGenLValue::visitInOutExpr(InOutExpr *e, SGFAccessKind accessKind, |
| LValueOptions options) { |
| return visitRec(e->getSubExpr(), accessKind, options); |
| } |
| |
| /// Emit an lvalue that refers to the given property. This is |
| /// designed to work with ManagedValue 'base's that are either +0 or +1. |
| LValue SILGenFunction::emitPropertyLValue(SILLocation loc, ManagedValue base, |
| CanType baseFormalType, |
| VarDecl *ivar, |
| LValueOptions options, |
| SGFAccessKind accessKind, |
| AccessSemantics semantics) { |
| SILGenLValue sgl(*this); |
| LValue lv; |
| |
| auto baseType = base.getType().getASTType(); |
| auto subMap = baseType->getContextSubstitutionMap( |
| SGM.M.getSwiftModule(), ivar->getDeclContext()); |
| |
| AccessStrategy strategy = |
| ivar->getAccessStrategy(semantics, |
| getFormalAccessKind(accessKind), |
| SGM.M.getSwiftModule(), |
| F.getResilienceExpansion()); |
| |
| auto baseAccessKind = |
| getBaseAccessKind(SGM, ivar, accessKind, strategy, baseFormalType); |
| |
| LValueTypeData baseTypeData = |
| getValueTypeData(baseAccessKind, baseFormalType, base.getValue()); |
| |
| // Refer to 'self' as the base of the lvalue. |
| lv.add<ValueComponent>(base, None, baseTypeData, |
| /*isRValue=*/!base.isLValue()); |
| |
| auto substFormalType = ivar->getInterfaceType().subst(subMap) |
| ->getCanonicalType().getReferenceStorageReferent(); |
| |
| lv.addMemberVarComponent(*this, loc, ivar, subMap, options, /*super*/ false, |
| accessKind, strategy, substFormalType); |
| return lv; |
| } |
| |
| // This is emitLoad that will handle re-abstraction and bridging for the client. |
| ManagedValue SILGenFunction::emitLoad(SILLocation loc, SILValue addr, |
| AbstractionPattern origFormalType, |
| CanType substFormalType, |
| const TypeLowering &rvalueTL, |
| SGFContext C, IsTake_t isTake, |
| bool isAddressGuaranteed) { |
| assert(addr->getType().isAddress()); |
| SILType addrRValueType = addr->getType().getReferenceStorageReferentType(); |
| |
| // Fast path: the types match exactly. |
| if (addrRValueType == rvalueTL.getLoweredType().getAddressType()) { |
| return emitLoad(loc, addr, rvalueTL, C, isTake, isAddressGuaranteed); |
| } |
| |
| // Otherwise, we need to reabstract or bridge. |
| auto conversion = |
| origFormalType.isClangType() |
| ? Conversion::getBridging(Conversion::BridgeFromObjC, |
| origFormalType.getType(), |
| substFormalType, rvalueTL.getLoweredType()) |
| : Conversion::getOrigToSubst(origFormalType, substFormalType); |
| |
| return emitConvertedRValue(loc, conversion, C, |
| [&](SILGenFunction &SGF, SILLocation loc, SGFContext C) { |
| return SGF.emitLoad(loc, addr, getTypeLowering(addrRValueType), |
| C, isTake, isAddressGuaranteed); |
| }); |
| } |
| |
| /// Load an r-value out of the given address. |
| /// |
| /// \param rvalueTL - the type lowering for the type-of-rvalue |
| /// of the address |
| /// \param isAddrGuaranteed - true if the value in this address |
| /// is guaranteed to be valid for the duration of the current |
| /// evaluation (see SGFContext::AllowGuaranteedPlusZero) |
| ManagedValue SILGenFunction::emitLoad(SILLocation loc, SILValue addr, |
| const TypeLowering &rvalueTL, |
| SGFContext C, IsTake_t isTake, |
| bool isAddrGuaranteed) { |
| // Get the lowering for the address type. We can avoid a re-lookup |
| // in the very common case of this being equivalent to the r-value |
| // type. |
| auto &addrTL = |
| (addr->getType() == rvalueTL.getLoweredType().getAddressType() |
| ? rvalueTL : getTypeLowering(addr->getType())); |
| |
| // Never do a +0 load together with a take. |
| bool isPlusZeroOk = (isTake == IsNotTake && |
| (isAddrGuaranteed ? C.isGuaranteedPlusZeroOk() |
| : C.isImmediatePlusZeroOk())); |
| |
| if (rvalueTL.isAddressOnly() && silConv.useLoweredAddresses()) { |
| // If the client is cool with a +0 rvalue, the decl has an address-only |
| // type, and there are no conversions, then we can return this as a +0 |
| // address RValue. |
| if (isPlusZeroOk && rvalueTL.getLoweredType() == addrTL.getLoweredType()) |
| return ManagedValue::forUnmanaged(addr); |
| |
| // Copy the address-only value. |
| return B.bufferForExpr( |
| loc, rvalueTL.getLoweredType(), rvalueTL, C, |
| [&](SILValue newAddr) { |
| emitSemanticLoadInto(loc, addr, addrTL, newAddr, rvalueTL, |
| isTake, IsInitialization); |
| }); |
| } |
| |
| // Ok, this is something loadable. If this is a non-take access at plus zero, |
| // we can perform a +0 load of the address instead of materializing a +1 |
| // value. |
| if (isPlusZeroOk && addrTL.getLoweredType() == rvalueTL.getLoweredType()) { |
| return B.createLoadBorrow(loc, ManagedValue::forUnmanaged(addr)); |
| } |
| |
| // Load the loadable value, and retain it if we aren't taking it. |
| SILValue loadedV = emitSemanticLoad(loc, addr, addrTL, rvalueTL, isTake); |
| return emitManagedRValueWithCleanup(loadedV); |
| } |
| |
| /// Load an r-value out of the given address. |
| /// |
| /// \param rvalueTL - the type lowering for the type-of-rvalue |
| /// of the address |
| /// \param isAddressGuaranteed - true if the value in this address |
| /// is guaranteed to be valid for the duration of the current |
| /// evaluation (see SGFContext::AllowGuaranteedPlusZero) |
| ManagedValue SILGenFunction::emitFormalAccessLoad(SILLocation loc, |
| SILValue addr, |
| const TypeLowering &rvalueTL, |
| SGFContext C, IsTake_t isTake, |
| bool isAddressGuaranteed) { |
| // Get the lowering for the address type. We can avoid a re-lookup |
| // in the very common case of this being equivalent to the r-value |
| // type. |
| auto &addrTL = (addr->getType() == rvalueTL.getLoweredType().getAddressType() |
| ? rvalueTL |
| : getTypeLowering(addr->getType())); |
| |
| // Never do a +0 load together with a take. |
| bool isPlusZeroOk = |
| (isTake == IsNotTake && (isAddressGuaranteed ? C.isGuaranteedPlusZeroOk() |
| : C.isImmediatePlusZeroOk())); |
| |
| if (rvalueTL.isAddressOnly() && silConv.useLoweredAddresses()) { |
| // If the client is cool with a +0 rvalue, the decl has an address-only |
| // type, and there are no conversions, then we can return this as a +0 |
| // address RValue. |
| if (isPlusZeroOk && rvalueTL.getLoweredType() == addrTL.getLoweredType()) |
| return ManagedValue::forUnmanaged(addr); |
| |
| // Copy the address-only value. |
| return B.formalAccessBufferForExpr( |
| loc, rvalueTL.getLoweredType(), rvalueTL, C, |
| [&](SILValue addressForCopy) { |
| emitSemanticLoadInto(loc, addr, addrTL, addressForCopy, rvalueTL, |
| isTake, IsInitialization); |
| }); |
| } |
| |
| // Ok, this is something loadable. If this is a non-take access at plus zero, |
| // we can perform a +0 load of the address instead of materializing a +1 |
| // value. |
| if (isPlusZeroOk && addrTL.getLoweredType() == rvalueTL.getLoweredType()) { |
| return B.createFormalAccessLoadBorrow(loc, |
| ManagedValue::forUnmanaged(addr)); |
| } |
| |
| // Load the loadable value, and retain it if we aren't taking it. |
| SILValue loadedV = emitSemanticLoad(loc, addr, addrTL, rvalueTL, isTake); |
| return emitFormalAccessManagedRValueWithCleanup(loc, loadedV); |
| } |
| |
| static void emitUnloweredStoreOfCopy(SILGenBuilder &B, SILLocation loc, |
| SILValue value, SILValue addr, |
| IsInitialization_t isInit) { |
| if (isInit) { |
| B.emitStoreValueOperation(loc, value, addr, StoreOwnershipQualifier::Init); |
| } else { |
| B.createAssign(loc, value, addr, AssignOwnershipQualifier::Unknown); |
| } |
| } |
| |
| SILValue SILGenFunction::emitConversionToSemanticRValue(SILLocation loc, |
| SILValue src, |
| const TypeLowering &valueTL) { |
| auto storageType = src->getType(); |
| auto swiftStorageType = storageType.castTo<ReferenceStorageType>(); |
| |
| switch (swiftStorageType->getOwnership()) { |
| case ReferenceOwnership::Strong: |
| llvm_unreachable("strong reference storage type should be impossible"); |
| #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: \ |
| /* Address-only storage types are handled with their underlying type. */ \ |
| llvm_unreachable("address-only pointers are handled elsewhere"); |
| #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: \ |
| return B.createCopy##Name##Value(loc, src); |
| #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: { \ |
| /* For loadable reference storage types, we need to generate a strong */ \ |
| /* retain and strip the box. */ \ |
| assert(storageType.castTo<Name##StorageType>()->isLoadable( \ |
| ResilienceExpansion::Maximal)); \ |
| return B.createCopy##Name##Value(loc, src); \ |
| } |
| #define UNCHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: { \ |
| /* For static reference storage types, we need to strip the box and */ \ |
| /* then do an (unsafe) retain. */ \ |
| auto type = storageType.castTo<Name##StorageType>(); \ |
| auto result = B.create##Name##ToRef(loc, src, \ |
| SILType::getPrimitiveObjectType(type.getReferentType())); \ |
| /* SEMANTIC ARC TODO: Does this need a cleanup? */ \ |
| return B.createCopyValue(loc, result); \ |
| } |
| #include "swift/AST/ReferenceStorage.def" |
| } |
| llvm_unreachable("impossible"); |
| } |
| |
| ManagedValue SILGenFunction::emitConversionToSemanticRValue( |
| SILLocation loc, ManagedValue src, const TypeLowering &valueTL) { |
| auto swiftStorageType = src.getType().castTo<ReferenceStorageType>(); |
| |
| switch (swiftStorageType->getOwnership()) { |
| case ReferenceOwnership::Strong: |
| llvm_unreachable("strong reference storage type should be impossible"); |
| #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: \ |
| /* Address-only storage types are handled with their underlying type. */ \ |
| llvm_unreachable("address-only pointers are handled elsewhere"); |
| #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: \ |
| /* Generate a strong retain and strip the box. */ \ |
| return B.createCopy##Name##Value(loc, src); |
| #define UNCHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: \ |
| /* Strip the box and then do an (unsafe) retain. */ \ |
| return B.createUnsafeCopy##Name##Value(loc, src); |
| #include "swift/AST/ReferenceStorage.def" |
| } |
| llvm_unreachable("impossible"); |
| } |
| |
| /// Given that the type-of-rvalue differs from the type-of-storage, |
| /// and given that the type-of-rvalue is loadable, produce a +1 scalar |
| /// of the type-of-rvalue. |
| static SILValue emitLoadOfSemanticRValue(SILGenFunction &SGF, |
| SILLocation loc, |
| SILValue src, |
| const TypeLowering &valueTL, |
| IsTake_t isTake) { |
| auto storageType = src->getType(); |
| auto swiftStorageType = storageType.castTo<ReferenceStorageType>(); |
| |
| switch (swiftStorageType->getOwnership()) { |
| case ReferenceOwnership::Strong: |
| llvm_unreachable("strong reference storage type should be impossible"); |
| #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: \ |
| return SGF.B.createLoad##Name(loc, src, isTake); |
| #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) \ |
| { \ |
| /* For loadable types, we need to strip the box. */ \ |
| /* If we are not performing a take, use a load_borrow. */ \ |
| if (!isTake) { \ |
| SILValue value = SGF.B.createLoadBorrow(loc, src); \ |
| SILValue strongValue = SGF.B.createCopy##Name##Value(loc, value); \ |
| SGF.B.createEndBorrow(loc, value, src); \ |
| return strongValue; \ |
| } \ |
| /* Otherwise perform a load take and destroy the stored value. */ \ |
| auto value = SGF.B.emitLoadValueOperation(loc, src, \ |
| LoadOwnershipQualifier::Take); \ |
| SILValue strongValue = SGF.B.createCopy##Name##Value(loc, value); \ |
| SGF.B.createDestroyValue(loc, value); \ |
| return strongValue; \ |
| } |
| #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: \ |
| ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) |
| #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: { \ |
| /* For loadable types, we need to strip the box. */ \ |
| auto type = storageType.castTo<Name##StorageType>(); \ |
| if (!type->isLoadable(ResilienceExpansion::Maximal)) { \ |
| return SGF.B.createLoad##Name(loc, src, isTake); \ |
| } \ |
| ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) \ |
| } |
| #define UNCHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: { \ |
| /* For static reference storage types, we need to strip the box. */ \ |
| auto type = storageType.castTo<Name##StorageType>(); \ |
| auto value = SGF.B.createLoad(loc, src, LoadOwnershipQualifier::Trivial); \ |
| auto result = SGF.B.create##Name##ToRef(loc, value, \ |
| SILType::getPrimitiveObjectType(type.getReferentType())); \ |
| /* SEMANTIC ARC TODO: Does this need a cleanup? */ \ |
| return SGF.B.createCopyValue(loc, result); \ |
| } |
| #include "swift/AST/ReferenceStorage.def" |
| #undef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER |
| } |
| llvm_unreachable("unhandled ownership"); |
| } |
| |
| /// Given that the type-of-rvalue differs from the type-of-storage, |
| /// store a +1 value (possibly not a scalar) of the type-of-rvalue |
| /// into the given address. |
| static void emitStoreOfSemanticRValue(SILGenFunction &SGF, |
| SILLocation loc, |
| SILValue value, |
| SILValue dest, |
| const TypeLowering &valueTL, |
| IsInitialization_t isInit) { |
| auto storageType = dest->getType(); |
| auto swiftStorageType = storageType.castTo<ReferenceStorageType>(); |
| |
| switch (swiftStorageType->getOwnership()) { |
| case ReferenceOwnership::Strong: |
| llvm_unreachable("strong reference storage type should be impossible"); |
| #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: { \ |
| SGF.B.createStore##Name(loc, value, dest, isInit); \ |
| /* store doesn't take ownership of the input, so cancel it out. */ \ |
| SGF.B.emitDestroyValueOperation(loc, value); \ |
| return; \ |
| } |
| #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) \ |
| { \ |
| auto typedValue = SGF.B.createRefTo##Name(loc, value, \ |
| storageType.getObjectType()); \ |
| auto copiedVal = SGF.B.createCopyValue(loc, typedValue); \ |
| emitUnloweredStoreOfCopy(SGF.B, loc, copiedVal, dest, isInit); \ |
| SGF.B.emitDestroyValueOperation(loc, value); \ |
| return; \ |
| } |
| #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: \ |
| ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) |
| #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: { \ |
| /* For loadable types, we need to enter the box by */ \ |
| /* turning the strong retain into an type-specific retain. */ \ |
| auto type = storageType.castTo<Name##StorageType>(); \ |
| /* FIXME: resilience */ \ |
| if (!type->isLoadable(ResilienceExpansion::Maximal)) { \ |
| SGF.B.createStore##Name(loc, value, dest, isInit); \ |
| /* store doesn't take ownership of the input, so cancel it out. */ \ |
| SGF.B.emitDestroyValueOperation(loc, value); \ |
| return; \ |
| } \ |
| ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) \ |
| } |
| #define UNCHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: { \ |
| /* For static reference storage types, we need to enter the box and */ \ |
| /* release the strong retain. */ \ |
| auto typedValue = SGF.B.createRefTo##Name(loc, value, \ |
| storageType.getObjectType()); \ |
| emitUnloweredStoreOfCopy(SGF.B, loc, typedValue, dest, isInit); \ |
| SGF.B.emitDestroyValueOperation(loc, value); \ |
| return; \ |
| } |
| #include "swift/AST/ReferenceStorage.def" |
| #undef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER |
| } |
| llvm_unreachable("impossible"); |
| } |
| |
| /// Load a value of the type-of-rvalue out of the given address as a |
| /// scalar. The type-of-rvalue must be loadable. |
| SILValue SILGenFunction::emitSemanticLoad(SILLocation loc, |
| SILValue src, |
| const TypeLowering &srcTL, |
| const TypeLowering &rvalueTL, |
| IsTake_t isTake) { |
| assert(srcTL.getLoweredType().getAddressType() == src->getType()); |
| assert(rvalueTL.isLoadable() || !silConv.useLoweredAddresses()); |
| |
| // Easy case: the types match. |
| if (srcTL.getLoweredType() == rvalueTL.getLoweredType()) { |
| return srcTL.emitLoadOfCopy(B, loc, src, isTake); |
| } |
| |
| return emitLoadOfSemanticRValue(*this, loc, src, rvalueTL, isTake); |
| } |
| |
| /// Load a value of the type-of-reference out of the given address |
| /// and into the destination address. |
| void SILGenFunction::emitSemanticLoadInto(SILLocation loc, |
| SILValue src, |
| const TypeLowering &srcTL, |
| SILValue dest, |
| const TypeLowering &destTL, |
| IsTake_t isTake, |
| IsInitialization_t isInit) { |
| assert(srcTL.getLoweredType().getAddressType() == src->getType()); |
| assert(destTL.getLoweredType().getAddressType() == dest->getType()); |
| |
| // Easy case: the types match. |
| if (srcTL.getLoweredType() == destTL.getLoweredType()) { |
| B.createCopyAddr(loc, src, dest, isTake, isInit); |
| return; |
| } |
| |
| auto rvalue = emitLoadOfSemanticRValue(*this, loc, src, srcTL, isTake); |
| emitUnloweredStoreOfCopy(B, loc, rvalue, dest, isInit); |
| } |
| |
| /// Store an r-value into the given address as an initialization. |
| void SILGenFunction::emitSemanticStore(SILLocation loc, |
| SILValue rvalue, |
| SILValue dest, |
| const TypeLowering &destTL, |
| IsInitialization_t isInit) { |
| assert(destTL.getLoweredType().getAddressType() == dest->getType()); |
| |
| // Easy case: the types match. |
| if (rvalue->getType() == destTL.getLoweredType()) { |
| assert(!silConv.useLoweredAddresses() |
| || (destTL.isAddressOnly() == rvalue->getType().isAddress())); |
| if (rvalue->getType().isAddress()) { |
| B.createCopyAddr(loc, rvalue, dest, IsTake, isInit); |
| } else { |
| emitUnloweredStoreOfCopy(B, loc, rvalue, dest, isInit); |
| } |
| return; |
| } |
| |
| auto &rvalueTL = getTypeLowering(rvalue->getType()); |
| emitStoreOfSemanticRValue(*this, loc, rvalue, dest, rvalueTL, isInit); |
| } |
| |
| /// Convert a semantic rvalue to a value of storage type. |
| SILValue SILGenFunction::emitConversionFromSemanticValue(SILLocation loc, |
| SILValue semanticValue, |
| SILType storageType) { |
| auto &destTL = getTypeLowering(storageType); |
| (void)destTL; |
| // Easy case: the types match. |
| if (semanticValue->getType() == storageType) { |
| return semanticValue; |
| } |
| |
| auto swiftStorageType = storageType.castTo<ReferenceStorageType>(); |
| switch (swiftStorageType->getOwnership()) { |
| case ReferenceOwnership::Strong: |
| llvm_unreachable("strong reference storage type should be impossible"); |
| #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: \ |
| llvm_unreachable("address-only types are never loadable"); |
| #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: { \ |
| SILValue value = B.createRefTo##Name(loc, semanticValue, storageType); \ |
| value = B.createCopyValue(loc, value); \ |
| B.emitDestroyValueOperation(loc, semanticValue); \ |
| return value; \ |
| } |
| #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: { \ |
| /* For loadable types, place into a box. */ \ |
| auto type = storageType.castTo<Name##StorageType>(); \ |
| assert(type->isLoadable(ResilienceExpansion::Maximal)); \ |
| (void) type; \ |
| SILValue value = B.createRefTo##Name(loc, semanticValue, storageType); \ |
| value = B.createCopyValue(loc, value); \ |
| B.emitDestroyValueOperation(loc, semanticValue); \ |
| return value; \ |
| } |
| #define UNCHECKED_REF_STORAGE(Name, ...) \ |
| case ReferenceOwnership::Name: { \ |
| /* For static reference storage types, place into a box. */ \ |
| SILValue value = B.createRefTo##Name(loc, semanticValue, storageType); \ |
| B.emitDestroyValueOperation(loc, semanticValue); \ |
| return value; \ |
| } |
| #include "swift/AST/ReferenceStorage.def" |
| } |
| llvm_unreachable("impossible"); |
| } |
| |
| static void emitTsanInoutAccess(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue address) { |
| assert(address.getType().isAddress()); |
| SILValue accessFnArgs[] = {address.getValue()}; |
| |
| SGF.B.createBuiltin(loc, SGF.getASTContext().getIdentifier("tsanInoutAccess"), |
| SGF.SGM.Types.getEmptyTupleType(), {}, accessFnArgs); |
| } |
| |
| /// Produce a physical address that corresponds to the given l-value |
| /// component. |
| static ManagedValue drillIntoComponent(SILGenFunction &SGF, |
| SILLocation loc, |
| PathComponent &&component, |
| ManagedValue base, |
| TSanKind tsanKind) { |
| bool isRValue = component.isRValue(); |
| ManagedValue addr = std::move(component).project(SGF, loc, base); |
| |
| if (!SGF.getASTContext().LangOpts.DisableTsanInoutInstrumentation && |
| (SGF.getModule().getOptions().Sanitizers & SanitizerKind::Thread) && |
| tsanKind == TSanKind::InoutAccess && !isRValue) { |
| emitTsanInoutAccess(SGF, loc, addr); |
| } |
| |
| return addr; |
| } |
| |
| /// Find the last component of the given lvalue and derive a base |
| /// location for it. |
| static PathComponent && |
| drillToLastComponent(SILGenFunction &SGF, |
| SILLocation loc, |
| LValue &&lv, |
| ManagedValue &addr, |
| TSanKind tsanKind = TSanKind::None) { |
| assert(lv.begin() != lv.end() && |
| "lvalue must have at least one component"); |
| |
| for (auto i = lv.begin(), e = lv.end() - 1; i != e; ++i) { |
| addr = drillIntoComponent(SGF, loc, std::move(**i), addr, tsanKind); |
| } |
| |
| return std::move(**(lv.end() - 1)); |
| } |
| |
| static ArgumentSource emitBaseValueForAccessor(SILGenFunction &SGF, |
| SILLocation loc, LValue &&lvalue, |
| CanType baseFormalType, |
| SILDeclRef accessor) { |
| ManagedValue base; |
| PathComponent &&component = |
| drillToLastComponent(SGF, loc, std::move(lvalue), base); |
| base = drillIntoComponent(SGF, loc, std::move(component), base, |
| TSanKind::None); |
| |
| return SGF.prepareAccessorBaseArg(loc, base, baseFormalType, accessor); |
| } |
| |
| RValue SILGenFunction::emitLoadOfLValue(SILLocation loc, LValue &&src, |
| SGFContext C, bool isBaseGuaranteed) { |
| assert(isReadAccess(src.getAccessKind())); |
| |
| // Any writebacks should be scoped to after the load. |
| FormalEvaluationScope scope(*this); |
| |
| // We shouldn't need to re-abstract here, but we might have to bridge. |
| // This should only happen if we have a global variable of NSString type. |
| auto origFormalType = src.getOrigFormalType(); |
| auto substFormalType = src.getSubstFormalType(); |
| auto &rvalueTL = getTypeLowering(src.getTypeOfRValue()); |
| |
| ManagedValue addr; |
| PathComponent &&component = |
| drillToLastComponent(*this, loc, std::move(src), addr); |
| |
| // If the last component is physical, drill down and load from it. |
| if (component.isPhysical()) { |
| auto projection = std::move(component).project(*this, loc, addr); |
| if (projection.getType().isAddress()) { |
| projection = emitLoad(loc, projection.getValue(), |
| origFormalType, substFormalType, |
| rvalueTL, C, IsNotTake, |
| isBaseGuaranteed); |
| } else if (isReadAccessResultOwned(src.getAccessKind()) && |
| !projection.isPlusOne(*this)) { |
| projection = projection.copy(*this, loc); |
| } |
| |
| return RValue(*this, loc, substFormalType, projection); |
| } |
| |
| // If the last component is logical, emit a get. |
| return std::move(component.asLogical()).get(*this, loc, addr, C); |
| } |
| |
| static AbstractionPattern |
| getFormalStorageAbstractionPattern(SILGenFunction &SGF, AbstractStorageDecl *field) { |
| if (auto var = dyn_cast<VarDecl>(field)) { |
| auto origType = SGF.SGM.Types.getAbstractionPattern(var); |
| return origType.getReferenceStorageReferentType(); |
| } |
| auto sub = cast<SubscriptDecl>(field); |
| return SGF.SGM.Types.getAbstractionPattern(sub); |
| } |
| |
| /// Produce a singular RValue for a load from the specified property. This is |
| /// designed to work with RValue ManagedValue bases that are either +0 or +1. |
| RValue SILGenFunction::emitRValueForStorageLoad( |
| SILLocation loc, ManagedValue base, CanType baseFormalType, |
| bool isSuper, AbstractStorageDecl *storage, |
| PreparedArguments &&subscriptIndices, |
| SubstitutionMap substitutions, |
| AccessSemantics semantics, Type propTy, SGFContext C, |
| bool isBaseGuaranteed) { |
| AccessStrategy strategy = |
| storage->getAccessStrategy(semantics, AccessKind::Read, |
| SGM.M.getSwiftModule(), |
| F.getResilienceExpansion()); |
| |
| // If we should call an accessor of some kind, do so. |
| if (strategy.getKind() != AccessStrategy::Storage) { |
| auto accessKind = SGFAccessKind::OwnedObjectRead; |
| |
| LValue lv = [&] { |
| if (!base) return LValue(); |
| |
| auto baseAccess = getBaseAccessKind(SGM, storage, accessKind, |
| strategy, baseFormalType); |
| return LValue::forValue(baseAccess, base, baseFormalType); |
| }(); |
| |
| lv.addMemberComponent(*this, loc, storage, substitutions, LValueOptions(), |
| isSuper, accessKind, strategy, |
| propTy->getCanonicalType(), |
| std::move(subscriptIndices), |
| /*index for diagnostics*/ nullptr); |
| |
| return emitLoadOfLValue(loc, std::move(lv), C, isBaseGuaranteed); |
| } |
| |
| assert(isa<VarDecl>(storage) && "only properties should have storage"); |
| auto field = cast<VarDecl>(storage); |
| assert(field->hasStorage() && |
| "Cannot directly access value without storage"); |
| |
| // For static variables, emit a reference to the global variable backing |
| // them. |
| // FIXME: This has to be dynamically looked up for classes, and |
| // dynamically instantiated for generics. |
| if (field->isStatic()) { |
| auto baseMeta = base.getType().castTo<MetatypeType>().getInstanceType(); |
| (void)baseMeta; |
| assert(!baseMeta->is<BoundGenericType>() && |
| "generic static stored properties not implemented"); |
| if (field->getDeclContext()->getSelfClassDecl() && |
| field->hasStorage()) |
| // FIXME: don't need to check hasStorage, already done above |
| assert(field->isFinal() && "non-final class stored properties not implemented"); |
| |
| return emitRValueForDecl(loc, field, propTy, semantics, C); |
| } |
| |
| |
| // rvalue MemberRefExprs are produced in two cases: when accessing a 'let' |
| // decl member, and when the base is a (non-lvalue) struct. |
| assert(baseFormalType->getAnyNominal() && |
| base.getType().getASTType()->getAnyNominal() && |
| "The base of an rvalue MemberRefExpr should be an rvalue value"); |
| |
| // If the accessed field is stored, emit a StructExtract on the base. |
| |
| auto substFormalType = propTy->getCanonicalType(); |
| auto &lowering = getTypeLowering(substFormalType); |
| |
| // Check for an abstraction difference. |
| AbstractionPattern origFormalType = |
| getFormalStorageAbstractionPattern(*this, field); |
| bool hasAbstractionChange = false; |
| auto &abstractedTL = getTypeLowering(origFormalType, substFormalType); |
| if (!origFormalType.isExactType(substFormalType)) { |
| hasAbstractionChange = |
| (abstractedTL.getLoweredType() != lowering.getLoweredType()); |
| } |
| |
| // If the base is a reference type, just handle this as loading the lvalue. |
| ManagedValue result; |
| if (baseFormalType->hasReferenceSemantics()) { |
| LValue LV = emitPropertyLValue(loc, base, baseFormalType, field, |
| LValueOptions(), |
| SGFAccessKind::OwnedObjectRead, |
| AccessSemantics::DirectToStorage); |
| auto loaded = emitLoadOfLValue(loc, std::move(LV), C, isBaseGuaranteed); |
| // If we don't have to reabstract, the load is sufficient. |
| if (!hasAbstractionChange) |
| return loaded; |
| |
| // Otherwise, bring the component up to +1 so we can reabstract it. |
| result = std::move(loaded).getAsSingleValue(*this, loc) |
| .copyUnmanaged(*this, loc); |
| } else if (!base.getType().isAddress()) { |
| // For non-address-only structs, we emit a struct_extract sequence. |
| result = B.createStructExtract(loc, base, field); |
| |
| if (result.getType().is<ReferenceStorageType>()) { |
| // For weak and unowned types, convert the reference to the right |
| // pointer, producing a +1. |
| result = emitConversionToSemanticRValue(loc, result, lowering); |
| |
| } else if (hasAbstractionChange || |
| (!C.isImmediatePlusZeroOk() && |
| !(C.isGuaranteedPlusZeroOk() && isBaseGuaranteed))) { |
| // If we have an abstraction change or if we have to produce a result at |
| // +1, then copy the value. If we know that our base will stay alive for |
| // the entire usage of this value, we can borrow the value at +0 for a |
| // guaranteed consumer. Otherwise, since we do not have enough information |
| // to know if the base's lifetime last's as long as our use of the access, |
| // we can only emit at +0 for immediate clients. |
| result = result.copyUnmanaged(*this, loc); |
| } |
| } else { |
| // Create a tiny unenforced access scope around a load from local memory. No |
| // cleanup is necessary since we directly emit the load here. This will |
| // probably go away with opaque values. |
| UnenforcedAccess access; |
| SILValue accessAddress = |
| access.beginAccess(*this, loc, base.getValue(), SILAccessKind::Read); |
| |
| // For address-only sequences, the base is in memory. Emit a |
| // struct_element_addr to get to the field, and then load the element as an |
| // rvalue. |
| SILValue ElementPtr = B.createStructElementAddr(loc, accessAddress, field); |
| |
| result = emitLoad(loc, ElementPtr, abstractedTL, |
| hasAbstractionChange ? SGFContext() : C, IsNotTake); |
| access.endAccess(*this); |
| } |
| |
| // If we're accessing this member with an abstraction change, perform that |
| // now. |
| if (hasAbstractionChange) |
| result = |
| emitOrigToSubstValue(loc, result, origFormalType, substFormalType, C); |
| return RValue(*this, loc, substFormalType, result); |
| } |
| |
| ManagedValue SILGenFunction::emitAddressOfLValue(SILLocation loc, |
| LValue &&src, |
| TSanKind tsanKind) { |
| // Write is only included in this list because of property behaviors. |
| // It's obviously unreasonable, though. |
| assert(src.getAccessKind() == SGFAccessKind::IgnoredRead || |
| src.getAccessKind() == SGFAccessKind::BorrowedAddressRead || |
| src.getAccessKind() == SGFAccessKind::OwnedAddressRead || |
| src.getAccessKind() == SGFAccessKind::ReadWrite || |
| src.getAccessKind() == SGFAccessKind::Write); |
| |
| ManagedValue addr; |
| PathComponent &&component = |
| drillToLastComponent(*this, loc, std::move(src), addr, tsanKind); |
| |
| addr = drillIntoComponent(*this, loc, std::move(component), addr, tsanKind); |
| assert(addr.getType().isAddress() && |
| "resolving lvalue did not give an address"); |
| return ManagedValue::forLValue(addr.getValue()); |
| } |
| |
| ManagedValue SILGenFunction::emitBorrowedLValue(SILLocation loc, |
| LValue &&src, |
| TSanKind tsanKind) { |
| assert(src.getAccessKind() == SGFAccessKind::IgnoredRead || |
| src.getAccessKind() == SGFAccessKind::BorrowedAddressRead || |
| src.getAccessKind() == SGFAccessKind::BorrowedObjectRead); |
| bool isIgnored = src.getAccessKind() == SGFAccessKind::IgnoredRead; |
| |
| ManagedValue base; |
| PathComponent &&component = |
| drillToLastComponent(*this, loc, std::move(src), base, tsanKind); |
| |
| auto value = |
| drillIntoComponent(*this, loc, std::move(component), base, tsanKind); |
| |
| // If project() returned an owned value, and the caller cares, borrow it. |
| if (value.hasCleanup() && !isIgnored) |
| value = value.formalAccessBorrow(*this, loc); |
| return value; |
| } |
| |
| LValue |
| SILGenFunction::emitOpenExistentialLValue(SILLocation loc, |
| LValue &&lv, |
| CanArchetypeType openedArchetype, |
| CanType formalRValueType, |
| SGFAccessKind accessKind) { |
| assert(!formalRValueType->hasLValueType()); |
| LValueTypeData typeData = { |
| accessKind, AbstractionPattern::getOpaque(), formalRValueType, |
| getLoweredType(formalRValueType).getASTType() |
| }; |
| |
| // Open up the existential. |
| auto rep = lv.getTypeOfRValue() |
| .getPreferredExistentialRepresentation(SGM.M); |
| switch (rep) { |
| case ExistentialRepresentation::Opaque: |
| case ExistentialRepresentation::Boxed: { |
| lv.add<OpenOpaqueExistentialComponent>(openedArchetype, typeData); |
| break; |
| } |
| case ExistentialRepresentation::Metatype: |
| case ExistentialRepresentation::Class: { |
| lv.add<OpenNonOpaqueExistentialComponent>(openedArchetype, typeData); |
| break; |
| } |
| case ExistentialRepresentation::None: |
| llvm_unreachable("cannot open non-existential"); |
| } |
| |
| return std::move(lv); |
| } |
| |
| static bool trySetterPeephole(SILGenFunction &SGF, SILLocation loc, |
| ArgumentSource &&src, LValue &&dest) { |
| // The last component must be a getter/setter. |
| // TODO: allow reabstraction here, too. |
| auto &component = **(dest.end() - 1); |
| if (component.getKind() != PathComponent::GetterSetterKind) |
| return false; |
| |
| // We cannot apply the peephole if the l-value includes an |
| // open-existential component because we need to make sure that |
| // the opened archetype is available everywhere during emission. |
| // TODO: should we instead just immediately open the existential |
| // during emitLValue and simply leave the opened address in the LValue? |
| // Or is there some reasonable way to detect that this is happening |
| // and avoid affecting cases where it is not necessary? |
| for (auto &componentPtr : dest) { |
| if (componentPtr->isOpenExistential()) |
| return false; |
| } |
| |
| auto &setterComponent = static_cast<GetterSetterComponent&>(component); |
| setterComponent.emitAssignWithSetter(SGF, loc, std::move(dest), |
| std::move(src)); |
| return true;; |
| } |
| |
| void SILGenFunction::emitAssignToLValue(SILLocation loc, RValue &&src, |
| LValue &&dest) { |
| emitAssignToLValue(loc, ArgumentSource(loc, std::move(src)), std::move(dest)); |
| } |
| |
| void SILGenFunction::emitAssignToLValue(SILLocation loc, |
| ArgumentSource &&src, |
| LValue &&dest) { |
| // Enter a FormalEvaluationScope so that formal access to independent LValue |
| // components do not overlap. Furthermore, use an ArgumentScope to force |
| // cleanup of materialized LValues immediately, before evaluating the next |
| // LValue. For example: (x[i], x[j]) = a, b |
| ArgumentScope argScope(*this, loc); |
| |
| // If the last component is a getter/setter component, use a special |
| // generation pattern that allows us to peephole the emission of the RHS. |
| if (trySetterPeephole(*this, loc, std::move(src), std::move(dest))) { |
| argScope.pop(); |
| return; |
| } |
| |
| // Otherwise, force the RHS now to preserve evaluation order. |
| auto srcLoc = src.getLocation(); |
| RValue srcValue = std::move(src).getAsRValue(*this); |
| |
| // Peephole: instead of materializing and then assigning into a |
| // translation component, untransform the value first. |
| while (dest.isLastComponentTranslation()) { |
| srcValue = std::move(dest.getLastTranslationComponent()) |
| .untranslate(*this, loc, std::move(srcValue)); |
| dest.dropLastTranslationComponent(); |
| } |
| |
| src = ArgumentSource(srcLoc, std::move(srcValue)); |
| |
| // Resolve all components up to the last, keeping track of value-type logical |
| // properties we need to write back to. |
| ManagedValue destAddr; |
| PathComponent &&component = |
| drillToLastComponent(*this, loc, std::move(dest), destAddr); |
| |
| // Write to the tail component. |
| if (component.isPhysical()) { |
| auto finalDestAddr = |
| std::move(component).project(*this, loc, destAddr); |
| assert(finalDestAddr.getType().isAddress()); |
| |
| auto value = std::move(src).getAsRValue(*this).ensurePlusOne(*this, loc); |
| std::move(value).assignInto(*this, loc, finalDestAddr.getValue()); |
| } else { |
| std::move(component.asLogical()).set(*this, loc, std::move(src), destAddr); |
| } |
| |
| // The writeback scope closing will propagate the value back up through the |
| // writeback chain. |
| argScope.pop(); |
| } |
| |
| void SILGenFunction::emitCopyLValueInto(SILLocation loc, LValue &&src, |
| Initialization *dest) { |
| auto skipPeephole = [&]{ |
| auto loaded = emitLoadOfLValue(loc, std::move(src), SGFContext(dest)); |
| if (!loaded.isInContext()) |
| std::move(loaded).forwardInto(*this, loc, dest); |
| }; |
| |
| // If the source is a physical lvalue, the destination is a single address, |
| // and there's no semantic conversion necessary, do a copy_addr from the |
| // lvalue into the destination. |
| if (!src.isPhysical()) |
| return skipPeephole(); |
| if (!dest->canPerformInPlaceInitialization()) |
| return skipPeephole(); |
| auto destAddr = dest->getAddressForInPlaceInitialization(*this, loc); |
| assert(src.getTypeOfRValue().getASTType() |
| == destAddr->getType().getASTType()); |
| |
| auto srcAddr = emitAddressOfLValue(loc, std::move(src)).getUnmanagedValue(); |
| |
| UnenforcedAccess access; |
| SILValue accessAddress = |
| access.beginAccess(*this, loc, destAddr, SILAccessKind::Modify); |
| B.createCopyAddr(loc, srcAddr, accessAddress, IsNotTake, IsInitialization); |
| access.endAccess(*this); |
| |
| dest->finishInitialization(*this); |
| } |
| |
| void SILGenFunction::emitAssignLValueToLValue(SILLocation loc, LValue &&src, |
| LValue &&dest) { |
| // Only perform the peephole if both operands are physical, there's no |
| // semantic conversion necessary, and exclusivity enforcement |
| // is not enabled. The peephole interferes with exclusivity enforcement |
| // because it causes the formal accesses to the source and destination to |
| // overlap. |
| bool peepholeConflict = |
| !src.isObviouslyNonConflicting(dest, SGFAccessKind::OwnedObjectRead, |
| SGFAccessKind::Write); |
| |
| if (peepholeConflict || !src.isPhysical() || !dest.isPhysical()) { |
| RValue loaded = emitLoadOfLValue(loc, std::move(src), SGFContext()); |
| emitAssignToLValue(loc, std::move(loaded), std::move(dest)); |
| return; |
| } |
| |
| auto &rvalueTL = getTypeLowering(src.getTypeOfRValue()); |
| |
| auto srcAddr = emitAddressOfLValue(loc, std::move(src)).getUnmanagedValue(); |
| auto destAddr = emitAddressOfLValue(loc, std::move(dest)).getUnmanagedValue(); |
| |
| if (srcAddr->getType() == destAddr->getType()) { |
| B.createCopyAddr(loc, srcAddr, destAddr, IsNotTake, IsNotInitialization); |
| } else { |
| // If there's a semantic conversion necessary, do a load then assign. |
| auto loaded = emitLoad(loc, srcAddr, rvalueTL, SGFContext(), IsNotTake); |
| loaded.assignInto(*this, loc, destAddr); |
| } |
| } |