| //===--- LValue.h - Logical LValue Representation ---------------*- C++ -*-===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // A storage structure for keeping track of logical lvalues during SILGen. |
| // |
| // In general, only the routines in SILGenLValue.cpp should actually be |
| // accessing LValues and their components. Everything else should just |
| // pass them around opaquely. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_LOWERING_LVALUE_H |
| #define SWIFT_LOWERING_LVALUE_H |
| |
| #include "FormalEvaluation.h" |
| #include "SILGenFunction.h" |
| #include "Scope.h" |
| |
| namespace swift { |
| namespace Lowering { |
| |
| class ArgumentSource; |
| class LogicalPathComponent; |
| class ManagedValue; |
| class PhysicalPathComponent; |
| class SILGenFunction; |
| class TranslationPathComponent; |
| |
| /// Information about the type of an l-value. |
| struct LValueTypeData { |
| /// The abstraction pattern of the l-value. |
| /// |
| /// The type-of-rvalue should always be the substituted formal type |
| /// lowered under this abstraction pattern. |
| AbstractionPattern OrigFormalType = AbstractionPattern::getInvalid(); |
| |
| /// The substituted formal object type of the l-value. |
| /// |
| /// Tn the most common case, this is the type of an l-value |
| /// expression as recorded in the AST, only with the |
| /// LValueType/InOutType stripped off. |
| CanType SubstFormalType; |
| |
| /// The lowered type of value that should be stored in the l-value. |
| /// |
| /// On physical path components, projection yields an address of |
| /// this type. On logical path components, materialize yields an |
| /// address of this type, set expects a value of this type, and |
| /// get yields an object of this type. |
| CanType TypeOfRValue; |
| |
| SGFAccessKind AccessKind; |
| |
| LValueTypeData() = default; |
| LValueTypeData(SGFAccessKind accessKind, AbstractionPattern origFormalType, |
| CanType substFormalType, CanType typeOfRValue) |
| : OrigFormalType(origFormalType), SubstFormalType(substFormalType), |
| TypeOfRValue(typeOfRValue), AccessKind(accessKind) { |
| assert(substFormalType->isMaterializable()); |
| } |
| |
| SGFAccessKind getAccessKind() const { return AccessKind; } |
| }; |
| |
| /// An l-value path component represents a chunk of the access path to |
| /// an object. Path components may be either "physical" or "logical". |
| /// A physical path involves elementary address manipulations; these |
| /// address manipulations may be in some way dynamic, but they are |
| /// ultimately just pointer arithmetic. A logical path requires |
| /// getter/setter logic. |
| /// |
| /// This divide between physical/logical is closely related to the |
| /// fragile/resilient split, with two primary differences: |
| /// - Any sort of implementation can be fragile. For example, a |
| /// computed variable can still be fragile, meaning that it is known |
| /// to be implemented with a getter/setter. The known |
| /// implementation must be a direct offset in order to qualify as |
| /// physical. |
| /// - A path component's implementation can be resilient and yet |
| /// still qualify for physical access if we are in a privileged |
| /// component. |
| class PathComponent { |
| LValueTypeData TypeData; |
| |
| friend class LValue; |
| unsigned AllocatedSize; |
| public: |
| enum KindTy { |
| // Physical lvalue kinds |
| RefElementKind, // ref_element_addr |
| TupleElementKind, // tuple_element_addr |
| StructElementKind, // struct_element_addr |
| OptionalObjectKind, // optional projection |
| OpenOpaqueExistentialKind, // opened opaque existential |
| AddressorKind, // var/subscript addressor |
| CoroutineAccessorKind, // coroutine accessor |
| ValueKind, // random base pointer as an lvalue |
| PhysicalKeyPathApplicationKind, // applying a key path |
| |
| // Logical LValue kinds |
| GetterSetterKind, // property or subscript getter/setter |
| MaterializeToTemporaryKind, |
| OwnershipKind, // weak pointer remapping |
| AutoreleasingWritebackKind, // autorelease pointer on set |
| WritebackPseudoKind, // a fake component to customize writeback |
| OpenNonOpaqueExistentialKind, // opened class or metatype existential |
| LogicalKeyPathApplicationKind, // applying a key path |
| // Translation LValue kinds (a subtype of logical) |
| OrigToSubstKind, // generic type substitution |
| SubstToOrigKind, // generic type substitution |
| |
| FirstLogicalKind = GetterSetterKind, |
| FirstTranslationKind = OrigToSubstKind, |
| }; |
| private: |
| const KindTy Kind : 8; |
| |
| // This anchor method serves three purposes: it aligns the class to |
| // a pointer boundary, it makes the class a primary base so that |
| // subclasses will be at offset zero, and it anchors the v-table |
| // to a specific file. |
| virtual void _anchor(); |
| |
| PathComponent(const PathComponent &) = delete; |
| PathComponent &operator=(const PathComponent &) = delete; |
| |
| protected: |
| PathComponent(LValueTypeData typeData, KindTy Kind) |
| : TypeData(typeData), Kind(Kind) {} |
| public: |
| virtual ~PathComponent() {} |
| |
| /// Returns sizeof(the final type), plus any extra storage required. |
| size_t allocated_size() const { return AllocatedSize; } |
| |
| /// Is this component physical or logical? If physical, this will |
| /// be a subclass of PhysicalPathComponent. If logical, this will |
| /// be a subclass of LogicalPathComponent. |
| bool isPhysical() const { return Kind < FirstLogicalKind; } |
| bool isLogical() const { return Kind >= FirstLogicalKind; } |
| bool isTranslation() const { return Kind >= FirstTranslationKind; } |
| |
| // These are implemented inline after the respective class declarations. |
| |
| PhysicalPathComponent &asPhysical(); |
| const PhysicalPathComponent &asPhysical() const; |
| |
| LogicalPathComponent &asLogical(); |
| const LogicalPathComponent &asLogical() const; |
| |
| TranslationPathComponent &asTranslation(); |
| const TranslationPathComponent &asTranslation() const; |
| |
| /// Apply this component as a projection to the given base component, |
| /// producing something usable as the base of the next component. |
| virtual ManagedValue project(SILGenFunction &SGF, |
| SILLocation loc, |
| ManagedValue base) && = 0; |
| |
| /// Is this some form of open-existential component? |
| bool isOpenExistential() const { |
| return getKind() == OpenOpaqueExistentialKind || |
| getKind() == OpenNonOpaqueExistentialKind; |
| } |
| |
| /// Is loading a value from this component guaranteed to have no observable |
| /// side effects? |
| virtual bool isLoadingPure() const { |
| // By default, don't assume any component is pure; components must opt-in. |
| return false; |
| } |
| |
| virtual bool isRValue() const { return false; } |
| |
| /// Returns the logical type-as-rvalue of the value addressed by the |
| /// component. This is always an object type, never an address. |
| SILType getTypeOfRValue() const { |
| return SILType::getPrimitiveObjectType(TypeData.TypeOfRValue); |
| } |
| AbstractionPattern getOrigFormalType() const { |
| return TypeData.OrigFormalType; |
| } |
| CanType getSubstFormalType() const { return TypeData.SubstFormalType; } |
| |
| const LValueTypeData &getTypeData() const { return TypeData; } |
| SGFAccessKind getAccessKind() const { return getTypeData().getAccessKind(); } |
| |
| KindTy getKind() const { return Kind; } |
| |
| void dump() const; |
| virtual void dump(raw_ostream &OS, unsigned indent = 0) const = 0; |
| }; |
| |
| /// An abstract class for "physical" path components, i.e. path |
| /// components that can be accessed as address manipulations. See the |
| /// comment for PathComponent for more information. |
| /// |
| /// The only operation on this component is `project`. |
| class PhysicalPathComponent : public PathComponent { |
| virtual void _anchor() override; |
| |
| protected: |
| PhysicalPathComponent(LValueTypeData typeData, KindTy Kind) |
| : PathComponent(typeData, Kind) { |
| assert(isPhysical() && "PhysicalPathComponent Kind isn't physical"); |
| } |
| }; |
| |
| inline PhysicalPathComponent &PathComponent::asPhysical() { |
| assert(isPhysical()); |
| return static_cast<PhysicalPathComponent&>(*this); |
| } |
| inline const PhysicalPathComponent &PathComponent::asPhysical() const { |
| assert(isPhysical()); |
| return static_cast<const PhysicalPathComponent&>(*this); |
| } |
| |
| /// An abstract class for "logical" path components, i.e. path |
| /// components that require getter/setter methods to access. See the |
| /// comment for PathComponent for more information. |
| class LogicalPathComponent : public PathComponent { |
| protected: |
| LogicalPathComponent(LValueTypeData typeData, KindTy Kind) |
| : PathComponent(typeData, Kind) { |
| assert(isLogical() && "LogicalPathComponent Kind isn't logical"); |
| } |
| |
| /// Read the value of this component, producing the right kind of result |
| /// for the given access kind (which is always some kind of read access). |
| ManagedValue projectForRead(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SGFAccessKind kind) &&; |
| |
| public: |
| /// Clone the path component onto the heap. |
| virtual std::unique_ptr<LogicalPathComponent> |
| clone(SILGenFunction &SGF, SILLocation l) const = 0; |
| |
| /// Set the property. |
| /// |
| /// \param base - always an address, but possibly an r-value |
| virtual void set(SILGenFunction &SGF, SILLocation loc, |
| ArgumentSource &&value, ManagedValue base) && = 0; |
| |
| /// Get the property. |
| /// |
| /// \param base - always an address, but possibly an r-value |
| virtual RValue get(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SGFContext c) && = 0; |
| |
| /// The default implementation of project performs a get or materializes |
| /// to a temporary as necessary. |
| ManagedValue project(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base) && override; |
| |
| struct AccessedStorage { |
| AbstractStorageDecl *Storage; |
| bool IsSuper; |
| const PreparedArguments *Indices; |
| Expr *IndexExprForDiagnostics; |
| }; |
| |
| /// Get the storage accessed by this component. |
| virtual Optional<AccessedStorage> getAccessedStorage() const = 0; |
| |
| /// Perform a writeback on the property. |
| /// |
| /// \param base - always an address, but possibly an r-value |
| virtual void writeback(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, |
| MaterializedLValue materialized, |
| bool isFinal); |
| }; |
| |
| inline LogicalPathComponent &PathComponent::asLogical() { |
| assert(isLogical()); |
| return static_cast<LogicalPathComponent&>(*this); |
| } |
| inline const LogicalPathComponent &PathComponent::asLogical() const { |
| assert(isLogical()); |
| return static_cast<const LogicalPathComponent&>(*this); |
| } |
| |
| /// An abstract class for components which translate values in some way. |
| class TranslationPathComponent : public LogicalPathComponent { |
| protected: |
| TranslationPathComponent(LValueTypeData typeData, KindTy kind) |
| : LogicalPathComponent(typeData, kind) { |
| assert(isTranslation() && |
| "TranslationPathComponent kind isn't value translation"); |
| } |
| |
| public: |
| Optional<AccessedStorage> getAccessedStorage() const override { |
| return None; |
| } |
| |
| RValue get(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue base, SGFContext c) && override; |
| |
| void set(SILGenFunction &SGF, SILLocation loc, |
| ArgumentSource &&value, ManagedValue base) && override; |
| |
| /// Transform from the original pattern. |
| virtual RValue translate(SILGenFunction &SGF, SILLocation loc, |
| RValue &&value, |
| SGFContext ctx = SGFContext()) && = 0; |
| |
| /// Transform into the original pattern. |
| virtual RValue untranslate(SILGenFunction &SGF, SILLocation loc, |
| RValue &&value, |
| SGFContext ctx = SGFContext()) && = 0; |
| |
| }; |
| |
| inline TranslationPathComponent &PathComponent::asTranslation() { |
| assert(isTranslation()); |
| return static_cast<TranslationPathComponent&>(*this); |
| } |
| inline const TranslationPathComponent &PathComponent::asTranslation() const { |
| assert(isTranslation()); |
| return static_cast<const TranslationPathComponent&>(*this); |
| } |
| |
| /// An lvalue represents a reference to storage holding a value |
| /// of a type, as opposed to an rvalue, which is an actual value |
| /// of the type. |
| class LValue { |
| std::vector<std::unique_ptr<PathComponent>> Path; |
| |
| public: |
| LValue() = default; |
| LValue(const LValue &other) = delete; |
| LValue(LValue &&other) = default; |
| |
| LValue &operator=(const LValue &) = delete; |
| LValue &operator=(LValue &&) = default; |
| |
| static LValue forValue(SGFAccessKind accessKind, ManagedValue value, |
| CanType substFormalType); |
| |
| static LValue forAddress(SGFAccessKind accessKind, ManagedValue address, |
| Optional<SILAccessEnforcement> enforcement, |
| AbstractionPattern origFormalType, |
| CanType substFormalType); |
| |
| bool isValid() const { return !Path.empty(); } |
| |
| /// Is loading a value from this lvalue guaranteed to have no observable side |
| /// effects? |
| bool isLoadingPure() { |
| assert(isValid()); |
| for (auto &component : Path) |
| if (!component->isLoadingPure()) |
| return false; |
| return true; |
| } |
| |
| /// Is this lvalue purely physical? |
| bool isPhysical() const { |
| assert(isValid()); |
| for (auto &component : Path) |
| if (!component->isPhysical()) |
| return false; |
| return true; |
| } |
| |
| /// Is the lvalue's final component physical? |
| bool isLastComponentPhysical() const { |
| assert(isValid()); |
| return Path.back()->isPhysical(); |
| } |
| |
| /// Is the lvalue's final component a translation component? |
| bool isLastComponentTranslation() const { |
| assert(isValid()); |
| return Path.back()->isTranslation(); |
| } |
| |
| /// Given that the last component is a translation component, |
| /// return it. |
| TranslationPathComponent &getLastTranslationComponent() & { |
| assert(isLastComponentTranslation()); |
| return Path.back()->asTranslation(); |
| } |
| |
| /// Given that the last component is a translation component, |
| /// peel it off. |
| void dropLastTranslationComponent() & { |
| assert(isLastComponentTranslation()); |
| Path.pop_back(); |
| } |
| |
| /// Assert that the given component is the last component in the |
| /// l-value, drop it. |
| void dropLastComponent(PathComponent &component) & { |
| assert(&component == Path.back().get()); |
| Path.pop_back(); |
| } |
| |
| /// Pop the last component off this LValue unsafely. Validates that the |
| /// component is of kind \p kind as a sanity check. |
| /// |
| /// Please be careful when using this! |
| void unsafelyDropLastComponent(PathComponent::KindTy kind) & { |
| assert(kind == Path.back()->getKind()); |
| Path.pop_back(); |
| } |
| |
| /// Add a new component at the end of the access path of this lvalue. |
| template <class T, class... As> |
| void add(As &&... args) { |
| Path.emplace_back(new T(std::forward<As>(args)...)); |
| } |
| |
| void addNonMemberVarComponent(SILGenFunction &SGF, SILLocation loc, |
| VarDecl *var, SubstitutionMap subs, |
| LValueOptions options, |
| SGFAccessKind accessKind, |
| AccessStrategy strategy, |
| CanType formalRValueType); |
| |
| /// Add a member component to the access path of this lvalue. |
| void addMemberComponent(SILGenFunction &SGF, SILLocation loc, |
| AbstractStorageDecl *storage, |
| SubstitutionMap subs, |
| LValueOptions options, |
| bool isSuper, |
| SGFAccessKind accessKind, |
| AccessStrategy accessStrategy, |
| CanType formalRValueType, |
| PreparedArguments &&indices, |
| Expr *indexExprForDiagnostics); |
| |
| void addMemberVarComponent(SILGenFunction &SGF, SILLocation loc, |
| VarDecl *var, |
| SubstitutionMap subs, |
| LValueOptions options, |
| bool isSuper, |
| SGFAccessKind accessKind, |
| AccessStrategy accessStrategy, |
| CanType formalRValueType, |
| bool isOnSelf = false); |
| |
| void addMemberSubscriptComponent(SILGenFunction &SGF, SILLocation loc, |
| SubscriptDecl *subscript, |
| SubstitutionMap subs, |
| LValueOptions options, |
| bool isSuper, |
| SGFAccessKind accessKind, |
| AccessStrategy accessStrategy, |
| CanType formalRValueType, |
| PreparedArguments &&indices, |
| Expr *indexExprForDiagnostics, |
| bool isOnSelfParameter = false); |
| |
| /// Add a subst-to-orig reabstraction component. That is, given |
| /// that this l-value trafficks in values following the substituted |
| /// abstraction pattern, make an l-value trafficking in values |
| /// following the original abstraction pattern. |
| void addSubstToOrigComponent(AbstractionPattern origType, |
| SILType loweredResultType); |
| |
| /// Add an orig-to-subst reabstraction component. That is, given |
| /// that this l-value trafficks in values following the original |
| /// abstraction pattern, make an l-value trafficking in values |
| /// following the substituted abstraction pattern. |
| void addOrigToSubstComponent(SILType loweredResultType); |
| |
| typedef std::vector<std::unique_ptr<PathComponent>>::iterator iterator; |
| typedef std::vector<std::unique_ptr<PathComponent>>::const_iterator |
| const_iterator; |
| |
| iterator begin() { return Path.begin(); } |
| iterator end() { return Path.end(); } |
| const_iterator begin() const { return Path.begin(); } |
| const_iterator end() const { return Path.end(); } |
| |
| const LValueTypeData &getTypeData() const { |
| return Path.back()->getTypeData(); |
| } |
| |
| /// Return the access kind that this l-value was emitted for. |
| SGFAccessKind getAccessKind() const { return getTypeData().getAccessKind(); } |
| |
| /// Returns the type-of-rvalue of the logical object referenced by |
| /// this l-value. Note that this may differ significantly from the |
| /// type of l-value. |
| SILType getTypeOfRValue() const { |
| return SILType::getPrimitiveObjectType(getTypeData().TypeOfRValue); |
| } |
| CanType getSubstFormalType() const { return getTypeData().SubstFormalType; } |
| AbstractionPattern getOrigFormalType() const { |
| return getTypeData().OrigFormalType; |
| } |
| |
| /// Returns true when the other access definitely does not begin a formal |
| /// access that would conflict with this the accesses begun by this |
| /// LValue. This is a best-effort attempt; it may return false in cases |
| /// where the two LValues do not conflict. |
| bool isObviouslyNonConflicting(const LValue &other, |
| SGFAccessKind selfAccess, |
| SGFAccessKind otherAccess); |
| |
| void dump() const; |
| void dump(raw_ostream &os, unsigned indent = 0) const; |
| }; |
| |
| /// RAII object used to enter an inout conversion scope. Writeback scopes formed |
| /// during the inout conversion scope will be no-ops. |
| class InOutConversionScope { |
| SILGenFunction &SGF; |
| public: |
| InOutConversionScope(SILGenFunction &SGF); |
| ~InOutConversionScope(); |
| }; |
| |
| // FIXME: Misnomer. This class is used for both shared (read) and exclusive |
| // (modify) formal borrows. |
| struct LLVM_LIBRARY_VISIBILITY ExclusiveBorrowFormalAccess : FormalAccess { |
| std::unique_ptr<LogicalPathComponent> component; |
| ManagedValue base; |
| MaterializedLValue materialized; |
| |
| ~ExclusiveBorrowFormalAccess() {} |
| ExclusiveBorrowFormalAccess(ExclusiveBorrowFormalAccess &&) = default; |
| ExclusiveBorrowFormalAccess & |
| operator=(ExclusiveBorrowFormalAccess &&) = default; |
| |
| ExclusiveBorrowFormalAccess(SILLocation loc, |
| std::unique_ptr<LogicalPathComponent> &&comp, |
| ManagedValue base, |
| MaterializedLValue materialized, |
| CleanupHandle cleanup) |
| : FormalAccess(sizeof(*this), FormalAccess::Exclusive, loc, cleanup), |
| component(std::move(comp)), base(base), materialized(materialized) {} |
| |
| void diagnoseConflict(const ExclusiveBorrowFormalAccess &rhs, |
| SILGenFunction &SGF) const; |
| |
| void performWriteback(SILGenFunction &SGF, bool isFinal) { |
| Scope S(SGF.Cleanups, CleanupLocation::get(loc)); |
| component->writeback(SGF, loc, base, materialized, isFinal); |
| } |
| |
| void finishImpl(SILGenFunction &SGF) override { |
| performWriteback(SGF, /*isFinal*/ true); |
| component.reset(); |
| } |
| }; |
| |
| struct LLVM_LIBRARY_VISIBILITY UnenforcedAccess { |
| // Make sure someone called `endAccess` before destroying this. |
| struct DeleterCheck { |
| void operator()(BeginAccessInst *) { |
| llvm_unreachable("access scope must be ended"); |
| } |
| }; |
| typedef std::unique_ptr<BeginAccessInst, DeleterCheck> BeginAccessPtr; |
| BeginAccessPtr beginAccessPtr; |
| |
| UnenforcedAccess() = default; |
| UnenforcedAccess(const UnenforcedAccess &other) = delete; |
| UnenforcedAccess(UnenforcedAccess &&other) = default; |
| |
| UnenforcedAccess &operator=(const UnenforcedAccess &) = delete; |
| UnenforcedAccess &operator=(UnenforcedAccess &&other) = default; |
| |
| // Return the a new begin_access if it was required, otherwise return the |
| // given `address`. |
| SILValue beginAccess(SILGenFunction &SGF, SILLocation loc, SILValue address, |
| SILAccessKind kind); |
| |
| // End the access and release beginAccessPtr. |
| void endAccess(SILGenFunction &SGF); |
| |
| // Emit the end_access (on a branch) without marking this access as ended. |
| void emitEndAccess(SILGenFunction &SGF); |
| }; |
| |
| /// Pseudo-formal access that emits access markers but does not actually |
| /// require enforcement. It may be used for access to formal memory that is |
| /// exempt from exclusivity checking, such as initialization, or it may be used |
| /// for accesses to local memory that are indistinguishable from formal access |
| /// at the SIL level. Adding the access markers in these cases gives SIL address |
| /// users a structural property that allows for exhaustive verification. |
| struct LLVM_LIBRARY_VISIBILITY UnenforcedFormalAccess : FormalAccess { |
| |
| static SILValue enter(SILGenFunction &SGF, SILLocation loc, SILValue address, |
| SILAccessKind kind); |
| |
| // access.beginAccessPtr is either the begin_access or null if no access was |
| // required. |
| UnenforcedAccess access; |
| |
| UnenforcedFormalAccess(SILLocation loc, UnenforcedAccess &&access, |
| CleanupHandle cleanup) |
| : FormalAccess(sizeof(*this), FormalAccess::Unenforced, loc, cleanup), |
| access(std::move(access)) {} |
| |
| // Emit the end_access (on a branch) without marking this access as ended. |
| void emitEndAccess(SILGenFunction &SGF); |
| |
| // Only called at the end formal evaluation scope. End this access. |
| void finishImpl(SILGenFunction &SGF) override; |
| }; |
| |
| } // namespace Lowering |
| } // namespace swift |
| |
| #endif |