| //===--- 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 "SILGen.h" |
| #include "ArgumentSource.h" |
| #include "LValue.h" |
| #include "RValue.h" |
| #include "Scope.h" |
| #include "Initialization.h" |
| #include "swift/AST/DiagnosticsSIL.h" |
| #include "swift/AST/DiagnosticsCommon.h" |
| #include "swift/AST/GenericEnvironment.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" |
| #include "ASTVisitor.h" |
| using namespace swift; |
| using namespace Lowering; |
| |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| struct LValueWritebackCleanup : Cleanup { |
| FormalEvaluationContext::stable_iterator Depth; |
| |
| LValueWritebackCleanup() : Depth() {} |
| |
| void emit(SILGenFunction &SGF, CleanupLocation loc) override { |
| auto &evaluation = *SGF.FormalEvalContext.find(Depth); |
| assert(evaluation.getKind() == FormalAccess::Exclusive); |
| auto &lvalue = static_cast<ExclusiveBorrowFormalAccess &>(evaluation); |
| lvalue.performWriteback(SGF, /*isFinal*/ false); |
| } |
| |
| void dump(SILGenFunction &) const override { |
| #ifndef NDEBUG |
| llvm::errs() << "LValueWritebackCleanup\n" |
| << "State: " << getState() << "Depth: " << Depth.getDepth() |
| << "\n"; |
| #endif |
| } |
| }; |
| |
| } // 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.InWritebackScope); |
| |
| // 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(); |
| } |
| |
| 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; |
| } |
| |
| component->diagnoseWritebackConflict(rhs.component.get(), loc, rhs.loc, SGF); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| |
| static CanType getSubstFormalRValueType(Expr *expr) { |
| return expr->getType()->getRValueType()->getCanonicalType(); |
| } |
| |
| static LValueTypeData getLogicalStorageTypeData(SILGenModule &SGM, |
| CanType substFormalType) { |
| AbstractionPattern origFormalType( |
| substFormalType.getReferenceStorageReferent()); |
| return { |
| origFormalType, |
| substFormalType, |
| SGM.Types.getLoweredType(origFormalType, substFormalType).getObjectType() |
| }; |
| } |
| |
| static LValueTypeData getPhysicalStorageTypeData(SILGenModule &SGM, |
| AbstractStorageDecl *storage, |
| CanType substFormalType) { |
| auto origFormalType = SGM.Types.getAbstractionPattern(storage) |
| .getReferenceStorageReferentType(); |
| return { |
| origFormalType, |
| substFormalType, |
| SGM.Types.getLoweredType(origFormalType, substFormalType).getObjectType() |
| }; |
| } |
| |
| 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, AccessKind> |
| { |
| |
| public: |
| SILGenFunction &SGF; |
| SILGenLValue(SILGenFunction &SGF) : SGF(SGF) {} |
| |
| LValue visitRec(Expr *e, AccessKind accessKind, |
| AbstractionPattern orig = AbstractionPattern::getInvalid()); |
| |
| /// Dummy handler to log unimplemented nodes. |
| LValue visitExpr(Expr *e, AccessKind accessKind); |
| |
| // Nodes that form the root of lvalue paths |
| LValue visitDiscardAssignmentExpr(DiscardAssignmentExpr *e, |
| AccessKind accessKind); |
| LValue visitDeclRefExpr(DeclRefExpr *e, AccessKind accessKind); |
| LValue visitOpaqueValueExpr(OpaqueValueExpr *e, AccessKind accessKind); |
| |
| // Nodes that make up components of lvalue paths |
| |
| LValue visitMemberRefExpr(MemberRefExpr *e, AccessKind accessKind); |
| LValue visitSubscriptExpr(SubscriptExpr *e, AccessKind accessKind); |
| LValue visitTupleElementExpr(TupleElementExpr *e, AccessKind accessKind); |
| LValue visitForceValueExpr(ForceValueExpr *e, AccessKind accessKind); |
| LValue visitBindOptionalExpr(BindOptionalExpr *e, AccessKind accessKind); |
| LValue visitOpenExistentialExpr(OpenExistentialExpr *e, |
| AccessKind accessKind); |
| LValue visitKeyPathApplicationExpr(KeyPathApplicationExpr *e, |
| AccessKind accessKind); |
| |
| // Expressions that wrap lvalues |
| |
| LValue visitInOutExpr(InOutExpr *e, AccessKind accessKind); |
| LValue visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *e, |
| AccessKind accessKind); |
| }; |
| |
| static ManagedValue |
| emitGetIntoTemporary(SILGenFunction &SGF, SILLocation loc, ManagedValue base, |
| std::unique_ptr<TemporaryInitialization> &&temporaryInit, |
| LogicalPathComponent &&component) { |
| // Emit a 'get' into the temporary. |
| RValue value = |
| std::move(component).get(SGF, loc, base, SGFContext(temporaryInit.get())); |
| |
| // Force the value into the temporary if necessary. |
| if (!value.isInContext()) { |
| std::move(value).forwardInto(SGF, loc, temporaryInit.get()); |
| } |
| |
| return temporaryInit->getManagedAddress(); |
| } |
| |
| ManagedValue LogicalPathComponent::getMaterialized(SILGenFunction &SGF, |
| SILLocation loc, |
| ManagedValue base, |
| AccessKind kind) && { |
| if (getTypeOfRValue().getSwiftRValueType()->hasOpenedExistential()) { |
| if (kind == AccessKind::Read) { |
| // Emit a 'get' into the temporary. |
| RValue value = |
| std::move(*this).get(SGF, loc, base, SGFContext()); |
| |
| // Create a temporary. |
| std::unique_ptr<TemporaryInitialization> temporaryInit = |
| SGF.emitFormalAccessTemporary(loc, |
| SGF.getTypeLowering(getTypeOfRValue())); |
| |
| std::move(value).forwardInto(SGF, loc, temporaryInit.get()); |
| |
| return temporaryInit->getManagedAddress(); |
| } |
| |
| assert(SGF.InWritebackScope && |
| "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); |
| |
| SILValue mv; |
| { |
| FormalEvaluationScope Scope(SGF); |
| |
| // Otherwise, we need to emit a get and set. Borrow the base for |
| // the getter. |
| ManagedValue getterBase = |
| base ? base.formalAccessBorrow(SGF, loc) : ManagedValue(); |
| |
| // Emit a 'get' into a temporary and then pop the borrow of base. |
| RValue value = |
| std::move(*this).get(SGF, loc, getterBase, SGFContext()); |
| |
| mv = std::move(value).forwardAsSingleValue(SGF, loc); |
| } |
| |
| auto &TL = SGF.getTypeLowering(getTypeOfRValue()); |
| |
| // Create a temporary. |
| std::unique_ptr<TemporaryInitialization> temporaryInit = |
| SGF.emitFormalAccessTemporary(loc, TL); |
| |
| SGF.emitSemanticStore(loc, mv, temporaryInit->getAddress(), |
| TL, IsInitialization); |
| temporaryInit->finishInitialization(SGF); |
| |
| auto temporary = temporaryInit->getManagedAddress(); |
| |
| // Push a writeback for the temporary. |
| pushWriteback(SGF, loc, std::move(clonedComponent), base, |
| MaterializedLValue(temporary)); |
| return temporary.unmanagedBorrow(); |
| } |
| |
| // If this is just for a read, emit a load into a temporary memory |
| // location. |
| if (kind == AccessKind::Read) { |
| // Create a temporary. |
| std::unique_ptr<TemporaryInitialization> temporaryInit = |
| SGF.emitFormalAccessTemporary(loc, |
| SGF.getTypeLowering(getTypeOfRValue())); |
| return emitGetIntoTemporary(SGF, loc, base, std::move(temporaryInit), |
| std::move(*this)); |
| } |
| |
| assert(SGF.InWritebackScope && |
| "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 temporary; |
| { |
| // Create a temporary. |
| std::unique_ptr<TemporaryInitialization> temporaryInit = |
| SGF.emitFormalAccessTemporary(loc, |
| SGF.getTypeLowering(getTypeOfRValue())); |
| |
| FormalEvaluationScope Scope(SGF); |
| |
| // Otherwise, we need to emit a get and set. Borrow the base for |
| // the getter. |
| ManagedValue getterBase = |
| base ? base.formalAccessBorrow(SGF, loc) : ManagedValue(); |
| |
| // Emit a 'get' into a temporary and then pop the borrow of base. |
| temporary = emitGetIntoTemporary( |
| SGF, loc, getterBase, std::move(temporaryInit), std::move(*this)); |
| } |
| |
| // Push a writeback for the temporary. |
| pushWriteback(SGF, loc, std::move(clonedComponent), base, |
| MaterializedLValue(temporary)); |
| return temporary.unmanagedBorrow(); |
| } |
| |
| 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 (!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, std::move(rvalue), base); |
| } |
| |
| InOutConversionScope::InOutConversionScope(SILGenFunction &SGF) |
| : SGF(SGF) |
| { |
| assert(SGF.InWritebackScope |
| && "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 LogicalPathComponent::_anchor() {} |
| |
| void PathComponent::dump() const { |
| print(llvm::errs()); |
| } |
| |
| /// Return the LValueTypeData for a SIL value with the given AST formal type. |
| static LValueTypeData getValueTypeData(CanType formalType, |
| SILValue value) { |
| return { |
| AbstractionPattern(formalType), |
| formalType, |
| value->getType().getObjectType() |
| }; |
| } |
| static LValueTypeData getValueTypeData(SILGenFunction &SGF, Expr *e) { |
| CanType formalType = getSubstFormalRValueType(e); |
| SILType loweredType = SGF.getLoweredType(formalType).getObjectType(); |
| |
| return { |
| 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, |
| valueTypeData.TypeOfRValue.getAddressType()); |
| |
| // 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: |
| AccessKind getBaseAccessKind(SILGenFunction &SGF, |
| AccessKind accessKind) const override { |
| llvm_unreachable("called getBaseAccessKind on pseudo-component"); |
| } |
| 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, |
| RValue &&value, ManagedValue base) && override { |
| llvm_unreachable("called set on a pseudo-component"); |
| } |
| ManagedValue getMaterialized(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, |
| AccessKind accessKind) && override { |
| llvm_unreachable("called getMaterialized on a pseudo-component"); |
| } |
| |
| void diagnoseWritebackConflict(LogicalPathComponent *rhs, |
| SILLocation loc1, SILLocation loc2, |
| SILGenFunction &SGF) override { |
| // do nothing |
| } |
| }; |
| |
| 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 { |
| assert(base.isLValue()); |
| SGF.B.createEndAccess(loc, base.getValue(), /*abort*/ false); |
| } |
| |
| void print(raw_ostream &OS) const override { |
| OS << "EndAccessPseudoComponent"; |
| } |
| }; |
| } // end anonymous namespace |
| |
| static SILValue enterAccessScope(SILGenFunction &SGF, SILLocation loc, |
| SILValue addr, LValueTypeData typeData, |
| AccessKind accessKind, |
| SILAccessEnforcement enforcement) { |
| auto silAccessKind = [&] { |
| switch (accessKind) { |
| case AccessKind::Read: |
| return SILAccessKind::Read; |
| case AccessKind::Write: |
| case AccessKind::ReadWrite: |
| return SILAccessKind::Modify; |
| } |
| }(); |
| |
| // Hack for materializeForSet emission, where we can't safely |
| // push a begin/end access. |
| if (!SGF.InWritebackScope) { |
| auto unpairedAccesses = SGF.UnpairedAccessesForMaterializeForSet; |
| assert(unpairedAccesses && |
| "tried to enter access scope without a writeback scope!"); |
| if (enforcement == SILAccessEnforcement::Dynamic) { |
| SGF.B.createBeginUnpairedAccess(loc, addr, unpairedAccesses->Buffer, |
| silAccessKind, enforcement); |
| unpairedAccesses->NumAccesses++; |
| } |
| return addr; |
| } |
| |
| // Enter the access. |
| addr = SGF.B.createBeginAccess(loc, addr, silAccessKind, enforcement); |
| |
| // 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; |
| } |
| |
| namespace { |
| class RefElementComponent : public PhysicalPathComponent { |
| VarDecl *Field; |
| SILType SubstFieldType; |
| public: |
| RefElementComponent(VarDecl *field, SILType substFieldType, |
| LValueTypeData typeData) |
| : PhysicalPathComponent(typeData, RefElementKind), |
| Field(field), SubstFieldType(substFieldType) {} |
| |
| ManagedValue offset(SILGenFunction &SGF, SILLocation loc, ManagedValue base, |
| AccessKind accessKind) && override { |
| assert(base.getType().isObject() && |
| "base for ref element component must be an object"); |
| 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. |
| base = base.formalAccessBorrow(SGF, loc); |
| SILValue result = |
| SGF.B.createRefElementAddr(loc, base.getUnmanagedValue(), |
| Field, SubstFieldType); |
| |
| if (auto enforcement = SGF.getDynamicEnforcement(Field)) { |
| result = enterAccessScope(SGF, loc, result, getTypeData(), |
| accessKind, *enforcement); |
| } |
| |
| return ManagedValue::forLValue(result); |
| } |
| |
| void print(raw_ostream &OS) const override { |
| OS << "RefElementComponent(" << Field->getName() << ")\n"; |
| } |
| }; |
| |
| class TupleElementComponent : public PhysicalPathComponent { |
| unsigned ElementIndex; |
| public: |
| TupleElementComponent(unsigned elementIndex, LValueTypeData typeData) |
| : PhysicalPathComponent(typeData, TupleElementKind), |
| ElementIndex(elementIndex) {} |
| |
| ManagedValue offset(SILGenFunction &SGF, SILLocation loc, ManagedValue base, |
| AccessKind accessKind) && override { |
| assert(base && "invalid value for element base"); |
| // 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 print(raw_ostream &OS) const override { |
| OS << "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) {} |
| |
| ManagedValue offset(SILGenFunction &SGF, SILLocation loc, ManagedValue base, |
| AccessKind accessKind) && override { |
| assert(base && "invalid value for element base"); |
| // 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 print(raw_ostream &OS) const override { |
| OS << "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 { |
| public: |
| ForceOptionalObjectComponent(LValueTypeData typeData) |
| : PhysicalPathComponent(typeData, OptionalObjectKind) {} |
| |
| ManagedValue offset(SILGenFunction &SGF, SILLocation loc, ManagedValue base, |
| AccessKind accessKind) && override { |
| // Assert that the optional value is present and return the projected out |
| // payload. |
| return SGF.emitPreconditionOptionalHasValue(loc, base); |
| } |
| |
| void print(raw_ostream &OS) const override { |
| OS << "ForceOptionalObjectComponent()\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); |
| } |
| |
| ManagedValue offset(SILGenFunction &SGF, SILLocation loc, ManagedValue base, |
| AccessKind accessKind) && override { |
| assert(base.getType().isExistentialType() && |
| "base for open existential component must be an existential"); |
| assert(base.getType().isAddress() && |
| "base value of open-existential component was not an address?"); |
| SILValue addr; |
| |
| auto rep = base.getType().getPreferredExistentialRepresentation(SGF.SGM.M); |
| switch (rep) { |
| case ExistentialRepresentation::Opaque: |
| addr = SGF.B.createOpenExistentialAddr( |
| loc, base.getValue(), getTypeOfRValue().getAddressType(), |
| getOpenedExistentialAccessFor(accessKind)); |
| break; |
| case ExistentialRepresentation::Boxed: { |
| auto &TL = SGF.getTypeLowering(base.getType()); |
| auto error = SGF.emitLoad(loc, base.getValue(), TL, |
| SGFContext(), IsNotTake); |
| addr = SGF.B.createOpenExistentialBox( |
| loc, error.getValue(), getTypeOfRValue().getAddressType()); |
| break; |
| } |
| default: |
| llvm_unreachable("Bad existential representation for address-only type"); |
| } |
| |
| return ManagedValue::forLValue(addr); |
| } |
| |
| void print(raw_ostream &OS) const override { |
| OS << "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) {} |
| |
| AccessKind getBaseAccessKind(SILGenFunction &SGF, |
| AccessKind kind) const override { |
| // Always use the same access kind for the base. |
| return kind; |
| } |
| |
| void diagnoseWritebackConflict(LogicalPathComponent *RHS, |
| SILLocation loc1, SILLocation loc2, |
| SILGenFunction &SGF) override { |
| // no useful writeback diagnostics at this point |
| } |
| |
| 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. |
| auto result = SGF.emitLoad(loc, base.getValue(), TL, |
| SGFContext(), IsNotTake); |
| |
| assert(refType.isAnyExistentialType() && |
| "base for open existential component must be an existential"); |
| ManagedValue ref; |
| if (refType.is<ExistentialMetatypeType>()) { |
| assert(refType.getPreferredExistentialRepresentation(SGF.SGM.M) |
| == ExistentialRepresentation::Metatype); |
| ref = ManagedValue::forUnmanaged( |
| SGF.B.createOpenExistentialMetatype(loc, |
| result.getUnmanagedValue(), |
| getTypeOfRValue())); |
| } else { |
| assert(refType.getPreferredExistentialRepresentation(SGF.SGM.M) |
| == ExistentialRepresentation::Class); |
| ref = SGF.B.createOpenExistentialRef(loc, result, getTypeOfRValue()); |
| } |
| |
| return RValue(SGF, loc, getSubstFormalType(), ref); |
| } |
| |
| void set(SILGenFunction &SGF, SILLocation loc, |
| RValue &&value, ManagedValue base) && override { |
| auto payload = std::move(value).forwardAsSingleValue(SGF, loc); |
| |
| 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 { |
| 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 print(raw_ostream &OS) const override { |
| OS << "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()); |
| } |
| |
| ManagedValue offset(SILGenFunction &SGF, SILLocation loc, ManagedValue base, |
| AccessKind accessKind) && 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(), |
| accessKind, *Enforcement); |
| |
| return ManagedValue::forLValue(addr); |
| } |
| |
| bool isRValue() const override { |
| return IsRValue; |
| } |
| |
| void print(raw_ostream &OS) const override { |
| OS << "ValueComponent()\n"; |
| } |
| }; |
| } // end anonymous namespace |
| |
| static bool isReadNoneFunction(const Expr *e) { |
| // If this is a curried call to an integer literal conversion operations, then |
| // we can "safely" assume it is readnone (btw, yes this is totally gross). |
| // This is better to be attribute driven, a la rdar://15587352. |
| if (auto *dre = dyn_cast<DeclRefExpr>(e)) { |
| DeclName name = dre->getDecl()->getFullName(); |
| return (name.getArgumentNames().size() == 1 && |
| name.getBaseName() == "init" && |
| !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)) |
| return il1->getValue() == cast<IntegerLiteralExpr>(e2)->getValue(); |
| 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; |
| } |
| |
| namespace { |
| /// A helper class for implementing a component that involves |
| /// calling accessors. |
| template <class Base> |
| class AccessorBasedComponent : public Base { |
| protected: |
| // The VarDecl or SubscriptDecl being get/set. |
| AbstractStorageDecl *decl; |
| bool IsSuper; |
| bool IsDirectAccessorUse; |
| std::vector<Substitution> substitutions; |
| |
| /// The subscript index expression. Useless |
| Expr *subscriptIndexExpr; |
| RValue subscripts; |
| |
| /// AST type of the base expression, in case the accessor call |
| /// requires re-abstraction. |
| CanType baseFormalType; |
| |
| struct AccessorArgs { |
| ArgumentSource base; |
| RValue subscripts; |
| }; |
| |
| /// 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) |
| result.base = SGF.prepareAccessorBaseArg(loc, base, baseFormalType, |
| accessor); |
| |
| if (subscripts) |
| result.subscripts = std::move(subscripts); |
| |
| return result; |
| } |
| |
| AccessorBasedComponent(PathComponent::KindTy kind, |
| AbstractStorageDecl *decl, |
| bool isSuper, bool isDirectAccessorUse, |
| SubstitutionList substitutions, |
| CanType baseFormalType, |
| LValueTypeData typeData, |
| Expr *subscriptIndexExpr, |
| RValue *optSubscripts) |
| : Base(typeData, kind), decl(decl), |
| IsSuper(isSuper), IsDirectAccessorUse(isDirectAccessorUse), |
| substitutions(substitutions.begin(), substitutions.end()), |
| subscriptIndexExpr(subscriptIndexExpr), |
| baseFormalType(baseFormalType) |
| { |
| if (optSubscripts) |
| subscripts = std::move(*optSubscripts); |
| } |
| |
| AccessorBasedComponent(const AccessorBasedComponent &copied, |
| SILGenFunction &SGF, |
| SILLocation loc) |
| : Base(copied.getTypeData(), copied.getKind()), |
| decl(copied.decl), |
| IsSuper(copied.IsSuper), |
| IsDirectAccessorUse(copied.IsDirectAccessorUse), |
| substitutions(copied.substitutions), |
| subscriptIndexExpr(copied.subscriptIndexExpr), |
| subscripts(copied.subscripts.copy(SGF, loc)) , |
| baseFormalType(copied.baseFormalType) {} |
| |
| virtual SILDeclRef getAccessor(SILGenFunction &SGF, |
| AccessKind kind) const = 0; |
| |
| AccessKind getBaseAccessKind(SILGenFunction &SGF, |
| AccessKind kind) const override { |
| SILDeclRef accessor = getAccessor(SGF, kind); |
| auto accessorSelf = SGF.SGM.Types.getConstantSelfParameter(accessor); |
| if (accessorSelf.getType() && accessorSelf.isIndirectMutating()) { |
| return AccessKind::ReadWrite; |
| } else { |
| return AccessKind::Read; |
| } |
| } |
| |
| void printBase(raw_ostream &OS, StringRef name) const { |
| OS << name << "(" << decl->getBaseName() << ")"; |
| if (IsSuper) OS << " isSuper"; |
| if (IsDirectAccessorUse) OS << " isDirectAccessorUse"; |
| if (subscriptIndexExpr) { |
| OS << " subscript_index:\n"; |
| subscriptIndexExpr->print(OS, 2); |
| } |
| OS << '\n'; |
| } |
| }; |
| |
| class GetterSetterComponent |
| : public AccessorBasedComponent<LogicalPathComponent> { |
| public: |
| |
| GetterSetterComponent(AbstractStorageDecl *decl, |
| bool isSuper, bool isDirectAccessorUse, |
| SubstitutionList substitutions, |
| CanType baseFormalType, |
| LValueTypeData typeData, |
| Expr *subscriptIndexExpr = nullptr, |
| RValue *subscriptIndex = nullptr) |
| : AccessorBasedComponent(GetterSetterKind, decl, isSuper, |
| isDirectAccessorUse, substitutions, |
| baseFormalType, typeData, subscriptIndexExpr, |
| subscriptIndex) |
| { |
| } |
| |
| GetterSetterComponent(const GetterSetterComponent &copied, |
| SILGenFunction &SGF, |
| SILLocation loc) |
| : AccessorBasedComponent(copied, SGF, loc) |
| { |
| } |
| |
| SILDeclRef getAccessor(SILGenFunction &SGF, |
| AccessKind accessKind) const override { |
| if (accessKind == AccessKind::Read) { |
| return SGF.getGetterDeclRef(decl, IsDirectAccessorUse); |
| } else { |
| return SGF.getSetterDeclRef(decl, IsDirectAccessorUse); |
| } |
| } |
| |
| void set(SILGenFunction &SGF, SILLocation loc, |
| RValue &&value, ManagedValue base) && override { |
| SILDeclRef setter = SGF.getSetterDeclRef(decl, IsDirectAccessorUse); |
| |
| 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.subscripts), |
| std::move(value)); |
| } |
| |
| bool shouldUseMaterializeForSet(SILGenFunction &SGF, |
| AccessKind accessKind) { |
| // If this access is for a read, we can just call the getter. |
| if (accessKind == AccessKind::Read) |
| return false; |
| |
| // If the declaration is dynamic, there's no materializeForSet. |
| if (decl->isDynamic()) |
| return false; |
| |
| // If the declaration was imported from C, we won't gain anything |
| // from using materializeForSet, and furthermore, it might not |
| // exist. |
| if (decl->hasClangNode()) |
| return false; |
| |
| // If the declaration is not in type context, there's no |
| // materializeForSet. |
| if (!decl->getDeclContext()->isTypeContext()) |
| return false; |
| |
| // If the declaration is in a different resilience domain, we have |
| // to use materializeForSet. |
| // |
| // FIXME: Use correct ResilienceExpansion if gen is @transparent |
| if (!decl->hasFixedLayout(SGF.SGM.M.getSwiftModule(), |
| ResilienceExpansion::Maximal)) |
| return true; |
| |
| // If the declaration is dynamically dispatched through a class, |
| // we have to use materializeForSet. |
| if (auto *classDecl = dyn_cast<ClassDecl>(decl->getDeclContext())) { |
| if (decl->isFinal() || classDecl->isFinal()) |
| return false; |
| |
| return true; |
| } |
| |
| // If the declaration is dynamically dispatched through a |
| // non-ObjC protocol, we have to use materializeForSet. |
| if (auto *protoDecl = dyn_cast<ProtocolDecl>(decl->getDeclContext())) |
| if (!protoDecl->isObjC()) |
| return true; |
| |
| return false; |
| } |
| |
| ManagedValue getMaterialized(SILGenFunction &SGF, |
| SILLocation loc, |
| ManagedValue base, |
| AccessKind accessKind) && override { |
| if (!shouldUseMaterializeForSet(SGF, accessKind)) { |
| return std::move(*this).LogicalPathComponent::getMaterialized(SGF, |
| loc, base, accessKind); |
| } |
| |
| assert(decl->getMaterializeForSetFunc() && |
| "polymorphic storage without materializeForSet"); |
| assert(SGF.InWritebackScope && |
| "materializing l-value for modification without writeback scope"); |
| |
| // Allocate opaque storage for the callback to use. |
| SILValue callbackStorage = SGF.emitTemporaryAllocation(loc, |
| SILType::getPrimitiveObjectType( |
| SGF.getASTContext().TheUnsafeValueBufferType)); |
| |
| // Allocate a temporary. |
| SILValue buffer = |
| SGF.emitTemporaryAllocation(loc, getTypeOfRValue()); |
| |
| // Clone the component without cloning the indices. We don't actually |
| // consume them in writeback(). |
| std::unique_ptr<LogicalPathComponent> clonedComponent( |
| [&]() -> LogicalPathComponent* { |
| // Steal the subscript values without copying them so that we |
| // can peek at them in diagnoseWritebackConflict. |
| // |
| // This is *amazingly* unprincipled. |
| RValue borrowedSubscripts; |
| RValue *optSubscripts = nullptr; |
| if (subscripts) { |
| CanType type = subscripts.getType(); |
| SmallVector<ManagedValue, 4> values; |
| std::move(subscripts).getAll(values); |
| subscripts = RValue::withPreExplodedElements(values, type); |
| borrowedSubscripts = RValue::withPreExplodedElements(values, type); |
| optSubscripts = &borrowedSubscripts; |
| } |
| return new GetterSetterComponent(decl, IsSuper, IsDirectAccessorUse, |
| substitutions, baseFormalType, |
| getTypeData(), subscriptIndexExpr, |
| optSubscripts); |
| }()); |
| |
| SILDeclRef materializeForSet = |
| SGF.getMaterializeForSetDeclRef(decl, IsDirectAccessorUse); |
| |
| MaterializedLValue materialized; |
| { |
| FormalEvaluationScope Scope(SGF); |
| |
| // If the base is a +1 r-value, just borrow it for materializeForSet. |
| // prepareAccessorArgs will copy it if necessary. |
| ManagedValue borrowedBase = |
| base ? base.formalAccessBorrow(SGF, loc) : ManagedValue(); |
| |
| auto args = std::move(*this).prepareAccessorArgs(SGF, loc, borrowedBase, |
| materializeForSet); |
| materialized = SGF.emitMaterializeForSetAccessor( |
| loc, materializeForSet, substitutions, std::move(args.base), |
| IsSuper, IsDirectAccessorUse, std::move(args.subscripts), buffer, |
| callbackStorage); |
| |
| // Mark a value-dependence on the base. We do this regardless |
| // of whether the base is trivial because even a trivial base |
| // may be value-dependent on something non-trivial. |
| if (base) { |
| SILValue temporary = materialized.temporary.getValue(); |
| materialized.temporary = ManagedValue::forUnmanaged( |
| SGF.B.createMarkDependence(loc, temporary, base.getValue())); |
| } |
| } |
| |
| // TODO: maybe needsWriteback should be a thin function pointer |
| // to which we pass the base? That would let us use direct |
| // access for stored properties with didSet. |
| pushWriteback(SGF, loc, std::move(clonedComponent), base, materialized); |
| |
| return ManagedValue::forLValue(materialized.temporary.getValue()); |
| } |
| |
| void writeback(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, MaterializedLValue materialized, |
| bool isFinal) override { |
| // If we don't have a callback, we don't have to conditionalize |
| // the writeback. |
| if (!materialized.callback) { |
| LogicalPathComponent::writeback(SGF, loc, |
| base, materialized, |
| isFinal); |
| return; |
| } |
| |
| // Otherwise, 'materialized' holds an optional callback and the |
| // callback storage. |
| |
| // Mark the writeback as auto-generated so that we don't get |
| // warnings if we manage to devirtualize materializeForSet. |
| loc.markAutoGenerated(); |
| |
| SILModule &M = SGF.SGM.M; |
| ASTContext &ctx = SGF.getASTContext(); |
| |
| SILBasicBlock *contBB = SGF.createBasicBlock(); |
| SILBasicBlock *writebackBB = SGF.createBasicBlock(SGF.B.getInsertionBB()); |
| |
| SGF.B.createSwitchEnum(loc, materialized.callback, /*defaultDest*/ nullptr, |
| { { ctx.getOptionalSomeDecl(), writebackBB }, |
| { ctx.getOptionalNoneDecl(), contBB } }); |
| |
| // The writeback block. |
| SGF.B.setInsertionPoint(writebackBB); { |
| FullExpr scope(SGF.Cleanups, CleanupLocation::get(loc)); |
| |
| auto emptyTupleTy = |
| SILType::getPrimitiveObjectType(TupleType::getEmpty(ctx)); |
| auto rawPointerTy = SILType::getRawPointerType(ctx); |
| |
| // The callback is a BB argument from the switch_enum. |
| SILValue callback = writebackBB->createPHIArgument( |
| rawPointerTy, ValueOwnershipKind::Trivial); |
| |
| // Cast the callback to the correct polymorphic function type. |
| SILFunctionTypeRepresentation rep; |
| if (isa<ProtocolDecl>(decl->getDeclContext())) |
| rep = SILFunctionTypeRepresentation::WitnessMethod; |
| else |
| rep = SILFunctionTypeRepresentation::Method; |
| |
| auto origCallbackFnType = SGF.SGM.Types.getMaterializeForSetCallbackType( |
| decl, materialized.genericSig, materialized.origSelfType, rep); |
| auto origCallbackType = SILType::getPrimitiveObjectType(origCallbackFnType); |
| callback = SGF.B.createPointerToThinFunction(loc, callback, origCallbackType); |
| |
| auto substCallbackFnType = origCallbackFnType->substGenericArgs( |
| M, substitutions); |
| auto metatypeType = |
| SGF.getSILType(substCallbackFnType->getParameters().back()); |
| |
| // We need to borrow the base here. We can't just consume it |
| // because we're in conditionally-executed code (and because |
| // this might be a non-final use). We also need to pass it |
| // indirectly. |
| SILValue baseAddress; |
| SILValue baseMetatype; |
| if (base) { |
| if (base.getType().isAddress()) { |
| baseAddress = base.getValue(); |
| } else { |
| AbstractionPattern origSelfType(materialized.genericSig, |
| materialized.origSelfType); |
| base = SGF.emitSubstToOrigValue(loc, base, origSelfType, |
| baseFormalType); |
| |
| baseAddress = SGF.emitTemporaryAllocation(loc, base.getType()); |
| if (base.getOwnershipKind() == ValueOwnershipKind::Guaranteed) { |
| SGF.B.createStoreBorrow(loc, base.getValue(), baseAddress); |
| } else { |
| SGF.B.emitStoreValueOperation(loc, base.getValue(), baseAddress, |
| StoreOwnershipQualifier::Init); |
| } |
| } |
| baseMetatype = SGF.B.createMetatype(loc, metatypeType); |
| |
| // Otherwise, we have to pass something; use an empty tuple |
| // and an undef metatype. |
| } else { |
| baseAddress = SILUndef::get(emptyTupleTy.getAddressType(), M); |
| baseMetatype = SILUndef::get(metatypeType, M); |
| } |
| |
| SILValue temporaryPointer = |
| SGF.B.createAddressToPointer(loc, |
| materialized.temporary.getValue(), |
| rawPointerTy); |
| |
| // Apply the callback. |
| SGF.B.createApply(loc, callback, |
| substitutions, { |
| temporaryPointer, |
| materialized.callbackStorage, |
| baseAddress, |
| baseMetatype |
| }, false); |
| } |
| |
| // Continue. |
| SGF.B.emitBlock(contBB, loc); |
| } |
| |
| RValue get(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SGFContext c) && override { |
| SILDeclRef getter = SGF.getGetterDeclRef(decl, IsDirectAccessorUse); |
| |
| 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.subscripts), c); |
| } |
| |
| 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 print(raw_ostream &OS) const override { |
| printBase(OS, "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. |
| void diagnoseWritebackConflict(LogicalPathComponent *RHS, |
| SILLocation loc1, SILLocation loc2, |
| SILGenFunction &SGF) override { |
| auto &rhs = (GetterSetterComponent&)*RHS; |
| |
| // If the decls match, then this could conflict. |
| if (decl != rhs.decl || IsSuper != rhs.IsSuper) return; |
| |
| // 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. |
| if (auto storage = dyn_cast<AbstractStorageDecl>(decl)) { |
| switch (storage->getStorageKind()) { |
| case AbstractStorageDecl::Stored: |
| case AbstractStorageDecl::StoredWithTrivialAccessors: |
| case AbstractStorageDecl::Addressed: |
| case AbstractStorageDecl::AddressedWithTrivialAccessors: |
| return; |
| // TODO: Stored properties with didSet accessors that don't look at the |
| // oldValue could also be addressed. |
| case AbstractStorageDecl::StoredWithObservers: |
| case AbstractStorageDecl::AddressedWithObservers: |
| break; |
| |
| case AbstractStorageDecl::InheritedWithObservers: |
| case AbstractStorageDecl::Computed: |
| case AbstractStorageDecl::ComputedWithMutableAddress: |
| break; |
| } |
| } |
| |
| // If the property is a generic requirement, allow aliases, because |
| // it may be conformed to using a stored property. |
| if (isa<ProtocolDecl>(decl->getDeclContext())) |
| return; |
| |
| // If this is a simple property access, then we must have a conflict. |
| if (!subscripts) { |
| assert(isa<VarDecl>(decl)); |
| SGF.SGM.diagnose(loc1, diag::writeback_overlap_property, decl->getBaseName()) |
| .highlight(loc1.getSourceRange()); |
| SGF.SGM.diagnose(loc2, diag::writebackoverlap_note) |
| .highlight(loc2.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 (!subscripts.isObviouslyEqual(rhs.subscripts)) { |
| // If the index value doesn't lower to literally the same SILValue's, |
| // do some fuzzy matching to catch the common case. |
| if (!subscriptIndexExpr || |
| !rhs.subscriptIndexExpr || |
| !areCertainlyEqualIndices(subscriptIndexExpr, |
| rhs.subscriptIndexExpr)) |
| 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 = loc1.getAsASTNode<SubscriptExpr>(); |
| auto expr2 = loc2.getAsASTNode<SubscriptExpr>(); |
| |
| if (expr1 && expr2) { |
| SGF.SGM.diagnose(loc1, diag::writeback_overlap_subscript) |
| .highlight(expr1->getBase()->getSourceRange()); |
| |
| SGF.SGM.diagnose(loc2, diag::writebackoverlap_note) |
| .highlight(expr2->getBase()->getSourceRange()); |
| |
| } else { |
| SGF.SGM.diagnose(loc1, diag::writeback_overlap_subscript) |
| .highlight(loc1.getSourceRange()); |
| SGF.SGM.diagnose(loc2, diag::writebackoverlap_note) |
| .highlight(loc2.getSourceRange()); |
| } |
| } |
| }; |
| |
| class UnpinPseudoComponent : public WritebackPseudoComponent { |
| public: |
| UnpinPseudoComponent(const LValueTypeData &typeData) |
| : WritebackPseudoComponent(typeData) {} |
| |
| private: |
| void writeback(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, |
| MaterializedLValue materialized, |
| bool isFinal) override { |
| // If this is final, we can consume the owner (stored as |
| // 'base'). If it isn't, we actually need to retain it, because |
| // we've still got a release active. |
| SILValue baseValue = (isFinal ? base.forward(SGF) : base.getValue()); |
| if (!isFinal) |
| baseValue = SGF.B.createCopyValue(loc, baseValue); |
| |
| SGF.B.createStrongUnpin(loc, baseValue, SGF.B.getDefaultAtomicity()); |
| } |
| |
| void print(raw_ostream &OS) const override { |
| OS << "UnpinPseudoComponent"; |
| } |
| }; |
| |
| /// A physical component which involves calling addressors. |
| class AddressorComponent |
| : public AccessorBasedComponent<PhysicalPathComponent> { |
| SILType SubstFieldType; |
| public: |
| AddressorComponent(AbstractStorageDecl *decl, |
| bool isSuper, bool isDirectAccessorUse, |
| SubstitutionList substitutions, |
| CanType baseFormalType, LValueTypeData typeData, |
| SILType substFieldType, |
| Expr *subscriptIndexExpr = nullptr, |
| RValue *subscriptIndex = nullptr) |
| : AccessorBasedComponent(AddressorKind, decl, isSuper, |
| isDirectAccessorUse, substitutions, |
| baseFormalType, typeData, subscriptIndexExpr, |
| subscriptIndex), |
| SubstFieldType(substFieldType) |
| { |
| } |
| |
| SILDeclRef getAccessor(SILGenFunction &SGF, |
| AccessKind accessKind) const override { |
| return SGF.getAddressorDeclRef(decl, accessKind, IsDirectAccessorUse); |
| } |
| |
| ManagedValue offset(SILGenFunction &SGF, SILLocation loc, ManagedValue base, |
| AccessKind accessKind) && override { |
| assert(SGF.InWritebackScope && |
| "offsetting l-value for modification without writeback scope"); |
| |
| SILDeclRef addressor = SGF.getAddressorDeclRef(decl, accessKind, |
| IsDirectAccessorUse); |
| std::pair<ManagedValue, ManagedValue> result; |
| { |
| FormalEvaluationScope scope(SGF); |
| |
| auto args = |
| std::move(*this).prepareAccessorArgs(SGF, loc, base, addressor); |
| result = SGF.emitAddressorAccessor( |
| loc, addressor, substitutions, std::move(args.base), IsSuper, |
| IsDirectAccessorUse, std::move(args.subscripts), SubstFieldType); |
| } |
| switch (cast<FuncDecl>(addressor.getDecl())->getAddressorKind()) { |
| case AddressorKind::NotAddressor: |
| llvm_unreachable("not an addressor!"); |
| |
| // For unsafe addressors, we have no owner pointer to manage. |
| case AddressorKind::Unsafe: |
| assert(!result.second); |
| return result.first; |
| |
| // For owning addressors, we can just let the owner get released |
| // at an appropriate point. |
| case AddressorKind::Owning: |
| case AddressorKind::NativeOwning: |
| return result.first; |
| |
| // For pinning addressors, we have to push a writeback. |
| case AddressorKind::NativePinning: { |
| std::unique_ptr<LogicalPathComponent> |
| component(new UnpinPseudoComponent(getTypeData())); |
| pushWriteback(SGF, loc, std::move(component), result.second, |
| MaterializedLValue()); |
| return result.first; |
| } |
| } |
| llvm_unreachable("bad addressor kind"); |
| } |
| |
| void print(raw_ostream &OS) const override { |
| printBase(OS, "AddressorComponent"); |
| } |
| }; |
| |
| /// A physical component which involves applying a key path. |
| class KeyPathApplicationComponent final : public PhysicalPathComponent { |
| ArgumentSource KeyPath; |
| public: |
| KeyPathApplicationComponent(LValueTypeData typeData, |
| ArgumentSource &&KeyPath) |
| : PhysicalPathComponent(typeData, KeyPathApplicationKind), |
| KeyPath(std::move(KeyPath)) |
| {} |
| |
| ManagedValue offset(SILGenFunction &SGF, SILLocation loc, ManagedValue base, |
| AccessKind accessKind) && override { |
| assert(SGF.InWritebackScope && |
| "offsetting l-value for modification without writeback scope"); |
| auto &C = SGF.getASTContext(); |
| auto keyPathTy = KeyPath.getSubstType()->castTo<BoundGenericType>(); |
| |
| FuncDecl *projectionFunction; |
| if (keyPathTy->getDecl() == C.getWritableKeyPathDecl()) { |
| // Turn the base lvalue into a pointer to pass to the projection |
| // function. |
| // This is OK since the materialized base is exclusive-borrowed for the |
| // duration of the access. |
| auto baseRawPtr = SGF.B.createAddressToPointer(loc, |
| base.getValue(), |
| SILType::getRawPointerType(SGF.getASTContext())); |
| auto basePtrTy = BoundGenericType::get(C.getUnsafeMutablePointerDecl(), |
| nullptr, |
| keyPathTy->getGenericArgs()[0]) |
| ->getCanonicalType(); |
| auto basePtr = SGF.B.createStruct(loc, |
| SILType::getPrimitiveObjectType(basePtrTy), |
| SILValue(baseRawPtr)); |
| base = ManagedValue::forUnmanaged(basePtr); |
| projectionFunction = C.getProjectKeyPathWritable(nullptr); |
| } else if (keyPathTy->getDecl() == C.getReferenceWritableKeyPathDecl()) { |
| projectionFunction = C.getProjectKeyPathReferenceWritable(nullptr); |
| // The base value is passed indirectly +1 so needs to be |
| // materialized if the base we have is +0 or a loaded value. |
| bool isBorrowed = base.isPlusZeroRValueOrTrivial() |
| && !base.getType().isTrivial(SGF.SGM.M); |
| if (!base.getType().isAddress() || isBorrowed) { |
| auto tmp = SGF.emitTemporaryAllocation(loc, base.getType()); |
| if (isBorrowed) |
| base.copyInto(SGF, tmp, loc); |
| else |
| base.forwardInto(SGF, loc, tmp); |
| base = SGF.emitManagedBufferWithCleanup(tmp); |
| } |
| } else { |
| llvm_unreachable("not a writable key path type?!"); |
| } |
| |
| Substitution args[] = { |
| Substitution(keyPathTy->getGenericArgs()[0], {}), |
| Substitution(keyPathTy->getGenericArgs()[1], {}), |
| }; |
| |
| auto subMap = projectionFunction->getGenericSignature() |
| ->getSubstitutionMap(args); |
| |
| // The projection function behaves like an owning addressor, returning |
| // a pointer to the projected value and an owner reference that keeps |
| // it alive. |
| auto keyPathValue = std::move(KeyPath).getAsSingleValue(SGF); |
| auto resultTuple = SGF.emitApplyOfLibraryIntrinsic(loc, |
| projectionFunction, |
| subMap, |
| {base, keyPathValue}, |
| SGFContext()); |
| SmallVector<ManagedValue, 2> members; |
| std::move(resultTuple).getAll(members); |
| auto projectedPtr = members[0]; |
| auto projectedOwner = members[1]; |
| |
| // Pass along the projected pointer. |
| auto rawValueField = *C.getUnsafeMutablePointerDecl() |
| ->getStoredProperties().begin(); |
| auto projectedRawPtr = SGF.B.createStructExtract(loc, |
| projectedPtr.getUnmanagedValue(), |
| rawValueField, |
| SILType::getRawPointerType(C)); |
| SILValue projectedAddr = SGF.B.createPointerToAddress(loc, |
| projectedRawPtr, |
| getTypeOfRValue().getAddressType(), |
| /*strict*/ true); |
| // Mark the projected address's dependence on the owner. |
| projectedAddr = SGF.B.createMarkDependence(loc, projectedAddr, |
| projectedOwner.getValue()); |
| return ManagedValue::forLValue(projectedAddr); |
| } |
| |
| void print(raw_ostream &OS) const override { |
| OS << "KeyPathApplicationComponent"; |
| } |
| }; |
| } // end anonymous namespace |
| |
| RValue |
| TranslationPathComponent::get(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SGFContext c) && { |
| // Load the original value. |
| RValue baseVal(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, |
| RValue &&value, ManagedValue base) && { |
| // 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(AbstractionPattern origType, |
| CanType substFormalType, |
| SILType loweredSubstType) |
| : TranslationPathComponent({ AbstractionPattern(substFormalType), |
| substFormalType, loweredSubstType }, |
| OrigToSubstKind), |
| OrigType(origType) |
| {} |
| |
| 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(OrigType, getSubstFormalType(), |
| getTypeOfRValue()); |
| return std::unique_ptr<LogicalPathComponent>(clone); |
| } |
| |
| void print(raw_ostream &OS) const override { |
| OS << "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(AbstractionPattern origType, |
| CanType substFormalType, |
| SILType loweredSubstType) |
| : TranslationPathComponent({ origType, substFormalType, loweredSubstType }, |
| SubstToOrigKind) |
| {} |
| |
| 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(getOrigFormalType(), getSubstFormalType(), |
| getTypeOfRValue()); |
| return std::unique_ptr<LogicalPathComponent>(clone); |
| } |
| |
| void print(raw_ostream &OS) const override { |
| OS << "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) { |
| } |
| |
| AccessKind getBaseAccessKind(SILGenFunction &SGF, |
| AccessKind kind) const override { |
| // Always use the same access kind for the base. |
| return kind; |
| } |
| |
| void diagnoseWritebackConflict(LogicalPathComponent *RHS, |
| SILLocation loc1, SILLocation loc2, |
| SILGenFunction &SGF) override { |
| // no useful writeback diagnostics at this point |
| } |
| |
| 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()); |
| |
| // Load the original value. |
| ManagedValue result = SGF.emitLoad(loc, base.getValue(), TL, |
| SGFContext(), IsNotTake); |
| return RValue(SGF, loc, getSubstFormalType(), result); |
| } |
| |
| void set(SILGenFunction &SGF, SILLocation loc, |
| RValue &&value, ManagedValue base) && override { |
| assert(base && "ownership component must not be root of lvalue path"); |
| auto &TL = SGF.getTypeLowering(base.getType()); |
| |
| SGF.emitSemanticStore(loc, |
| std::move(value).forwardAsSingleValue(SGF, loc), |
| 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 print(raw_ostream &OS) const override { |
| OS << "OwnershipComponent(...)\n"; |
| } |
| }; |
| } // end anonymous namespace |
| |
| LValue LValue::forValue(ManagedValue value, |
| CanType substFormalType) { |
| if (value.getType().isObject()) { |
| LValueTypeData typeData = getValueTypeData(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(value, None, AbstractionPattern(substFormalType), |
| substFormalType); |
| } |
| } |
| |
| LValue LValue::forAddress(ManagedValue address, |
| Optional<SILAccessEnforcement> enforcement, |
| AbstractionPattern origFormalType, |
| CanType substFormalType) { |
| assert(address.isLValue()); |
| LValueTypeData typeData = { |
| origFormalType, substFormalType, address.getType().getObjectType() |
| }; |
| |
| LValue lv; |
| lv.add<ValueComponent>(address, enforcement, typeData); |
| return lv; |
| } |
| |
| void LValue::addMemberComponent(SILGenFunction &SGF, SILLocation loc, |
| AbstractStorageDecl *storage, |
| SubstitutionList subs, |
| bool isSuper, |
| AccessKind accessKind, |
| AccessSemantics accessSemantics, |
| AccessStrategy accessStrategy, |
| CanType formalRValueType, |
| RValue &&indices) { |
| if (auto var = dyn_cast<VarDecl>(storage)) { |
| assert(!indices); |
| addMemberVarComponent(SGF, loc, var, subs, isSuper, |
| accessKind, accessSemantics, accessStrategy, |
| formalRValueType); |
| } else { |
| auto subscript = cast<SubscriptDecl>(storage); |
| addMemberSubscriptComponent(SGF, loc, subscript, subs, isSuper, |
| accessKind, accessSemantics, accessStrategy, |
| formalRValueType, std::move(indices)); |
| } |
| } |
| |
| 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. |
| } |
| add<OrigToSubstComponent>(getOrigFormalType(), getSubstFormalType(), |
| loweredSubstType); |
| } |
| |
| 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. |
| } |
| |
| add<SubstToOrigComponent>(origType, getSubstFormalType(), loweredSubstType); |
| } |
| |
| void LValue::dump() const { |
| print(llvm::errs()); |
| } |
| |
| void LValue::print(raw_ostream &OS) const { |
| for (const auto &component : *this) { |
| component->print(OS); |
| } |
| } |
| |
| LValue SILGenFunction::emitLValue(Expr *e, AccessKind accessKind) { |
| // Some lvalue nodes (namely BindOptionalExprs) require immediate evaluation |
| // of their subexpression, so we must have a writeback scope open while |
| // building an lvalue. |
| assert(InWritebackScope && "must be in a writeback scope"); |
| |
| LValue r = SILGenLValue(*this).visit(e, accessKind); |
| // 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; |
| } |
| |
| LValue SILGenLValue::visitRec(Expr *e, AccessKind accessKind, |
| AbstractionPattern orig) { |
| // Non-lvalue types (references, values, metatypes, etc) form the root of a |
| // logical l-value. |
| if (!e->getType()->is<LValueType>() && !e->getType()->is<InOutType>()) { |
| // Decide if we can evaluate this expression at +0 for the rest of the |
| // lvalue. |
| SGFContext Ctx; |
| ManagedValue rv; |
| |
| // Calls through opaque protocols can be done with +0 rvalues. This allows |
| // us to avoid materializing copies of existentials. |
| if (SGF.SGM.Types.isIndirectPlusZeroSelfParameter(e->getType())) |
| Ctx = SGFContext::AllowGuaranteedPlusZero; |
| else if (auto *DRE = dyn_cast<DeclRefExpr>(e)) { |
| // Any reference to "self" can be done at +0 so long as it is a direct |
| // access, since we know it is guaranteed. |
| // TODO: it would be great to factor this even lower into SILGen to the |
| // point where we can see that the parameter is +0 guaranteed. Note that |
| // this handles the case in initializers where there is actually a stack |
| // allocation for it as well. |
| if (isa<ParamDecl>(DRE->getDecl()) && |
| DRE->getDecl()->getFullName() == SGF.getASTContext().Id_self && |
| DRE->getDecl()->isImplicit()) { |
| Ctx = SGFContext::AllowGuaranteedPlusZero; |
| if (SGF.SelfInitDelegationState != SILGenFunction::NormalSelf) { |
| // This needs to be inlined since there is a Formal Evaluation Scope |
| // in emitRValueForDecl that causing any borrow for this LValue to be |
| // popped too soon. |
| auto *vd = cast<ParamDecl>(DRE->getDecl()); |
| ManagedValue selfLValue = SGF.emitLValueForDecl( |
| DRE, vd, DRE->getType()->getCanonicalType(), AccessKind::Read, |
| DRE->getAccessSemantics()); |
| rv = SGF.emitRValueForSelfInDelegationInit( |
| e, DRE->getType()->getCanonicalType(), |
| selfLValue.getLValueAddress(), Ctx) |
| .getScalarValue(); |
| } |
| } else 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 (!rv) { |
| // For an rvalue base, apply the reabstraction (if any) eagerly, since |
| // there's no need for writeback. |
| if (orig.isValid()) |
| rv = SGF.emitRValueAsOrig(e, orig, |
| SGF.getTypeLowering(orig, e->getType()->getRValueType())); |
| else |
| rv = SGF.emitRValueAsSingleValue(e, Ctx); |
| } |
| CanType formalType = getSubstFormalRValueType(e); |
| auto typeData = getValueTypeData(formalType, rv.getValue()); |
| LValue lv; |
| lv.add<ValueComponent>(rv, None, typeData, /*isRValue=*/true); |
| return lv; |
| } |
| |
| auto lv = visit(e, accessKind); |
| // If necessary, handle reabstraction with a SubstToOrigComponent that handles |
| // writeback in the original representation. |
| if (orig.isValid()) { |
| auto &origTL = SGF.getTypeLowering(orig, e->getType()->getRValueType()); |
| if (lv.getTypeOfRValue() != origTL.getLoweredType().getObjectType()) |
| lv.addSubstToOrigComponent(orig, origTL.getLoweredType().getObjectType()); |
| } |
| return lv; |
| } |
| |
| LValue SILGenLValue::visitExpr(Expr *e, AccessKind accessKind) { |
| e->dump(llvm::errs()); |
| llvm_unreachable("unimplemented lvalue expr"); |
| } |
| |
| SubstitutionList |
| SILGenModule::getNonMemberVarDeclSubstitutions(VarDecl *var) { |
| SubstitutionList substitutions; |
| auto *dc = var->getDeclContext(); |
| if (auto *genericEnv = dc->getGenericEnvironmentOfContext()) |
| substitutions = genericEnv->getForwardingSubstitutions(); |
| return substitutions; |
| } |
| |
| // For now, we don't need either an AccessKind or an |
| // AccessSemantics, because addressors are always directly |
| // dispatched. |
| static void |
| addNonMemberVarDeclAddressorComponent(SILGenModule &SGM, VarDecl *var, |
| CanType formalRValueType, |
| LValue &lvalue) { |
| assert(!lvalue.isValid()); |
| auto typeData = getPhysicalStorageTypeData(SGM, var, formalRValueType); |
| SILType storageType = SGM.Types.getLoweredType(var->getType()).getAddressType(); |
| lvalue.add<AddressorComponent>(var, /*isSuper=*/ false, /*direct*/ true, |
| SGM.getNonMemberVarDeclSubstitutions(var), |
| CanType(), typeData, storageType); |
| } |
| |
| LValue |
| SILGenFunction::emitLValueForAddressedNonMemberVarDecl(SILLocation loc, |
| VarDecl *var, |
| CanType formalRValueType, |
| AccessKind accessKind, |
| AccessSemantics semantics) { |
| LValue lv; |
| addNonMemberVarDeclAddressorComponent(SGM, var, formalRValueType, lv); |
| return lv; |
| } |
| |
| static LValue emitLValueForNonMemberVarDecl(SILGenFunction &SGF, |
| SILLocation loc, VarDecl *var, |
| CanType formalRValueType, |
| AccessKind accessKind, |
| AccessSemantics semantics) { |
| LValue lv; |
| |
| switch (var->getAccessStrategy(semantics, accessKind)) { |
| |
| case AccessStrategy::DispatchToAccessor: |
| llvm_unreachable("can't polymorphically access non-member variable"); |
| |
| // If it's a computed variable, push a reference to the getter and setter. |
| case AccessStrategy::DirectToAccessor: { |
| auto typeData = getLogicalStorageTypeData(SGF.SGM, formalRValueType); |
| lv.add<GetterSetterComponent>(var, /*isSuper=*/false, /*direct*/ true, |
| SGF.SGM.getNonMemberVarDeclSubstitutions(var), |
| CanType(), typeData); |
| break; |
| } |
| |
| case AccessStrategy::Addressor: { |
| addNonMemberVarDeclAddressorComponent(SGF.SGM, var, formalRValueType, lv); |
| break; |
| } |
| |
| case AccessStrategy::Storage: { |
| // If it's a physical value (e.g. a local variable in memory), push its |
| // address. |
| auto address = SGF.emitLValueForDecl(loc, var, formalRValueType, |
| accessKind, semantics); |
| assert(address.isLValue() && |
| "physical lvalue decl ref must evaluate to an address"); |
| auto typeData = getPhysicalStorageTypeData(SGF.SGM, var, formalRValueType); |
| |
| Optional<SILAccessEnforcement> enforcement; |
| if (!var->isLet()) { |
| if (var->getDeclContext()->isLocalContext()) { |
| enforcement = SGF.getUnknownEnforcement(var); |
| } else if (var->getDeclContext()->isModuleScopeContext()) { |
| enforcement = SGF.getDynamicEnforcement(var); |
| } else { |
| assert(var->getDeclContext()->isTypeContext() && |
| !var->isInstanceMember()); |
| enforcement = SGF.getDynamicEnforcement(var); |
| } |
| } |
| |
| lv.add<ValueComponent>(address, enforcement, typeData); |
| |
| if (address.getType().is<ReferenceStorageType>()) |
| lv.add<OwnershipComponent>(typeData); |
| break; |
| } |
| |
| case AccessStrategy::BehaviorStorage: |
| // TODO: Behaviors aren't supported for non-instance properties yet. |
| llvm_unreachable("not implemented"); |
| } |
| |
| return lv; |
| } |
| |
| |
| LValue SILGenLValue::visitDiscardAssignmentExpr(DiscardAssignmentExpr *e, |
| AccessKind accessKind) { |
| LValueTypeData typeData = getValueTypeData(SGF, e); |
| |
| SILValue address = SGF.emitTemporaryAllocation(e, typeData.TypeOfRValue); |
| address = SGF.B.createMarkUninitialized(e, address, |
| MarkUninitializedInst::Var); |
| LValue lv; |
| lv.add<ValueComponent>(SGF.emitManagedBufferWithCleanup(address), |
| None, typeData); |
| return lv; |
| } |
| |
| |
| LValue SILGenLValue::visitDeclRefExpr(DeclRefExpr *e, AccessKind accessKind) { |
| // The only non-member decl that can be an lvalue is VarDecl. |
| return emitLValueForNonMemberVarDecl(SGF, e, cast<VarDecl>(e->getDecl()), |
| getSubstFormalRValueType(e), |
| accessKind, |
| e->getAccessSemantics()); |
| } |
| |
| LValue SILGenLValue::visitOpaqueValueExpr(OpaqueValueExpr *e, |
| AccessKind accessKind) { |
| // Handle an opaque lvalue that refers to an opened existential. |
| auto known = SGF.OpaqueValueExprs.find(e); |
| if (known != SGF.OpaqueValueExprs.end()) { |
| // Dig the open-existential expression out of the list. |
| OpenExistentialExpr *opened = known->second; |
| SGF.OpaqueValueExprs.erase(known); |
| |
| // Do formal evaluation of the underlying existential lvalue. |
| auto lv = visitRec(opened->getExistentialValue(), accessKind); |
| lv = SGF.emitOpenExistentialLValue( |
| opened, std::move(lv), |
| CanArchetypeType(opened->getOpenedArchetype()), |
| e->getType()->getLValueOrInOutObjectType()->getCanonicalType(), |
| accessKind); |
| return lv; |
| } |
| |
| assert(SGF.OpaqueValues.count(e) && "Didn't bind OpaqueValueExpr"); |
| |
| auto &entry = SGF.OpaqueValues.find(e)->second; |
| assert(!entry.HasBeenConsumed && "opaque value already consumed"); |
| entry.HasBeenConsumed = true; |
| |
| RegularLocation loc(e); |
| LValue lv; |
| lv.add<ValueComponent>(entry.Value.borrow(SGF, loc), None, |
| getValueTypeData(SGF, e)); |
| return lv; |
| } |
| |
| LValue SILGenLValue::visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *e, |
| AccessKind accessKind) { |
| SGF.emitIgnoredExpr(e->getLHS()); |
| return visitRec(e->getRHS(), accessKind); |
| } |
| |
| static AccessKind getBaseAccessKindForAccessor(FuncDecl *accessor) { |
| if (accessor->isMutating()) { |
| return AccessKind::ReadWrite; |
| } else { |
| return AccessKind::Read; |
| } |
| } |
| |
| /// Return the appropriate access kind for the base l-value of a |
| /// particular member, which is being accessed in a particular way. |
| static AccessKind getBaseAccessKind(AbstractStorageDecl *member, |
| AccessKind accessKind, |
| AccessStrategy strategy) { |
| switch (strategy) { |
| // Assume that the member only partially projects the enclosing value. |
| case AccessStrategy::Storage: |
| return (accessKind == AccessKind::Read |
| ? AccessKind::Read : AccessKind::ReadWrite); |
| |
| case AccessStrategy::Addressor: |
| return getBaseAccessKindForAccessor( |
| member->getAddressorForAccess(accessKind)); |
| |
| case AccessStrategy::DirectToAccessor: |
| case AccessStrategy::DispatchToAccessor: |
| if (accessKind == AccessKind::Read) { |
| return getBaseAccessKindForAccessor(member->getGetter()); |
| } else { |
| return getBaseAccessKindForAccessor(member->getSetter()); |
| } |
| |
| case AccessStrategy::BehaviorStorage: |
| // We should only access the behavior storage for initialization purposes. |
| assert(accessKind == AccessKind::Write); |
| return AccessKind::Write; |
| } |
| llvm_unreachable("bad access strategy"); |
| } |
| |
| LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, |
| AccessKind accessKind) { |
| // MemberRefExpr can refer to type and function members, but the only case |
| // that can be an lvalue is a VarDecl. |
| VarDecl *var = cast<VarDecl>(e->getMember().getDecl()); |
| AccessStrategy strategy = |
| var->getAccessStrategy(e->getAccessSemantics(), accessKind); |
| |
| LValue lv = visitRec(e->getBase(), |
| getBaseAccessKind(var, accessKind, strategy)); |
| assert(lv.isValid()); |
| |
| CanType substFormalRValueType = getSubstFormalRValueType(e); |
| lv.addMemberVarComponent(SGF, e, var, e->getMember().getSubstitutions(), |
| e->isSuper(), accessKind, e->getAccessSemantics(), |
| strategy, substFormalRValueType); |
| return lv; |
| } |
| |
| void LValue::addMemberVarComponent(SILGenFunction &SGF, SILLocation loc, |
| VarDecl *var, |
| SubstitutionList subs, |
| bool isSuper, |
| AccessKind accessKind, |
| AccessSemantics accessSemantics, |
| AccessStrategy strategy, |
| CanType formalRValueType) { |
| CanType baseFormalType = getSubstFormalType(); |
| |
| // Use the property accessors if the variable has accessors and this isn't a |
| // direct access to underlying storage. |
| if (strategy == AccessStrategy::DirectToAccessor || |
| strategy == AccessStrategy::DispatchToAccessor) { |
| auto typeData = getLogicalStorageTypeData(SGF.SGM, formalRValueType); |
| add<GetterSetterComponent>(var, isSuper, |
| strategy == AccessStrategy::DirectToAccessor, |
| subs, baseFormalType, typeData); |
| return; |
| } |
| |
| assert(strategy == AccessStrategy::Addressor || |
| strategy == AccessStrategy::Storage || |
| strategy == AccessStrategy::BehaviorStorage); |
| |
| // Otherwise, the lvalue access is performed with a fragile element reference. |
| // Find the substituted storage type. |
| SILType varStorageType = |
| SGF.SGM.Types.getSubstitutedStorageType(var, formalRValueType); |
| |
| // For static variables, emit a reference to the global variable backing |
| // them. |
| // FIXME: This has to be dynamically looked up for classes, and |
| // dynamically instantiated for generics. |
| if (strategy == AccessStrategy::Storage && var->isStatic()) { |
| // FIXME: this implicitly drops the earlier components, but maybe |
| // we ought to evaluate them for side-effects even during the |
| // formal access? |
| *this = emitLValueForNonMemberVarDecl(SGF, loc, var, |
| formalRValueType, |
| accessKind, accessSemantics); |
| return; |
| } |
| |
| auto typeData = getPhysicalStorageTypeData(SGF.SGM, var, formalRValueType); |
| |
| // For behavior initializations, we should have set up a marking proxy that |
| // replaces the access path. |
| if (strategy == AccessStrategy::BehaviorStorage) { |
| auto addr = SGF.VarLocs.find(var); |
| assert(addr != SGF.VarLocs.end() && addr->second.value); |
| Path.clear(); |
| add<ValueComponent>(ManagedValue::forUnmanaged(addr->second.value), |
| None, typeData); |
| // For member variables, this access is done w.r.t. a base computation that |
| // was already emitted. This member is accessed off of it. |
| } else if (strategy == AccessStrategy::Addressor) { |
| add<AddressorComponent>(var, isSuper, /*direct*/ true, subs, |
| baseFormalType, typeData, varStorageType); |
| } else if (baseFormalType->mayHaveSuperclass()) { |
| add<RefElementComponent>(var, varStorageType, typeData); |
| } else { |
| assert(baseFormalType->getStructOrBoundGenericStruct()); |
| add<StructElementComponent>(var, varStorageType, typeData); |
| } |
| |
| // If the member has weak or unowned storage, convert it away. |
| if (varStorageType.is<ReferenceStorageType>()) { |
| add<OwnershipComponent>(typeData); |
| } |
| } |
| |
| LValue SILGenLValue::visitSubscriptExpr(SubscriptExpr *e, |
| AccessKind accessKind) { |
| auto decl = cast<SubscriptDecl>(e->getDecl().getDecl()); |
| |
| auto accessSemantics = e->getAccessSemantics(); |
| auto strategy = decl->getAccessStrategy(accessSemantics, accessKind); |
| |
| LValue lv = visitRec(e->getBase(), |
| getBaseAccessKind(decl, accessKind, strategy)); |
| assert(lv.isValid()); |
| |
| Expr *indexExpr = e->getIndex(); |
| // FIXME: This admits varargs tuples, which should only be handled as part of |
| // argument emission. |
| RValue index = SGF.emitRValue(indexExpr); |
| |
| CanType formalRValueType = getSubstFormalRValueType(e); |
| lv.addMemberSubscriptComponent(SGF, e, decl, e->getDecl().getSubstitutions(), |
| e->isSuper(), accessKind, accessSemantics, |
| strategy, formalRValueType, std::move(index), |
| indexExpr); |
| return lv; |
| } |
| |
| LValue SILGenLValue::visitKeyPathApplicationExpr(KeyPathApplicationExpr *e, |
| AccessKind accessKind) { |
| // Determine the base access strategy based on the strategy of this access. |
| auto keyPathTy = e->getKeyPath()->getType()->castTo<BoundGenericType>(); |
| AccessKind subAccess; |
| if (keyPathTy->getDecl() == SGF.getASTContext().getWritableKeyPathDecl()) { |
| // Assume the keypath only partially projects the root value. |
| subAccess = (accessKind == AccessKind::Read |
| ? AccessKind::Read : AccessKind::ReadWrite); |
| } else { |
| // The base is only ever read from a read-only or reference-writable |
| // keypath. |
| subAccess = AccessKind::Read; |
| } |
| |
| // The base should be reabstracted to the maximal abstraction pattern. |
| LValue lv = visitRec(e->getBase(), subAccess, |
| AbstractionPattern::getOpaque()); |
| |
| // The result will end up projected at the maximal abstraction level too. |
| auto resultTy = e->getType()->getRValueType()->getCanonicalType(); |
| auto resultSILTy = SGF.getLoweredType(AbstractionPattern::getOpaque(), |
| resultTy); |
| |
| |
| lv.add<KeyPathApplicationComponent>( |
| LValueTypeData(AbstractionPattern::getOpaque(), resultTy, |
| resultSILTy.getObjectType()), |
| ArgumentSource(e->getKeyPath())); |
| |
| // Reabstract to the substituted abstraction level if necessary. |
| auto substResultSILTy = SGF.getLoweredType(resultTy); |
| if (resultSILTy.getObjectType() != substResultSILTy.getObjectType()) { |
| lv.addOrigToSubstComponent(substResultSILTy); |
| } |
| |
| return lv; |
| } |
| |
| void LValue::addMemberSubscriptComponent(SILGenFunction &SGF, SILLocation loc, |
| SubscriptDecl *decl, |
| SubstitutionList subs, |
| bool isSuper, |
| AccessKind accessKind, |
| AccessSemantics accessSemantics, |
| AccessStrategy strategy, |
| CanType formalRValueType, |
| RValue &&indices, |
| Expr *indexExprForDiagnostics) { |
| CanType baseFormalType = getSubstFormalType(); |
| |
| if (strategy == AccessStrategy::DirectToAccessor || |
| strategy == AccessStrategy::DispatchToAccessor) { |
| auto typeData = getLogicalStorageTypeData(SGF.SGM, formalRValueType); |
| add<GetterSetterComponent>(decl, isSuper, |
| strategy == AccessStrategy::DirectToAccessor, |
| subs, baseFormalType, typeData, |
| indexExprForDiagnostics, &indices); |
| } else { |
| assert(strategy == AccessStrategy::Addressor); |
| auto typeData = getPhysicalStorageTypeData(SGF.SGM, decl, formalRValueType); |
| auto storageType = |
| SGF.SGM.Types.getSubstitutedStorageType(decl, formalRValueType); |
| add<AddressorComponent>(decl, isSuper, /*direct*/ true, |
| subs, baseFormalType, typeData, storageType, |
| indexExprForDiagnostics, &indices); |
| } |
| } |
| |
| bool LValue::isObviouslyNonConflicting(const LValue &other, |
| AccessKind selfAccess, |
| AccessKind otherAccess) { |
| // Reads never conflict with reads. |
| if (selfAccess == AccessKind::Read && otherAccess == AccessKind::Read) |
| return true; |
| |
| // We can cover more cases here. |
| return false; |
| } |
| |
| LValue SILGenLValue::visitTupleElementExpr(TupleElementExpr *e, |
| AccessKind accessKind) { |
| unsigned index = e->getFieldNumber(); |
| LValue lv = visitRec(e->getBase(), |
| accessKind == AccessKind::Read |
| ? AccessKind::Read : AccessKind::ReadWrite); |
| |
| auto baseTypeData = lv.getTypeData(); |
| LValueTypeData typeData = { |
| baseTypeData.OrigFormalType.getTupleElementType(index), |
| cast<TupleType>(baseTypeData.SubstFormalType).getElementType(index), |
| baseTypeData.TypeOfRValue.getTupleElementType(index) |
| }; |
| |
| lv.add<TupleElementComponent>(index, typeData); |
| return lv; |
| } |
| |
| LValue SILGenLValue::visitOpenExistentialExpr(OpenExistentialExpr *e, |
| AccessKind accessKind) { |
| // If the opaque value is not an lvalue, open the existential immediately. |
| if (!e->getOpaqueValue()->getType()->is<LValueType>()) { |
| return SGF.emitOpenExistentialExpr<LValue>(e, |
| [&](Expr *subExpr) -> LValue { |
| return visitRec(subExpr, |
| accessKind); |
| }); |
| } |
| |
| // Record the fact that we're opening this existential. The actual |
| // opening operation will occur when we see the OpaqueValueExpr. |
| bool inserted = SGF.OpaqueValueExprs.insert({e->getOpaqueValue(), e}).second; |
| (void)inserted; |
| assert(inserted && "already have this opened existential?"); |
| |
| // Visit the subexpression. |
| LValue lv = visitRec(e->getSubExpr(), accessKind); |
| |
| // Sanity check that we did see the OpaqueValueExpr. |
| assert(SGF.OpaqueValueExprs.count(e->getOpaqueValue()) == 0 && |
| "opened existential not removed?"); |
| return lv; |
| } |
| |
| static LValueTypeData |
| getOptionalObjectTypeData(SILGenFunction &SGF, |
| const LValueTypeData &baseTypeData) { |
| EnumElementDecl *someDecl = SGF.getASTContext().getOptionalSomeDecl(); |
| |
| return { |
| baseTypeData.OrigFormalType.getAnyOptionalObjectType(), |
| baseTypeData.SubstFormalType.getAnyOptionalObjectType(), |
| baseTypeData.TypeOfRValue.getEnumElementType(someDecl, SGF.SGM.M), |
| }; |
| } |
| |
| LValue SILGenLValue::visitForceValueExpr(ForceValueExpr *e, |
| AccessKind accessKind) { |
| LValue lv = visitRec(e->getSubExpr(), accessKind); |
| LValueTypeData typeData = getOptionalObjectTypeData(SGF, lv.getTypeData()); |
| lv.add<ForceOptionalObjectComponent>(typeData); |
| return lv; |
| } |
| |
| LValue SILGenLValue::visitBindOptionalExpr(BindOptionalExpr *e, |
| AccessKind accessKind) { |
| // Do formal evaluation of the base l-value. |
| LValue optLV = visitRec(e->getSubExpr(), accessKind); |
| |
| LValueTypeData optTypeData = optLV.getTypeData(); |
| LValueTypeData valueTypeData = getOptionalObjectTypeData(SGF, optTypeData); |
| |
| // The chaining operator immediately begins a formal access to the |
| // base l-value. In concrete terms, this means we can immediately |
| // evaluate the base down to an address. |
| ManagedValue optAddr = |
| SGF.emitAddressOfLValue(e, std::move(optLV), accessKind); |
| |
| // Bind the value, branching to the destination address if there's no |
| // value there. |
| SGF.emitBindOptional(e, optAddr, e->getDepth()); |
| |
| // Project out the payload on the success branch. We can just use a |
| // naked ValueComponent here; this is effectively a separate l-value. |
| ManagedValue valueAddr = |
| getAddressOfOptionalValue(SGF, e, optAddr, valueTypeData); |
| LValue valueLV; |
| valueLV.add<ValueComponent>(valueAddr, None, valueTypeData); |
| return valueLV; |
| } |
| |
| LValue SILGenLValue::visitInOutExpr(InOutExpr *e, AccessKind accessKind) { |
| return visitRec(e->getSubExpr(), accessKind); |
| } |
| |
| /// Emit an lvalue that refers to the given property. This is |
| /// designed to work with ManagedValue 'base's that are either +0 or +1. |
| LValue SILGenFunction::emitPropertyLValue(SILLocation loc, ManagedValue base, |
| CanType baseFormalType, |
| VarDecl *ivar, AccessKind accessKind, |
| AccessSemantics semantics) { |
| SILGenLValue sgl(*this); |
| LValue lv; |
| |
| auto baseType = base.getType().getSwiftRValueType(); |
| auto subMap = baseType->getContextSubstitutionMap( |
| SGM.M.getSwiftModule(), ivar->getDeclContext()); |
| |
| SmallVector<Substitution, 4> subs; |
| if (auto *genericSig = ivar->getDeclContext()->getGenericSignatureOfContext()) |
| genericSig->getSubstitutions(subMap, subs); |
| |
| LValueTypeData baseTypeData = getValueTypeData(baseFormalType, |
| base.getValue()); |
| |
| // Refer to 'self' as the base of the lvalue. |
| lv.add<ValueComponent>(base, None, baseTypeData, |
| /*isRValue=*/!base.isLValue()); |
| |
| auto substFormalType = ivar->getInterfaceType().subst(subMap) |
| ->getCanonicalType(); |
| |
| AccessStrategy strategy = |
| ivar->getAccessStrategy(semantics, accessKind); |
| |
| |
| // Use the property accessors if the variable has accessors and this |
| // isn't a direct access to underlying storage. |
| if (strategy == AccessStrategy::DirectToAccessor || |
| strategy == AccessStrategy::DispatchToAccessor) { |
| auto typeData = getLogicalStorageTypeData(SGM, substFormalType); |
| lv.add<GetterSetterComponent>(ivar, /*super*/ false, |
| strategy == AccessStrategy::DirectToAccessor, |
| subs, baseFormalType, typeData); |
| return lv; |
| } |
| |
| assert(strategy == AccessStrategy::Addressor || |
| strategy == AccessStrategy::Storage); |
| |
| // Find the substituted storage type. |
| SILType varStorageType = |
| SGM.Types.getSubstitutedStorageType(ivar, substFormalType); |
| |
| auto typeData = getPhysicalStorageTypeData(SGM, ivar, substFormalType); |
| |
| if (strategy == AccessStrategy::Addressor) { |
| lv.add<AddressorComponent>(ivar, /*super*/ false, /*direct*/ true, |
| subs, baseFormalType, typeData, varStorageType); |
| } else if (baseFormalType->hasReferenceSemantics()) { |
| lv.add<RefElementComponent>(ivar, varStorageType, typeData); |
| } else { |
| lv.add<StructElementComponent>(ivar, varStorageType, typeData); |
| } |
| |
| if (varStorageType.is<ReferenceStorageType>()) { |
| auto formalRValueType = |
| ivar->getDeclContext()->mapTypeIntoContext(ivar->getInterfaceType()) |
| ->getReferenceStorageReferent() |
| ->getCanonicalType(); |
| auto typeData = |
| getPhysicalStorageTypeData(SGM, ivar, formalRValueType); |
| lv.add<OwnershipComponent>(typeData); |
| } |
| |
| return lv; |
| } |
| |
| /// Load an r-value out of the given address. |
| /// |
| /// \param rvalueTL - the type lowering for the type-of-rvalue |
| /// of the address |
| /// \param isGuaranteedValid - true if the value in this address |
| /// is guaranteed to be valid for the duration of the current |
| /// evaluation (see SGFContext::AllowGuaranteedPlusZero) |
| ManagedValue SILGenFunction::emitLoad(SILLocation loc, SILValue addr, |
| const TypeLowering &rvalueTL, |
| SGFContext C, IsTake_t isTake, |
| bool isGuaranteedValid) { |
| // Get the lowering for the address type. We can avoid a re-lookup |
| // in the very common case of this being equivalent to the r-value |
| // type. |
| auto &addrTL = |
| (addr->getType() == rvalueTL.getLoweredType().getAddressType() |
| ? rvalueTL : getTypeLowering(addr->getType())); |
| |
| // Never do a +0 load together with a take. |
| bool isPlusZeroOk = (isTake == IsNotTake && |
| (isGuaranteedValid ? C.isGuaranteedPlusZeroOk() |
| : C.isImmediatePlusZeroOk())); |
| |
| if (rvalueTL.isAddressOnly() && silConv.useLoweredAddresses()) { |
| // If the client is cool with a +0 rvalue, the decl has an address-only |
| // type, and there are no conversions, then we can return this as a +0 |
| // address RValue. |
| if (isPlusZeroOk && rvalueTL.getLoweredType() == addrTL.getLoweredType()) |
| return ManagedValue::forUnmanaged(addr); |
| |
| // Copy the address-only value. |
| return B.bufferForExpr( |
| loc, rvalueTL.getLoweredType(), rvalueTL, C, |
| [&](SILValue newAddr) { |
| emitSemanticLoadInto(loc, addr, addrTL, newAddr, rvalueTL, |
| isTake, IsInitialization); |
| }); |
| } |
| |
| // Ok, this is something loadable. If this is a non-take access at plus zero, |
| // we can perform a +0 load of the address instead of materializing a +1 |
| // value. |
| if (isPlusZeroOk && addrTL.getLoweredType() == rvalueTL.getLoweredType()) { |
| return B.createLoadBorrow(loc, ManagedValue::forUnmanaged(addr)); |
| } |
| |
| // Load the loadable value, and retain it if we aren't taking it. |
| SILValue loadedV = emitSemanticLoad(loc, addr, addrTL, rvalueTL, isTake); |
| return emitManagedRValueWithCleanup(loadedV); |
| } |
| |
| /// Load an r-value out of the given address. |
| /// |
| /// \param rvalueTL - the type lowering for the type-of-rvalue |
| /// of the address |
| /// \param isGuaranteedValid - true if the value in this address |
| /// is guaranteed to be valid for the duration of the current |
| /// evaluation (see SGFContext::AllowGuaranteedPlusZero) |
| ManagedValue SILGenFunction::emitFormalAccessLoad(SILLocation loc, |
| SILValue addr, |
| const TypeLowering &rvalueTL, |
| SGFContext C, IsTake_t isTake, |
| bool isGuaranteedValid) { |
| // Get the lowering for the address type. We can avoid a re-lookup |
| // in the very common case of this being equivalent to the r-value |
| // type. |
| auto &addrTL = (addr->getType() == rvalueTL.getLoweredType().getAddressType() |
| ? rvalueTL |
| : getTypeLowering(addr->getType())); |
| |
| // Never do a +0 load together with a take. |
| bool isPlusZeroOk = |
| (isTake == IsNotTake && (isGuaranteedValid ? C.isGuaranteedPlusZeroOk() |
| : C.isImmediatePlusZeroOk())); |
| |
| if (rvalueTL.isAddressOnly() && silConv.useLoweredAddresses()) { |
| // If the client is cool with a +0 rvalue, the decl has an address-only |
| // type, and there are no conversions, then we can return this as a +0 |
| // address RValue. |
| if (isPlusZeroOk && rvalueTL.getLoweredType() == addrTL.getLoweredType()) |
| return ManagedValue::forUnmanaged(addr); |
| |
| // Copy the address-only value. |
| return B.formalAccessBufferForExpr( |
| loc, rvalueTL.getLoweredType(), rvalueTL, C, |
| [&](SILValue addressForCopy) { |
| emitSemanticLoadInto(loc, addr, addrTL, addressForCopy, rvalueTL, |
| isTake, IsInitialization); |
| }); |
| } |
| |
| // Ok, this is something loadable. If this is a non-take access at plus zero, |
| // we can perform a +0 load of the address instead of materializing a +1 |
| // value. |
| if (isPlusZeroOk && addrTL.getLoweredType() == rvalueTL.getLoweredType()) { |
| return B.createFormalAccessLoadBorrow(loc, |
| ManagedValue::forUnmanaged(addr)); |
| } |
| |
| // Load the loadable value, and retain it if we aren't taking it. |
| SILValue loadedV = emitSemanticLoad(loc, addr, addrTL, rvalueTL, isTake); |
| return emitFormalAccessManagedRValueWithCleanup(loc, loadedV); |
| } |
| |
| static void emitUnloweredStoreOfCopy(SILGenBuilder &B, SILLocation loc, |
| SILValue value, SILValue addr, |
| IsInitialization_t isInit) { |
| if (isInit) { |
| B.emitStoreValueOperation(loc, value, addr, StoreOwnershipQualifier::Init); |
| } else { |
| B.createAssign(loc, value, addr); |
| } |
| } |
| |
| SILValue SILGenFunction::emitConversionToSemanticRValue(SILLocation loc, |
| SILValue src, |
| const TypeLowering &valueTL) { |
| // Weak storage types are handled with their underlying type. |
| assert(!src->getType().is<WeakStorageType>() && |
| "weak pointers are always the right optional types"); |
| |
| // For @unowned(safe) types, we need to generate a strong retain and |
| // strip the unowned box. |
| if (auto unownedType = src->getType().getAs<UnownedStorageType>()) { |
| assert(unownedType->isLoadable(ResilienceExpansion::Maximal)); |
| (void) unownedType; |
| |
| B.createStrongRetainUnowned(loc, src, B.getDefaultAtomicity()); |
| return B.createUnownedToRef(loc, src, |
| SILType::getPrimitiveObjectType(unownedType.getReferentType())); |
| } |
| |
| // For @unowned(unsafe) types, we need to strip the unmanaged box |
| // and then do an (unsafe) retain. |
| if (auto unmanagedType = src->getType().getAs<UnmanagedStorageType>()) { |
| auto result = B.createUnmanagedToRef(loc, src, |
| SILType::getPrimitiveObjectType(unmanagedType.getReferentType())); |
| // SEMANTIC ARC TODO: Does this need a cleanup? |
| return B.createCopyValue(loc, result); |
| } |
| |
| llvm_unreachable("unexpected storage type that differs from type-of-rvalue"); |
| } |
| |
| ManagedValue SILGenFunction::emitConversionToSemanticRValue( |
| SILLocation loc, ManagedValue src, const TypeLowering &valueTL) { |
| // Weak storage types are handled with their underlying type. |
| assert(!src.getType().is<WeakStorageType>() && |
| "weak pointers are always the right optional types"); |
| |
| // For @unowned(safe) types, we need to generate a strong retain and |
| // strip the unowned box. |
| if (src.getType().is<UnownedStorageType>()) { |
| return B.createCopyUnownedValue(loc, src); |
| } |
| |
| // For @unowned(unsafe) types, we need to strip the unmanaged box |
| // and then do an (unsafe) retain. |
| if (src.getType().is<UnmanagedStorageType>()) { |
| return B.createUnsafeCopyUnownedValue(loc, src); |
| } |
| |
| llvm_unreachable("unexpected storage type that differs from type-of-rvalue"); |
| } |
| |
| /// Given that the type-of-rvalue differs from the type-of-storage, |
| /// and given that the type-of-rvalue is loadable, produce a +1 scalar |
| /// of the type-of-rvalue. |
| static SILValue emitLoadOfSemanticRValue(SILGenFunction &SGF, |
| SILLocation loc, |
| SILValue src, |
| const TypeLowering &valueTL, |
| IsTake_t isTake) { |
| SILType storageType = src->getType(); |
| |
| // For @weak types, we need to create an Optional<T>. |
| // Optional<T> is currently loadable, but it probably won't be forever. |
| if (storageType.is<WeakStorageType>()) |
| return SGF.B.createLoadWeak(loc, src, isTake); |
| |
| // For @unowned(safe) types, we need to strip the unowned box. |
| if (auto unownedType = storageType.getAs<UnownedStorageType>()) { |
| if (!unownedType->isLoadable(ResilienceExpansion::Maximal)) { |
| return SGF.B.createLoadUnowned(loc, src, isTake); |
| } |
| |
| auto unownedValue = |
| SGF.B.emitLoadValueOperation(loc, src, LoadOwnershipQualifier::Take); |
| SGF.B.createStrongRetainUnowned(loc, unownedValue, SGF.B.getDefaultAtomicity()); |
| if (isTake) |
| SGF.B.createUnownedRelease(loc, unownedValue, SGF.B.getDefaultAtomicity()); |
| return SGF.B.createUnownedToRef( |
| loc, unownedValue, |
| SILType::getPrimitiveObjectType(unownedType.getReferentType())); |
| } |
| |
| // For @unowned(unsafe) types, we need to strip the unmanaged box. |
| if (auto unmanagedType = src->getType().getAs<UnmanagedStorageType>()) { |
| auto value = SGF.B.createLoad(loc, src, LoadOwnershipQualifier::Trivial); |
| auto result = SGF.B.createUnmanagedToRef(loc, value, |
| SILType::getPrimitiveObjectType(unmanagedType.getReferentType())); |
| // SEMANTIC ARC TODO: Does this need a cleanup? |
| return SGF.B.createCopyValue(loc, result); |
| } |
| |
| // NSString * must be bridged to String. |
| if (storageType.getSwiftRValueType() == SGF.SGM.Types.getNSStringType()) { |
| auto nsstr = SGF.B.createLoad(loc, src, LoadOwnershipQualifier::Copy); |
| auto str = SGF.emitBridgedToNativeValue(loc, |
| ManagedValue::forUnmanaged(nsstr), |
| SILFunctionTypeRepresentation::CFunctionPointer, |
| SGF.SGM.Types.getStringType()); |
| return str.forward(SGF); |
| } |
| |
| llvm_unreachable("unexpected storage type that differs from type-of-rvalue"); |
| } |
| |
| /// Given that the type-of-rvalue differs from the type-of-storage, |
| /// store a +1 value (possibly not a scalar) of the type-of-rvalue |
| /// into the given address. |
| static void emitStoreOfSemanticRValue(SILGenFunction &SGF, |
| SILLocation loc, |
| SILValue value, |
| SILValue dest, |
| const TypeLowering &valueTL, |
| IsInitialization_t isInit) { |
| auto storageType = dest->getType(); |
| |
| // For @weak types, we need to break down an Optional<T> and then |
| // emit the storeWeak ourselves. |
| if (storageType.is<WeakStorageType>()) { |
| SGF.B.createStoreWeak(loc, value, dest, isInit); |
| |
| // store_weak doesn't take ownership of the input, so cancel it out. |
| SGF.B.emitDestroyValueOperation(loc, value); |
| return; |
| } |
| |
| // For @unowned(safe) types, we need to enter the unowned box by |
| // turning the strong retain into an unowned retain. |
| if (auto unownedType = storageType.getAs<UnownedStorageType>()) { |
| // FIXME: resilience |
| if (!unownedType->isLoadable(ResilienceExpansion::Maximal)) { |
| SGF.B.createStoreUnowned(loc, value, dest, isInit); |
| |
| // store_unowned doesn't take ownership of the input, so cancel it out. |
| SGF.B.emitDestroyValueOperation(loc, value); |
| return; |
| } |
| |
| auto unownedValue = |
| SGF.B.createRefToUnowned(loc, value, storageType.getObjectType()); |
| SGF.B.createUnownedRetain(loc, unownedValue, SGF.B.getDefaultAtomicity()); |
| emitUnloweredStoreOfCopy(SGF.B, loc, unownedValue, dest, isInit); |
| SGF.B.emitDestroyValueOperation(loc, value); |
| return; |
| } |
| |
| // For @unowned(unsafe) types, we need to enter the unmanaged box and |
| // release the strong retain. |
| if (storageType.is<UnmanagedStorageType>()) { |
| auto unmanagedValue = |
| SGF.B.createRefToUnmanaged(loc, value, storageType.getObjectType()); |
| emitUnloweredStoreOfCopy(SGF.B, loc, unmanagedValue, dest, isInit); |
| SGF.B.emitDestroyValueOperation(loc, value); |
| return; |
| } |
| |
| llvm_unreachable("unexpected storage type that differs from type-of-rvalue"); |
| } |
| |
| /// Load a value of the type-of-rvalue out of the given address as a |
| /// scalar. The type-of-rvalue must be loadable. |
| SILValue SILGenFunction::emitSemanticLoad(SILLocation loc, |
| SILValue src, |
| const TypeLowering &srcTL, |
| const TypeLowering &rvalueTL, |
| IsTake_t isTake) { |
| assert(srcTL.getLoweredType().getAddressType() == src->getType()); |
| assert(rvalueTL.isLoadable() || !silConv.useLoweredAddresses()); |
| |
| // Easy case: the types match. |
| if (srcTL.getLoweredType() == rvalueTL.getLoweredType()) { |
| return srcTL.emitLoadOfCopy(B, loc, src, isTake); |
| } |
| |
| return emitLoadOfSemanticRValue(*this, loc, src, rvalueTL, isTake); |
| } |
| |
| /// Load a value of the type-of-reference out of the given address |
| /// and into the destination address. |
| void SILGenFunction::emitSemanticLoadInto(SILLocation loc, |
| SILValue src, |
| const TypeLowering &srcTL, |
| SILValue dest, |
| const TypeLowering &destTL, |
| IsTake_t isTake, |
| IsInitialization_t isInit) { |
| assert(srcTL.getLoweredType().getAddressType() == src->getType()); |
| assert(destTL.getLoweredType().getAddressType() == dest->getType()); |
| |
| // Easy case: the types match. |
| if (srcTL.getLoweredType() == destTL.getLoweredType()) { |
| B.createCopyAddr(loc, src, dest, isTake, isInit); |
| return; |
| } |
| |
| auto rvalue = emitLoadOfSemanticRValue(*this, loc, src, srcTL, isTake); |
| emitUnloweredStoreOfCopy(B, loc, rvalue, dest, isInit); |
| } |
| |
| /// Store an r-value into the given address as an initialization. |
| void SILGenFunction::emitSemanticStore(SILLocation loc, |
| SILValue rvalue, |
| SILValue dest, |
| const TypeLowering &destTL, |
| IsInitialization_t isInit) { |
| assert(destTL.getLoweredType().getAddressType() == dest->getType()); |
| |
| // Easy case: the types match. |
| if (rvalue->getType() == destTL.getLoweredType()) { |
| assert(!silConv.useLoweredAddresses() |
| || (destTL.isAddressOnly() == rvalue->getType().isAddress())); |
| if (rvalue->getType().isAddress()) { |
| B.createCopyAddr(loc, rvalue, dest, IsTake, isInit); |
| } else { |
| emitUnloweredStoreOfCopy(B, loc, rvalue, dest, isInit); |
| } |
| return; |
| } |
| |
| auto &rvalueTL = getTypeLowering(rvalue->getType()); |
| emitStoreOfSemanticRValue(*this, loc, rvalue, dest, rvalueTL, isInit); |
| } |
| |
| /// Convert a semantic rvalue to a value of storage type. |
| SILValue SILGenFunction::emitConversionFromSemanticValue(SILLocation loc, |
| SILValue semanticValue, |
| SILType storageType) { |
| auto &destTL = getTypeLowering(storageType); |
| (void)destTL; |
| // Easy case: the types match. |
| if (semanticValue->getType() == storageType) { |
| return semanticValue; |
| } |
| |
| // @weak types are never loadable, so we don't need to handle them here. |
| |
| // For @unowned types, place into an unowned box. |
| if (auto unownedType = storageType.getAs<UnownedStorageType>()) { |
| assert(unownedType->isLoadable(ResilienceExpansion::Maximal)); |
| (void) unownedType; |
| |
| SILValue unowned = B.createRefToUnowned(loc, semanticValue, storageType); |
| B.createUnownedRetain(loc, unowned, B.getDefaultAtomicity()); |
| B.emitDestroyValueOperation(loc, semanticValue); |
| return unowned; |
| } |
| |
| // For @unmanaged types, place into an unmanaged box. |
| if (storageType.is<UnmanagedStorageType>()) { |
| SILValue unmanaged = |
| B.createRefToUnmanaged(loc, semanticValue, storageType); |
| B.emitDestroyValueOperation(loc, semanticValue); |
| return unmanaged; |
| } |
| |
| llvm_unreachable("unexpected storage type that differs from type-of-rvalue"); |
| } |
| |
| static void emitTsanInoutAccess(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue address) { |
| assert(address.getType().isAddress()); |
| SILValue accessFnArgs[] = {address.getValue()}; |
| |
| SGF.B.createBuiltin(loc, SGF.getASTContext().getIdentifier("tsanInoutAccess"), |
| SGF.SGM.Types.getEmptyTupleType(), {}, accessFnArgs); |
| } |
| |
| /// Produce a physical address that corresponds to the given l-value |
| /// component. |
| static ManagedValue drillIntoComponent(SILGenFunction &SGF, |
| SILLocation loc, |
| PathComponent &&component, |
| ManagedValue base, |
| AccessKind accessKind, |
| TSanKind tsanKind) { |
| bool isRValue = component.isRValue(); |
| ManagedValue addr; |
| if (component.isPhysical()) { |
| addr = std::move(component.asPhysical()).offset(SGF, loc, base, accessKind); |
| } else { |
| auto &lcomponent = component.asLogical(); |
| addr = std::move(lcomponent).getMaterialized(SGF, loc, base, accessKind); |
| } |
| |
| if (!SGF.getASTContext().LangOpts.DisableTsanInoutInstrumentation && |
| SGF.getModule().getOptions().Sanitize == SanitizerKind::Thread && |
| tsanKind == TSanKind::InoutAccess && !isRValue) { |
| emitTsanInoutAccess(SGF, loc, addr); |
| } |
| |
| return addr; |
| } |
| |
| /// Find the last component of the given lvalue and derive a base |
| /// location for it. |
| static PathComponent && |
| drillToLastComponent(SILGenFunction &SGF, |
| SILLocation loc, |
| LValue &&lv, |
| ManagedValue &addr, |
| AccessKind accessKind, |
| TSanKind tsanKind = TSanKind::None) { |
| assert(lv.begin() != lv.end() && |
| "lvalue must have at least one component"); |
| |
| // Remember all the access kinds we needed along the path. |
| SmallVector<AccessKind, 8> pathAccessKinds; |
| for (auto i = lv.end(), e = lv.begin() + 1; i != e; --i) { |
| pathAccessKinds.push_back(accessKind); |
| accessKind = (*(i-1))->getBaseAccessKind(SGF, accessKind); |
| } |
| |
| for (auto i = lv.begin(), e = lv.end() - 1; i != e; ++i) { |
| addr = drillIntoComponent(SGF, loc, std::move(**i), addr, accessKind, |
| tsanKind); |
| accessKind = pathAccessKinds.pop_back_val(); |
| } |
| |
| return std::move(**(lv.end() - 1)); |
| } |
| |
| RValue SILGenFunction::emitLoadOfLValue(SILLocation loc, LValue &&src, |
| SGFContext C, bool isGuaranteedValid) { |
| // Any writebacks should be scoped to after the load. |
| FormalEvaluationScope scope(*this); |
| |
| auto substFormalType = src.getSubstFormalType(); |
| auto &rvalueTL = getTypeLowering(src.getTypeOfRValue()); |
| |
| ManagedValue addr; |
| PathComponent &&component = |
| drillToLastComponent(*this, loc, std::move(src), addr, AccessKind::Read); |
| |
| // If the last component is physical, just drill down and load from it. |
| if (component.isPhysical()) { |
| addr = std::move(component.asPhysical()) |
| .offset(*this, loc, addr, AccessKind::Read); |
| return RValue(*this, loc, substFormalType, |
| emitLoad(loc, addr.getValue(), |
| rvalueTL, C, IsNotTake, |
| isGuaranteedValid)); |
| } |
| |
| // If the last component is logical, just emit a get. |
| return std::move(component.asLogical()).get(*this, loc, addr, C); |
| } |
| |
| ManagedValue SILGenFunction::emitAddressOfLValue(SILLocation loc, |
| LValue &&src, |
| AccessKind accessKind, |
| TSanKind tsanKind) { |
| ManagedValue addr; |
| PathComponent &&component = |
| drillToLastComponent(*this, loc, std::move(src), addr, accessKind, |
| tsanKind); |
| |
| addr = drillIntoComponent(*this, loc, std::move(component), addr, accessKind, |
| tsanKind); |
| assert(addr.getType().isAddress() && |
| "resolving lvalue did not give an address"); |
| return ManagedValue::forLValue(addr.getValue()); |
| } |
| |
| LValue |
| SILGenFunction::emitOpenExistentialLValue(SILLocation loc, |
| LValue &&lv, |
| CanArchetypeType openedArchetype, |
| CanType formalRValueType, |
| AccessKind accessKind) { |
| assert(!formalRValueType->isLValueType()); |
| LValueTypeData typeData = { |
| AbstractionPattern::getOpaque(), formalRValueType, |
| getLoweredType(formalRValueType).getObjectType() |
| }; |
| |
| // Open up the existential. |
| auto rep = lv.getTypeOfRValue() |
| .getPreferredExistentialRepresentation(SGM.M); |
| switch (rep) { |
| case ExistentialRepresentation::Opaque: |
| case ExistentialRepresentation::Boxed: { |
| lv.add<OpenOpaqueExistentialComponent>(openedArchetype, typeData); |
| break; |
| } |
| case ExistentialRepresentation::Metatype: |
| case ExistentialRepresentation::Class: { |
| lv.add<OpenNonOpaqueExistentialComponent>(openedArchetype, typeData); |
| break; |
| } |
| case ExistentialRepresentation::None: |
| llvm_unreachable("cannot open non-existential"); |
| } |
| |
| return std::move(lv); |
| } |
| |
| void SILGenFunction::emitAssignToLValue(SILLocation loc, RValue &&src, |
| LValue &&dest) { |
| FormalEvaluationScope scope(*this); |
| |
| // Peephole: instead of materializing and then assigning into a |
| // translation component, untransform the value first. |
| while (dest.isLastComponentTranslation()) { |
| src = std::move(dest.getLastTranslationComponent()) |
| .untranslate(*this, loc, std::move(src)); |
| dest.dropLastTranslationComponent(); |
| } |
| |
| // Resolve all components up to the last, keeping track of value-type logical |
| // properties we need to write back to. |
| ManagedValue destAddr; |
| PathComponent &&component = |
| drillToLastComponent(*this, loc, std::move(dest), destAddr, |
| AccessKind::ReadWrite); |
| |
| // Write to the tail component. |
| if (component.isPhysical()) { |
| auto finalDestAddr = |
| std::move(component.asPhysical()).offset(*this, loc, destAddr, |
| AccessKind::Write); |
| |
| std::move(src).assignInto(*this, loc, finalDestAddr.getValue()); |
| } else { |
| std::move(component.asLogical()).set(*this, loc, std::move(src), destAddr); |
| } |
| |
| // The writeback scope closing will propagate the value back up through the |
| // writeback chain. |
| } |
| |
| void SILGenFunction::emitCopyLValueInto(SILLocation loc, LValue &&src, |
| Initialization *dest) { |
| auto skipPeephole = [&]{ |
| auto loaded = emitLoadOfLValue(loc, std::move(src), SGFContext(dest)); |
| if (!loaded.isInContext()) |
| std::move(loaded).forwardInto(*this, loc, dest); |
| }; |
| |
| // If the source is a physical lvalue, the destination is a single address, |
| // and there's no semantic conversion necessary, do a copy_addr from the |
| // lvalue into the destination. |
| if (!src.isPhysical()) |
| return skipPeephole(); |
| if (!dest->canPerformInPlaceInitialization()) |
| return skipPeephole(); |
| auto destAddr = dest->getAddressForInPlaceInitialization(*this, loc); |
| assert(src.getTypeOfRValue().getSwiftRValueType() |
| == destAddr->getType().getSwiftRValueType()); |
| |
| auto srcAddr = emitAddressOfLValue(loc, std::move(src), AccessKind::Read) |
| .getUnmanagedValue(); |
| B.createCopyAddr(loc, srcAddr, destAddr, IsNotTake, IsInitialization); |
| dest->finishInitialization(*this); |
| } |
| |
| void SILGenFunction::emitAssignLValueToLValue(SILLocation loc, LValue &&src, |
| LValue &&dest) { |
| // Only perform the peephole if both operands are physical, there's no |
| // semantic conversion necessary, and exclusivity enforcement |
| // is not enabled. The peephole interferes with exclusivity enforcement |
| // because it causes the formal accesses to the source and destination to |
| // overlap. |
| bool peepholeConflict = |
| !src.isObviouslyNonConflicting(dest, AccessKind::Read, AccessKind::Write); |
| |
| if (peepholeConflict || !src.isPhysical() || !dest.isPhysical()) { |
| RValue loaded = emitLoadOfLValue(loc, std::move(src), SGFContext()); |
| emitAssignToLValue(loc, std::move(loaded), std::move(dest)); |
| return; |
| } |
| |
| auto &rvalueTL = getTypeLowering(src.getTypeOfRValue()); |
| |
| auto srcAddr = emitAddressOfLValue(loc, std::move(src), AccessKind::Read) |
| .getUnmanagedValue(); |
| auto destAddr = emitAddressOfLValue(loc, std::move(dest), AccessKind::Write) |
| .getUnmanagedValue(); |
| |
| if (srcAddr->getType() == destAddr->getType()) { |
| B.createCopyAddr(loc, srcAddr, destAddr, IsNotTake, IsNotInitialization); |
| } else { |
| // If there's a semantic conversion necessary, do a load then assign. |
| auto loaded = emitLoad(loc, srcAddr, rvalueTL, SGFContext(), IsNotTake); |
| loaded.assignInto(*this, loc, destAddr); |
| } |
| } |