| //===--- 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/AST/PropertyWrappers.h" |
| #include "swift/AST/TypeCheckRequests.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->getBaseIdentifier()) |
| .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(TypeExpansionContext context, |
| SILGenModule &SGM, |
| SGFAccessKind accessKind, |
| AbstractionPattern origFormalType, |
| CanType substFormalType) { |
| return { |
| accessKind, origFormalType, substFormalType, |
| SGM.Types.getLoweredRValueType(context, origFormalType, substFormalType)}; |
| } |
| |
| static LValueTypeData getLogicalStorageTypeData(TypeExpansionContext context, |
| SILGenModule &SGM, |
| SGFAccessKind accessKind, |
| CanType substFormalType) { |
| assert(!isa<ReferenceStorageType>(substFormalType)); |
| AbstractionPattern origFormalType( |
| substFormalType.getReferenceStorageReferent()); |
| return getAbstractedTypeData(context, SGM, accessKind, origFormalType, |
| substFormalType); |
| } |
| |
| static LValueTypeData getPhysicalStorageTypeData(TypeExpansionContext context, |
| SILGenModule &SGM, |
| SGFAccessKind accessKind, |
| AbstractStorageDecl *storage, |
| CanType substFormalType) { |
| assert(!isa<ReferenceStorageType>(substFormalType)); |
| auto origFormalType = SGM.Types.getAbstractionPattern(storage) |
| .getReferenceStorageReferentType(); |
| return getAbstractedTypeData(context, 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; |
| |
| auto storage = AccessedStorage::compute(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() == |
| ExistentialRepresentation::Boxed) && |
| "base value of open-existential component was not an address or a " |
| "boxed existential?"); |
| SILValue addr; |
| |
| auto rep = base.getType().getPreferredExistentialRepresentation(); |
| 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); |
| // Error comes back to us with a +1 cleanup that is not a formal |
| // access cleanup. We need it to be that so that the load nests |
| // properly with other lvalue scoped things like the borrow below. |
| error = SGF.emitFormalAccessManagedRValueWithCleanup( |
| loc, error.forward(SGF)); |
| } |
| SILType addrType = getTypeOfRValue().getAddressType(); |
| addr = SGF.B.createOpenExistentialBox(loc, error, addrType).getValue(); |
| 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() |
| == ExistentialRepresentation::Metatype); |
| ref = ManagedValue::forUnmanaged( |
| SGF.B.createOpenExistentialMetatype(loc, |
| result.getUnmanagedValue(), |
| getTypeOfRValue())); |
| } else { |
| assert(refType.getPreferredExistentialRepresentation() |
| == 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)) { |
| const DeclName name = dre->getDecl()->getName(); |
| 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( |
| SGF.getTypeExpansionContext(), accessor); |
| return accessorSelf.getInterfaceType() |
| && 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) |
| { |
| } |
| |
| bool canRewriteSetAsPropertyWrapperInit(SILGenFunction &SGF) const { |
| if (auto *VD = dyn_cast<VarDecl>(Storage)) { |
| // If this is not a wrapper property that can be initialized from |
| // a value of the wrapped type, we can't perform the initialization. |
| auto wrapperInfo = VD->getPropertyWrapperBackingPropertyInfo(); |
| if (!wrapperInfo.initializeFromOriginal) |
| return false; |
| |
| bool isAssignmentToSelfParamInInit = |
| IsOnSelfParameter && |
| isa<ConstructorDecl>(SGF.FunctionDC->getAsDecl()); |
| |
| // Assignment to a wrapped property can only be re-written to initialization for |
| // members of `self` in an initializer, and for local variables. |
| if (!(isAssignmentToSelfParamInInit || VD->getDeclContext()->isLocalContext())) |
| return false; |
| |
| // If this var isn't in a type context, assignment will always use the setter |
| // if there is an initial value. |
| if (!VD->getDeclContext()->isTypeContext() && |
| wrapperInfo.wrappedValuePlaceholder->getOriginalWrappedValue()) |
| return false; |
| |
| // If we have a nonmutating setter on a value type, the call |
| // captures all of 'self' and we cannot rewrite an assignment |
| // into an initialization. |
| |
| // Unless this is an assignment to a self parameter inside a |
| // constructor, in which case we would like to still emit a |
| // assign_by_wrapper because the setter will be deleted by lowering |
| // anyway. |
| if (!isAssignmentToSelfParamInInit && |
| !VD->isSetterMutating() && |
| VD->getDeclContext()->getSelfNominalTypeDecl() && |
| VD->isInstanceMember() && |
| !VD->getDeclContext()->getDeclaredInterfaceType() |
| ->hasReferenceSemantics()) { |
| return false; |
| } |
| |
| // If this property wrapper uses autoclosure in it's initializer, |
| // the argument types of the setter and initializer shall be |
| // different, so we don't rewrite an assignment into an |
| // initialization. |
| return !wrapperInfo.wrappedValuePlaceholder->isAutoClosure(); |
| } |
| |
| return false; |
| } |
| |
| 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); |
| } |
| |
| /// Determine whether the backing variable for the given |
| /// property (that has a wrapper) is visible from the |
| /// current context. |
| static bool isBackingVarVisible(VarDecl *field, |
| DeclContext *fromDC){ |
| VarDecl *backingVar = field->getPropertyWrapperBackingProperty(); |
| return backingVar->isAccessibleFrom(fromDC); |
| } |
| |
| void set(SILGenFunction &SGF, SILLocation loc, |
| ArgumentSource &&value, ManagedValue base) && override { |
| assert(getAccessorDecl()->isSetter()); |
| SILDeclRef setter = Accessor; |
| |
| if (canRewriteSetAsPropertyWrapperInit(SGF) && |
| !Storage->isStatic() && |
| isBackingVarVisible(cast<VarDecl>(Storage), |
| SGF.FunctionDC)) { |
| // This is wrapped property. Instead of emitting a setter, emit an |
| // assign_by_wrapper with the allocating initializer function and the |
| // setter function as arguments. DefiniteInitializtion will then decide |
| // between the two functions, depending if it's an initialization or a |
| // re-assignment. |
| // |
| VarDecl *field = cast<VarDecl>(Storage); |
| VarDecl *backingVar = field->getPropertyWrapperBackingProperty(); |
| assert(backingVar); |
| auto FieldType = field->getValueInterfaceType(); |
| auto ValType = backingVar->getValueInterfaceType(); |
| if (!Substitutions.empty()) { |
| FieldType = FieldType.subst(Substitutions); |
| ValType = ValType.subst(Substitutions); |
| } |
| |
| // TODO: revist minimal |
| SILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType( |
| TypeExpansionContext::minimal(), backingVar, |
| ValType->getCanonicalType()); |
| auto typeData = |
| getLogicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM, |
| getTypeData().AccessKind, |
| ValType->getCanonicalType()); |
| |
| // Get the address of the storage property. |
| ManagedValue proj; |
| if (!BaseFormalType) { |
| proj = SGF.maybeEmitValueOfLocalVarDecl( |
| backingVar, AccessKind::Write); |
| } else if (BaseFormalType->mayHaveSuperclass()) { |
| RefElementComponent REC(backingVar, LValueOptions(), varStorageType, |
| typeData); |
| proj = std::move(REC).project(SGF, loc, base); |
| } else { |
| assert(BaseFormalType->getStructOrBoundGenericStruct()); |
| StructElementComponent SEC(backingVar, varStorageType, typeData); |
| proj = std::move(SEC).project(SGF, loc, base); |
| } |
| |
| // The property wrapper backing initializer forms an instance of |
| // the backing storage type from a wrapped value. |
| SILDeclRef initConstant( |
| field, SILDeclRef::Kind::PropertyWrapperBackingInitializer); |
| SILValue initFRef = SGF.emitGlobalFunctionRef(loc, initConstant); |
| |
| PartialApplyInst *initPAI = |
| SGF.B.createPartialApply(loc, initFRef, |
| Substitutions, ArrayRef<SILValue>(), |
| ParameterConvention::Direct_Guaranteed); |
| ManagedValue initFn = SGF.emitManagedRValueWithCleanup(initPAI); |
| |
| // Create the allocating setter function. It captures the base address. |
| auto setterInfo = |
| SGF.getConstantInfo(SGF.getTypeExpansionContext(), setter); |
| SILValue setterFRef; |
| if (setter.hasDecl() && setter.getDecl()->shouldUseObjCDispatch()) { |
| // Emit a thunk we might have to bridge arguments. |
| auto foreignSetterThunk = setter.asForeign(false); |
| setterFRef = |
| SGF.emitDynamicMethodRef( |
| loc, foreignSetterThunk, |
| SGF.SGM.Types |
| .getConstantInfo(SGF.getTypeExpansionContext(), foreignSetterThunk) |
| .SILFnType) |
| .getValue(); |
| |
| } else { |
| setterFRef = SGF.emitGlobalFunctionRef(loc, setter, setterInfo); |
| } |
| |
| CanSILFunctionType setterTy = setterFRef->getType().castTo<SILFunctionType>(); |
| SILFunctionConventions setterConv(setterTy, SGF.SGM.M); |
| |
| // Emit captures for the setter |
| SmallVector<SILValue, 4> capturedArgs; |
| auto captureInfo = SGF.SGM.Types.getLoweredLocalCaptures(setter); |
| if (!captureInfo.getCaptures().empty()) { |
| SmallVector<ManagedValue, 4> captures; |
| SGF.emitCaptures(loc, setter, CaptureEmission::AssignByWrapper, captures); |
| |
| for (auto capture : captures) |
| capturedArgs.push_back(capture.forward(SGF)); |
| } else { |
| assert(base); |
| |
| SILValue capturedBase; |
| unsigned argIdx = setterConv.getNumSILArguments() - 1; |
| |
| if (setterConv.getSILArgumentConvention(argIdx).isInoutConvention()) { |
| capturedBase = base.getValue(); |
| } else { |
| capturedBase = base.copy(SGF, loc).forward(SGF); |
| } |
| |
| // If the base is a reference and the setter expects a value, emit a |
| // load. This pattern is emitted for property wrappers with a |
| // nonmutating setter, for example. |
| if (base.getType().isAddress() && |
| base.getType().getObjectType() == |
| setterConv.getSILArgumentType(argIdx, |
| SGF.getTypeExpansionContext())) { |
| capturedBase = SGF.B.createTrivialLoadOr( |
| loc, capturedBase, LoadOwnershipQualifier::Take); |
| } |
| |
| capturedArgs.push_back(capturedBase); |
| } |
| |
| PartialApplyInst *setterPAI = |
| SGF.B.createPartialApply(loc, setterFRef, |
| Substitutions, capturedArgs, |
| ParameterConvention::Direct_Guaranteed); |
| ManagedValue setterFn = SGF.emitManagedRValueWithCleanup(setterPAI); |
| |
| // Create the assign_by_wrapper with the allocator and setter. |
| // FIXME: This should use CallEmission instead of doing everything manually. |
| assert(value.isRValue()); |
| ManagedValue Mval = std::move(value).asKnownRValue(SGF). |
| getAsSingleValue(SGF, loc); |
| auto substSetterTy = setterTy->substGenericArgs(SGF.SGM.M, Substitutions, |
| SGF.getTypeExpansionContext()); |
| auto param = substSetterTy->getParameters()[0]; |
| SILType loweredSubstArgType = Mval.getType(); |
| if (param.isIndirectInOut()) { |
| loweredSubstArgType = |
| SILType::getPrimitiveAddressType(loweredSubstArgType.getASTType()); |
| } |
| auto loweredSubstParamTy = SILType::getPrimitiveType( |
| param.getArgumentType(SGF.SGM.M, substSetterTy, |
| SGF.getTypeExpansionContext()), |
| loweredSubstArgType.getCategory()); |
| // Handle reabstraction differences. |
| if (Mval.getType() != loweredSubstParamTy) { |
| Mval = SGF.emitSubstToOrigValue(loc, Mval, |
| SGF.SGM.Types.getAbstractionPattern(field), |
| FieldType->getCanonicalType()); |
| } |
| |
| // If we need the argument in memory, materialize an address. |
| if (setterConv.getSILArgumentConvention(0).isIndirectConvention() && |
| !Mval.getType().isAddress()) { |
| Mval = Mval.materialize(SGF, loc); |
| } |
| |
| SGF.B.createAssignByWrapper(loc, Mval.forward(SGF), proj.forward(SGF), |
| initFn.getValue(), setterFn.getValue(), |
| AssignOwnershipQualifier::Unknown); |
| |
| return; |
| } |
| |
| 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, loc, tmp); |
| 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 origType = AbstractionPattern::getOpaque(); |
| auto loweredTy = SGF.getLoweredType(origType, value.getSubstRValueType()); |
| |
| auto setValue = |
| std::move(value).getAsSingleValue(SGF, origType, loweredTy); |
| 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->getInvocationGenericSignature(), |
| keyPathTy->getGenericArgs(), {}); |
| |
| auto substFnType = projectFnType->substGenericArgs( |
| SGF.SGM.M, subs, SGF.getTypeExpansionContext()); |
| |
| // 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()->getName() == 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.getTypeExpansionContext(), 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.getTypeExpansionContext(), 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->getOpaqueAccessor(accessorKind)); |
| |
| switch (accessorKind) { |
| case AccessorKind::Get: |
| case AccessorKind::Set: { |
| auto typeData = getLogicalStorageTypeData( |
| SGF.getTypeExpansionContext(), SGF.SGM, AccessKind, FormalRValueType); |
| return asImpl().emitUsingGetterSetter(accessor, isDirect, typeData); |
| } |
| |
| case AccessorKind::Address: |
| case AccessorKind::MutableAddress: { |
| auto typeData = |
| getPhysicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM, |
| AccessKind, Storage, FormalRValueType); |
| return asImpl().emitUsingAddressor(accessor, isDirect, typeData); |
| } |
| |
| case AccessorKind::Read: |
| case AccessorKind::Modify: { |
| auto typeData = |
| getPhysicalStorageTypeData(SGF.getTypeExpansionContext(), 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"); |
| } |
| }; |
| } |
| |
| static LValue emitLValueForNonMemberVarDecl(SILGenFunction &SGF, |
| SILLocation loc, |
| ConcreteDeclRef declRef, |
| CanType formalRValueType, |
| SGFAccessKind accessKind, |
| LValueOptions options, |
| AccessSemantics semantics) { |
| LValue lv; |
| |
| auto *var = cast<VarDecl>(declRef.getDecl()); |
| auto subs = declRef.getSubstitutions(); |
| if (!subs) |
| subs = SGF.F.getForwardingSubstitutionMap(); |
| |
| auto access = getFormalAccessKind(accessKind); |
| auto strategy = var->getAccessStrategy(semantics, access, |
| SGF.SGM.M.getSwiftModule(), |
| SGF.F.getResilienceExpansion()); |
| |
| lv.addNonMemberVarComponent(SGF, loc, var, subs, |
| options, accessKind, strategy, formalRValueType); |
| |
| return lv; |
| } |
| |
| /// Map a SILGen access kind back to an AST access kind. |
| static AccessKind mapAccessKind(SGFAccessKind accessKind) { |
| switch (accessKind) { |
| case SGFAccessKind::IgnoredRead: |
| case SGFAccessKind::BorrowedAddressRead: |
| case SGFAccessKind::BorrowedObjectRead: |
| case SGFAccessKind::OwnedAddressRead: |
| case SGFAccessKind::OwnedObjectRead: |
| return AccessKind::Read; |
| |
| case SGFAccessKind::Write: |
| return AccessKind::Write; |
| |
| case SGFAccessKind::ReadWrite: |
| return AccessKind::ReadWrite; |
| } |
| } |
| |
| void LValue::addNonMemberVarComponent(SILGenFunction &SGF, SILLocation loc, |
| VarDecl *var, |
| SubstitutionMap subs, |
| LValueOptions options, |
| SGFAccessKind accessKind, |
| AccessStrategy strategy, |
| CanType formalRValueType) { |
| struct NonMemberVarAccessEmitter : |
| AccessEmitter<NonMemberVarAccessEmitter, VarDecl> { |
| LValue &LV; |
| SILLocation Loc; |
| SubstitutionMap Subs; |
| LValueOptions Options; |
| |
| NonMemberVarAccessEmitter(SILGenFunction &SGF, SILLocation loc, |
| VarDecl *var, 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, Subs, |
| CanType(), typeData, storageType, nullptr, |
| PreparedArguments(), |
| /* isOnSelfParameter */ false); |
| } |
| |
| void emitUsingCoroutineAccessor(SILDeclRef accessor, bool isDirect, |
| LValueTypeData typeData) { |
| LV.add<CoroutineAccessorComponent>( |
| Storage, accessor, |
| /*isSuper*/ false, isDirect, Subs, CanType(), typeData, nullptr, |
| PreparedArguments(), /*isOnSelfParameter*/ false); |
| } |
| |
| void emitUsingGetterSetter(SILDeclRef accessor, bool isDirect, |
| LValueTypeData typeData) { |
| LV.add<GetterSetterComponent>( |
| Storage, accessor, |
| /*isSuper=*/false, isDirect, Subs, CanType(), typeData, nullptr, |
| PreparedArguments(), /* isOnSelfParameter */ false); |
| } |
| |
| void emitUsingMaterialization(AccessStrategy readStrategy, |
| AccessStrategy writeStrategy, |
| LValueTypeData typeData) { |
| LV.add<MaterializeToTemporaryComponent>( |
| Storage, /*super*/ false, Subs, 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 astAccessKind = mapAccessKind(this->AccessKind); |
| auto address = SGF.maybeEmitValueOfLocalVarDecl(Storage, astAccessKind); |
| |
| // 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) { |
| |