diff --git a/include/swift/AST/AccessorKinds.def b/include/swift/AST/AccessorKinds.def index ad5f4dbd44330..7efc9a7f9efdc 100644 --- a/include/swift/AST/AccessorKinds.def +++ b/include/swift/AST/AccessorKinds.def @@ -100,6 +100,14 @@ ANY_ADDRESSOR(ID, KEYWORD) #endif +/// INIT_ACCESSOR(ID, KEYWORD) +/// The given accessor is an init accessor. +/// +/// Defaults to SINGLETON_ACCESSOR(ID, KEYWORD). +#ifndef INIT_ACCESSOR +#define INIT_ACCESSOR(ID, KEYWORD) SINGLETON_ACCESSOR(ID, KEYWORD) +#endif + // Suppress entries for accessors which can't be written in source code. #ifndef SUPPRESS_ARTIFICIAL_ACCESSORS #define SUPPRESS_ARTIFICIAL_ACCESSORS 0 @@ -174,11 +182,16 @@ IMMUTABLE_ADDRESSOR(Address, unsafeAddress) /// of the type). MUTABLE_ADDRESSOR(MutableAddress, unsafeMutableAddress) +/// This is an init accessor: a function that is called when DI +/// re-writes assignment to initialization. +INIT_ACCESSOR(Init, init) + #ifdef LAST_ACCESSOR -LAST_ACCESSOR(MutableAddress) +LAST_ACCESSOR(Init) #undef LAST_ACCESSOR #endif +#undef INIT_ACCESSOR #undef IMMUTABLE_ADDRESSOR #undef MUTABLE_ADDRESSOR #undef ANY_ADDRESSOR diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 9f053b3a7da13..a585969207595 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -55,6 +55,7 @@ class Decl; class AbstractFunctionDecl; class FuncDecl; class ClassDecl; +class AccessorDecl; class GenericFunctionType; class LazyConformanceLoader; class LazyMemberLoader; @@ -1548,6 +1549,68 @@ class SpecializeAttr final } }; +class InitializesAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend TrailingObjects; + + size_t numProperties; + + InitializesAttr(SourceLoc atLoc, SourceRange range, + ArrayRef properties); + +public: + static InitializesAttr *create(ASTContext &ctx, + SourceLoc atLoc, SourceRange range, + ArrayRef properties); + + size_t numTrailingObjects(OverloadToken) const { + return numProperties; + } + + unsigned getNumProperties() const { return numProperties; } + + ArrayRef getProperties() const { + return {getTrailingObjects(), numProperties}; + } + + ArrayRef getPropertyDecls(AccessorDecl *attachedTo) const; + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Initializes; + } +}; + +class AccessesAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend TrailingObjects; + + size_t numProperties; + + AccessesAttr(SourceLoc atLoc, SourceRange range, + ArrayRef properties); + +public: + static AccessesAttr *create(ASTContext &ctx, + SourceLoc atLoc, SourceRange range, + ArrayRef properties); + + size_t numTrailingObjects(OverloadToken) const { + return numProperties; + } + + ArrayRef getProperties() const { + return {getTrailingObjects(), numProperties}; + } + + ArrayRef getPropertyDecls(AccessorDecl *attachedTo) const; + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Accesses; + } +}; + /// The @_implements attribute, which treats a decl as the implementation for /// some named protocol requirement (but otherwise not-visible by that name). class ImplementsAttr : public DeclAttribute { diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index f58642759ea26..f42b9e1788176 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -47,6 +47,7 @@ #include "swift/Basic/Range.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Support/TrailingObjects.h" +#include #include namespace swift { @@ -184,6 +185,7 @@ enum class DescriptiveDeclKind : uint8_t { DistributedMethod, Getter, Setter, + InitAccessor, Addressor, MutableAddressor, ReadAccessor, @@ -3980,6 +3982,16 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// Return a collection of the stored member variables of this type. ArrayRef getStoredProperties() const; + /// Return a collection of all properties with init accessors in + /// this type. + ArrayRef getInitAccessorProperties() const; + + /// Establish a mapping between properties that could be iniitalized + /// via other properties by means of init accessors. This mapping is + /// one-to-many because we allow intersecting `initializes(...)`. + void collectPropertiesInitializableByInitAccessors( + std::multimap &result) const; + /// Return a collection of the stored member variables of this type, along /// with placeholders for unimportable stored properties. ArrayRef getStoredPropertiesAndMissingMemberPlaceholders() const; @@ -7563,6 +7575,10 @@ class AccessorDecl final : public FuncDecl { llvm_unreachable("bad accessor kind"); } + bool isInitAccessor() const { + return (getAccessorKind() == AccessorKind::Init); + } + /// \returns true if this is non-mutating due to applying a 'mutating' /// attribute. For example a "mutating set" accessor. bool isExplicitNonMutating() const; diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index da94125b16c59..dfa6f57e42eac 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -309,6 +309,9 @@ ERROR(observing_accessor_in_subscript,none, ERROR(getset_cannot_be_implied,none, "variable with implied type cannot have implied getter/setter", ()) +ERROR(init_accessor_expected_name,none, + "expected property name in init accessor effect", ()) + // Import ERROR(decl_expected_module_name,none, "expected module name in import declaration", ()) @@ -2117,5 +2120,13 @@ ERROR(sil_markuncheckedreferencebinding_requires_attribute,none, ERROR(sil_markuncheckedreferencebinding_invalid_attribute,none, "Attribute '[%0]' can not be applied to mark_unchecked_reference_binding", (StringRef)) +//------------------------------------------------------------------------------ +// MARK: Init accessors +//------------------------------------------------------------------------------ + +ERROR(init_accessor_is_not_on_property,none, + "init accessors could only be associated with properties", + ()) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index cf176a032c90b..df082a3ce52fd 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -189,6 +189,9 @@ ERROR(ivar_not_initialized_at_superinit,none, ERROR(ivar_not_initialized_at_implicit_superinit,none, "property '%0' not initialized at implicitly generated super.init call", (StringRef, bool)) +ERROR(ivar_not_initialized_by_init_accessor,none, + "property %0 not initialized by init accessor", + (DeclName)) ERROR(self_use_before_fully_init,none, "'self' used in %select{method call|property access}1 %0 before " diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index d0e7a3fa5105e..d97bcbb471485 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5853,6 +5853,8 @@ ERROR(no_opaque_return_type_of,none, ERROR(objc_observing_accessor, none, "observing accessors are not allowed to be marked @objc", ()) +ERROR(objc_init_accessor, none, + "init accessors cannot be marked @objc", ()) ERROR(objc_addressor, none, "addressors are not allowed to be marked @objc", ()) ERROR(objc_coroutine_accessor, none, @@ -7272,6 +7274,25 @@ NOTE(opt_out_from_missing_reflection_metadata_attr,none, " unavailable extension", (StringRef)) +//------------------------------------------------------------------------------ +// MARK: Init accessors +//------------------------------------------------------------------------------ + +ERROR(init_accessor_can_refer_only_to_properties,none, + "init accessor cannot refer to %0 %1; init accessors can refer only" + " to stored properties", + (DescriptiveDeclKind, DeclNameRef)) + +ERROR(init_accessor_initializes_attribute_on_other_declaration,none, + "initalizes(...) attribute could only be used with init accessors", + ()) +ERROR(init_accessor_accesses_attribute_on_other_declaration,none, + "accesses(...) attribute could only be used with init accessors", + ()) +ERROR(init_accessor_property_both_init_and_accessed,none, + "property %0 cannot be both initialized and accessed", + (DeclName)) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index a5ce6fa03011c..1e1e9a515c36f 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -1671,6 +1671,25 @@ class StoredPropertiesAndMissingMembersRequest : bool isCached() const { return true; } }; +/// Request to obtain a list of computed properties with init accesors +/// in the given nominal type. +class InitAccessorPropertiesRequest : + public SimpleRequest(NominalTypeDecl *), + RequestFlags::Cached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + ArrayRef + evaluate(Evaluator &evaluator, NominalTypeDecl *decl) const; + +public: + bool isCached() const { return true; } +}; + class StorageImplInfoRequest : public SimpleRequest(DeclAttribute *, AccessorDecl *, + ArrayRef), + RequestFlags::Cached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + ArrayRef evaluate(Evaluator &evaluator, DeclAttribute *attr, + AccessorDecl *attachedTo, + ArrayRef) const; + +public: + bool isCached() const { return true; } +}; + #define SWIFT_TYPEID_ZONE TypeChecker #define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def" #include "swift/Basic/DefineTypeIDZone.h" diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index efa1a9e384118..581a7bd27d8b2 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -306,6 +306,9 @@ SWIFT_REQUEST(TypeChecker, StoredPropertiesAndMissingMembersRequest, ArrayRef(NominalTypeDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, StoredPropertiesRequest, ArrayRef(NominalTypeDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, InitAccessorPropertiesRequest, + ArrayRef(NominalTypeDecl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, StructuralTypeRequest, Type(TypeAliasDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, SuperclassTypeRequest, @@ -481,3 +484,7 @@ SWIFT_REQUEST(TypeChecker, IsNonUserModuleRequest, SWIFT_REQUEST(TypeChecker, TypeCheckObjCImplementationRequest, unsigned(ExtensionDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, InitAccessorReferencedVariablesRequest, + ArrayRef(DeclAttribute *, AccessorDecl *, + ArrayRef), + Cached, NoLocationInfo) diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 67bdd87cff6ff..5ed336205881a 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -120,6 +120,7 @@ EXPERIMENTAL_FEATURE(NamedOpaqueTypes, false) EXPERIMENTAL_FEATURE(FlowSensitiveConcurrencyCaptures, false) EXPERIMENTAL_FEATURE(CodeItemMacros, true) EXPERIMENTAL_FEATURE(TupleConformances, false) +EXPERIMENTAL_FEATURE(InitAccessors, false) // Whether to enable @_used and @_section attributes EXPERIMENTAL_FEATURE(SymbolLinkageMarkers, true) diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index c928e4672715a..4022fc3b1fd08 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -143,6 +143,7 @@ NODE(ImplErrorResult) NODE(InOut) NODE(InfixOperator) CONTEXT_NODE(Initializer) +CONTEXT_NODE(InitAccessor) NODE(Isolated) NODE(KeyPathGetterThunkHelper) NODE(KeyPathSetterThunkHelper) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 347f967418bf7..ce377110167b9 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1266,6 +1266,10 @@ class Parser { AccessorKind currentKind, SourceLoc const& currentLoc); + ParserStatus parseInitAccessorEffects(ParsedAccessors &accessors, + AccessorKind currentKind, + DeclAttributes &Attributes); + /// Parse accessors provided as a separate list, for use in macro /// expansions. void parseTopLevelAccessors( diff --git a/include/swift/SIL/InstructionUtils.h b/include/swift/SIL/InstructionUtils.h index 59aedfe4a404d..994e1e3cdbc7e 100644 --- a/include/swift/SIL/InstructionUtils.h +++ b/include/swift/SIL/InstructionUtils.h @@ -145,6 +145,10 @@ SILValue isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI); /// init or set function. bool onlyUsedByAssignByWrapper(PartialApplyInst *PAI); +/// Returns true if \p PAI is only used by an \c assign_or_init +/// instruction as init or set function. +bool onlyUsedByAssignOrInit(PartialApplyInst *PAI); + /// Returns the runtime effects of \p inst. /// /// Predicts which runtime calls are called in the generated code for `inst`. diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index a0021f2a385b8..50dad6f9889c2 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -956,6 +956,14 @@ class SILBuilder { getSILDebugLocation(Loc), Src, Dest, Initializer, Setter, mode)); } + AssignOrInitInst *createAssignOrInit(SILLocation Loc, SILValue Src, + SILValue Initializer, + SILValue Setter, + AssignOrInitInst::Mode Mode) { + return insert(new (getModule()) AssignOrInitInst( + getSILDebugLocation(Loc), Src, Initializer, Setter, Mode)); + } + StoreBorrowInst *createStoreBorrow(SILLocation Loc, SILValue Src, SILValue DestAddr) { return insert(new (getModule()) @@ -1010,7 +1018,11 @@ class SILBuilder { SILValue src) { return createMarkUninitialized(Loc, src, MarkUninitializedInst::RootSelf); } - + MarkUninitializedInst *createMarkUninitializedOut(SILLocation Loc, + SILValue src) { + return createMarkUninitialized(Loc, src, MarkUninitializedInst::Out); + } + MarkFunctionEscapeInst *createMarkFunctionEscape(SILLocation Loc, ArrayRef vars) { return insert( diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 7603a0636679e..08ee52619e548 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1352,6 +1352,17 @@ void SILCloner::visitAssignByWrapperInst(AssignByWrapperInst *Inst) { getOpValue(Inst->getSetter()), Inst->getMode())); } +template +void SILCloner::visitAssignOrInitInst(AssignOrInitInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction( + Inst, getBuilder().createAssignOrInit( + getOpLocation(Inst->getLoc()), + getOpValue(Inst->getSrc()), + getOpValue(Inst->getInitializer()), + getOpValue(Inst->getSetter()), Inst->getMode())); +} + template void SILCloner::visitMarkUninitializedInst(MarkUninitializedInst *Inst) { diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 18fdd3ef53353..95c3b1a59b2b0 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -382,6 +382,9 @@ struct SILDeclRef { return kind == Kind::Initializer || kind == Kind::Destroyer; } + /// True if the SILDeclRef references an init accessor declaration. + bool isInitAccessor() const; + /// True if the function should be treated as transparent. bool isTransparent() const; /// True if the function should have its body serialized. diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 5f5b23fa2d727..8ded5a9ba68aa 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -4935,6 +4935,78 @@ class AssignByWrapperInst } }; +/// AssignOrInitInst - Represents an abstract assignment via a init accessor +/// or a setter, which may either be an initialization or a store sequence. +/// This is only valid in Raw SIL. +/// +/// Note that this instruction does not inherit from AssignInstBase because +/// there is no physical destination of the assignment. Both the init +/// and the setter are factored into functions. +class AssignOrInitInst + : public InstructionBase, + public CopyLikeInstruction { + friend SILBuilder; + USE_SHARED_UINT8; + + FixedOperandList<3> Operands; + + /// Marks all of the properties in `initializes(...)` list that + /// have been initialized before this intruction to help Raw SIL + /// lowering to emit destroys. + llvm::BitVector Assignments; + +public: + enum Mode { + /// The mode is not decided yet (by DefiniteInitialization). + Unknown, + + /// The initializer is called with Src as argument. + Init, + + /// The setter is called with Src as argument. + Set + }; + +private: + AssignOrInitInst(SILDebugLocation DebugLoc, + SILValue Src, SILValue Initializer, + SILValue Setter, Mode mode); + +public: + SILValue getSrc() const { return Operands[0].get(); } + SILValue getInitializer() const { return Operands[1].get(); } + SILValue getSetter() { return Operands[2].get(); } + + Mode getMode() const { + return Mode(sharedUInt8().AssignOrInitInst.mode); + } + + void setMode(Mode mode) { + sharedUInt8().AssignOrInitInst.mode = uint8_t(mode); + } + + /// Mark a property from `initializes(...)` list as initialized + /// before this instruction. + void markAsInitialized(VarDecl *property); + void markAsInitialized(unsigned propertyIdx); + + /// Check whether a property from `initializes(...)` list with + /// the given index has already been initialized and requires + /// destroy before it could be re-initialized. + bool isPropertyAlreadyInitialized(unsigned propertyIdx); + + unsigned getNumInitializedProperties() const; + + ArrayRef getInitializedProperties() const; + ArrayRef getAccessedProperties() const; + + ArrayRef getAllOperands() const { return Operands.asArray(); } + MutableArrayRef getAllOperands() { return Operands.asArray(); } + + AccessorDecl *getReferencedInitAccessor() const; +}; + /// Indicates that a memory location is uninitialized at this point and needs to /// be initialized by the end of the function and before any escape point for /// this instruction. This is only valid in Raw SIL. @@ -4974,6 +5046,10 @@ class MarkUninitializedInst /// DelegatingSelfAllocated designates "self" in a delegating class /// initializer where memory has already been allocated. DelegatingSelfAllocated, + + /// Out designates an indirectly returned result. + /// This is the result that has to be checked for initialization. + Out, }; private: Kind ThisKind; @@ -4988,6 +5064,7 @@ class MarkUninitializedInst Kind getMarkUninitializedKind() const { return ThisKind; } bool isVar() const { return ThisKind == Var; } + bool isOut() const { return ThisKind == Out; } bool isRootSelf() const { return ThisKind == RootSelf; } diff --git a/include/swift/SIL/SILNode.h b/include/swift/SIL/SILNode.h index c6bef9604fafc..ec9a7edfae6b6 100644 --- a/include/swift/SIL/SILNode.h +++ b/include/swift/SIL/SILNode.h @@ -187,6 +187,7 @@ class alignas(8) SILNode : SHARED_FIELD(LoadInst, uint8_t ownershipQualifier); SHARED_FIELD(AssignInst, uint8_t ownershipQualifier); SHARED_FIELD(AssignByWrapperInst, uint8_t mode); + SHARED_FIELD(AssignOrInitInst, uint8_t mode); SHARED_FIELD(StringLiteralInst, uint8_t encoding); SHARED_FIELD(SelectEnumInstBase, bool hasDefault); SHARED_FIELD(SwitchValueInst, bool hasDefault); diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index 443bc06064e34..31eec73a10239 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -820,6 +820,8 @@ NON_VALUE_INST(AssignInst, assign, SILInstruction, MayWrite, DoesNotRelease) NON_VALUE_INST(AssignByWrapperInst, assign_by_wrapper, SILInstruction, MayWrite, DoesNotRelease) +NON_VALUE_INST(AssignOrInitInst, assign_or_init, + SILInstruction, MayWrite, DoesNotRelease) NON_VALUE_INST(MarkFunctionEscapeInst, mark_function_escape, SILInstruction, None, DoesNotRelease) NON_VALUE_INST(DebugValueInst, debug_value, diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 21d3a5b001fa9..6b4de6b2e8976 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -81,6 +81,8 @@ static StringRef getCodeForAccessorKind(AccessorKind kind) { return "lu"; case AccessorKind::MutableAddress: return "au"; + case AccessorKind::Init: + return "i"; } llvm_unreachable("bad accessor kind"); } diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 0827b4321e180..15b4c7a67a6e1 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3218,6 +3218,10 @@ static bool usesFeatureSymbolLinkageMarkers(Decl *decl) { }); } +static bool usesFeatureInitAccessors(Decl *decl) { + return false; +} + static bool usesFeatureLayoutPrespecialization(Decl *decl) { auto &attrs = decl->getAttrs(); return std::any_of(attrs.begin(), attrs.end(), [](auto *attr) { @@ -4328,6 +4332,7 @@ void PrintAST::visitAccessorDecl(AccessorDecl *decl) { break; case AccessorKind::Set: case AccessorKind::WillSet: + case AccessorKind::Init: recordDeclLoc(decl, [&]{ Printer << getAccessorLabel(decl->getAccessorKind()); @@ -4342,6 +4347,7 @@ void PrintAST::visitAccessorDecl(AccessorDecl *decl) { } } }); + break; } // handle effects specifiers before the body diff --git a/lib/AST/AccessRequests.cpp b/lib/AST/AccessRequests.cpp index ef3fbb9d31d92..a32ef4f720acb 100644 --- a/lib/AST/AccessRequests.cpp +++ b/lib/AST/AccessRequests.cpp @@ -61,6 +61,9 @@ AccessLevelRequest::evaluate(Evaluator &evaluator, ValueDecl *D) const { case AccessorKind::DidSet: // These are only needed to synthesize the setter. return AccessLevel::Private; + case AccessorKind::Init: + // These are only called from designated initializers. + return AccessLevel::Private; } } diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index fbbd910050475..daee76ed386e6 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1588,6 +1588,10 @@ StringRef DeclAttribute::getAttrName() const { return "<>"; case DAK_Specialize: return "_specialize"; + case DAK_Initializes: + return "initializes"; + case DAK_Accesses: + return "accesses"; case DAK_Implements: return "_implements"; case DAK_ClangImporterSynthesizedType: @@ -2362,6 +2366,38 @@ TransposeAttr *TransposeAttr::create(ASTContext &context, bool implicit, std::move(originalName), parameterIndices); } +InitializesAttr::InitializesAttr(SourceLoc atLoc, SourceRange range, + ArrayRef properties) + : DeclAttribute(DAK_Initializes, atLoc, range, /*implicit*/false), + numProperties(properties.size()) { + std::uninitialized_copy(properties.begin(), properties.end(), + getTrailingObjects()); +} + +InitializesAttr * +InitializesAttr::create(ASTContext &ctx, SourceLoc atLoc, SourceRange range, + ArrayRef properties) { + unsigned size = totalSizeToAlloc(properties.size()); + void *mem = ctx.Allocate(size, alignof(InitializesAttr)); + return new (mem) InitializesAttr(atLoc, range, properties); +} + +AccessesAttr::AccessesAttr(SourceLoc atLoc, SourceRange range, + ArrayRef properties) + : DeclAttribute(DAK_Accesses, atLoc, range, /*implicit*/false), + numProperties(properties.size()) { + std::uninitialized_copy(properties.begin(), properties.end(), + getTrailingObjects()); +} + +AccessesAttr * +AccessesAttr::create(ASTContext &ctx, SourceLoc atLoc, SourceRange range, + ArrayRef properties) { + unsigned size = totalSizeToAlloc(properties.size()); + void *mem = ctx.Allocate(size, alignof(AccessesAttr)); + return new (mem) AccessesAttr(atLoc, range, properties); +} + ImplementsAttr::ImplementsAttr(SourceLoc atLoc, SourceRange range, TypeRepr *TyR, DeclName MemberName, @@ -2504,6 +2540,26 @@ DeclAttributes::getEffectiveSendableAttr() const { return assumedAttr; } +ArrayRef +InitializesAttr::getPropertyDecls(AccessorDecl *attachedTo) const { + auto &ctx = attachedTo->getASTContext(); + return evaluateOrDefault( + ctx.evaluator, + InitAccessorReferencedVariablesRequest{ + const_cast(this), attachedTo, getProperties()}, + {}); +} + +ArrayRef +AccessesAttr::getPropertyDecls(AccessorDecl *attachedTo) const { + auto &ctx = attachedTo->getASTContext(); + return evaluateOrDefault( + ctx.evaluator, + InitAccessorReferencedVariablesRequest{const_cast(this), + attachedTo, getProperties()}, + {}); +} + void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) { if (attr) attr->print(out); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 05af5c01269cd..a31957bdc58c2 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -254,6 +254,9 @@ DescriptiveDeclKind Decl::getDescriptiveKind() const { case AccessorKind::Modify: return DescriptiveDeclKind::ModifyAccessor; + + case AccessorKind::Init: + return DescriptiveDeclKind::InitAccessor; } llvm_unreachable("bad accessor kind"); } @@ -354,6 +357,7 @@ StringRef Decl::getDescriptiveKindName(DescriptiveDeclKind K) { ENTRY(MutableAddressor, "mutableAddress accessor"); ENTRY(ReadAccessor, "_read accessor"); ENTRY(ModifyAccessor, "_modify accessor"); + ENTRY(InitAccessor, "init acecssor"); ENTRY(EnumElement, "enum case"); ENTRY(Module, "module"); ENTRY(Missing, "missing decl"); @@ -4880,6 +4884,28 @@ ArrayRef NominalTypeDecl::getStoredProperties() const { {}); } +ArrayRef +NominalTypeDecl::getInitAccessorProperties() const { + auto &ctx = getASTContext(); + auto mutableThis = const_cast(this); + return evaluateOrDefault( + ctx.evaluator, + InitAccessorPropertiesRequest{mutableThis}, + {}); +} + +void NominalTypeDecl::collectPropertiesInitializableByInitAccessors( + std::multimap &result) const { + for (auto *property : getInitAccessorProperties()) { + auto *initAccessor = property->getAccessor(AccessorKind::Init); + if (auto *initAttr = + initAccessor->getAttrs().getAttribute()) { + for (auto *subsumed : initAttr->getPropertyDecls(initAccessor)) + result.insert({subsumed, property}); + } + } +} + ArrayRef NominalTypeDecl::getStoredPropertiesAndMissingMemberPlaceholders() const { auto &ctx = getASTContext(); @@ -6689,10 +6715,28 @@ bool VarDecl::isSettable(const DeclContext *UseDC, // 'let's are only ever settable from a specific DeclContext. if (UseDC == nullptr) return false; - + // 'let' properties in structs/classes are only ever settable in their - // designated initializer(s). + // designated initializer(s) or by init accessors. if (isInstanceMember()) { + // Init accessors allow assignments to `let` properties if a + // property is part of `initializes(...)` list. + if (auto *accessor = + dyn_cast(const_cast(UseDC))) { + // Check whether this property is part of `initializes(...)` list, + // and allow assignment/mutation if so. DI would be responsible + // for checking for re-assignment. + if (auto *initAttr = + accessor->getAttrs().getAttribute()) { + return llvm::is_contained(initAttr->getPropertyDecls(accessor), + const_cast(this)); + } + + // If there is no `initializes` attribute, no referenced properties + // can be assignment to or mutated. + return false; + } + auto *CD = dyn_cast(UseDC); if (!CD) return false; @@ -8324,7 +8368,8 @@ DeclName AbstractFunctionDecl::getEffectiveFullName() const { case AccessorKind::Set: case AccessorKind::DidSet: - case AccessorKind::WillSet: { + case AccessorKind::WillSet: + case AccessorKind::Init: { SmallVector argNames; // The implicit value/buffer parameter. argNames.push_back(Identifier()); @@ -9305,6 +9350,7 @@ bool AccessorDecl::isAssumedNonMutating() const { case AccessorKind::DidSet: case AccessorKind::MutableAddress: case AccessorKind::Modify: + case AccessorKind::Init: return false; } llvm_unreachable("bad accessor kind"); diff --git a/lib/AST/PrettyStackTrace.cpp b/lib/AST/PrettyStackTrace.cpp index e2dd3b6554f29..747faf50f5170 100644 --- a/lib/AST/PrettyStackTrace.cpp +++ b/lib/AST/PrettyStackTrace.cpp @@ -91,6 +91,9 @@ void swift::printDeclDescription(llvm::raw_ostream &out, const Decl *D, case AccessorKind::Modify: out << "modify"; break; + case AccessorKind::Init: + out << "init"; + break; } out << " for " << ASD->getName(); diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index c686357441ac2..c359cc0b799de 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -3704,6 +3704,7 @@ NodePointer Demangler::demangleAccessor(NodePointer ChildNode) { case 'W': Kind = Node::Kind::DidSet; break; case 'r': Kind = Node::Kind::ReadAccessor; break; case 'M': Kind = Node::Kind::ModifyAccessor; break; + case 'i': Kind = Node::Kind::InitAccessor; break; case 'a': switch (nextChar()) { case 'O': Kind = Node::Kind::OwningMutableAddressor; break; diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index ad5445054f444..248e0b1ce4493 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -415,6 +415,7 @@ class NodePrinter { case Node::Kind::GlobalGetter: case Node::Kind::Identifier: case Node::Kind::Index: + case Node::Kind::InitAccessor: case Node::Kind::IVarInitializer: case Node::Kind::IVarDestroyer: case Node::Kind::ImplDifferentiabilityKind: @@ -2597,6 +2598,9 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth, case Node::Kind::ModifyAccessor: return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, "modify"); + case Node::Kind::InitAccessor: + return printAbstractStorage(Node->getFirstChild(), depth, asPrefixContext, + "init"); case Node::Kind::Allocator: return printEntity( Node, depth, asPrefixContext, TypePrinting::FunctionStyle, diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 33764e098936d..90bd6c0655c84 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -1244,6 +1244,11 @@ ManglingError Remangler::mangleDidSet(Node *node, EntityContext &ctx, return mangleAccessor(node->getFirstChild(), "W", ctx, depth + 1); } +ManglingError Remangler::mangleInitAccessor(Node *node, EntityContext &ctx, + unsigned depth) { + return mangleAccessor(node->getFirstChild(), "i", ctx, depth + 1); +} + ManglingError Remangler::mangleOwningMutableAddressor(Node *node, EntityContext &ctx, unsigned depth) { diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 3ef91629f5693..63b6043b72b8b 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -2091,6 +2091,10 @@ ManglingError Remangler::mangleInitializer(Node *node, unsigned depth) { return ManglingError::Success; } +ManglingError Remangler::mangleInitAccessor(Node *node, unsigned depth) { + return mangleAbstractStorage(node->getFirstChild(), "i", depth + 1); +} + ManglingError Remangler::manglePropertyWrapperBackingInitializer(Node *node, unsigned depth) { RETURN_IF_ERROR(mangleChildNodes(node, depth + 1)); diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index d397286df5437..d4ce0fbe47bce 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -545,6 +545,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { case AccessorKind::Modify: Kind = ".modify"; break; + case AccessorKind::Init: + Kind = ".init"; + break; } SmallVector Buf; diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 990261912331b..1f295f27106d8 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1228,6 +1228,9 @@ class IRGenSILFunction : void visitAssignByWrapperInst(AssignByWrapperInst *i) { llvm_unreachable("assign_by_wrapper is not valid in canonical SIL"); } + void visitAssignOrInitInst(AssignOrInitInst *i) { + llvm_unreachable("assign_or_init is not valid in canonical SIL"); + } void visitMarkUninitializedInst(MarkUninitializedInst *i) { llvm_unreachable("mark_uninitialized is not valid in canonical SIL"); } diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp index 6a13b9737ffb8..90c862a75a30e 100644 --- a/lib/Index/Index.cpp +++ b/lib/Index/Index.cpp @@ -56,6 +56,9 @@ printArtificialName(const swift::AbstractStorageDecl *ASD, AccessorKind AK, llvm case AccessorKind::WillSet: OS << "willSet:" << ASD->getName() ; return false; + case AccessorKind::Init: + OS << "init:" << ASD->getName(); + return false; case AccessorKind::Address: case AccessorKind::MutableAddress: diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 7b0708f6c0f91..0f30ebf3b387d 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -275,6 +275,7 @@ SymbolSubKind index::getSubKindForAccessor(AccessorKind AK) { return SymbolSubKind::SwiftAccessorMutableAddressor; case AccessorKind::Read: return SymbolSubKind::SwiftAccessorRead; case AccessorKind::Modify: return SymbolSubKind::SwiftAccessorModify; + case AccessorKind::Init: return SymbolSubKind::SwiftAccessorInit; } llvm_unreachable("Unhandled AccessorKind in switch."); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index c2f9193a8da19..8a35d5faea9a4 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3377,6 +3377,14 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, break; } + case DAK_Initializes: { + llvm_unreachable("InitializesAttr not yet implemented"); + } + + case DAK_Accesses: { + llvm_unreachable("AccessesAttr not yet implemented"); + } + case DAK_Implements: { ParserResult Attr = parseImplementsAttribute(AtLoc, Loc); if (Attr.isNonNull()) { @@ -6863,7 +6871,7 @@ static ParameterList *parseOptionalAccessorArgument(SourceLoc SpecifierLoc, // 'set' and 'willSet' have a (value) parameter, 'didSet' takes an (oldValue) // parameter and 'get' and always takes a () parameter. if (Kind != AccessorKind::Set && Kind != AccessorKind::WillSet && - Kind != AccessorKind::DidSet) + Kind != AccessorKind::DidSet && Kind != AccessorKind::Init) return nullptr; SourceLoc StartLoc, NameLoc, EndLoc; @@ -6981,6 +6989,8 @@ static StringRef getAccessorNameForDiagnostic(AccessorKind accessorKind, return "'willSet'"; case AccessorKind::DidSet: return "'didSet'"; + case AccessorKind::Init: + return article ? "an init accessor" : "init accessor"; } llvm_unreachable("bad accessor kind"); } @@ -7005,7 +7015,7 @@ static void diagnoseRedundantAccessors(Parser &P, SourceLoc loc, /*already*/ true); } -static bool isAllowedInLimitedSyntax(AccessorKind kind) { +static bool isAllowedInProtocolRequirement(AccessorKind kind) { switch (kind) { case AccessorKind::Get: case AccessorKind::Set: @@ -7017,6 +7027,7 @@ static bool isAllowedInLimitedSyntax(AccessorKind kind) { case AccessorKind::DidSet: case AccessorKind::Read: case AccessorKind::Modify: + case AccessorKind::Init: return false; } llvm_unreachable("bad accessor kind"); @@ -7085,7 +7096,10 @@ static bool parseAccessorIntroducer(Parser &P, } } - if (!P.Tok.is(tok::identifier) || P.Tok.isEscapedIdentifier()) { + bool isInitAccessor = (P.Context.LangOpts.hasFeature(Feature::InitAccessors) + && P.Tok.is(tok::kw_init)); + if (!(P.Tok.is(tok::identifier) || isInitAccessor) || + P.Tok.isEscapedIdentifier()) { return true; } #define SUPPRESS_ARTIFICIAL_ACCESSORS 1 @@ -7172,6 +7186,71 @@ ParserStatus Parser::parseGetEffectSpecifier(ParsedAccessors &accessors, return Status; } +template +static ParserStatus parseInitAccessorEffect(Parser &P, + DeclAttributes &attributes, + StringRef attrName) { + ParserStatus status; + + if (P.Tok.isContextualKeyword(attrName)) { + auto effectLoc = P.consumeToken(); + if (!P.Tok.is(tok::l_paren)) { + P.diagnose(P.Tok.getLoc(), diag::attr_expected_lparen, + attrName, true); + status.setIsParseError(); + return status; + } + + // Consume '(' + P.consumeToken(); + + bool hasNextProperty = false; + // Consume the identifier list + SmallVector properties; + do { + Identifier propertyName; + SourceLoc propertyNameLoc; + if (P.parseIdentifier(propertyName, propertyNameLoc, + diag::init_accessor_expected_name, + /*diagnoseDollarPrefix=*/true)) { + status.setIsParseError(); + return status; + } + + properties.push_back(propertyName); + + // Parse the comma, if the list continues. + hasNextProperty = P.consumeIf(tok::comma); + } while (hasNextProperty); + + if (!P.Tok.is(tok::r_paren)) { + P.diagnose(P.Tok.getLoc(), diag::attr_expected_rparen, + attrName, true); + status.setIsParseError(); + return status; + } + + // Consume ')' + SourceLoc rParenLoc = P.consumeToken(); + + auto *attr = EffectAttr::create(P.Context, SourceLoc(), + SourceRange(effectLoc, rParenLoc), + properties); + attributes.add(attr); + } + + return status; +} + +ParserStatus Parser::parseInitAccessorEffects(ParsedAccessors &accessors, + AccessorKind currentKind, + DeclAttributes &attrs) { + ParserStatus status; + status |= parseInitAccessorEffect(*this, attrs, "initializes"); + status |= parseInitAccessorEffect(*this, attrs, "accesses"); + return status; +} + bool Parser::parseAccessorAfterIntroducer( SourceLoc Loc, AccessorKind Kind, ParsedAccessors &accessors, bool &hasEffectfulGet, ParameterList *Indices, bool &parsingLimitedSyntax, @@ -7186,6 +7265,7 @@ bool Parser::parseAccessorAfterIntroducer( SourceLoc throwsLoc; Status |= parseGetEffectSpecifier(accessors, asyncLoc, throwsLoc, hasEffectfulGet, Kind, Loc); + Status |= parseInitAccessorEffects(accessors, Kind, Attributes); // Set up a function declaration. auto accessor = @@ -7350,7 +7430,7 @@ ParserStatus Parser::parseGetSet(ParseDeclOptions Flags, ParameterList *Indices, // For now, immediately reject illegal accessors in protocols just to // avoid having to deal with them everywhere. - if (parsingLimitedSyntax && !isAllowedInLimitedSyntax(Kind)) { + if (parsingLimitedSyntax && !isAllowedInProtocolRequirement(Kind)) { diagnose(Loc, diag::expected_getset_in_protocol); continue; } @@ -7726,6 +7806,13 @@ void Parser::ParsedAccessors::classify(Parser &P, AbstractStorageDecl *storage, } else if (Modify) { diagnoseConflictingAccessors(P, Modify, MutableAddress); } + + if (Init) { + if (!storage->getDeclContext()->getSelfNominalTypeDecl() || + isa(storage)) { + P.diagnose(Init->getLoc(), diag::init_accessor_is_not_on_property); + } + } } diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index 4e6bd470bfbfb..8689457c3085d 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -625,6 +625,16 @@ OperandOwnershipClassifier::visitAssignByWrapperInst(AssignByWrapperInst *i) { return OperandOwnership::InstantaneousUse; // initializer/setter closure } +OperandOwnership +OperandOwnershipClassifier::visitAssignOrInitInst(AssignOrInitInst *i) { + if (getValue() == i->getSrc()) { + return OperandOwnership::DestroyingConsume; + } + + // initializer/setter closure + return OperandOwnership::InstantaneousUse; +} + OperandOwnership OperandOwnershipClassifier::visitStoreInst(StoreInst *i) { if (getValue() != i->getSrc()) { return OperandOwnership::TrivialUse; diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 8e3a9fef8f3c5..cef7d684530d6 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -736,6 +736,16 @@ AbstractFunctionDecl *SILDeclRef::getAbstractFunctionDecl() const { return dyn_cast(getDecl()); } +bool SILDeclRef::isInitAccessor() const { + if (kind != Kind::Func || !hasDecl()) + return false; + + if (auto accessor = dyn_cast(getDecl())) + return accessor->getAccessorKind() == AccessorKind::Init; + + return false; +} + /// True if the function should be treated as transparent. bool SILDeclRef::isTransparent() const { if (isEnumElement()) diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 31dc6fd0daed9..4afc45feb516f 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2351,6 +2351,75 @@ static CanSILFunctionType getSILFunctionType( TC.Context, witnessMethodConformance); } +static CanSILFunctionType getSILFunctionTypeForInitAccessor( + TypeConverter &TC, TypeExpansionContext context, + AbstractionPattern origType, CanAnyFunctionType substAccessorType, + SILExtInfoBuilder extInfoBuilder, const Conventions &conventions, + SILDeclRef accessorRef) { + auto *accessor = cast(accessorRef.getDecl()); + + CanGenericSignature genericSig = substAccessorType.getOptGenericSignature(); + + Optional contextRAII; + if (genericSig) + contextRAII.emplace(TC, genericSig); + + SmallVector inputs; + + // First compute `initialValue` input. + { + ForeignInfo foreignInfo; + DestructureInputs destructurer(context, TC, conventions, foreignInfo, + inputs); + destructurer.destructure( + origType, substAccessorType.getParams(), + extInfoBuilder.withRepresentation(SILFunctionTypeRepresentation::Thin)); + } + + // Drop `self` parameter. + inputs.pop_back(); + + // `accesses(...)` appear as `inout` parameters because they could be + // read from and modified. + if (auto *attr = accessor->getAttrs().getAttribute()) { + for (auto *property : attr->getPropertyDecls(accessor)) { + inputs.push_back( + SILParameterInfo(property->getInterfaceType()->getCanonicalType(), + ParameterConvention::Indirect_Inout)); + } + } + + SmallVector results; + + // `initializes(...)` appear as `@out` result because they are initialized + // by the accessor. + if (auto *attr = accessor->getAttrs().getAttribute()) { + for (auto *property : attr->getPropertyDecls(accessor)) { + results.push_back( + SILResultInfo(property->getInterfaceType()->getCanonicalType(), + ResultConvention::Indirect)); + } + } + + auto calleeConvention = ParameterConvention::Direct_Unowned; + if (extInfoBuilder.hasContext()) + calleeConvention = conventions.getCallee(); + + // Map '@Sendable' to the appropriate `@Sendable` modifier. + auto silExtInfo = + SILExtInfoBuilder() + .withRepresentation(SILFunctionTypeRepresentation::Thin) + .withConcurrent(substAccessorType->getExtInfo().isSendable()) + .build(); + + return SILFunctionType::get( + /*genericSig=*/genericSig, silExtInfo, SILCoroutineKind::None, + calleeConvention, inputs, + /*yields=*/{}, results, /*errorResult=*/None, + /*patternSubs=*/SubstitutionMap(), + /*invocationSubs=*/SubstitutionMap(), TC.Context); +} + //===----------------------------------------------------------------------===// // Deallocator SILFunctionTypes //===----------------------------------------------------------------------===// @@ -2617,8 +2686,14 @@ static CanSILFunctionType getNativeSILFunctionType( case SILDeclRef::Kind::Func: { // If we have a setter, use the special setter convention. This ensures // that we take normal parameters at +1. - if (constant && constant->isSetter()) { - return getSILFunctionTypeForConventions(DefaultSetterConventions()); + if (constant) { + if (constant->isSetter()) { + return getSILFunctionTypeForConventions(DefaultSetterConventions()); + } else if (constant->isInitAccessor()) { + return getSILFunctionTypeForInitAccessor( + TC, context, origType, substInterfaceType, extInfoBuilder, + DefaultSetterConventions(), *constant); + } } return getSILFunctionTypeForConventions( DefaultConventions(NormalParameterConvention::Guaranteed)); diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 96fa4921cdc2c..e42d9e4ae1658 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1256,6 +1256,67 @@ AssignByWrapperInst::AssignByWrapperInst(SILDebugLocation Loc, sharedUInt8().AssignByWrapperInst.mode = uint8_t(mode); } +AssignOrInitInst::AssignOrInitInst(SILDebugLocation Loc, SILValue Src, + SILValue Initializer, SILValue Setter, + AssignOrInitInst::Mode Mode) + : InstructionBase(Loc), + Operands(this, Src, Initializer, Setter) { + assert(Initializer->getType().is()); + sharedUInt8().AssignOrInitInst.mode = uint8_t(Mode); + Assignments.resize(getNumInitializedProperties()); +} + +void AssignOrInitInst::markAsInitialized(VarDecl *property) { + auto toInitProperties = getInitializedProperties(); + for (unsigned index : indices(toInitProperties)) { + if (toInitProperties[index] == property) { + markAsInitialized(index); + break; + } + } +} + +void AssignOrInitInst::markAsInitialized(unsigned propertyIdx) { + assert(propertyIdx < getNumInitializedProperties()); + Assignments.set(propertyIdx); +} + +bool AssignOrInitInst::isPropertyAlreadyInitialized(unsigned propertyIdx) { + assert(propertyIdx < Assignments.size()); + return Assignments.test(propertyIdx); +} + +AccessorDecl *AssignOrInitInst::getReferencedInitAccessor() const { + auto *initRef = cast(getInitializer()); + auto *accessorRef = initRef->getReferencedFunctionOrNull(); + assert(accessorRef); + return dyn_cast_or_null(accessorRef->getDeclContext()); +} + +unsigned AssignOrInitInst::getNumInitializedProperties() const { + if (auto *accessor = getReferencedInitAccessor()) { + auto *initAttr = accessor->getAttrs().getAttribute(); + return initAttr ? initAttr->getNumProperties() : 0; + } + return 0; +} + +ArrayRef AssignOrInitInst::getInitializedProperties() const { + if (auto *accessor = getReferencedInitAccessor()) { + if (auto *initAttr = accessor->getAttrs().getAttribute()) + return initAttr->getPropertyDecls(accessor); + } + return {}; +} + +ArrayRef AssignOrInitInst::getAccessedProperties() const { + if (auto *accessor = getReferencedInitAccessor()) { + if (auto *accessAttr = accessor->getAttrs().getAttribute()) + return accessAttr->getPropertyDecls(accessor); + } + return {}; +} + MarkFunctionEscapeInst * MarkFunctionEscapeInst::create(SILDebugLocation Loc, ArrayRef Elements, SILFunction &F) { diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 6c31f61cf9495..1ccf6a64051f1 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -341,6 +341,9 @@ void SILDeclRef::print(raw_ostream &OS) const { case AccessorKind::Modify: OS << "!modify"; break; + case AccessorKind::Init: + OS << "!init"; + break; } break; } @@ -1768,11 +1771,36 @@ class SILPrinter : public SILInstructionVisitor { *this << "[assign_wrapped_value] "; break; } + *this << getIDAndType(AI->getDest()) << ", init " << getIDAndType(AI->getInitializer()) << ", set " << getIDAndType(AI->getSetter()); } + void visitAssignOrInitInst(AssignOrInitInst *AI) { + switch (AI->getMode()) { + case AssignOrInitInst::Unknown: + break; + case AssignOrInitInst::Init: + *this << "[init] "; + break; + case AssignOrInitInst::Set: + *this << "[set] "; + break; + } + + // Print all of the properties that have been previously initialized. + for (unsigned i = 0, n = AI->getNumInitializedProperties(); i != n; ++i) { + if (AI->isPropertyAlreadyInitialized(i)) { + *this << "[assign=" << i << "] "; + } + } + + *this << getIDAndType(AI->getSrc()); + *this << ", init " << getIDAndType(AI->getInitializer()) + << ", set " << getIDAndType(AI->getSetter()); + } + void visitMarkUninitializedInst(MarkUninitializedInst *MU) { switch (MU->getMarkUninitializedKind()) { case MarkUninitializedInst::Var: *this << "[var] "; break; @@ -1788,6 +1816,9 @@ class SILPrinter : public SILInstructionVisitor { case MarkUninitializedInst::DelegatingSelfAllocated: *this << "[delegatingselfallocated] "; break; + case MarkUninitializedInst::Out: + *this << "[out] "; + break; } *this << getIDAndType(MU->getOperand()); printForwardingOwnershipKind(MU, MU->getOperand()); diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 81bc973c2d2b6..c2d9693356487 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -2332,6 +2332,71 @@ static bool parseAssignByWrapperMode(AssignByWrapperInst::Mode &Result, return false; } +static bool parseAssignOrInitMode(AssignOrInitInst::Mode &Result, + SILParser &P) { + StringRef Str; + if (!parseSILOptional(Str, P)) { + Result = AssignOrInitInst::Unknown; + return false; + } + + auto Tmp = llvm::StringSwitch(Str) + .Case("init", AssignOrInitInst::Init) + .Case("set", AssignOrInitInst::Set) + .Default(AssignOrInitInst::Unknown); + + // Return true (following the conventions in this file) if we fail. + if (Tmp == AssignOrInitInst::Unknown) + return true; + + Result = Tmp; + return false; +} + +static bool +parseAssignOrInitAssignments(llvm::SmallVectorImpl &assignments, + SILParser &SP) { + // Could be more than one [assign=] attributes. + for (;;) { + SourceLoc loc; + + // Consume '[' + if (!SP.P.consumeIf(tok::l_square)) + return false; + + // Consume the identifier which should be "assign" + { + Identifier Id; + if (SP.parseSILIdentifier(Id, loc, diag::expected_in_attribute_list)) + return true; + + if (!Id.is("assign")) { + SP.P.diagnose(loc, diag::sil_invalid_attribute_for_expected, Id.str(), + "assign"); + return true; + } + } + + uint64_t index; + + // Consume '=' + if (!SP.P.consumeIf(tok::equal)) { + SP.P.diagnose(loc, diag::expected_equal_in_sil_instr); + return true; + } + + // Consume the property index. + if (SP.parseInteger(index, diag::expected_in_attribute_list)) + return true; + + // Consume ']' + if (SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list)) + return true; + + assignments.push_back(index); + } +} + // Parse a list of integer indices, prefaced with the given string label. // Returns true on error. static bool parseIndexList(Parser &P, StringRef label, @@ -4468,6 +4533,8 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, Kind = MarkUninitializedInst::DelegatingSelf; else if (KindId.str() == "delegatingselfallocated") Kind = MarkUninitializedInst::DelegatingSelfAllocated; + else if (KindId.str() == "out") + Kind = MarkUninitializedInst::Out; else { P.diagnose(KindLoc, diag::expected_tok_in_sil_instr, "var, rootself, crossmodulerootself, derivedself, " @@ -4673,6 +4740,31 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ResultVal = B.createCopyableToMoveOnlyWrapperAddr(InstLoc, addrVal); break; } + + case SILInstructionKind::AssignOrInitInst: { + SILValue Src, InitFn, SetFn; + AssignOrInitInst::Mode Mode; + llvm::SmallVector assignments; + + if (parseAssignOrInitMode(Mode, *this) || + parseAssignOrInitAssignments(assignments, *this) || + parseTypedValueRef(Src, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseVerbatim("init") || parseTypedValueRef(InitFn, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseVerbatim("set") || parseTypedValueRef(SetFn, B) || + parseSILDebugLocation(InstLoc, B)) + return true; + + auto *AI = B.createAssignOrInit(InstLoc, Src, InitFn, SetFn, Mode); + + for (unsigned index : assignments) + AI->markAsInitialized(index); + + ResultVal = AI; + break; + } + case SILInstructionKind::BeginAccessInst: case SILInstructionKind::BeginUnpairedAccessInst: case SILInstructionKind::EndAccessInst: diff --git a/lib/SIL/Utils/AddressWalker.cpp b/lib/SIL/Utils/AddressWalker.cpp index 54b2c9d935ef4..1984de1c25ba8 100644 --- a/lib/SIL/Utils/AddressWalker.cpp +++ b/lib/SIL/Utils/AddressWalker.cpp @@ -125,7 +125,8 @@ AddressUseKind TransitiveAddressWalker::walk(SILValue projectedAddress) && { isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || - isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 50131fdd14dd0..e1c1ba61b4b35 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -403,6 +403,25 @@ bool swift::onlyUsedByAssignByWrapper(PartialApplyInst *PAI) { return usedByAssignByWrapper; } +bool swift::onlyUsedByAssignOrInit(PartialApplyInst *PAI) { + bool usedByAssignOrInit = false; + for (Operand *Op : PAI->getUses()) { + SILInstruction *user = Op->getUser(); + if (isa(user)) { + usedByAssignOrInit = true; + continue; + } + + if (isa(user)) { + continue; + } + + return false; + } + + return usedByAssignOrInit; +} + static RuntimeEffect metadataEffect(SILType ty) { ClassDecl *cl = ty.getClassOrBoundGenericClass(); if (cl && !cl->hasKnownSwiftImplementation()) @@ -521,6 +540,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType) case SILInstructionKind::EndBorrowInst: case SILInstructionKind::AssignInst: case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::AssignOrInitInst: case SILInstructionKind::MarkFunctionEscapeInst: case SILInstructionKind::EndLifetimeInst: case SILInstructionKind::EndApplyInst: diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 78706279f1e62..e9ec5e80e38da 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -2624,6 +2624,7 @@ void swift::visitAccessedAddress(SILInstruction *I, case SILInstructionKind::AssignInst: case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::AssignOrInitInst: visitor(&I->getAllOperands()[AssignInst::Dest]); return; diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 9ae4951d62e24..919535d744857 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -2722,6 +2722,46 @@ class SILVerifier : public SILVerifierBase { checkAssignByWrapperArgs(Src->getType(), setterConv); } + void checkAssigOrInitInstAccessorArgs(SILType argTy, + SILFunctionConventions &conv) { + unsigned argIdx = conv.getSILArgIndexOfFirstParam(); + checkAssignByWrapperArgsRecursively(argTy, conv, argIdx); + } + + void checkAssignOrInitInst(AssignOrInitInst *AI) { + SILValue Src = AI->getSrc(); + require(AI->getModule().getStage() == SILStage::Raw, + "assign_or_init can only exist in raw SIL"); + + SILValue initFn = AI->getInitializer(); + SILValue setterFn = AI->getSetter(); + + CanSILFunctionType initTy = initFn->getType().castTo(); + // Check init - it's an unapplied reference that takes property addresses + // and `initialValue`. + { + // We need to map un-applied function reference into context before + // check `initialValue` argument. + auto subs = cast(setterFn)->getSubstitutionMap(); + initTy = initTy->substGenericArgs(F.getModule(), subs, + F.getTypeExpansionContext()); + + SILFunctionConventions initConv(initTy, AI->getModule()); + require(initConv.getNumIndirectSILResults() == + AI->getInitializedProperties().size(), + "init function has invalid number of indirect results"); + checkAssigOrInitInstAccessorArgs(Src->getType(), initConv); + } + + // Check setter - it's a partially applied reference which takes + // `initialValue`. + CanSILFunctionType setterTy = setterFn->getType().castTo(); + SILFunctionConventions setterConv(setterTy, AI->getModule()); + require(setterConv.getNumIndirectSILResults() == 0, + "set function has indirect results"); + checkAssignByWrapperArgs(Src->getType(), setterConv); + }; + #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ void checkLoad##Name##Inst(Load##Name##Inst *LWI) { \ require(LWI->getType().isObject(), "Result of load must be an object"); \ diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 801fd654d64e9..996d654321465 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -875,6 +875,16 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { break; } + if (constant.isInitAccessor()) { + auto *accessor = cast(constant.getDecl()); + preEmitFunction(constant, f, accessor); + PrettyStackTraceSILFunction X("silgen init accessor", f); + f->createProfiler(constant); + SILGenFunction(*this, *f, accessor).emitInitAccessor(accessor); + postEmitFunction(constant, f); + break; + } + auto *fd = cast(constant.getDecl()); preEmitFunction(constant, f, fd); diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 14ca717d1388a..93a158f8eeb60 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -30,6 +30,7 @@ #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILUndef.h" #include "swift/SIL/TypeLowering.h" +#include using namespace swift; using namespace Lowering; @@ -271,6 +272,47 @@ static RValue maybeEmitPropertyWrapperInitFromValue( subs, std::move(arg)); } +static void emitApplyOfInitAccessor(SILGenFunction &SGF, SILLocation loc, + AccessorDecl *accessor, SILValue selfValue, + SILType selfTy, RValue &&initialValue) { + SmallVector arguments; + + auto emitFieldReference = [&](VarDecl *field) { + auto fieldTy = + selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); + return SGF.B.createStructElementAddr(loc, selfValue, field, + fieldTy.getAddressType()); + }; + + // First, let's emit all of the indirect results. + if (auto *initAttr = accessor->getAttrs().getAttribute()) { + for (auto *property : initAttr->getPropertyDecls(accessor)) { + arguments.push_back(emitFieldReference(property)); + } + } + + // `initialValue` + std::move(initialValue).forwardAll(SGF, arguments); + + // And finally, all of the properties in `accesses(...)` list which are + // `inout` arguments. + if (auto *accessAttr = accessor->getAttrs().getAttribute()) { + for (auto *property : accessAttr->getPropertyDecls(accessor)) { + arguments.push_back(emitFieldReference(property)); + } + } + + SubstitutionMap subs; + if (auto *env = + accessor->getDeclContext()->getGenericEnvironmentOfContext()) { + subs = env->getForwardingSubstitutionMap(); + } + + SILValue accessorRef = + SGF.emitGlobalFunctionRef(loc, SGF.getAccessorDeclRef(accessor)); + (void)SGF.B.createApply(loc, accessorRef, subs, arguments, ApplyOptions()); +} + static SubstitutionMap getSubstitutionsForPropertyInitializer( DeclContext *dc, NominalTypeDecl *nominal) { @@ -312,6 +354,12 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, auto selfIfaceTy = selfDecl->getInterfaceType(); SILType selfTy = SGF.getSILTypeInContext(selfResultInfo, loweredFunctionTy); + auto *decl = selfTy.getStructOrBoundGenericStruct(); + assert(decl && "not a struct?!"); + + std::multimap initializedViaAccessor; + decl->collectPropertiesInitializableByInitAccessors(initializedViaAccessor); + // Emit the indirect return argument, if any. SILValue resultSlot; if (selfTy.isAddress()) { @@ -324,6 +372,10 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, VD->setSpecifier(ParamSpecifier::InOut); VD->setInterfaceType(selfIfaceTy); resultSlot = SGF.F.begin()->createFunctionArgument(selfTy, VD); + } else if (!initializedViaAccessor.empty()) { + // Allocate "self" on stack which we are going to use to + // reference/init fields and then load to return. + resultSlot = SGF.emitTemporaryAllocation(Loc, selfTy); } LoweredParamsInContextGenerator loweredParams(SGF); @@ -343,15 +395,34 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, (void) loweredParams.claimNext(); loweredParams.finish(); - auto *decl = selfTy.getStructOrBoundGenericStruct(); - assert(decl && "not a struct?!"); - auto subs = getSubstitutionsForPropertyInitializer(decl, decl); // If we have an indirect return slot, initialize it in-place. if (resultSlot) { + // Tracks all the init accessors we have emitted + // because they can initialize more than one property. + llvm::SmallPtrSet emittedInitAccessors; + auto elti = elements.begin(), eltEnd = elements.end(); for (VarDecl *field : decl->getStoredProperties()) { + + // Handle situations where this stored propery is initialized + // via a call to an init accessor on some other property. + if (initializedViaAccessor.count(field)) { + auto *initProperty = initializedViaAccessor.find(field)->second; + auto *initAccessor = initProperty->getAccessor(AccessorKind::Init); + + if (emittedInitAccessors.count(initAccessor)) + continue; + + emitApplyOfInitAccessor(SGF, Loc, initAccessor, resultSlot, selfTy, + std::move(*elti)); + + emittedInitAccessors.insert(initAccessor); + ++elti; + continue; + } + auto fieldTy = selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); SILValue slot = @@ -423,6 +494,16 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, } } + // Load as "take" from our stack allocation and return. + if (!selfTy.isAddress() && !initializedViaAccessor.empty()) { + auto resultValue = SGF.B.emitLoadValueOperation( + Loc, resultSlot, LoadOwnershipQualifier::Take); + + SGF.B.createReturn(ImplicitReturnLocation(Loc), resultValue, + std::move(functionLevelScope)); + return; + } + SGF.B.createReturn(ImplicitReturnLocation(Loc), SGF.emitEmptyTuple(Loc), std::move(functionLevelScope)); return; @@ -1481,3 +1562,81 @@ void SILGenFunction::emitIVarInitializer(SILDeclRef ivarInitializer) { emitEpilog(loc); } + +void SILGenFunction::emitInitAccessor(AccessorDecl *accessor) { + RegularLocation loc(accessor); + loc.markAutoGenerated(); + + auto accessorTy = F.getLoweredFunctionType(); + + auto createArgument = [&](VarDecl *property, SILType type, + bool markUninitialized = false) { + auto *arg = ParamDecl::createImplicit( + getASTContext(), property->getBaseIdentifier(), + property->getBaseIdentifier(), type.getASTType()->mapTypeOutOfContext(), + accessor, ParamSpecifier::InOut); + + RegularLocation loc(property); + loc.markAutoGenerated(); + + SILValue argValue = F.begin()->createFunctionArgument(type, arg); + VarLocs[arg] = + markUninitialized + ? VarLoc::get(B.createMarkUninitializedOut(loc, argValue)) + : VarLoc::get(argValue); + + InitAccessorArgumentMappings[property] = arg; + }; + + // First, emit results, this is our "initializes(...)" properties and + // require DI to check that each property is fully initialized. + if (auto *initAttr = accessor->getAttrs().getAttribute()) { + auto initializedProperties = initAttr->getPropertyDecls(accessor); + for (unsigned i = 0, n = initializedProperties.size(); i != n; ++i) { + auto *property = initializedProperties[i]; + auto propertyTy = + getSILTypeInContext(accessorTy->getResults()[i], accessorTy); + createArgument(property, propertyTy, /*markUninitialized=*/true); + } + } + + // Collect all of the parameters that represent properties listed by + // "accesses" attribute. They have to be emitted in order of arguments which + // means after the "newValue" which is emitted by \c emitBasicProlog. + Optional> accessedProperties; + { + if (auto *accessAttr = accessor->getAttrs().getAttribute()) + accessedProperties = accessAttr->getPropertyDecls(accessor); + } + + // Emit `newValue` argument. + emitBasicProlog(accessor->getParameters(), /*selfParam=*/nullptr, + TupleType::getEmpty(F.getASTContext()), accessor, + /*throws=*/false, /*throwsLoc=*/SourceLoc(), + /*ignored parameters*/ + accessedProperties ? accessedProperties->size() : 0); + + // Emit arguments for all `accesses(...)` properties. + if (accessedProperties) { + auto propertyIter = accessedProperties->begin(); + auto propertyArgs = accessorTy->getParameters().slice( + accessorTy->getNumParameters() - accessedProperties->size()); + + for (const auto &argument : propertyArgs) { + createArgument(*propertyIter, getSILTypeInContext(argument, accessorTy)); + ++propertyIter; + } + } + + prepareEpilog(accessor->getResultInterfaceType(), accessor->hasThrows(), + CleanupLocation(accessor)); + + emitProfilerIncrement(accessor->getTypecheckedBody()); + + // Emit the actual function body as usual + emitStmt(accessor->getTypecheckedBody()); + + emitEpilog(accessor); + + mergeCleanupBlocks(); +} diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index a749212a17846..6c04a9f890a5e 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -1720,3 +1720,14 @@ SILValue SILGenFunction::emitWrapIntegerLiteral(SILLocation loc, auto propertyValue = emitWrapIntegerLiteral(loc, propertyTy, value); return B.createStruct(loc, ty, propertyValue); } + +ParamDecl *SILGenFunction::isMappedToInitAccessorArgument(VarDecl *property) { + assert(isa(FunctionDC) && + cast(FunctionDC)->isInitAccessor()); + + auto arg = InitAccessorArgumentMappings.find(property); + if (arg == InitAccessorArgumentMappings.end()) + return nullptr; + + return arg->second; +} diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 0cadb928cc271..49f7ba54215b2 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -453,6 +453,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// need to be emitted inside the next brace statement. llvm::SmallVector LocalAuxiliaryDecls; + /// The mappings between instance properties referenced by this init + /// accessor (via initializes/accesses attributes) and and argument + /// declarations synthesized to access them in the body. + llvm::DenseMap InitAccessorArgumentMappings; + // Context information for tracking an `async let` child task. struct AsyncLetChildTask { SILValue asyncLet; // RawPointer to the async let state @@ -799,6 +804,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// Emit a method that destroys the ivars of a class. void emitIVarDestroyer(SILDeclRef ivarDestroyer); + /// Generates code for the given init accessor represented by AccessorDecl. + /// This emits the body code and replaces all `self.` references + /// with either argument (if property appears in `acesses` list`) or result + /// value assignment. + void emitInitAccessor(AccessorDecl *accessor); + /// Generates code to destroy the instance variables of a class. /// /// \param selfValue The 'self' value. @@ -2755,6 +2766,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction CanPackType formalPackType, unsigned componentIndex, SILValue currentIndexWithinComponent); + + /// If context is init accessor, find a mapping between the given type + /// property and argument declaration synthesized for it. + ParamDecl *isMappedToInitAccessorArgument(VarDecl *property); }; diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index f3769c5b90c92..12d6113274b5c 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1509,6 +1509,33 @@ namespace { return false; } + /// Whether an assignment 'x = y' can be re-written as a call to an + /// init accessor declared by 'x'. + bool canRewriteSetAsInitAccessor(SILGenFunction &SGF) const { + auto *varDecl = dyn_cast(Storage); + if (!varDecl || varDecl->isStatic() || + varDecl->getDeclContext()->isLocalContext()) + return false; + + auto *fnDecl = SGF.FunctionDC->getAsDecl(); + bool isAssignmentToSelfParamInInit = + IsOnSelfParameter && isa(fnDecl) && + // Convenience initializers only contain assignments and not + // initializations. + !(cast(fnDecl)->isConvenienceInit()); + + // Assignment to a wrapped property can only be re-written to initialization for + // members of `self` in an initializer, and for local variables. + if (!isAssignmentToSelfParamInInit) + return false; + + auto *initAccessor = varDecl->getAccessor(AccessorKind::Init); + if (!initAccessor) + return false; + + return true; + } + void emitAssignWithSetter(SILGenFunction &SGF, SILLocation loc, LValue &&dest, ArgumentSource &&value) { assert(getAccessorDecl()->isSetter()); @@ -1675,6 +1702,36 @@ namespace { return Mval; }; + if (canRewriteSetAsInitAccessor(SGF)) { + // Emit an assign_or_init with the allocating initializer function and the + // setter function as arguments. DefiniteInitialization will then decide + // between the two functions, depending if it's an init call or a + // set call. + VarDecl *field = cast(Storage); + auto FieldType = field->getValueInterfaceType(); + if (!Substitutions.empty()) { + FieldType = FieldType.subst(Substitutions); + } + + // Emit the init accessor function partially applied to the base. + auto *initAccessor = field->getOpaqueAccessor(AccessorKind::Init); + auto initConstant = SGF.getAccessorDeclRef(initAccessor); + SILValue initFRef = SGF.emitGlobalFunctionRef(loc, initConstant); + + // Emit the set accessor function partially applied to the base. + auto setterFRef = getSetterFRef(); + auto setterTy = getSetterType(setterFRef); + SILFunctionConventions setterConv(setterTy, SGF.SGM.M); + auto setterFn = emitPartialSetterApply(setterFRef, setterConv); + + // Create the assign_or_init with the initializer and setter. + auto value = emitValue(field, FieldType, setterTy, setterConv); + SGF.B.createAssignOrInit(loc, value.forward(SGF), initFRef, + setterFn.getValue(), + AssignOrInitInst::Unknown); + return; + } + if (canRewriteSetAsPropertyWrapperInit(SGF) && !Storage->isStatic() && isBackingVarVisible(cast(Storage), @@ -2943,7 +3000,11 @@ namespace { case AccessorKind::WillSet: case AccessorKind::DidSet: llvm_unreachable("cannot use accessor directly to perform an access"); + + case AccessorKind::Init: + llvm_unreachable("init accessor not yet implemented"); } + llvm_unreachable("bad kind"); } }; @@ -3498,11 +3559,12 @@ static bool isCallToSelfOfCurrentFunction(SILGenFunction &SGF, LookupExpr *e) { cast(SGF.FunctionDC->getAsDecl()), false); } -static bool isCurrentFunctionReadAccess(SILGenFunction &SGF) { +static bool isCurrentFunctionAccessor(SILGenFunction &SGF, + AccessorKind accessorKind) { auto *contextAccessorDecl = dyn_cast_or_null(SGF.FunctionDC->getAsDecl()); return contextAccessorDecl && - contextAccessorDecl->getAccessorKind() == AccessorKind::Read; + contextAccessorDecl->getAccessorKind() == accessorKind; } LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, @@ -3512,6 +3574,19 @@ LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, // that can be an lvalue is a VarDecl. VarDecl *var = cast(e->getMember().getDecl()); + // A reference to an instance property in init accessor body + // has to be remapped into an argument reference because all + // of the properties from initialized/accesses lists are passed + // to init accessors individually via arguments. + if (isCurrentFunctionAccessor(SGF, AccessorKind::Init)) { + if (auto *arg = SGF.isMappedToInitAccessorArgument(var)) { + auto subs = e->getMember().getSubstitutions(); + return emitLValueForNonMemberVarDecl( + SGF, e, ConcreteDeclRef(arg, subs), getSubstFormalRValueType(e), + accessKind, options, AccessSemantics::Ordinary, None); + } + } + auto accessSemantics = e->getAccessSemantics(); AccessStrategy strategy = var->getAccessStrategy(accessSemantics, @@ -3521,7 +3596,7 @@ LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, bool isOnSelfParameter = isCallToSelfOfCurrentFunction(SGF, e); - bool isContextRead = isCurrentFunctionReadAccess(SGF); + bool isContextRead = isCurrentFunctionAccessor(SGF, AccessorKind::Read); // If we are inside _read, calling self.get, and the _read we are inside of is // the same as the as self's variable and the current function is a @@ -3722,7 +3797,7 @@ LValue SILGenLValue::visitSubscriptExpr(SubscriptExpr *e, SGF.F.getResilienceExpansion()); bool isOnSelfParameter = isCallToSelfOfCurrentFunction(SGF, e); - bool isContextRead = isCurrentFunctionReadAccess(SGF); + bool isContextRead = isCurrentFunctionAccessor(SGF, AccessorKind::Read); // If we are inside _read, calling self.get, and the _read we are inside of is // the same as the as self's variable and the current function is a @@ -5027,7 +5102,8 @@ static bool trySetterPeephole(SILGenFunction &SGF, SILLocation loc, } auto &setterComponent = static_cast(component); - if (setterComponent.canRewriteSetAsPropertyWrapperInit(SGF)) + if (setterComponent.canRewriteSetAsPropertyWrapperInit(SGF) || + setterComponent.canRewriteSetAsInitAccessor(SGF)) return false; setterComponent.emitAssignWithSetter(SGF, loc, std::move(dest), diff --git a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp index 73138ef18e785..8581c8b3dad41 100644 --- a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp +++ b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp @@ -284,7 +284,8 @@ static void checkUsesOfAccess(BeginAccessInst *access) { auto user = use->getUser(); assert(!isa(user)); assert(!isa(user) || - onlyUsedByAssignByWrapper(cast(user))); + onlyUsedByAssignByWrapper(cast(user)) || + onlyUsedByAssignOrInit(cast(user))); } #endif } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 4c8e388b8f1ed..4b8278859f3c4 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -31,6 +31,10 @@ using namespace ownership; // Utility //===----------------------------------------------------------------------===// +static bool isVariableOrResult(MarkUninitializedInst *MUI) { + return MUI->isVar() || MUI->isOut(); +} + static void gatherDestroysOfContainer(const DIMemoryObjectInfo &memoryInfo, DIElementUseInfo &useInfo) { auto *uninitMemory = memoryInfo.getUninitializedValue(); @@ -106,7 +110,7 @@ computeMemorySILType(MarkUninitializedInst *MUI, SILValue Address) { // If this is a let variable we're initializing, remember this so we don't // allow reassignment. - if (!MUI->isVar()) + if (!isVariableOrResult(MUI)) return {MemorySILType, false}; auto *VDecl = MUI->getLoc().getAsASTNode(); @@ -417,7 +421,7 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, Result = "self"; else if (ValueDecl *VD = dyn_cast_or_null(getLoc().getAsASTNode())) - Result = std::string(VD->getBaseIdentifier()); + Result = VD->hasName() ? VD->getBaseIdentifier().str() : "_"; else Result = ""; @@ -459,7 +463,7 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, // If we are analyzing a variable, we can generally get the decl associated // with it. - if (MemoryInst->isVar()) + if (isVariableOrResult(MemoryInst)) return MemoryInst->getLoc().getAsASTNode(); // Otherwise, we can't. @@ -516,7 +520,7 @@ SingleValueInstruction *DIMemoryObjectInfo::findUninitializedSelfValue() const { // If instruction is not a local variable, it could only // be some kind of `self` (root, delegating, derived etc.) // see \c MarkUninitializedInst::Kind for more details. - if (!MUI->isVar()) + if (!isVariableOrResult(MUI)) return ::getUninitializedValue(MUI); } } @@ -525,7 +529,7 @@ SingleValueInstruction *DIMemoryObjectInfo::findUninitializedSelfValue() const { ConstructorDecl *DIMemoryObjectInfo::getActorInitSelf() const { // is it 'self'? - if (!MemoryInst->isVar()) + if (!isVariableOrResult(MemoryInst)) { if (auto decl = dyn_cast_or_null(getASTType()->getAnyNominal())) // is it for an actor? @@ -535,6 +539,7 @@ ConstructorDecl *DIMemoryObjectInfo::getActorInitSelf() const { if (auto *ctor = dyn_cast_or_null( silFn->getDeclContext()->getAsDecl())) return ctor; + } return nullptr; } @@ -549,7 +554,7 @@ bool DIMemoryUse::onlyTouchesTrivialElements( const DIMemoryObjectInfo &MI) const { // assign_by_wrapper calls functions to assign a value. This is not // considered as trivial. - if (isa(Inst)) + if (isa(Inst) || isa(Inst)) return false; auto *F = Inst->getFunction(); @@ -650,25 +655,28 @@ class ElementUseCollector { private: void collectUses(SILValue Pointer, unsigned BaseEltNo); bool addClosureElementUses(PartialApplyInst *pai, Operand *argUse); + void collectAssignOrInitUses(PartialApplyInst *pai, Operand *argUse, + unsigned BaseEltNo = 0); void collectClassSelfUses(SILValue ClassPointer); void collectClassSelfUses(SILValue ClassPointer, SILType MemorySILType, llvm::SmallDenseMap &EN); void addElementUses(unsigned BaseEltNo, SILType UseTy, SILInstruction *User, - DIUseKind Kind); + DIUseKind Kind, NullablePtr Field = 0); void collectTupleElementUses(TupleElementAddrInst *TEAI, unsigned BaseEltNo); void collectStructElementUses(StructElementAddrInst *SEAI, unsigned BaseEltNo); }; } // end anonymous namespace -/// addElementUses - An operation (e.g. load, store, inout use, etc) on a value +/// addElementUses - An operation (e.g. load, store, inout usec etc) on a value /// acts on all of the aggregate elements in that value. For example, a load /// of $*(Int,Int) is a use of both Int elements of the tuple. This is a helper /// to keep the Uses data structure up to date for aggregate uses. void ElementUseCollector::addElementUses(unsigned BaseEltNo, SILType UseTy, - SILInstruction *User, DIUseKind Kind) { + SILInstruction *User, DIUseKind Kind, + NullablePtr Field) { // If we're in a subelement of a struct or enum, just mark the struct, not // things that come after it in a parent tuple. unsigned NumElements = 1; @@ -678,7 +686,7 @@ void ElementUseCollector::addElementUses(unsigned BaseEltNo, SILType UseTy, getElementCountRec(TypeExpansionContext(*User->getFunction()), Module, UseTy, IsSelfOfNonDelegatingInitializer); - trackUse(DIMemoryUse(User, Kind, BaseEltNo, NumElements)); + trackUse(DIMemoryUse(User, Kind, BaseEltNo, NumElements, Field)); } /// Given a tuple_element_addr or struct_element_addr, compute the new @@ -1083,7 +1091,12 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { if (auto *PAI = dyn_cast(User)) { if (onlyUsedByAssignByWrapper(PAI)) continue; - + + if (onlyUsedByAssignOrInit(PAI)) { + collectAssignOrInitUses(PAI, Op, BaseEltNo); + continue; + } + if (BaseEltNo == 0 && addClosureElementUses(PAI, Op)) continue; } @@ -1174,6 +1187,50 @@ bool ElementUseCollector::addClosureElementUses(PartialApplyInst *pai, return true; } +void +ElementUseCollector::collectAssignOrInitUses(PartialApplyInst *pai, + Operand *argUse, + unsigned BaseEltNo) { + for (Operand *Op : pai->getUses()) { + SILInstruction *User = Op->getUser(); + if (!isa(User) || Op->getOperandNumber() != 2) { + continue; + } + + /// AssignOrInit doesn't operate on `self` so we need to make sure + /// that the flag is dropped before calling \c addElementUses. + llvm::SaveAndRestore X(IsSelfOfNonDelegatingInitializer, false); + + auto *inst = cast(User); + auto *typeDC = inst->getReferencedInitAccessor() + ->getDeclContext() + ->getSelfNominalTypeDecl(); + + auto selfTy = pai->getOperand(1)->getType(); + + auto addUse = [&](VarDecl *property, DIUseKind useKind) { + auto expansionContext = TypeExpansionContext(*pai->getFunction()); + auto type = selfTy.getFieldType(property, Module, expansionContext); + addElementUses(Module.getFieldIndex(typeDC, property), type, User, + useKind, property); + }; + + auto initializedElts = inst->getInitializedProperties(); + if (initializedElts.empty()) { + // Add a placeholder use that doesn't touch elements to make sure that + // the `assign_or_init` instruction gets the kind set when `initializes` + // list is empty. + trackUse(DIMemoryUse(User, DIUseKind::InitOrAssign, BaseEltNo, 0)); + } else { + for (auto *property : initializedElts) + addUse(property, DIUseKind::InitOrAssign); + } + + for (auto *property : inst->getAccessedProperties()) + addUse(property, DIUseKind::Load); + } +} + /// collectClassSelfUses - Collect all the uses of a 'self' pointer in a class /// constructor. The memory object has class type. void ElementUseCollector::collectClassSelfUses(SILValue ClassPointer) { @@ -1576,6 +1633,11 @@ void ElementUseCollector::collectClassSelfUses( if (onlyUsedByAssignByWrapper(PAI)) continue; + if (onlyUsedByAssignOrInit(PAI)) { + collectAssignOrInitUses(PAI, Op); + continue; + } + if (addClosureElementUses(PAI, Op)) continue; diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index 99b6e9c00cf37..860b6a0261a3c 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -116,7 +116,9 @@ class DIMemoryObjectInfo { unsigned getNumElements() const { return NumElements; } /// Return true if this is 'self' in any kind of initializer. - bool isAnyInitSelf() const { return !MemoryInst->isVar(); } + bool isAnyInitSelf() const { + return !MemoryInst->isVar() && !MemoryInst->isOut(); + } /// Return uninitialized value of 'self' if current memory object /// is located in an initializer (of any kind). @@ -150,7 +152,7 @@ class DIMemoryObjectInfo { if (MemoryInst->isDelegatingSelf()) return false; - if (!MemoryInst->isVar()) { + if (!MemoryInst->isVar() && !MemoryInst->isOut()) { if (auto decl = getASTType()->getAnyNominal()) { if (isa(decl)) { return true; @@ -200,6 +202,7 @@ class DIMemoryObjectInfo { bool isNonDelegatingInit() const { switch (MemoryInst->getMarkUninitializedKind()) { case MarkUninitializedInst::Var: + case MarkUninitializedInst::Out: return false; case MarkUninitializedInst::RootSelf: case MarkUninitializedInst::CrossModuleRootSelf: @@ -222,6 +225,8 @@ class DIMemoryObjectInfo { return MemoryInst->isDelegatingSelfAllocated(); } + bool isOut() const { return MemoryInst->isOut(); } + enum class EndScopeKind { Borrow, Access }; /// Given an element number (in the flattened sense) return a pointer to a @@ -259,9 +264,10 @@ enum DIUseKind { /// value. Assign, - /// The instruction is an assignment of a wrapped value with an already initialized - /// backing property wrapper. - AssignWrappedValue, + /// The instruction is a setter call for a computed property after all of + /// self is initialized. This is used for property wrappers and for init + /// accessors. + Set, /// The instruction is a store to a member of a larger struct value. PartialStore, @@ -308,9 +314,12 @@ struct DIMemoryUse { /// track of which tuple elements are affected. unsigned FirstElement, NumElements; - DIMemoryUse(SILInstruction *Inst, DIUseKind Kind, unsigned FE, unsigned NE) - : Inst(Inst), Kind(Kind), FirstElement(FE), NumElements(NE) { - } + NullablePtr Field; + + DIMemoryUse(SILInstruction *Inst, DIUseKind Kind, unsigned FE, unsigned NE, + NullablePtr Field = 0) + : Inst(Inst), Kind(Kind), FirstElement(FE), NumElements(NE), + Field(Field) {} DIMemoryUse() : Inst(nullptr) {} diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 85263506253de..12cb54a377583 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -566,7 +566,7 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory, case DIUseKind::Escape: continue; case DIUseKind::Assign: - case DIUseKind::AssignWrappedValue: + case DIUseKind::Set: case DIUseKind::IndirectIn: case DIUseKind::InitOrAssign: case DIUseKind::InOutArgument: @@ -1094,7 +1094,7 @@ void LifetimeChecker::doIt() { continue; case DIUseKind::Assign: - case DIUseKind::AssignWrappedValue: + case DIUseKind::Set: // Instructions classified as assign are only generated when lowering // InitOrAssign instructions in regions known to be initialized. Since // they are already known to be definitely init, don't reprocess them. @@ -1152,6 +1152,18 @@ void LifetimeChecker::doIt() { return; } + // All of the indirect results marked as "out" have to be fully initialized + // before their lifetime ends. + if (TheMemory.isOut() && Uses.empty()) { + auto loc = TheMemory.getLoc(); + + std::string propertyName; + auto *property = TheMemory.getPathStringToElement(0, propertyName); + diagnose(Module, F.getLocation(), + diag::ivar_not_initialized_by_init_accessor, property->getName()); + EmittedErrorLocs.push_back(loc); + } + // If the memory object has nontrivial type, then any destroy/release of the // memory object will destruct the memory. If the memory (or some element // thereof) is not initialized on some path, the bad things happen. Process @@ -1436,14 +1448,30 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { // If this is an initialization or a normal assignment, upgrade the store to // an initialization or assign in the uses list so that clients know about it. if (isFullyUninitialized) { - Use.Kind = DIUseKind::Initialization; + // If this is a placeholder use of `assign_or_init` instruction, + // check whether all of the fields are initialized - if so, call a setter, + // otherwise call init accessor. + if (isa(Use.Inst) && Use.NumElements == 0) { + auto allFieldsInitialized = + getAnyUninitializedMemberAtInst(Use.Inst, 0, + TheMemory.getNumElements()) == -1; + Use.Kind = + allFieldsInitialized ? DIUseKind::Set : DIUseKind::Initialization; + } else { + Use.Kind = DIUseKind::Initialization; + } } else if (isFullyInitialized && isa(Use.Inst)) { // If some fields are uninitialized, re-write assign_by_wrapper to assignment // of the backing wrapper. If all fields are initialized, assign to the wrapped // value. auto allFieldsInitialized = getAnyUninitializedMemberAtInst(Use.Inst, 0, TheMemory.getNumElements()) == -1; - Use.Kind = allFieldsInitialized ? DIUseKind::AssignWrappedValue : DIUseKind::Assign; + Use.Kind = allFieldsInitialized ? DIUseKind::Set : DIUseKind::Assign; + } else if (isFullyInitialized && isa(Use.Inst)) { + auto allFieldsInitialized = + getAnyUninitializedMemberAtInst(Use.Inst, 0, + TheMemory.getNumElements()) == -1; + Use.Kind = allFieldsInitialized ? DIUseKind::Set : DIUseKind::Assign; } else if (isFullyInitialized) { Use.Kind = DIUseKind::Assign; } else { @@ -1470,7 +1498,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { HasConditionalInitAssign = true; return; } - + // Otherwise, we have a definite init or assign. Make sure the instruction // itself is tagged properly. NeedsUpdateForInitState.push_back(UseID); @@ -1593,6 +1621,7 @@ void LifetimeChecker::handleInOutUse(const DIMemoryUse &Use) { case AccessorKind::MutableAddress: case AccessorKind::DidSet: case AccessorKind::WillSet: + case AccessorKind::Init: return true; } llvm_unreachable("bad kind"); @@ -2262,7 +2291,8 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { "delegating inits have a single elt"); // Lower Assign instructions if needed. - if (isa(Use.Inst) || isa(Use.Inst)) + if (isa(Use.Inst) || isa(Use.Inst) || + isa(Use.Inst)) NeedsUpdateForInitState.push_back(UseID); } else { // super.init also requires that all ivars are initialized before the @@ -2296,7 +2326,7 @@ void LifetimeChecker::updateInstructionForInitState(unsigned UseID) { Use.Kind == DIUseKind::SelfInit) InitKind = IsInitialization; else { - assert(Use.Kind == DIUseKind::Assign || Use.Kind == DIUseKind::AssignWrappedValue); + assert(Use.Kind == DIUseKind::Assign || Use.Kind == DIUseKind::Set); InitKind = IsNotInitialization; } @@ -2356,6 +2386,30 @@ void LifetimeChecker::updateInstructionForInitState(unsigned UseID) { return; } + + if (auto *AI = dyn_cast(Inst)) { + // Remove this instruction from our data structures, since we will be + // removing it. + Use.Inst = nullptr; + llvm::erase_if(NonLoadUses[Inst], [&](unsigned id) { return id == UseID; }); + + switch (Use.Kind) { + case DIUseKind::Assign: + AI->markAsInitialized(Use.Field.get()); + LLVM_FALLTHROUGH; + case DIUseKind::Initialization: + AI->setMode(AssignOrInitInst::Init); + break; + case DIUseKind::Set: + AI->setMode(AssignOrInitInst::Set); + break; + default: + llvm_unreachable("Wrong use kind for assign_or_init"); + } + + return; + } + if (auto *AI = dyn_cast(Inst)) { // Remove this instruction from our data structures, since we will be // removing it. @@ -2369,7 +2423,7 @@ void LifetimeChecker::updateInstructionForInitState(unsigned UseID) { case DIUseKind::Assign: AI->setMode(AssignByWrapperInst::Assign); break; - case DIUseKind::AssignWrappedValue: + case DIUseKind::Set: AI->setMode(AssignByWrapperInst::AssignWrappedValue); break; default: diff --git a/lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp b/lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp index e5e5fb1c4135a..2bd4aa8980e65 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp @@ -183,6 +183,7 @@ bool isUseOfSelfInInitializer(Operand *oper) { if (auto *MUI = dyn_cast(value)) { switch (MUI->getMarkUninitializedKind()) { case MarkUninitializedInst::Kind::Var: + case MarkUninitializedInst::Kind::Out: return false; case MarkUninitializedInst::Kind::RootSelf: case MarkUninitializedInst::Kind::CrossModuleRootSelf: diff --git a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp index d06d94ad30e6a..179b7f5beeb6a 100644 --- a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp +++ b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "raw-sil-inst-lowering" +#include "swift/AST/Decl.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" @@ -168,6 +169,15 @@ static void getAssignByWrapperArgs(SmallVectorImpl &args, "initializer or setter has too many arguments"); } +static void emitInitAccessorInitialValueArgument( + SmallVectorImpl &args, SILValue src, + const SILFunctionConventions &convention, SILBuilder &forProjections, + SILBuilder &forCleanup) { + unsigned argIdx = convention.getSILArgIndexOfFirstParam(); + getAssignByWrapperArgsRecursively(args, src, argIdx, convention, + forProjections, forCleanup); +} + static void lowerAssignByWrapperInstruction(SILBuilderWithScope &b, AssignByWrapperInst *inst, @@ -250,6 +260,119 @@ lowerAssignByWrapperInstruction(SILBuilderWithScope &b, inst->eraseFromParent(); } +static void +lowerAssignOrInitInstruction(SILBuilderWithScope &b, + AssignOrInitInst *inst, + SmallSetVector &toDelete) { + LLVM_DEBUG(llvm::dbgs() << " *** Lowering " << *inst << "\n"); + + ++numAssignRewritten; + + SILValue src = inst->getSrc(); + SILLocation loc = inst->getLoc(); + SILBuilderWithScope forCleanup(std::next(inst->getIterator())); + + switch (inst->getMode()) { + case AssignOrInitInst::Unknown: + assert(b.getModule().getASTContext().hadError() && + "assign_or_init must have a valid mode"); + // In case DefiniteInitialization already gave up with an error, just + // treat the assign_or_init as an "init". + LLVM_FALLTHROUGH; + case AssignOrInitInst::Init: { + SILValue initFn = inst->getInitializer(); + CanSILFunctionType fTy = initFn->getType().castTo(); + SILFunctionConventions convention(fTy, inst->getModule()); + + auto *setterPA = dyn_cast(inst->getSetter()); + assert(setterPA); + + auto selfValue = setterPA->getOperand(1); + auto isRefSelf = selfValue->getType().getASTType()->mayHaveSuperclass(); + + SILValue selfRef; + if (isRefSelf) { + selfRef = b.emitBeginBorrowOperation(loc, selfValue); + } else { + selfRef = b.createBeginAccess(loc, selfValue, SILAccessKind::Modify, + SILAccessEnforcement::Dynamic, + /*noNestedConflict=*/false, + /*fromBuiltin=*/false); + } + + auto emitFieldReference = [&](VarDecl *field, + bool emitDestroy = false) -> SILValue { + SILValue fieldRef; + if (isRefSelf) { + fieldRef = b.createRefElementAddr(loc, selfRef, field); + } else { + fieldRef = b.createStructElementAddr(loc, selfRef, field); + } + + if (emitDestroy) + b.createDestroyAddr(loc, fieldRef); + + return fieldRef; + }; + + SmallVector arguments; + + // First, emit all of the properties listed in `initializes(...)`. They + // are passed as indirect results. + { + auto toInitialize = inst->getInitializedProperties(); + for (unsigned index : indices(toInitialize)) { + arguments.push_back(emitFieldReference( + toInitialize[index], + /*emitDestroy=*/inst->isPropertyAlreadyInitialized(index))); + } + } + + // Now emit `initialValue` which is the only argument specified + // by the user. + emitInitAccessorInitialValueArgument(arguments, src, convention, b, + forCleanup); + + // And finally, emit all of the `accesses(...)` properties. + for (auto *property : inst->getAccessedProperties()) + arguments.push_back(emitFieldReference(property)); + + b.createApply(loc, initFn, setterPA->getSubstitutionMap(), arguments); + + if (isRefSelf) { + b.emitEndBorrowOperation(loc, selfRef); + } else { + b.createEndAccess(loc, selfRef, /*aborted=*/false); + } + + // The unused partial_apply violates memory lifetime rules in case "self" + // is an inout. Therefore we cannot keep it as a dead closure to be + // cleaned up later. We have to delete it in this pass. + toDelete.insert(inst->getSetter()); + + // Also the argument of the closure (which usually is a "load") has to be + // deleted to avoid memory lifetime violations. + if (setterPA->getNumArguments() == 1) + toDelete.insert(setterPA->getArgument(0)); + break; + } + case AssignOrInitInst::Set: { + SILValue setterFn = inst->getSetter(); + CanSILFunctionType fTy = setterFn->getType().castTo(); + SILFunctionConventions convention(fTy, inst->getModule()); + assert(!convention.hasIndirectSILResults()); + SmallVector args; + getAssignByWrapperArgs(args, src, convention, b, forCleanup); + b.createApply(loc, setterFn, SubstitutionMap(), args); + + // Again, we have to delete the unused init accessor reference. + toDelete.insert(inst->getInitializer()); + break; + } + } + inst->eraseFromParent(); +} + static void deleteDeadAccessMarker(BeginAccessInst *BA) { SmallVector Users; for (Operand *Op : BA->getUses()) { @@ -323,6 +446,13 @@ static bool lowerRawSILOperations(SILFunction &fn) { continue; } + if (auto *ai = dyn_cast(inst)) { + SILBuilderWithScope b(ai); + lowerAssignOrInitInstruction(b, ai, toDelete); + changed = true; + continue; + } + // mark_uninitialized just becomes a noop, resolving to its operand. if (auto *mui = dyn_cast(inst)) { mui->replaceAllUsesWith(mui->getOperand()); diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index a02429ae68112..c3df1681f59ba 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -318,6 +318,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::StoreInst: case SILInstructionKind::AssignInst: case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::AssignOrInitInst: case SILInstructionKind::MarkFunctionEscapeInst: case SILInstructionKind::DebugValueInst: case SILInstructionKind::DebugStepInst: diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index d2c5ca4e21c5b..c9008d1388410 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -1029,6 +1029,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::WitnessMethodInst: case SILInstructionKind::AssignInst: case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::AssignOrInitInst: case SILInstructionKind::CheckedCastBranchInst: case SILInstructionKind::CheckedCastAddrBranchInst: case SILInstructionKind::ClassMethodInst: diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index fc9b3cc28d9e3..58fc9c88688ad 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -4476,6 +4476,7 @@ bool InvalidMemberRefOnExistential::diagnoseAsError() { case AccessorKind::Set: case AccessorKind::WillSet: case AccessorKind::DidSet: + case AccessorKind::Init: // Ignore references to the 'newValue' or 'oldValue' parameters. if (AccessorParams.front() == PD) { return true; diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 4e3ca4c3b4ce4..459f001824393 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -294,6 +294,12 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, if (ICK == ImplicitConstructorKind::Memberwise) { assert(isa(decl) && "Only struct have memberwise constructor"); + std::multimap initializedViaAccessor; + decl->collectPropertiesInitializableByInitAccessors(initializedViaAccessor); + + // A single property could be used to initialize N other stored + // properties via a call to its init accessor. + llvm::SmallPtrSet usedInitProperties; for (auto member : decl->getMembers()) { auto var = dyn_cast(member); if (!var) @@ -302,6 +308,20 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) continue; + // Check whether this property could be initialized via init accessor. + // + // Note that we check for a single match here because intersecting + // properties are going to be diagnosed. + if (initializedViaAccessor.count(var) == 1) { + auto *initializerProperty = initializedViaAccessor.find(var)->second; + // Parameter for this property is already emitted. + if (usedInitProperties.count(initializerProperty)) + continue; + + var = initializerProperty; + usedInitProperties.insert(initializerProperty); + } + accessLevel = std::min(accessLevel, var->getFormalAccess()); params.push_back(createMemberwiseInitParameter(decl, Loc, var)); diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 7152cfae928d3..4b17e5bfb1859 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -4586,6 +4586,7 @@ class ObjCSelectorWalker : public ASTWalker { case AccessorKind::MutableAddress: case AccessorKind::Read: case AccessorKind::Modify: + case AccessorKind::Init: llvm_unreachable("cannot be @objc"); } } else { diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 27e5f5ecc6081..ae66572e3f267 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -294,6 +294,8 @@ class AttributeChecker : public AttributeVisitor { void visitDiscardableResultAttr(DiscardableResultAttr *attr); void visitDynamicReplacementAttr(DynamicReplacementAttr *attr); void visitTypeEraserAttr(TypeEraserAttr *attr); + void visitInitializesAttr(InitializesAttr *attr); + void visitAccessesAttr(AccessesAttr *attr); void visitImplementsAttr(ImplementsAttr *attr); void visitNoMetadataAttr(NoMetadataAttr *attr); @@ -3548,6 +3550,44 @@ void AttributeChecker::visitTypeEraserAttr(TypeEraserAttr *attr) { (void)attr->hasViableTypeEraserInit(cast(D)); } +void AttributeChecker::visitInitializesAttr(InitializesAttr *attr) { + auto *accessor = dyn_cast(D); + if (!accessor || accessor->getAccessorKind() != AccessorKind::Init) { + diagnose(attr->getLocation(), + diag::init_accessor_initializes_attribute_on_other_declaration); + return; + } + + (void)attr->getPropertyDecls(accessor); +} + +void AttributeChecker::visitAccessesAttr(AccessesAttr *attr) { + auto *accessor = dyn_cast(D); + if (!accessor || accessor->getAccessorKind() != AccessorKind::Init) { + diagnose(attr->getLocation(), + diag::init_accessor_accesses_attribute_on_other_declaration); + return; + } + + // Check whether there are any intersections between initializes(...) and + // accesses(...) attributes. + + Optional> initializedProperties; + if (auto *initAttr = D->getAttrs().getAttribute()) { + initializedProperties.emplace(initAttr->getPropertyDecls(accessor)); + } + + if (initializedProperties) { + for (auto *property : attr->getPropertyDecls(accessor)) { + if (llvm::is_contained(*initializedProperties, property)) { + diagnose(attr->getLocation(), + diag::init_accessor_property_both_init_and_accessed, + property->getName()); + } + } + } +} + void AttributeChecker::visitImplementsAttr(ImplementsAttr *attr) { DeclContext *DC = D->getDeclContext(); @@ -4901,6 +4941,8 @@ static DescriptiveDeclKind getAccessorDescriptiveDeclKind(AccessorKind kind) { return DescriptiveDeclKind::Addressor; case AccessorKind::MutableAddress: return DescriptiveDeclKind::MutableAddressor; + case AccessorKind::Init: + return DescriptiveDeclKind::InitAccessor; } } @@ -7511,3 +7553,65 @@ void TypeChecker::checkReflectionMetadataAttributes(ExtensionDecl *ED) { }); } } + +ArrayRef InitAccessorReferencedVariablesRequest::evaluate( + Evaluator &evaluator, DeclAttribute *attr, AccessorDecl *attachedTo, + ArrayRef referencedVars) const { + auto &ctx = attachedTo->getASTContext(); + + auto *storage = attachedTo->getStorage(); + + auto typeDC = storage->getDeclContext()->getSelfNominalTypeDecl(); + if (!typeDC) + return ctx.AllocateCopy(ArrayRef()); + + SmallVector results; + + bool failed = false; + for (auto name : referencedVars) { + auto propertyResults = typeDC->lookupDirect(DeclName(name)); + switch (propertyResults.size()) { + case 0: { + ctx.Diags.diagnose(attr->getLocation(), diag::cannot_find_type_in_scope, + DeclNameRef(name)); + failed = true; + break; + } + + case 1: { + auto *member = propertyResults.front(); + + // Only stored properties are supported. + if (auto *var = dyn_cast(member)) { + if (var->getImplInfo().hasStorage()) { + results.push_back(var); + break; + } + } + + ctx.Diags.diagnose(attr->getLocation(), + diag::init_accessor_can_refer_only_to_properties, + member->getDescriptiveKind(), member->createNameRef()); + failed = true; + break; + } + + default: + ctx.Diags.diagnose(attr->getLocation(), + diag::ambiguous_member_overload_set, + DeclNameRef(name)); + + for (auto *choice : propertyResults) { + ctx.Diags.diagnose(choice, diag::decl_declared_here, choice->getName()); + } + + failed = true; + break; + } + } + + if (failed) + return ctx.AllocateCopy(ArrayRef()); + + return ctx.AllocateCopy(results); +} diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 2ab576ddda04e..dd0f7655608e3 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -373,6 +373,8 @@ static bool doesAccessorNeedDynamicAttribute(AccessorDecl *accessor) { storage->getWriteImpl() == WriteImplKind::StoredWithObservers) return storage->isDynamic(); return false; + case AccessorKind::Init: + return false; } llvm_unreachable("covered switch"); } @@ -1693,6 +1695,7 @@ SelfAccessKindRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const { case AccessorKind::Read: break; + case AccessorKind::Init: case AccessorKind::MutableAddress: case AccessorKind::Set: case AccessorKind::Modify: @@ -1784,6 +1787,7 @@ static ParamDecl *getOriginalParamFromAccessor(AbstractStorageDecl *storage, case AccessorKind::DidSet: case AccessorKind::WillSet: case AccessorKind::Set: + case AccessorKind::Init: if (param == accessorParams->get(0)) { // This is the 'newValue' or 'oldValue' parameter. return nullptr; @@ -2118,6 +2122,7 @@ ResultTypeRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { case AccessorKind::DidSet: case AccessorKind::WillSet: case AccessorKind::Set: + case AccessorKind::Init: return TupleType::getEmpty(ctx); // Addressor result types can get complicated because of the owner. diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 09501281c6aaa..2a0972ab954d7 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -717,6 +717,13 @@ bool swift::isRepresentableInObjC( .limitBehavior(behavior); Reason.describe(accessor); return false; + + case AccessorKind::Init: + diagnoseAndRemoveAttr(accessor, Reason.getAttr(), + diag::objc_init_accessor) + .limitBehavior(behavior); + Reason.describe(accessor); + return false; } llvm_unreachable("bad kind"); } @@ -1402,6 +1409,7 @@ Optional shouldMarkAsObjC(const ValueDecl *VD, bool allowImplicit) { case AccessorKind::Modify: case AccessorKind::Read: case AccessorKind::WillSet: + case AccessorKind::Init: return false; case AccessorKind::MutableAddress: diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 3c341969b2f8f..d4aff7bc2bffc 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1581,6 +1581,8 @@ namespace { UNINTERESTING_ATTR(ObjCMembers) UNINTERESTING_ATTR(ObjCRuntimeName) UNINTERESTING_ATTR(RestatedObjCConformance) + UNINTERESTING_ATTR(Initializes) + UNINTERESTING_ATTR(Accesses) UNINTERESTING_ATTR(Implements) UNINTERESTING_ATTR(StaticInitializeObjCMetadata) UNINTERESTING_ATTR(ClangImporterSynthesizedType) @@ -2277,6 +2279,7 @@ OverriddenDeclsRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { case AccessorKind::DidSet: case AccessorKind::Address: case AccessorKind::MutableAddress: + case AccessorKind::Init: // These accessors are never part of the opaque set. Bail out early // to avoid computing the overridden declarations of the storage. return noResults; diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 47ba8b414b25a..e8197806b3d10 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -6799,6 +6799,7 @@ swift::findWitnessedObjCRequirements(const ValueDecl *witness, case AccessorKind::MutableAddress: case AccessorKind::Read: case AccessorKind::Modify: + case AccessorKind::Init: // These accessors are never exposed to Objective-C. return result; case AccessorKind::DidSet: diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 3dd3b13c7cfa0..59e5aed5f5b63 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -283,6 +283,22 @@ StoredPropertiesAndMissingMembersRequest::evaluate(Evaluator &evaluator, return decl->getASTContext().AllocateCopy(results); } +ArrayRef +InitAccessorPropertiesRequest::evaluate(Evaluator &evaluator, + NominalTypeDecl *decl) const { + SmallVector results; + for (auto *member : decl->getMembers()) { + auto *var = dyn_cast(member); + if (!var || !var->getAccessor(AccessorKind::Init)) { + continue; + } + + results.push_back(var); + } + + return decl->getASTContext().AllocateCopy(results); +} + /// Check whether the pattern may have storage. /// /// This query is careful not to trigger accessor macro expansion, which @@ -2058,6 +2074,9 @@ synthesizeAccessorBody(AbstractFunctionDecl *fn, void *) { case AccessorKind::Address: case AccessorKind::MutableAddress: break; + + case AccessorKind::Init: + llvm_unreachable("init accessor not yet implemented"); } llvm_unreachable("bad synthesized function kind"); } @@ -2568,6 +2587,7 @@ IsAccessorTransparentRequest::evaluate(Evaluator &evaluator, case AccessorKind::Read: case AccessorKind::Modify: + case AccessorKind::Init: break; case AccessorKind::WillSet: @@ -3477,6 +3497,7 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator, bool hasSetter = storage->getParsedAccessor(AccessorKind::Set); bool hasModify = storage->getParsedAccessor(AccessorKind::Modify); bool hasMutableAddress = storage->getParsedAccessor(AccessorKind::MutableAddress); + bool hasInit = storage->getParsedAccessor(AccessorKind::Init); auto *DC = storage->getDeclContext(); // 'get', 'read', and a non-mutable addressor are all exclusive. @@ -3490,7 +3511,7 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator, // If there's a writing accessor of any sort, there must also be a // reading accessor. - } else if (hasSetter || hasModify || hasMutableAddress) { + } else if (hasInit || hasSetter || hasModify || hasMutableAddress) { readImpl = ReadImplKind::Get; // Subscripts always have to have some sort of accessor; they can't be diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index e74bf4846f906..2710e85a9fdcb 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -5483,6 +5483,36 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { break; } + case decls_block::Initializes_DECL_ATTR: { + ArrayRef rawPropertyIDs; + serialization::decls_block::InitializesDeclAttrLayout:: + readRecord(scratch, rawPropertyIDs); + + SmallVector properties; + for (auto rawID : rawPropertyIDs) { + properties.push_back(MF.getIdentifier(rawID)); + } + + Attr = InitializesAttr::create(ctx, SourceLoc(), SourceRange(), + properties); + break; + } + + case decls_block::Accesses_DECL_ATTR: { + ArrayRef rawPropertyIDs; + serialization::decls_block::AccessesDeclAttrLayout:: + readRecord(scratch, rawPropertyIDs); + + SmallVector properties; + for (auto rawID : rawPropertyIDs) { + properties.push_back(MF.getIdentifier(rawID)); + } + + Attr = AccessesAttr::create(ctx, SourceLoc(), SourceRange(), + properties); + break; + } + case decls_block::DynamicReplacement_DECL_ATTR: { bool isImplicit; uint64_t numArgs; diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 8f2895a7cb49a..5308c97b99e37 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -2439,6 +2439,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, break; } case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::AssignOrInitInst: llvm_unreachable("not supported"); case SILInstructionKind::BindMemoryInst: { assert(RecordKind == SIL_ONE_TYPE_VALUES && diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index e213184de491a..e2f871b82df3f 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 788; // noimplicitcopy addr +const uint16_t SWIFTMODULE_VERSION_MINOR = 790; // add `out` kind to mark uninitialized instruction /// A standard hash seed used for all string hashes in a serialized module. /// @@ -330,6 +330,7 @@ enum AccessorKind : uint8_t { MutableAddress, Read, Modify, + Init, }; using AccessorKindField = BCFixed<4>; @@ -2226,6 +2227,16 @@ namespace decls_block { BCArray // target function pieces, spi groups, type erased params >; + using InitializesDeclAttrLayout = BCRecordLayout< + Initializes_DECL_ATTR, + BCArray // initialized properties + >; + + using AccessesDeclAttrLayout = BCRecordLayout< + Accesses_DECL_ATTR, + BCArray // initialized properties + >; + using DifferentiableDeclAttrLayout = BCRecordLayout< Differentiable_DECL_ATTR, BCFixed<1>, // Implicit flag. diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 80db9cfb78ec6..02bc669075a96 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2906,6 +2906,34 @@ class Serializer::DeclSerializer : public DeclVisitor { return; } + case DAK_Initializes: { + auto abbrCode = S.DeclTypeAbbrCodes[InitializesDeclAttrLayout::Code]; + auto attr = cast(DA); + + SmallVector properties; + for (auto identifier : attr->getProperties()) { + properties.push_back(S.addDeclBaseNameRef(identifier)); + } + + InitializesDeclAttrLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, properties); + return; + } + + case DAK_Accesses: { + auto abbrCode = S.DeclTypeAbbrCodes[AccessesDeclAttrLayout::Code]; + auto attr = cast(DA); + + SmallVector properties; + for (auto identifier : attr->getProperties()) { + properties.push_back(S.addDeclBaseNameRef(identifier)); + } + + AccessesDeclAttrLayout::emitRecord( + S.Out, S.ScratchRecord, abbrCode, properties); + return; + } + case DAK_DynamicReplacement: { auto abbrCode = S.DeclTypeAbbrCodes[DynamicReplacementDeclAttrLayout::Code]; diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index e9cf5f492565e..841942ea9afe5 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -2183,6 +2183,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { break; } case SILInstructionKind::AssignByWrapperInst: + case SILInstructionKind::AssignOrInitInst: llvm_unreachable("not supported"); case SILInstructionKind::BindMemoryInst: { auto *BI = cast(&SI); diff --git a/test/Interpreter/init_accessors.swift b/test/Interpreter/init_accessors.swift new file mode 100644 index 0000000000000..69e5f52ea99fa --- /dev/null +++ b/test/Interpreter/init_accessors.swift @@ -0,0 +1,334 @@ +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-feature -Xfrontend InitAccessors) | %FileCheck %s +// RUN: %target-run-simple-swift(-O -Xfrontend -enable-experimental-feature -Xfrontend InitAccessors) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: asserts + +struct TestInit { + var x: Int + var y: Int + var full: (Int, Int) + + var point: (Int, Int) { + init(initialValue) initializes(y, full) accesses(x) { + self.y = initialValue.1 + self.full = (self.x, self.y) + } + + get { full } + set { full = newValue } + } + + init(x: Int, y: Int) { + self.x = x + self.point = (x, y) + } +} + +do { + let test = TestInit(x: 0, y: -1) + print("test-init: \(test.point)") + // CHECK: test-init: (0, -1) +} + +struct TestSetter { + var x: Int + var y: Int + + var point: (Int, Int) { + init(initialValue) accesses(x, y) { + } + + get { (x, y) } + set { } + } + + init(x: Int, y: Int) { + self.x = x + self.y = y + self.point = (x, y) + } +} + +do { + let test = TestSetter(x: 0, y: -2) + print("test-setter: \(test.point)") + // CHECK: test-setter: (0, -2) +} + +struct TestInitThenSetter { + var x: Int + var y: Int + + var point: (Int, Int) { + init(initialValue) initializes(x, y) { + self.x = initialValue.0 + self.y = initialValue.1 + } + + get { (x, y) } + + set { + x = newValue.0 + y = newValue.1 + } + } + + init(x: Int, y: Int) { + self.point = (x, y) + + if x == 1 { + self.point = (0, 0) + } + } +} + +do { + let test = TestInitThenSetter(x: 1, y: 2) + print("test-init-then-setter: \(test.point)") + // CHECK: test-init-then-setter: (0, 0) +} + +struct TestPartialInt { + var x: Int + var y: Int + + var pointX: Int { + init(newValue) initializes(x) { + self.x = newValue + } + + get { x } + set { self.x = newValue } + } + + var pointY: Int { + init(newValue) initializes(y) { + self.y = newValue + } + + get { y } + set { self.y = newValue } + } + + init(x: Int, y: Int) { + // Init + self.pointX = x + // Init + self.pointY = y + + // Setter + self.pointX = 1 + // Setter + self.pointY = 2 + } +} + +do { + let test = TestPartialInt(x: 0, y: -1) + print("test-partial-init: (\(test.pointX), \(test.pointY))") + // CHECK: test-partial-init: (1, 2) +} + +struct TestNoInitAndInit { + var x: Int + var y: Int + + var pointX: Int { + init(initalValue) accesses(x) { + } + + get { x } + set { } + } + + var pointY: Int { + init(initialValue) initializes(y) { + self.y = initialValue + } + + get { y } + set { } + } + + init(x: Int, y: Int) { + self.x = x + self.pointX = x + self.pointY = y + print("TestNoInitAndInit(x: \(self.x), y: \(self.y))") + } +} + +do { + _ = TestNoInitAndInit(x: 10, y: -10) + // CHECK: TestNoInitAndInit(x: 10, y: -10) +} + +class TestClass { + var x: Int + var y: (Int, [String]) + + var data: (Int, (Int, [String])) { + init(initialValue) initializes(x, y) { + x = initialValue.0 + y = initialValue.1 + } + + get { (x, y) } + set { + x = newValue.0 + y = newValue.1 + } + } + + init(x: Int, y: (Int, [String])) { + self.data = (x, y) + } +} + +do { + let test = TestClass(x: 20, y: (0, ["a", "b"])) + print("test-class: \(test.data)") + // CHECK: test-class: (20, (0, ["a", "b"])) +} + +struct TestGeneric { + var a: T + var b: T + var c: U + + var data: (T, T) { + init(initialValue) initializes(a, b) accesses(c) { + a = initialValue.0 + b = initialValue.1 + print("TestGeneric(c: \(c))") + } + + get { (a, b) } + set { } + } + + init(a: T, b: T, c: U) { + self.c = c + self.data = (a, b) + self.data = (b, a) + } +} + +do { + let test = TestGeneric(a: 42, b: 0, c: [42, "a"] as [Any]) + print("test-generic: data = \(test.data)") + // CHECK: TestGeneric(c: [42, "a"]) + // CHECK-NEXT: test-generic: data = (42, 0) +} + +func test_local_with_memberwise() { + class MyValue {} + + struct TestMemberwiseConcrete { + var a: Int + var b: String + + var pair: (Int, String) { + init(initialValue) initializes(a, b) { + a = initialValue.0 + b = initialValue.1 + } + + get { (a, b) } + set { } + } + + var c: [MyValue] + } + + let concrete = TestMemberwiseConcrete(pair: (0, "a"), c: []) + print(concrete) + + struct TestMemberwiseGeneric where C: RangeReplaceableCollection, C.Element == T { + var _a: T + var _b: String + var _c: C + + var a: T { + init(initialValue) initializes(_a) { + _a = initialValue + } + + get { _a } + set { } + } + + var pair: (String, C) { + init(initialValue) initializes(_b, _c) accesses(_a) { + _b = initialValue.0 + _c = initialValue.1 + _c.append(_a) + } + + get { (_b, _c) } + set { } + } + } + + let generic = TestMemberwiseGeneric(a: 1, pair: ("a", [0])) + print(generic) +} + +test_local_with_memberwise() +// CHECK: TestMemberwiseConcrete(a: 0, b: "a", c: []) +// CHECK-NEXT: TestMemberwiseGeneric>(_a: 1, _b: "a", _c: [0, 1]) + +func test_assignments() { + struct Test { + var _a: Int + var _b: Int + + var a: Int { + init(initialValue) initializes(_a) { + self._a = initialValue + print("a-init-accessor: \(self._a)") + } + get { _a } + set { _a = newValue + 1 } + } + + var pair: (Int, Int) { + init(initialValue) initializes(_a, _b) { + _a = initialValue.0 + _b = initialValue.1 + } + + get { (_a, _b) } + set { } + } + + init(a: Int) { + // init + self.a = a + // re-assignment + self.a = a + 1 + self._b = 42 + // set + self.a = a + 2 + } + + init(a: Int, b: Int) { + self.a = a + self.pair = (0, b) + } + } + + let test1 = Test(a: 0) + print("test-assignments-1: \(test1.pair)") + + let test2 = Test(a: 0, b: 2) + print("test-assignments-2: \(test2.pair)") +} + +test_assignments() +// CHECK: a-init-accessor: 0 +// CHECK-NEXT: a-init-accessor: 1 +// CHECK-NEXT: test-assignments-1: (3, 42) +// CHECK-NEXT: a-init-accessor: 0 +// CHECK-NEXT: test-assignments-2: (0, 2) diff --git a/test/SILOptimizer/init_accessor_definite_init_diagnostics.swift b/test/SILOptimizer/init_accessor_definite_init_diagnostics.swift new file mode 100644 index 0000000000000..5198db703c0c8 --- /dev/null +++ b/test/SILOptimizer/init_accessor_definite_init_diagnostics.swift @@ -0,0 +1,141 @@ +// RUN: %target-swift-frontend -enable-experimental-feature InitAccessors -enable-copy-propagation=requested-passes-only -emit-sil -primary-file %s -o /dev/null -verify + +// REQUIRES: asserts + +struct Test1 { + var x: Int // expected-note {{variable defined here}} + var y: Int // expected-note {{variable defined here}} + var full: (Int, Int) + + var test1: (Int, Int) { + init(initialValue) initializes(y, full) accesses(x) { + self.full = (self.x, self.y) // expected-error {{variable 'y' used before being initialized}} + } + + get { full } + set { self.full = newValue } + } + + var pointY: Int { + init(initialValue) initializes(y) { + self.y = initialValue // Ok + } + + get { y } + set {} + } + + var errorPoint1: (Int, Int) { + init(initialValue) initializes(x, y) { + // expected-error@-1 {{property 'x' not initialized by init accessor}} + // expected-error@-2 {{property 'y' not initialized by init accessor}} + } + + get { (x, y) } + set { } + } + + var errorPoint2: (Int, Int) { + init(initialValue) initializes(x, y) { + // expected-error@-1 {{property 'y' not initialized by init accessor}} + self.x = initialValue.0 + } + + get { (x, y) } + set { } + } + + var errorPoint3: (Int, Int) { + init(initialValue) initializes(x, y) { + self.y = initialValue.1 + print(y) // Ok + print(x) // expected-error {{variable 'x' used before being initialized}} + } + + get { (x, y) } + set { } + } + + init(x: Int, y: Int) { + self.x = x + self.y = y + self.full = (x, y) + } +} + +struct TestPartial { + var x: Int + var y: Int + + var point: (Int, Int) { + init(initialValue) initializes(x, y) { + self.x = initialValue.0 + self.y = initialValue.1 + } + + get { (x, y) } + set { } + } + + init(x: Int, y: Int) { + self.x = x + self.point = (x, y) // Ok (x is going to get `destroy_addr`) + } + + init(_ x: Int, _ y: Int) { + self.x = x + self.y = y + self.point = (x, y) // Ok (calls a setter) + } +} + +struct TestDoubleInit1 { + let x: Int // expected-note {{change 'let' to 'var' to make it mutable}} + + var invalidPointX: Int { + init(initialValue) initializes(x) { + self.x = initialValue + self.x = 42 // expected-error {{immutable value 'x' may only be initialized once}} + } + + get { x } + set { } + } +} + +struct TestDoubleInit2 { + let x: Int // expected-note {{change 'let' to 'var' to make it mutable}} + + var pointX: Int { + init(initialValue) initializes(x) { + self.x = initialValue + } + + get { x } + set { } + } + + init(x: Int) { + self.pointX = x + self.x = 0 // expected-error {{immutable value 'self.x' may only be initialized once}} + } +} + +struct TestAccessBeforeInit { + var _x: Int + var x: Int { + init(initialValue) initializes(_x) accesses(y) { + _x = initialValue + } + + get { _x } + set {} + } + + var y: Int + + init(x: Int, y: Int) { + self.x = x // expected-error {{variable 'self.y' used before being initialized}} + self.y = y + } +} diff --git a/test/SILOptimizer/init_accessor_raw_sil_lowering.swift b/test/SILOptimizer/init_accessor_raw_sil_lowering.swift new file mode 100644 index 0000000000000..f12398a4d37d9 --- /dev/null +++ b/test/SILOptimizer/init_accessor_raw_sil_lowering.swift @@ -0,0 +1,81 @@ +// RUN: %target-swift-frontend -enable-experimental-feature InitAccessors -Xllvm -sil-print-after=definite-init -emit-sil -module-name assign_or_init_lowering %s -o /dev/null 2>&1 | %FileCheck %s + +// REQUIRES: asserts + +struct Test1 { + var _a: Int + var _b: String + + var a: Int { + init(initialValue) initializes(_a) { + _a = initialValue + } + + get { _a } + set { } + } + + var b: String { + init(initialValue) initializes(_a, _b) { + _a = 0 + _b = initialValue + } + + get { _b } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s23assign_or_init_lowering5Test1V1aACSi_tcfC : $@convention(method) (Int, @thin Test1.Type) -> @owned Test1 + init(a: Int) { + // CHECK: assign_or_init [init] [[VALUE:%.*]] : $Int, init {{.*}} : $@convention(thin) (Int) -> @out Int, set {{.*}} : $@callee_guaranteed (Int) -> () + self.a = a + // CHECK: assign_or_init [init] [assign=0] [[VALUE:%.*]] : $String, init {{.*}} : $@convention(thin) (@owned String) -> (@out Int, @out String), set {{.*}} : $@callee_guaranteed (@owned String) -> () + self.a = -1 + self.b = "" + // CHECK: assign_or_init [set] [[VALUE:%.*]] : $Int, init {{.*}} : $@convention(thin) (Int) -> @out Int, set {{.*}} : $@callee_guaranteed (Int) -> () + self.a = a + } +} + +struct Test2 { + var _a: Int + var _b: T + var _c: String + + var pair: (Int, T) { + init(initialValue) initializes(_a, _b) { + _a = initialValue.0 + _b = initialValue.1 + } + + get { (_a, _b) } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s23assign_or_init_lowering5Test2V1a1bACyxGSi_xtcfC : $@convention(method) (Int, @in T, @thin Test2.Type) -> @out Test2 + init(a: Int, b: T) { + // CHECK: assign_or_init [init] [[VALUE:%.*]] : $*(Int, T), init {{.*}} : $@convention(thin) <τ_0_0> (Int, @in τ_0_0) -> (@out Int, @out τ_0_0), set {{.*}} : $@callee_guaranteed (Int, @in T) -> () + self.pair = (a, b) + // CHECK: assign_or_init [init] [assign=0] [assign=1] [[VALUE:%.*]] : $*(Int, T), init {{.*}} : $@convention(thin) <τ_0_0> (Int, @in τ_0_0) -> (@out Int, @out τ_0_0), set {{.*}} : $@callee_guaranteed (Int, @in T) -> () + self.pair = (0, b) + self._c = "" + // CHECK: assign_or_init [set] [[VALUE:%.*]] : $*(Int, T), init {{.*}} : $@convention(thin) <τ_0_0> (Int, @in τ_0_0) -> (@out Int, @out τ_0_0), set {{.*}} : $@callee_guaranteed (Int, @in T) -> () + self.pair = (1, b) + } +} + +struct Test { + var test: Int { + init { + } + + get { 42 } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s23assign_or_init_lowering4TestV1vACSi_tcfC : $@convention(method) (Int, @thin Test.Type) -> Test + init(v: Int) { + // CHECK: assign_or_init [set] %0 : $Int, init {{.*}} : $@convention(thin) (Int) -> (), set {{.*}} : $@callee_guaranteed (Int) -> () + self.test = v + } +} diff --git a/test/SILOptimizer/init_accessors.swift b/test/SILOptimizer/init_accessors.swift new file mode 100644 index 0000000000000..a67bbb376f49e --- /dev/null +++ b/test/SILOptimizer/init_accessors.swift @@ -0,0 +1,443 @@ +// RUN: %target-swift-frontend -enable-experimental-feature InitAccessors -primary-file %s -Onone -emit-sil \ +// RUN: -Xllvm -sil-print-after=raw-sil-inst-lowering \ +// RUN: -o /dev/null -module-name init_accessors 2>&1 | %FileCheck %s + +// REQUIRES: asserts + +struct TestInit { + var x: Int + var y: Int + var full: (Int, Int) + + var point: (Int, Int) { + // CHECK-LABEL: sil private [ossa] @$s14init_accessors8TestInitV5pointSi_Sitvi : $@convention(thin) (Int, Int, @inout Int) -> (@out Int, @out (Int, Int)) + // CHECK: bb0([[Y_REF:%.*]] : $*Int, [[FULL_REF:%.*]] : $*(Int, Int), [[X_VAL:%.*]] : $Int, [[Y_VAL:%.*]] : $Int, [[X_REF:%.*]] : $*Int): + // + // CHECK: [[INITIAL_VALUE:%.*]] = tuple ([[X_VAL]] : $Int, [[Y_VAL]] : $Int) + // CHECK: ([[X_VAL:%.*]], [[Y_VAL:%.*]]) = destructure_tuple [[INITIAL_VALUE]] : $(Int, Int) + // CHECK: [[Y_ACCESS:%.*]] = begin_access [modify] [static] [[Y_REF]] : $*Int + // CHECK-NEXT: store [[Y_VAL]] to [trivial] [[Y_ACCESS]] : $*Int + // CHECK-NEXT: end_access [[Y_ACCESS]] : $*Int + // + // CHECK-NEXT: [[X_ACCESS:%.*]] = begin_access [read] [static] [[X_REF]] : $*Int + // CHECK-NEXT: [[X_VAL:%.*]] = load [trivial] [[X_ACCESS]] : $*Int + // CHECK-NEXT: end_access [[X_ACCESS]] : $*Int + // + // CHECK-NEXT: [[Y_ACCESS:%.*]] = begin_access [read] [static] [[Y_REF]] : $*Int + // CHECK-NEXT: [[Y_VAL:%.*]] = load [trivial] [[Y_ACCESS]] : $*Int + // CHECK-NEXT: end_access [[Y_ACCESS]] : $*Int + // + // CHECK-NEXT: [[FULL_ACCESS:%.*]] = begin_access [modify] [static] [[FULL_REF]] : $*(Int, Int) + // CHECK-NEXT: [[FULL_ELT_0:%.*]] = tuple_element_addr [[FULL_ACCESS]] : $*(Int, Int), 0 + // CHECK-NEXT: store [[X_VAL]] to [trivial] [[FULL_ELT_0]] : $*Int + // CHECK-NEXT: [[FULL_ELT_1:%.*]] = tuple_element_addr [[FULL_ACCESS]] : $*(Int, Int), 1 + // CHECK-NEXT: store [[Y_VAL]] to [trivial] [[FULL_ELT_1]] : $*Int + // CHECK-NEXT: end_access [[FULL_ACCESS]] : $*(Int, Int) + init(initialValue) initializes(y, full) accesses(x) { + self.y = initialValue.1 + self.full = (self.x, self.y) + } + + get { full } + set { full = newValue } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors8TestInitV1x1yACSi_SitcfC : $@convention(method) (Int, Int, @thin TestInit.Type) -> TestInit + // CHECK: // function_ref TestInit.point.init + // CHECK-NEXT: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors8TestInitV5pointSi_Sitvi : $@convention(thin) (Int, Int, @inout Int) -> (@out Int, @out (Int, Int)) + // CHECK: [[SELF_VALUE:%.*]] = begin_access [modify] [dynamic] {{.*}} : $*TestInit + // CHECK: [[Y_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestInit, #TestInit.y + // CHECK-NEXT: [[FULL_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestInit, #TestInit.full + // CHECK-NEXT: ([[X_VAL:%.*]], [[Y_VAL:%.*]]) = destructure_tuple {{.*}} : $(Int, Int) + // CHECK-NEXT: [[X_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestInit, #TestInit.x + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[Y_REF]], [[FULL_REF]], [[X_VAL]], [[Y_VAL]], [[X_REF]]) : $@convention(thin) (Int, Int, @inout Int) -> (@out Int, @out (Int, Int)) + // CHECK-NEXT: end_access [[SELF_VALUE]] : $*TestInit + init(x: Int, y: Int) { + self.x = x + self.point = (x, y) + } +} + +struct TestSetter { + var x: Int + var y: Int + + var point: (Int, Int) { + init(initialValue) accesses(x, y) { + } + + get { (x, y) } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors10TestSetterV1x1yACSi_SitcfC : $@convention(method) (Int, Int, @thin TestSetter.Type) -> TestSetter + // CHECK: [[SETTER_REF:%.*]] = function_ref @$s14init_accessors10TestSetterV5pointSi_Sitvs : $@convention(method) (Int, Int, @inout TestSetter) -> () + // CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]([[SELF_VALUE:%.*]]) : $@convention(method) (Int, Int, @inout TestSetter) -> () + // CHECK: {{.*}} = apply [[SETTER_CLOSURE]]({{.*}}) : $@callee_guaranteed (Int, Int) -> () + init(x: Int, y: Int) { + self.x = x + self.y = y + self.point = (x, y) + } +} + +struct TestInitThenSetter { + var x: Int + var y: Int + + var point: (Int, Int) { + init(initialValue) initializes(x, y) { + self.x = initialValue.0 + self.y = initialValue.1 + } + + get { (x, y) } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors18TestInitThenSetterV1x1yACSi_SitcfC : $@convention(method) (Int, Int, @thin TestInitThenSetter.Type) -> TestInitThenSetter + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors18TestInitThenSetterV5pointSi_Sitvi : $@convention(thin) (Int, Int) -> (@out Int, @out Int) + // CHECK: [[X_REF:%.*]] = struct_element_addr {{.*}} : $*TestInitThenSetter, #TestInitThenSetter.x + // CHECK-NEXT: [[Y_REF:%.*]] = struct_element_addr {{.*}} : $*TestInitThenSetter, #TestInitThenSetter.y + // CHECK: {{.*}} = apply [[INIT_ACCESSOR]]([[X_REF]], [[Y_REF]], {{.*}}) : $@convention(thin) (Int, Int) -> (@out Int, @out Int) + // + // CHECK: [[SETTER_REF:%.*]] = function_ref @$s14init_accessors18TestInitThenSetterV5pointSi_Sitvs : $@convention(method) (Int, Int, @inout TestInitThenSetter) -> () + // CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]([[SELF_VALUE:%.*]]) : $@convention(method) (Int, Int, @inout TestInitThenSetter) -> () + // CHECK: ([[ZERO_X:%.*]], [[ZERO_Y:%.*]]) = destructure_tuple {{.*}} : $(Int, Int) + // CHECK: {{.*}} = apply [[SETTER_CLOSURE]]([[ZERO_X]], [[ZERO_Y]]) : $@callee_guaranteed (Int, Int) -> () + init(x: Int, y: Int) { + self.point = (x, y) + + if x == 1 { + self.point = (0, 0) + } + } +} + +struct TestPartialInt { + var x: Int + var y: Int + + var pointX: Int { + init(newValue) initializes(x) { + self.x = newValue + } + + get { x } + set { self.x = newValue } + } + + var pointY: Int { + init(newValue) initializes(y) { + self.y = newValue + } + + get { y } + set { self.y = newValue } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors14TestPartialIntV1x1yACSi_SitcfC : $@convention(method) (Int, Int, @thin TestPartialInt.Type) -> TestPartialInt + // + // CHECK: [[INIT_REF:%.*]] = function_ref @$s14init_accessors14TestPartialIntV6pointXSivi : $@convention(thin) (Int) -> @out Int + // CHECK: [[X_REF:%.*]] = struct_element_addr {{.*}} : $*TestPartialInt, #TestPartialInt.x + // CHECK-NEXT: {{.*}} = apply [[INIT_REF]]([[X_REF]], %0) : $@convention(thin) (Int) -> @out Int + // + // CHECK: [[INIT_REF:%.*]] = function_ref @$s14init_accessors14TestPartialIntV6pointYSivi : $@convention(thin) (Int) -> @out Int + // CHECK: [[Y_REF:%.*]] = struct_element_addr {{.*}} : $*TestPartialInt, #TestPartialInt.y + // CHECK-NEXT: {{.*}} = apply [[INIT_REF]]([[Y_REF]], %1) : $@convention(thin) (Int) -> @out Int + // + // CHECK: [[BUILTIN_ONE:%.*]] = integer_literal $Builtin.IntLiteral, 1 + // CHECK: [[SETTER_REF:%.*]] = function_ref @$s14init_accessors14TestPartialIntV6pointXSivs : $@convention(method) (Int, @inout TestPartialInt) -> () + // CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]({{.*}}) : $@convention(method) (Int, @inout TestPartialInt) -> () + // CHECK-NEXT: {{.*}} = apply [[SETTER_CLOSURE]]({{.*}}) : $@callee_guaranteed (Int) -> () + // + // CHECK: [[BUILTIN_TWO:%.*]] = integer_literal $Builtin.IntLiteral, 2 + // CHECK: [[SETTER_REF:%.*]] = function_ref @$s14init_accessors14TestPartialIntV6pointYSivs : $@convention(method) (Int, @inout TestPartialInt) -> () + // CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]({{.*}}) : $@convention(method) (Int, @inout TestPartialInt) -> () + // CHECK-NEXT: {{.*}} = apply [[SETTER_CLOSURE]]({{.*}}) : $@callee_guaranteed (Int) -> () + init(x: Int, y: Int) { + // Init + self.pointX = x + // Init + self.pointY = y + + // Setter + self.pointX = 1 + // Setter + self.pointY = 2 + + } +} + +struct TestNoInitAndInit { + var x: Int + var y: Int + + var pointX: Int { + init(initalValue) accesses(x) { + } + + get { x } + set { } + } + + var pointY: Int { + init(initialValue) initializes(y) { + self.y = initialValue + } + + get { y } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors013TestNoInitAndE0V1x1yACSi_SitcfC : $@convention(method) (Int, Int, @thin TestNoInitAndInit.Type) -> TestNoInitAndInit + // + // CHECK: [[INIT_REF:%.*]] = function_ref @$s14init_accessors013TestNoInitAndE0V6pointXSivi : $@convention(thin) (Int, @inout Int) -> () + // CHECK: [[SELF_REF:%.*]] = begin_access [modify] [dynamic] {{.*}} : $*TestNoInitAndInit + // CHECK-NEXT: [[X_REF:%.*]] = struct_element_addr [[SELF_REF]] : $*TestNoInitAndInit, #TestNoInitAndInit.x + // CHECK-NEXT: {{.*}} = apply [[INIT_REF]](%0, [[X_REF]]) : $@convention(thin) (Int, @inout Int) -> () + // CHECK-NEXT: end_access [[SELF_REF]] : $*TestNoInitAndInit + // + // CHECK: [[INIT_REF:%.*]] = function_ref @$s14init_accessors013TestNoInitAndE0V6pointYSivi : $@convention(thin) (Int) -> @out Int + // CHECK: [[SELF_REF:%.*]] = begin_access [modify] [dynamic] {{.*}} : $*TestNoInitAndInit + // CHECK-NEXT: [[Y_REF:%.*]] = struct_element_addr [[SELF_REF]] : $*TestNoInitAndInit, #TestNoInitAndInit.y + // CHECK-NEXT: {{.*}} = apply [[INIT_REF]]([[Y_REF]], %1) : $@convention(thin) (Int) -> @out Int + // CHECK-NEXT: end_access [[SELF_REF]] : $*TestNoInitAndInit + init(x: Int, y: Int) { + self.x = x + self.pointX = x + self.pointY = y + print("Point(x: \(self.x), y: \(self.y)") + } +} + +class TestClass { + var x: Int + var y: (Int, [String]) + + var data: (Int, (Int, [String])) { + // CHECK-LABEL: sil private [ossa] @$s14init_accessors9TestClassC4dataSi_Si_SaySSGttvi : $@convention(thin) (Int, Int, @owned Array) -> (@out Int, @out (Int, Array)) + // CHECK: bb0([[X_REF:%.*]] : $*Int, [[Y_REF:%.*]] : $*(Int, Array), [[X_VAL:%.*]] : $Int, [[Y_VAL_0:%.*]] : $Int, [[Y_VAL_1:%.*]] : @owned $Array): + // + // CHECK: ([[X_VAL:%.*]], [[Y_VAL:%.*]]) = destructure_tuple {{.*}} : $(Int, (Int, Array)) + // CHECK: [[X_ACCESS:%.*]] = begin_access [modify] [static] [[X_REF]] : $*Int + // CHECK-NEXT: store [[X_VAL]] to [trivial] [[X_ACCESS]] : $*Int + // CHECK-NEXT: end_access [[X_ACCESS]] : $*Int + // + // CHECK: ([[X_VAL:%.*]], [[Y_VAL:%.*]]) = destructure_tuple {{.*}} : $(Int, (Int, Array)) + // CHECK: ([[Y_VAL_0:%.*]], [[Y_VAL_1:%.*]]) = destructure_tuple {{.*}} : $(Int, Array) + // CHECK: [[Y_ACCESS:%.*]] = begin_access [modify] [static] [[Y_REF]] : $*(Int, Array) + // CHECK-NEXT: [[Y_ELT_0:%.*]] = tuple_element_addr [[Y_ACCESS]] : $*(Int, Array), 0 + // CHECK-NEXT: store [[Y_VAL_0]] to [trivial] [[Y_ELT_0]] : $*Int + // CHECK-NEXT: [[Y_ELT_1:%.*]] = tuple_element_addr [[Y_ACCESS]] : $*(Int, Array), 1 + // CHECK-NEXT: store [[Y_VAL_1]] to [init] [[Y_ELT_1]] : $*Array + // CHECK-NEXT: end_access [[Y_ACCESS]] : $*(Int, Array) + init(initialValue) initializes(x, y) { + x = initialValue.0 + y = initialValue.1 + } + + get { (x, y) } + set { + x = newValue.0 + y = newValue.1 + } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors9TestClassC1x1yACSi_Si_SaySSGttcfc : $@convention(method) (Int, Int, @owned Array, @owned TestClass) -> @owned TestClass + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors9TestClassC4dataSi_Si_SaySSGttvi : $@convention(thin) (Int, Int, @owned Array) -> (@out Int, @out (Int, Array)) + // CHECK: [[SELF_VALUE:%.*]] = copy_value %3 : $TestClass + // CHECK: [[SELF_REF:%.*]] = begin_borrow [[SELF_VALUE]] : $TestClass + // CHECK: [[X_REF:%.*]] = ref_element_addr [[SELF_REF]] : $TestClass, #TestClass.x + // CHECK-NEXT: [[Y_REF:%.*]] = ref_element_addr [[SELF_REF]] : $TestClass, #TestClass.y + // + // CHECK-NEXT: ([[X_VAL:%.*]], [[Y_VAL:%.*]]) = destructure_tuple {{.*}} : $(Int, (Int, Array)) + // CHECK-NEXT: ([[Y_VAL_0:%.*]], [[Y_VAL_1:%.*]]) = destructure_tuple [[Y_VAL]] : $(Int, Array) + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[X_REF]], [[Y_REF]], [[X_VAL]], [[Y_VAL_0]], [[Y_VAL_1]]) : $@convention(thin) (Int, Int, @owned Array) -> (@out Int, @out (Int, Array)) + init(x: Int, y: (Int, [String])) { + self.data = (x, y) + } +} + +struct TestGeneric { + var a: T + var b: T + var c: U + + // CHECK-LABEL: sil private [ossa] @$s14init_accessors11TestGenericV4datax_xtvi : $@convention(thin) (@in T, @in T, @inout U) -> (@out T, @out T) + // + // CHECK: bb0([[A_REF:%.*]] : $*T, [[B_REF:%.*]] : $*T, [[A_VALUE:%.*]] : $*T, [[B_VALUE:%.*]] : $*T, [[C_REF:%.*]] : $*U): + // + // CHECK: [[A_ACCESS:%.*]] = begin_access [modify] [static] [[A_REF]] : $*T + // CHECK-NEXT: copy_addr [take] {{.*}} to [init] [[A_ACCESS]] : $*T + // CHECK-NEXT: end_access [[A_ACCESS]] : $*T + // + // CHECK: [[B_ACCESS:%.*]] = begin_access [modify] [static] [[B_REF]] : $*T + // CHECK-NEXT: copy_addr [take] {{.*}} to [init] [[B_ACCESS]] : $*T + // CHECK-NEXT: end_access [[B_ACCESS]] : $*T + // + // CHECK: [[C_ACCESS:%.*]] = begin_access [read] [static] [[C_REF]] : $*U + // CHECK-NEXT: [[C_AS_ANY:%.*]] = init_existential_addr {{.*}} : $*Any, $U + // CHECK-NEXT: copy_addr [[C_ACCESS]] to [init] [[C_AS_ANY]] : $*U + // CHECK-NEXT: end_access [[C_ACCESS]] : $*U + var data: (T, T) { + init(initialValue) initializes(a, b) accesses(c) { + a = initialValue.0 + b = initialValue.1 + print(c) + } + + get { (a, b) } + set { } + } + + // CHECK-LABEL: sil hidden [ossa] @$s14init_accessors11TestGenericV1a1b1cACyxq_Gx_xq_tcfC : $@convention(method) (@in T, @in T, @in U, @thin TestGeneric.Type) -> @out TestGeneric + // + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors11TestGenericV4datax_xtvi : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_0, @inout τ_0_1) -> (@out τ_0_0, @out τ_0_0) + // CHECK: {{.*}} = apply [[INIT_ACCESSOR]]({{.*}}) : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_0, @inout τ_0_1) -> (@out τ_0_0, @out τ_0_0) + // + // CHECK: [[SETTER:%.*]] = function_ref @$s14init_accessors11TestGenericV4datax_xtvs : $@convention(method) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_0, @inout TestGeneric<τ_0_0, τ_0_1>) -> () + // CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER]]([[SELF_VALUE:%.*]]) : $@convention(method) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_0, @inout TestGeneric<τ_0_0, τ_0_1>) -> () + // CHECK: %55 = apply [[SETTER_CLOSURE]]({{.*}}) : $@callee_guaranteed (@in T, @in T) -> () + // CHECK-NEXT: end_access [[SELF_VALUE]] : $*TestGeneric + init(a: T, b: T, c: U) { + self.c = c + self.data = (a, b) + self.data = (b, a) + } +} + +func test_local_with_memberwise() { + class MyValue {} + + struct TestMemberwiseConcrete { + var a: Int + var b: String + + var pair: (Int, String) { + init(initialValue) initializes(a, b) { + a = initialValue.0 + b = initialValue.1 + } + + get { (a, b) } + set { } + } + + var c: [MyValue] + + // CHECK-LABEL: sil private [ossa] @$s14init_accessors26test_local_with_memberwiseyyF22TestMemberwiseConcreteL_V4pair1cADSi_SSt_SayAaByyF7MyValueL_CGtcfC : $@convention(method) (Int, @owned String, @owned Array, @thin TestMemberwiseConcrete.Type) -> @owned TestMemberwiseConcrete + // CHECK: [[SELF_VALUE:%.*]] = alloc_stack $TestMemberwiseConcrete + // CHECK-NEXT: [[A_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseConcrete, #TestMemberwiseConcrete.a + // CHECK-NEXT: [[B_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseConcrete, #TestMemberwiseConcrete.b + // CHECK: [[INIT_ACCESSOR_REF:%.*]] = function_ref @$s14init_accessors26test_local_with_memberwiseyyF22TestMemberwiseConcreteL_V4pairSi_SStvi : $@convention(thin) (Int, @owned String) -> (@out Int, @out String) + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR_REF]]([[A_REF]], [[B_REF]], %0, %1) : $@convention(thin) (Int, @owned String) -> (@out Int, @out String) + // CHECK-NEXT: [[C_REF:%.*]] = struct_element_addr %4 : $*TestMemberwiseConcrete, #TestMemberwiseConcrete.c + // CHECK-NEXT: store %2 to [init] [[C_REF]] : $*Array + // CHECK-NEXT: [[RESULT:%.*]] = load [take] [[SELF_VALUE]] : $*TestMemberwiseConcrete + // CHECK-NEXT: dealloc_stack [[SELF_VALUE]] : $*TestMemberwiseConcrete + // CHECK-NEXT: return [[RESULT]] : $TestMemberwiseConcrete + } + + _ = TestMemberwiseConcrete(pair: (0, "a"), c: []) + + struct TestMemberwiseGeneric where C: RangeReplaceableCollection, C.Element == T { + var _a: T + var _b: String + var _c: C + + var a: T { + init(initialValue) initializes(_a) { + _a = initialValue + } + + get { _a } + set { } + } + + var pair: (String, C) { + init(initialValue) initializes(_b, _c) accesses(_a) { + _b = initialValue.0 + _c = initialValue.1 + _c.append(_a) + } + + get { (_b, _c) } + set { } + } + + // CHECK-LABEL: sil private [ossa] @$s14init_accessors26test_local_with_memberwiseyyF21TestMemberwiseGenericL_V1a4pairADyxq_Gx_SS_q_ttcfC : $@convention(method) (@in T, @owned String, @in C, @thin TestMemberwiseGeneric.Type) -> @out TestMemberwiseGeneric + // CHECK: bb0([[SELF_VALUE:%.*]] : $*TestMemberwiseGeneric, [[A_VALUE:%*.]] : $*T, [[B_VALUE:%.*]] : @owned $String, [[C_VALUE:%.*]] : $*C, [[METATYPE:%.*]] : $@thin TestMemberwiseGeneric.Type): + // CHECK-NEXT: [[A_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseGeneric, #TestMemberwiseGeneric._a + // CHECK: [[INIT_ACCESSOR_REF:%.*]] = function_ref @$s14init_accessors26test_local_with_memberwiseyyF21TestMemberwiseGenericL_V1axvi : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == τ_0_1.Element, τ_0_1 : RangeReplaceableCollection> (@in τ_0_0) -> @out τ_0_0 // user: %7 + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR_REF]]([[A_REF]], [[A_VALUE]]) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == τ_0_1.Element, τ_0_1 : RangeReplaceableCollection> (@in τ_0_0) -> @out τ_0_0 + // CHECK-NEXT: [[B_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseGeneric, #TestMemberwiseGeneric._b + // CHECK-NEXT: [[C_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseGeneric, #TestMemberwiseGeneric._c + // CHECK-NEXT: [[A_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseGeneric, #TestMemberwiseGeneric._a + // CHECK: [[INIT_ACCESSOR_REF:%.*]] = function_ref @$s14init_accessors26test_local_with_memberwiseyyF21TestMemberwiseGenericL_V4pairSS_q_tvi : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == τ_0_1.Element, τ_0_1 : RangeReplaceableCollection> (@owned String, @in τ_0_1, @inout τ_0_0) -> (@out String, @out τ_0_1) + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR_REF]]([[B_REF]], [[C_REF]], [[B_VALUE]], [[C_VALUE]], [[A_REF]]) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == τ_0_1.Element, τ_0_1 : RangeReplaceableCollection> (@owned String, @in τ_0_1, @inout τ_0_0) -> (@out String, @out τ_0_1) + // CHECK-NEXT: [[VOID:%.*]] = tuple () + // CHECK-NEXT: return [[VOID]] : $() + } + + _ = TestMemberwiseGeneric(a: 1, pair: ("a", [0])) +} + +func test_assignments() { + struct Test { + var _a: Int + var _b: Int + + var a: Int { + init(initialValue) initializes(_a) { + self._a = initialValue + } + get { _a } + set { _a = newValue } + } + + var pair: (Int, Int) { + init(initialValue) initializes(_a, _b) { + _a = initialValue.0 + _b = initialValue.1 + } + + get { (_a, _b) } + set { } + } + + // CHECK-LABEL: sil private [ossa] @$s14init_accessors16test_assignmentsyyF4TestL_V1aADSi_tcfC : $@convention(method) (Int, @thin Test.Type) -> Test + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V1aSivi : $@convention(thin) (Int) -> @out Int + // CHECK: [[A_REF:%.*]] = struct_element_addr {{.*}} : $*Test, #Test._a + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[A_REF]], %0) : $@convention(thin) (Int) -> @out Int + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V1aSivi : $@convention(thin) (Int) -> @out Int + // CHECK: [[A_REF:%.*]] = struct_element_addr {{.*}} : $*Test, #Test._a + // CHECK-NEXT: destroy_addr [[A_REF]] : $*Int + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[A_REF]], %0) : $@convention(thin) (Int) -> @out Int + // CHECK: [[B_REF:%.*]] = struct_element_addr {{.*}} : $*Test, #Test._b + // CHECK-NEXT: store {{.*}} to [trivial] [[B_REF]] : $*Int + // CHECK: [[SETTER_REF:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V1aSivs : $@convention(method) (Int, @inout Test) -> () + // CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]([[SELF_VALUE:%.*]]) : $@convention(method) (Int, @inout Test) -> () + // CHECK: {{.*}} = apply [[SETTER_CLOSURE]](%0) : $@callee_guaranteed (Int) -> () + init(a: Int) { + self.a = a + self.a = a + self._b = 42 + self.a = a + } + + // CHECK-LABEL: sil private [ossa] @$s14init_accessors16test_assignmentsyyF4TestL_V1a1bADSi_SitcfC : $@convention(method) (Int, Int, @thin Test.Type) -> Test + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V1aSivi : $@convention(thin) (Int) -> @out Int + // CHECK: [[A_REF:%.*]] = struct_element_addr {{.*}} : $*Test, #Test._a + // CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[A_REF]], %0) : $@convention(thin) (Int) -> @out Int + // CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors16test_assignmentsyyF4TestL_V4pairSi_Sitvi : $@convention(thin) (Int, Int) -> (@out Int, @out Int) + // CHECK: [[A_REF:%.*]] = struct_element_addr [[SELF_VALUE:%.*]] : $*Test, #Test._a + // CHECK-NEXT: destroy_addr [[A_REF]] : $*Int + // CHECK-NEXT: [[B_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*Test, #Test._b + // CHECK: {{.*}} = apply [[INIT_ACCESSOR]]([[A_REF]], [[B_REF]], {{.*}}) : $@convention(thin) (Int, Int) -> (@out Int, @out Int) + init(a: Int, b: Int) { + self.a = a + self.pair = (0, b) + } + } +} diff --git a/test/decl/var/init_accessors.swift b/test/decl/var/init_accessors.swift new file mode 100644 index 0000000000000..93eeb37bdc5af --- /dev/null +++ b/test/decl/var/init_accessors.swift @@ -0,0 +1,138 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-feature InitAccessors + +// REQUIRES: asserts + +func test_empty_init_accessor() { + struct Test { + var empty: Int { + init { // Ok + } + + get { 42 } + set { } + } + + var noArgs: Int { + init(initialValue) { // Ok + } + + get { 42 } + set { } + } + } +} + +func test_invalid_init_accessor_use() { + var other: String = "" // expected-warning {{}} + var x: Int { + init(initialValue) initializes(other) {} + // expected-error@-1 {{init accessors could only be associated with properties}} + + get { 42 } + } + + struct X { + subscript(x: Int) -> Bool { + init(initialValue) {} // expected-error {{init accessors could only be associated with properties}} + + get { false } + } + } +} + +func test_use_of_initializes_accesses_on_non_inits() { + struct Test1 { + var x: Int + var y: String + + var _x: Int { + init(initialValue) initializes(x) accesses(y) { // Ok + } + + get { x } + } + + var _y: String { + get { y } + set(initialValue) initializes(y) {} + // expected-error@-1 {{initalizes(...) attribute could only be used with init accessors}} + } + + var _q: String { + get { y } + set(initialValue) accesses(x) {} + // expected-error@-1 {{accesses(...) attribute could only be used with init accessors}} + } + } +} + +func test_invalid_refs_in_init_attrs() { + struct Test { + var c: Int { get { 42 } } + var x: Int { + init(initialValue) initializes(a) accesses(b, c) {} + // expected-error@-1 {{find type 'a' in scope}} + // expected-error@-2 {{find type 'b' in scope}} + // expected-error@-3 {{init accessor cannot refer to property 'c'; init accessors can refer only to stored properties}} + } + + var y: String { + init(initialValue) initializes(test) {} + // expected-error@-1 {{ambiguous reference to member 'test'}} + } + + func test(_: Int) {} // expected-note {{'test' declared here}} + func test(_: String) -> Int { 42 } // expected-note {{'test' declared here}} + } +} + +func test_assignment_to_let_properties() { + struct Test { + let x: Int + let y: Int // expected-note {{change 'let' to 'var' to make it mutable}} + + var pointX: Int { + init(initialValue) initializes(x) accesses(y) { + self.x = initialValue // Ok + self.y = 42 // expected-error {{cannot assign to property: 'y' is a 'let' constant}} + } + } + + var point: (Int, Int) { + init(initialValue) initializes(x, y) { + self.x = initialValue.0 // Ok + self.y = initialValue.1 // Ok + } + } + } +} + +func test_duplicate_and_computed_lazy_properties() { + struct Test1 { + var _a: Int + var _b: Int + + var a: Int { + init(initialValue) initializes(_b, _a) accesses(_a) { + // expected-error@-1 {{property '_a' cannot be both initialized and accessed}} + } + } + } + + struct Test2 { + var _a: Int + + var a: Int { + init(initialValue) initializes(a, c) accesses(_a, b) {} + // expected-error@-1 {{init accessor cannot refer to property 'a'; init accessors can refer only to stored properties}} + // expected-error@-2 {{init accessor cannot refer to property 'b'; init accessors can refer only to stored properties}} + // expected-error@-3 {{init accessor cannot refer to property 'c'; init accessors can refer only to stored properties}} + } + + var b: Int { + get { 42 } + } + + lazy var c: Int = 42 + } +} diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp index a8d0d9266c513..a77254e38d6ce 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp @@ -369,6 +369,8 @@ UIdent SwiftLangSupport::getUIDForAccessor(const ValueDecl *D, return IsRef ? KindRefAccessorRead : KindDeclAccessorRead; case AccessorKind::Modify: return IsRef ? KindRefAccessorModify : KindDeclAccessorModify; + case AccessorKind::Init: + return IsRef ? KindRefAccessorInit : KindDeclAccessorInit; } llvm_unreachable("Unhandled AccessorKind in switch."); diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 985b40212da4e..463f801d3949d 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -3426,6 +3426,9 @@ class ASTCommentPrinter : public ASTWalker { case AccessorKind::Modify: OS << ""; diff --git a/utils/gyb_sourcekit_support/UIDs.py b/utils/gyb_sourcekit_support/UIDs.py index fe3e2e72a178c..1500d4a9b698b 100644 --- a/utils/gyb_sourcekit_support/UIDs.py +++ b/utils/gyb_sourcekit_support/UIDs.py @@ -318,6 +318,10 @@ def __init__(self, internal_name, external_name): 'source.lang.swift.decl.function.accessor.modify'), KIND('RefAccessorModify', 'source.lang.swift.ref.function.accessor.modify'), + KIND('DeclAccessorInit', + 'source.lang.swift.decl.function.accessor.init'), + KIND('RefAccessorInit', + 'source.lang.swift.ref.function.accessor.init'), KIND('DeclConstructor', 'source.lang.swift.decl.function.constructor'), KIND('RefConstructor', 'source.lang.swift.ref.function.constructor'), KIND('DeclDestructor', 'source.lang.swift.decl.function.destructor'), diff --git a/utils/gyb_syntax_support/AttributeKinds.py b/utils/gyb_syntax_support/AttributeKinds.py index 13f6001dc23ea..8322bb3a449c2 100644 --- a/utils/gyb_syntax_support/AttributeKinds.py +++ b/utils/gyb_syntax_support/AttributeKinds.py @@ -590,9 +590,17 @@ def __init__(self, name, swift_name=None): swift_name='atReasync', code=110), - # 111 was an experimental @completionHandlerAsync and is now unused - - # 113 was experimental _unsafeSendable and is now unused + DeclAttribute('initializes', 'Initializes', + OnAccessor, + ABIStableToAdd, ABIStableToRemove, + APIBreakingToAdd, APIBreakingToRemove, + code=111), + # Note: 112 is used by 'nonisolated' + DeclAttribute('accesses', 'Accesses', + OnAccessor, + ABIStableToAdd, ABIStableToRemove, + APIBreakingToAdd, APIBreakingToRemove, + code=113), SimpleDeclAttribute('_unsafeInheritExecutor', 'UnsafeInheritExecutor', OnFunc, UserInaccessible,