diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift index 6966ea2be9b9e..3f1b5b50cea8b 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift @@ -125,7 +125,7 @@ extension AddressUseVisitor { case is SwitchEnumAddrInst, is CheckedCastAddrBranchInst, is SelectEnumAddrInst, is InjectEnumAddrInst, is StoreInst, is StoreUnownedInst, is StoreWeakInst, - is AssignInst, is AssignByWrapperInst, is AssignOrInitInst, + is AssignInst, is AssignOrInitInst, is TupleAddrConstructorInst, is InitBlockStorageHeaderInst, is RetainValueAddrInst, is ReleaseValueAddrInst, is DestroyAddrInst, is DeallocStackInst, diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index a06a948cd20e2..9758fd5e00525 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -347,8 +347,6 @@ final public class AssignInst : Instruction, StoringInstruction { } } -final public class AssignByWrapperInst : Instruction, StoringInstruction {} - final public class AssignOrInitInst : Instruction, StoringInstruction {} /// Instruction that copy or move from a source to destination address. diff --git a/SwiftCompilerSources/Sources/SIL/Registration.swift b/SwiftCompilerSources/Sources/SIL/Registration.swift index 103c10390c9da..25cdfcab10060 100644 --- a/SwiftCompilerSources/Sources/SIL/Registration.swift +++ b/SwiftCompilerSources/Sources/SIL/Registration.swift @@ -43,7 +43,6 @@ private func registerSILClasses() { register(StoreUnownedInst.self) register(StoreBorrowInst.self) register(AssignInst.self) - register(AssignByWrapperInst.self) register(AssignOrInitInst.self) register(CopyAddrInst.self) register(ExplicitCopyAddrInst.self) diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 820dd7b0800b6..1a9f36293486b 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -368,6 +368,7 @@ Entities entity-spec ::= entity 'fa' // runtime discoverable attribute generator entity-spec ::= 'fi' // non-local variable initializer entity-spec ::= 'fP' // property wrapper backing initializer + entity-spec ::= 'fF' // property wrapped field init accessor entity-spec ::= 'fW' // property wrapper init from projected value entity-spec ::= 'fD' // deallocating destructor; untyped entity-spec ::= 'fZ' // isolated deallocating destructor; untyped diff --git a/docs/SIL/Instructions.md b/docs/SIL/Instructions.md index 3ea429b5c5324..d9fd4a00ceb7c 100644 --- a/docs/SIL/Instructions.md +++ b/docs/SIL/Instructions.md @@ -1064,43 +1064,49 @@ a sequence that also correctly destroys the current value. This instruction is only valid in Raw SIL and is rewritten as appropriate by the definitive initialization pass. -### assign_by_wrapper - -``` -sil-instruction ::= 'assign_by_wrapper' sil-operand 'to' mode? sil-operand ',' 'init' sil-operand ',' 'set' sil-operand - -mode ::= '[init]' | '[assign]' | '[assign_wrapped_value]' - -assign_by_wrapper %0 : $S to %1 : $*T, init %2 : $F, set %3 : $G -// $S can be a value or address type -// $T must be the type of a property wrapper. -// $F must be a function type, taking $S as a single argument (or multiple arguments in case of a tuple) and returning $T -// $G must be a function type, taking $S as a single argument (or multiple arguments in case of a tuple) and without a return value -``` - -Similar to the [assign](#assign) instruction, but the assignment is done -via a delegate. - -Initially the instruction is created with no mode. Once the mode is -decided (by the definitive initialization pass), the instruction is -lowered as follows: - -If the mode is `initialization`, the function `%2` is called with `%0` -as argument. The result is stored to `%1`. In case of an address type, -`%1` is simply passed as a first out-argument to `%2`. - -The `assign` mode works similar to `initialization`, except that the -destination is "assigned" rather than "initialized". This means that -the existing value in the destination is destroyed before the new value -is stored. - -If the mode is `assign_wrapped_value`, the function `%3` is called with -`%0` as argument. As `%3` is a setter (e.g. for the property in the -containing nominal type), the destination address `%1` is not used in -this case. - -This instruction is only valid in Raw SIL and is rewritten as -appropriate by the definitive initialization pass. +### assign_or_init + +``` +sil-instruction ::= 'assign_or_init' mode? attached-property ',' self-or-local ',' sil-operand ',' 'value' ',' sil-operand ',' 'init' sil-operand ',' 'set' sil-operand + +mode ::= '[init]' | '[assign]' +attached-property ::= '#' sil-decl-ref +self-or-local ::= 'self' | 'local' + +// Nominal Context: +assign_or_init #MyStruct.x, self %A, value %V, init %I, set %S +// Local Context (only emitted with compiler synthesized thunks currently): +assign_or_init #x, local %L, value %V, init %I, set %S +``` + +Assigns or initializes a computed property with an attached init accessor. +This instruction is emitted during SILGen without an explicit mode. +The definitive initialization (DI) pass resolves the mode and rewrites +the instruction accordingly: + +- `[init]`: In this mode, the init accessor `%I` is called with `%V` +as an argument. +- `[assign]`: In this mode, the setter function `%S` is called with `%V` +as an argument. + +This instruction is only valid in Raw SIL and is rewritten as appropriate by +the DI pass. + +Operand Roles: +- `attached-property`: The property being written to. For nominal contexts, this +refers to a property with an attached init accessor (e.g. `#MyStruct.x`). For local +contexts, it refers to a local variable name (e.g. `#x`). +- `self-or-local`: + - `self %A`: Refers to the instance of the type that owns the property with the + attached init accessor. + - `local %L`: Indicates the assignment is to a local variable (`%L`) rather than + a property of a nominal type. While init accessors are not currently available to be + used in local contexts in user-authored code, the compiler can synthesize an `assign_or_init` + in local contexts using an init accessor thunk in special cases. +- `value %V`: The input value passed to either the `init` or `set` function, depending on +the selected DI mode. +- `init %I`: A partially applied function implementing the property's init accessor. +- `set %S`: A partially applied function implementing the property's setter. ### mark_uninitialized diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 8f292e19ab20b..c4933fc4009fd 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -246,6 +246,8 @@ class ASTMangler : public Mangler { std::string mangleInitializerEntity(const VarDecl *var, SymbolKind SKind); std::string mangleBackingInitializerEntity(const VarDecl *var, SymbolKind SKind = SymbolKind::Default); + std::string manglePropertyWrappedFieldInitAccessorEntity( + const VarDecl *var, SymbolKind SKind = SymbolKind::Default); std::string mangleInitFromProjectedValueEntity(const VarDecl *var, SymbolKind SKind = SymbolKind::Default); @@ -722,6 +724,7 @@ class ASTMangler : public Mangler { void appendInitializerEntity(const VarDecl *var); void appendBackingInitializerEntity(const VarDecl *var); + void appendPropertyWrappedFieldInitAccessorEntity(const VarDecl *var); void appendInitFromProjectedValueEntity(const VarDecl *var); CanType getDeclTypeForMangling(const ValueDecl *decl, diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 7d99dc6e94d62..8783d75428d34 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -213,6 +213,7 @@ NODE(PrefixOperator) NODE(PrivateDeclName) NODE(PropertyDescriptor) CONTEXT_NODE(PropertyWrapperBackingInitializer) +CONTEXT_NODE(PropertyWrappedFieldInitAccessor) CONTEXT_NODE(PropertyWrapperInitFromProjectedValue) CONTEXT_NODE(Protocol) CONTEXT_NODE(ProtocolSymbolicReference) diff --git a/include/swift/SIL/AddressWalker.h b/include/swift/SIL/AddressWalker.h index 08f4b3a3b8690..2b680b69c7038 100644 --- a/include/swift/SIL/AddressWalker.h +++ b/include/swift/SIL/AddressWalker.h @@ -214,8 +214,7 @@ 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) || @@ -229,7 +228,8 @@ TransitiveAddressWalker::walk(SILValue projectedAddress) { isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || - isa(user) || isa(user)) { + isa(user) || + isa(user)) { callVisitUse(op); continue; } diff --git a/include/swift/SIL/InstructionUtils.h b/include/swift/SIL/InstructionUtils.h index 12722d64ac3c0..9fa2a3ca1bb06 100644 --- a/include/swift/SIL/InstructionUtils.h +++ b/include/swift/SIL/InstructionUtils.h @@ -158,10 +158,6 @@ bool isInstrumentation(SILInstruction *Instruction); /// argument of the partial apply if it is. SILValue isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI); -/// Returns true if \p PAI is only used by an assign_by_wrapper instruction as -/// 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); diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index f9e058dff2a08..01fc361410a07 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -972,25 +972,13 @@ class SILBuilder { Qualifier)); } - AssignByWrapperInst *createAssignByWrapper(SILLocation Loc, SILValue Src, - SILValue Dest, - SILValue Initializer, - SILValue Setter, - AssignByWrapperInst::Mode mode) { - return insert(new (getModule()) AssignByWrapperInst( - getSILDebugLocation(Loc), Src, Dest, Initializer, Setter, mode)); - } - - AssignOrInitInst *createAssignOrInit(SILLocation Loc, - VarDecl *Property, - SILValue Self, - SILValue Src, - SILValue Initializer, - SILValue Setter, + AssignOrInitInst *createAssignOrInit(SILLocation Loc, VarDecl *Property, + SILValue SelfOrLocal, SILValue Src, + SILValue Initializer, SILValue Setter, AssignOrInitInst::Mode Mode) { - return insert(new (getModule()) - AssignOrInitInst(getSILDebugLocation(Loc), Property, Self, - Src, Initializer, Setter, Mode)); + return insert(new (getModule()) AssignOrInitInst( + getSILDebugLocation(Loc), Property, SelfOrLocal, Src, Initializer, + Setter, Mode)); } StoreBorrowInst *createStoreBorrow(SILLocation Loc, SILValue Src, diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index fb6b0506a0e14..124958361e5af 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1525,27 +1525,14 @@ void SILCloner::visitAssignInst(AssignInst *Inst) { Inst->getOwnershipQualifier())); } -template -void SILCloner::visitAssignByWrapperInst(AssignByWrapperInst *Inst) { - getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - recordClonedInstruction( - Inst, getBuilder().createAssignByWrapper( - getOpLocation(Inst->getLoc()), - getOpValue(Inst->getSrc()), getOpValue(Inst->getDest()), - getOpValue(Inst->getInitializer()), - getOpValue(Inst->getSetter()), Inst->getMode())); -} - template void SILCloner::visitAssignOrInitInst(AssignOrInitInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); recordClonedInstruction( Inst, getBuilder().createAssignOrInit( - getOpLocation(Inst->getLoc()), - Inst->getProperty(), - getOpValue(Inst->getSelf()), - getOpValue(Inst->getSrc()), - getOpValue(Inst->getInitializer()), + getOpLocation(Inst->getLoc()), Inst->getProperty(), + getOpValue(Inst->getSelfOrLocalOperand()), + getOpValue(Inst->getSrc()), getOpValue(Inst->getInitializer()), getOpValue(Inst->getSetter()), Inst->getMode())); } @@ -3538,9 +3525,7 @@ visitSwitchEnumAddrInst(SwitchEnumAddrInst *Inst) { getOpValue(Inst->getOperand()), DefaultBB, CaseBBs)); } - - template void SILCloner::visitSelectEnumInst(SelectEnumInst *Inst) { diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index d17a7fb205dc3..6a5a0377a159b 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -167,6 +167,10 @@ struct SILDeclRef { /// The asynchronous main entry-point function. AsyncEntryPoint, + + /// An init accessor that calls a propery wrapped field's + /// backing storage initializer + PropertyWrappedFieldInitAccessor }; /// Represents the variants of a back deployable function. diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 91707f8e951e7..f2c25bb776e32 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -5230,52 +5230,7 @@ class AssignInst } }; -/// AssignByWrapperInst - Represents an abstract assignment via a wrapper, -/// which may either be an initialization or a store sequence. This is only -/// valid in Raw SIL. -class AssignByWrapperInst - : public AssignInstBase { - friend SILBuilder; - USE_SHARED_UINT8; - -public: - enum Mode { - /// The mode is not decided yet (by DefiniteInitialization). - Unknown, - - /// The initializer is called with Src as argument. The result is stored to - /// Dest. - Initialization, - - // Like ``Initialization``, except that the destination is "assigned" rather - // than "initialized". This means that the existing value in the destination - // is destroyed before the new value is stored. - Assign, - - /// The setter is called with Src as argument. The Dest is not used in this - /// case. - AssignWrappedValue - }; - -private: - AssignByWrapperInst(SILDebugLocation DebugLoc, - SILValue Src, SILValue Dest, SILValue Initializer, - SILValue Setter, Mode mode); - -public: - SILValue getInitializer() { return Operands[2].get(); } - SILValue getSetter() { return Operands[3].get(); } - - Mode getMode() const { - return Mode(sharedUInt8().AssignByWrapperInst.mode); - } - - void setMode(Mode mode) { - sharedUInt8().AssignByWrapperInst.mode = uint8_t(mode); - } -}; - -/// AssignOrInitInst - Represents an abstract assignment via a init accessor +/// AssignOrInitInst - Represents an abstract assignment via an init accessor /// or a setter, which may either be an initialization or a store sequence. /// This is only valid in Raw SIL. /// @@ -5312,16 +5267,23 @@ class AssignOrInitInst }; private: - AssignOrInitInst(SILDebugLocation DebugLoc, VarDecl *P, SILValue Self, + AssignOrInitInst(SILDebugLocation DebugLoc, VarDecl *P, SILValue SelfOrLocal, SILValue Src, SILValue Initializer, SILValue Setter, Mode mode); public: VarDecl *getProperty() const { return Property; } - SILValue getSelf() const { return Operands[0].get(); } SILValue getSrc() const { return Operands[1].get(); } SILValue getInitializer() const { return Operands[2].get(); } - SILValue getSetter() { return Operands[3].get(); } + SILValue getSetter() { return Operands[3].get(); } + + // Init accessors currently don't support local contexts. + // The `PropertyWrappedFieldInitAccessor` thunk must work for both local + // and nominal contexts. For locals, we work around this by storing the + // projected local address directly in the original `Self` operand. + // Callers of this method conditionally handle its result based on + // the current DeclContext + SILValue getSelfOrLocalOperand() const { return Operands[0].get(); } Mode getMode() const { return Mode(sharedUInt8().AssignOrInitInst.mode); @@ -5343,7 +5305,8 @@ class AssignOrInitInst unsigned getNumInitializedProperties() const; - ArrayRef getInitializedProperties() const; + void forEachInitializedProperty( + llvm::function_ref callback) const; ArrayRef getAccessedProperties() const; ArrayRef getAllOperands() const { return Operands.asArray(); } @@ -5351,6 +5314,7 @@ class AssignOrInitInst StringRef getPropertyName() const; AccessorDecl *getReferencedInitAccessor() const; + DeclContext *getDeclContextOrNull() const; }; /// Indicates that a memory location is uninitialized at this point and needs to diff --git a/include/swift/SIL/SILNode.h b/include/swift/SIL/SILNode.h index 091c4b5441775..39a358c631a58 100644 --- a/include/swift/SIL/SILNode.h +++ b/include/swift/SIL/SILNode.h @@ -120,7 +120,6 @@ class alignas(8) SILNode : enum { NumStoreOwnershipQualifierBits = 2 }; enum { NumLoadOwnershipQualifierBits = 2 }; enum { NumAssignOwnershipQualifierBits = 2 }; - enum { NumAssignByWrapperModeBits = 2 }; enum { NumSILAccessKindBits = 2 }; enum { NumSILAccessEnforcementBits = 3 }; enum { NumAllocRefTailTypesBits = 4 }; @@ -196,7 +195,6 @@ class alignas(8) SILNode : SHARED_FIELD(StoreInst, uint8_t ownershipQualifier); 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(SwitchValueInst, bool hasDefault); diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index 7b8bdb2498e55..ec872de025599 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -837,8 +837,6 @@ NON_VALUE_INST(StoreInst, store, SILInstruction, MayHaveSideEffects, MayRelease) 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, diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index b7f53971dcc8b..ea4620d67277f 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -253,6 +253,16 @@ std::string ASTMangler::mangleBackingInitializerEntity(const VarDecl *var, return finalize(); } +std::string +ASTMangler::manglePropertyWrappedFieldInitAccessorEntity(const VarDecl *var, + SymbolKind SKind) { + llvm::SaveAndRestore X(AllowInverses, inversesAllowed(var)); + beginMangling(); + appendPropertyWrappedFieldInitAccessorEntity(var); + appendSymbolKind(SKind); + return finalize(); +} + std::string ASTMangler::mangleInitFromProjectedValueEntity(const VarDecl *var, SymbolKind SKind) { llvm::SaveAndRestore X(AllowInverses, inversesAllowed(var)); @@ -4106,6 +4116,14 @@ void ASTMangler::appendBackingInitializerEntity(const VarDecl *var) { appendOperator("fP"); } +void ASTMangler::appendPropertyWrappedFieldInitAccessorEntity( + const VarDecl *var) { + llvm::SaveAndRestore X(AllowInverses, inversesAllowed(var)); + BaseEntitySignature base(var); + appendEntity(var, base, "vp", var->isStatic()); + appendOperator("fF"); +} + void ASTMangler::appendInitFromProjectedValueEntity(const VarDecl *var) { llvm::SaveAndRestore X(AllowInverses, inversesAllowed(var)); BaseEntitySignature base(var); diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index e6b5902ad0cb2..9f0cf8c40b4d0 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -2140,6 +2140,7 @@ bool Demangle::nodeConsumesGenericArgs(Node *node) { case Node::Kind::DefaultArgumentInitializer: case Node::Kind::Initializer: case Node::Kind::PropertyWrapperBackingInitializer: + case Node::Kind::PropertyWrappedFieldInitAccessor: case Node::Kind::PropertyWrapperInitFromProjectedValue: case Node::Kind::Static: return false; @@ -4135,6 +4136,10 @@ NodePointer Demangler::demangleFunctionEntity() { Args = None; Kind = Node::Kind::PropertyWrapperBackingInitializer; break; + case 'F': + Args = None; + Kind = Node::Kind::PropertyWrappedFieldInitAccessor; + break; case 'W': Args = None; Kind = Node::Kind::PropertyWrapperInitFromProjectedValue; diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 476425c5e3a92..ae9bd05ddcce1 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -412,6 +412,7 @@ bool NodePrinter::isSimpleType(NodePointer Node) { case Node::Kind::CompileTimeLiteral: case Node::Kind::ConstValue: case Node::Kind::PropertyWrapperBackingInitializer: + case Node::Kind::PropertyWrappedFieldInitAccessor: case Node::Kind::PropertyWrapperInitFromProjectedValue: case Node::Kind::KeyPathGetterThunkHelper: case Node::Kind::KeyPathSetterThunkHelper: @@ -1583,6 +1584,10 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth, return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType, /*hasName*/ false, "property wrapper backing initializer"); + case Node::Kind::PropertyWrappedFieldInitAccessor: + return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType, + /*hasName*/ false, + "property wrapped field init accessor"); case Node::Kind::PropertyWrapperInitFromProjectedValue: return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType, /*hasName*/ false, @@ -3536,7 +3541,9 @@ NodePointer NodePrinter::printEntity(NodePointer Entity, unsigned depth, if (Entity->getKind() == Node::Kind::DefaultArgumentInitializer || Entity->getKind() == Node::Kind::Initializer || Entity->getKind() == Node::Kind::PropertyWrapperBackingInitializer || - Entity->getKind() == Node::Kind::PropertyWrapperInitFromProjectedValue) { + Entity->getKind() == Node::Kind::PropertyWrappedFieldInitAccessor || + Entity->getKind() == + Node::Kind::PropertyWrapperInitFromProjectedValue) { Printer << " of "; } else { Printer << " in "; diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 58a55afb69b35..d09cfc884521d 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -1188,6 +1188,11 @@ ManglingError Remangler::manglePropertyWrapperBackingInitializer( return mangleSimpleEntity(node, 'I', "P", ctx, depth + 1); } +ManglingError Remangler::manglePropertyWrappedFieldInitAccessor( + Node *node, EntityContext &ctx, unsigned depth) { + return mangleSimpleEntity(node, 'I', "F", ctx, depth + 1); +} + ManglingError Remangler::manglePropertyWrapperInitFromProjectedValue( Node *node, EntityContext &ctx, unsigned depth) { return mangleSimpleEntity(node, 'I', "W", ctx, depth + 1); diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 228f890f7593c..05c0fc5391e46 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -632,6 +632,7 @@ ManglingError Remangler::mangleGenericArgs(Node *node, char &Separator, case Node::Kind::DefaultArgumentInitializer: case Node::Kind::Initializer: case Node::Kind::PropertyWrapperBackingInitializer: + case Node::Kind::PropertyWrappedFieldInitAccessor: case Node::Kind::PropertyWrapperInitFromProjectedValue: case Node::Kind::Static: if (!fullSubstitutionMap) @@ -2354,6 +2355,13 @@ Remangler::manglePropertyWrapperBackingInitializer(Node *node, unsigned depth) { return ManglingError::Success; } +ManglingError +Remangler::manglePropertyWrappedFieldInitAccessor(Node *node, unsigned depth) { + RETURN_IF_ERROR(mangleChildNodes(node, depth + 1)); + Buffer << "fF"; + return ManglingError::Success; +} + ManglingError Remangler::manglePropertyWrapperInitFromProjectedValue(Node *node, unsigned depth) { @@ -4197,6 +4205,7 @@ bool Demangle::isSpecialized(Node *node) { case Node::Kind::ImplicitClosure: case Node::Kind::Initializer: case Node::Kind::PropertyWrapperBackingInitializer: + case Node::Kind::PropertyWrappedFieldInitAccessor: case Node::Kind::PropertyWrapperInitFromProjectedValue: case Node::Kind::DefaultArgumentInitializer: case Node::Kind::Getter: @@ -4242,6 +4251,7 @@ ManglingErrorOr Demangle::getUnspecialized(Node *node, case Node::Kind::ImplicitClosure: case Node::Kind::Initializer: case Node::Kind::PropertyWrapperBackingInitializer: + case Node::Kind::PropertyWrappedFieldInitAccessor: case Node::Kind::PropertyWrapperInitFromProjectedValue: case Node::Kind::DefaultArgumentInitializer: case Node::Kind::Static: diff --git a/lib/IRGen/GenObjC.cpp b/lib/IRGen/GenObjC.cpp index 044ec406fc9cb..da103c5605279 100644 --- a/lib/IRGen/GenObjC.cpp +++ b/lib/IRGen/GenObjC.cpp @@ -661,6 +661,7 @@ namespace { case SILDeclRef::Kind::EnumElement: case SILDeclRef::Kind::GlobalAccessor: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: case SILDeclRef::Kind::EntryPoint: case SILDeclRef::Kind::AsyncEntryPoint: diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index c9678b127d800..6e485779116b5 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1238,9 +1238,6 @@ class IRGenSILFunction : void visitAssignInst(AssignInst *i) { llvm_unreachable("assign is not valid in canonical SIL"); } - 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"); } diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index fe122f90d96eb..f55009b802b4f 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -629,17 +629,6 @@ OperandOwnership OperandOwnershipClassifier::visitAssignInst(AssignInst *i) { return OperandOwnership::DestroyingConsume; } -OperandOwnership -OperandOwnershipClassifier::visitAssignByWrapperInst(AssignByWrapperInst *i) { - if (getValue() == i->getSrc()) { - return OperandOwnership::DestroyingConsume; - } - if (getValue() == i->getDest()) { - return OperandOwnership::TrivialUse; - } - return OperandOwnership::InstantaneousUse; // initializer/setter closure -} - OperandOwnership OperandOwnershipClassifier::visitAssignOrInitInst(AssignOrInitInst *i) { if (getValue() == i->getSrc()) { diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 5a4b131627d2e..1baa72aa5cd32 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -303,6 +303,7 @@ bool SILDeclRef::hasUserWrittenCode() const { // Non-implicit decls generally have user-written code. if (!isImplicit()) { switch (kind) { + case Kind::PropertyWrappedFieldInitAccessor: case Kind::PropertyWrapperBackingInitializer: { // Only has user-written code if any of the property wrappers have // arguments to apply. Otherwise, it's just a forwarding initializer for @@ -386,6 +387,7 @@ bool SILDeclRef::hasUserWrittenCode() const { case Kind::IVarInitializer: case Kind::IVarDestroyer: case Kind::PropertyWrapperBackingInitializer: + case Kind::PropertyWrappedFieldInitAccessor: case Kind::PropertyWrapperInitFromProjectedValue: case Kind::EntryPoint: case Kind::AsyncEntryPoint: @@ -521,6 +523,7 @@ static LinkageLimit getLinkageLimit(SILDeclRef constant) { return constant.isSerialized() ? Limit::AlwaysEmitIntoClient : Limit::None; case Kind::PropertyWrapperBackingInitializer: + case Kind::PropertyWrappedFieldInitAccessor: case Kind::PropertyWrapperInitFromProjectedValue: { if (!d->getDeclContext()->isTypeContext()) { // If the backing initializer is to be serialized, only use non-ABI public @@ -1408,6 +1411,10 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { return mangler.mangleBackingInitializerEntity(cast(getDecl()), SKind); + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: + return mangler.manglePropertyWrappedFieldInitAccessorEntity( + cast(getDecl()), SKind); + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: return mangler.mangleInitFromProjectedValueEntity(cast(getDecl()), SKind); @@ -1763,6 +1770,7 @@ Expr *SILDeclRef::getInitializationExpr() const { } return init; } + case Kind::PropertyWrappedFieldInitAccessor: case Kind::PropertyWrapperBackingInitializer: { auto *var = cast(getDecl()); auto wrapperInfo = var->getPropertyWrapperInitializerInfo(); diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index bcf5e921621a3..0799eeca1702a 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2872,9 +2872,8 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor( TypeConverter &TC, TypeExpansionContext context, AbstractionPattern origType, CanAnyFunctionType substAccessorType, SILExtInfoBuilder extInfoBuilder, const Conventions &conventions, - SILDeclRef accessorRef) { - auto *accessor = cast(accessorRef.getDecl()); - + SILDeclRef constant) { + auto *declContext = constant.getDecl()->getDeclContext(); CanGenericSignature genericSig = substAccessorType.getOptGenericSignature(); std::optional contextRAII; @@ -2903,8 +2902,10 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor( assert(!unimplementable && "should never have an opaque AP here"); } - // Drop `self` parameter. - inputs.pop_back(); + // Drop `self` parameter if inside a nominal context + // Local context do not include the `self` parameter + if (!declContext->isLocalContext()) + inputs.pop_back(); auto getLoweredTypeOfProperty = [&](VarDecl *property) { auto type = property->getInterfaceType(); @@ -2915,27 +2916,38 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor( // accessed properties appear as `inout` parameters because they could be // read from and modified. - for (auto *property : accessor->getAccessedProperties()) { - inputs.push_back(SILParameterInfo(getLoweredTypeOfProperty(property), - ParameterConvention::Indirect_Inout)); + if (auto *accessor = dyn_cast(constant.getDecl())) { + for (auto *property : accessor->getAccessedProperties()) { + inputs.push_back(SILParameterInfo(getLoweredTypeOfProperty(property), + ParameterConvention::Indirect_Inout)); + } } // Make a new 'self' parameter. - auto selfInterfaceType = MetatypeType::get( - accessor->getDeclContext()->getSelfInterfaceType()); - AbstractionPattern origSelfType(genericSig, - selfInterfaceType->getCanonicalType()); - auto loweredSelfType = TC.getLoweredType( - origSelfType, selfInterfaceType->getCanonicalType(), context); - inputs.push_back(SILParameterInfo(loweredSelfType.getASTType(), - ParameterConvention::Direct_Unowned)); + if (!declContext->isLocalContext()) { + auto selfInterfaceType = + MetatypeType::get(declContext->getSelfInterfaceType()); + AbstractionPattern origSelfType(genericSig, + selfInterfaceType->getCanonicalType()); + auto loweredSelfType = TC.getLoweredType( + origSelfType, selfInterfaceType->getCanonicalType(), context); + inputs.push_back(SILParameterInfo(loweredSelfType.getASTType(), + ParameterConvention::Direct_Unowned)); + } SmallVector results; // initialized properties appear as `@out` results because they are // initialized by the accessor. - for (auto *property : accessor->getInitializedProperties()) { - results.push_back(SILResultInfo(getLoweredTypeOfProperty(property), + if (auto *accessor = dyn_cast(constant.getDecl())) { + for (auto *property : accessor->getInitializedProperties()) { + results.push_back(SILResultInfo(getLoweredTypeOfProperty(property), + ResultConvention::Indirect)); + } + } else { + auto *varDecl = dyn_cast(constant.getDecl()); + auto backingStorage = varDecl->getPropertyWrapperBackingProperty(); + results.push_back(SILResultInfo(getLoweredTypeOfProperty(backingStorage), ResultConvention::Indirect)); } @@ -3250,6 +3262,10 @@ static CanSILFunctionType getNativeSILFunctionType( case SILDeclRef::Kind::IVarDestroyer: return getSILFunctionTypeForConventions( DefaultConventions(NormalParameterConvention::Guaranteed)); + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: + return getSILFunctionTypeForInitAccessor( + TC, context, origType, substInterfaceType, extInfoBuilder, + DefaultSetterConventions(), *constant); case SILDeclRef::Kind::Deallocator: return getSILFunctionTypeForConventions(DeallocatorConventions()); case SILDeclRef::Kind::IsolatedDeallocator: { @@ -4117,6 +4133,7 @@ static ObjCSelectorFamily getObjCSelectorFamily(SILDeclRef c) { case SILDeclRef::Kind::DefaultArgGenerator: case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: case SILDeclRef::Kind::EntryPoint: case SILDeclRef::Kind::AsyncEntryPoint: @@ -4415,6 +4432,7 @@ TypeConverter::getDeclRefRepresentation(SILDeclRef c) { case SILDeclRef::Kind::DefaultArgGenerator: case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: case SILDeclRef::Kind::IsolatedDeallocator: return SILFunctionTypeRepresentation::Thin; @@ -4821,6 +4839,7 @@ static AbstractFunctionDecl *getBridgedFunction(SILDeclRef declRef) { case SILDeclRef::Kind::DefaultArgGenerator: case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: case SILDeclRef::Kind::IVarInitializer: case SILDeclRef::Kind::IVarDestroyer: diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index b9d7549a48a2a..19892dafade05 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1317,35 +1317,26 @@ AssignInst::AssignInst(SILDebugLocation Loc, SILValue Src, SILValue Dest, sharedUInt8().AssignInst.ownershipQualifier = uint8_t(Qualifier); } -AssignByWrapperInst::AssignByWrapperInst(SILDebugLocation Loc, - SILValue Src, SILValue Dest, - SILValue Initializer, SILValue Setter, - AssignByWrapperInst::Mode mode) - : AssignInstBase(Loc, Src, Dest, Initializer, Setter) { - assert(Initializer->getType().is()); - sharedUInt8().AssignByWrapperInst.mode = uint8_t(mode); -} - AssignOrInitInst::AssignOrInitInst(SILDebugLocation Loc, VarDecl *P, - SILValue Self, SILValue Src, + SILValue SelfOrLocal, SILValue Src, SILValue Initializer, SILValue Setter, AssignOrInitInst::Mode Mode) : InstructionBase(Loc), - Operands(this, Self, Src, Initializer, Setter), Property(P) { + Operands(this, SelfOrLocal, Src, Initializer, Setter), Property(P) { 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; + unsigned idx = 0; + this->forEachInitializedProperty([&](VarDecl *p) { + if (p == property) { + markAsInitialized(idx); } - } + idx++; + }); } void AssignOrInitInst::markAsInitialized(unsigned propertyIdx) { @@ -1366,14 +1357,28 @@ AccessorDecl *AssignOrInitInst::getReferencedInitAccessor() const { return Property->getOpaqueAccessor(AccessorKind::Init); } +DeclContext *AssignOrInitInst::getDeclContextOrNull() const { + if (auto accessorDecl = getReferencedInitAccessor()) + return accessorDecl->getDeclContext(); + return getProperty()->getDeclContext(); +} + unsigned AssignOrInitInst::getNumInitializedProperties() const { - return getInitializedProperties().size(); + unsigned count = 0; + forEachInitializedProperty([&](VarDecl *property) { count++; }); + return count; } -ArrayRef AssignOrInitInst::getInitializedProperties() const { - if (auto *accessor = getReferencedInitAccessor()) - return accessor->getInitializedProperties(); - return {}; +void AssignOrInitInst::forEachInitializedProperty( + llvm::function_ref callback) const { + if (auto *accessor = getReferencedInitAccessor()) { + for (auto *property : accessor->getInitializedProperties()) + callback(property); + } else { + // Only the backing storage property/local variavle + auto *backingVar = Property->getPropertyWrapperBackingProperty(); + callback(backingVar); + } } ArrayRef AssignOrInitInst::getAccessedProperties() const { diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index e3b1bf2c790d5..72e1f2e8af498 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -441,6 +441,9 @@ void SILDeclRef::print(raw_ostream &OS) const { case SILDeclRef::Kind::PropertyWrapperBackingInitializer: OS << "!backinginit"; break; + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: + OS << "!wrappedfieldinitaccessor"; + break; case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: OS << "!projectedvalueinit"; break; @@ -1967,27 +1970,6 @@ class SILPrinter : public SILInstructionVisitor { *this << getIDAndType(AI->getDest()); } - void visitAssignByWrapperInst(AssignByWrapperInst *AI) { - *this << getIDAndType(AI->getSrc()) << " to "; - switch (AI->getMode()) { - case AssignByWrapperInst::Unknown: - break; - case AssignByWrapperInst::Initialization: - *this << "[init] "; - break; - case AssignByWrapperInst::Assign: - *this << "[assign] "; - break; - case AssignByWrapperInst::AssignWrappedValue: - *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: @@ -2008,10 +1990,15 @@ class SILPrinter : public SILInstructionVisitor { } *this << "#"; - printFullContext(AI->getProperty()->getDeclContext(), PrintState.OS); - *this << AI->getPropertyName(); + auto declContext = AI->getProperty()->getDeclContext(); - *this << ", self " << getIDAndType(AI->getSelf()); + if (!declContext->isLocalContext()) { + printFullContext(declContext, PrintState.OS); + *this << AI->getPropertyName() << ", self "; + } else { + *this << AI->getPropertyName() << ", local "; + } + *this << getIDAndType(AI->getSelfOrLocalOperand()); *this << ", value " << getIDAndType(AI->getSrc()); *this << ", init " << getIDAndType(AI->getInitializer()) << ", set " << getIDAndType(AI->getSetter()); diff --git a/lib/SIL/IR/SILSymbolVisitor.cpp b/lib/SIL/IR/SILSymbolVisitor.cpp index 5c1a14f492aca..550290d51e589 100644 --- a/lib/SIL/IR/SILSymbolVisitor.cpp +++ b/lib/SIL/IR/SILSymbolVisitor.cpp @@ -621,6 +621,8 @@ class SILSymbolVisitorImpl : public ASTVisitor { if (initInfo.hasInitFromWrappedValue() && !VD->isStatic()) { addFunction(SILDeclRef( VD, SILDeclRef::Kind::PropertyWrapperBackingInitializer)); + addFunction( + SILDeclRef(VD, SILDeclRef::Kind::PropertyWrappedFieldInitAccessor)); } } visitAbstractStorageDecl(VD); diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index e60bd90469826..fe81431b2da03 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -3992,6 +3992,41 @@ static CanAnyFunctionType getPropertyWrapperBackingInitializerInterfaceType( resultType, info); } +static CanAnyFunctionType getPropertyWrappedFieldInitAccessorInterfaceType( + TypeConverter &TC, SILDeclRef wrappedPropertyRef) { + auto *wrappedProperty = cast(wrappedPropertyRef.getDecl()); + CanType wrappedValueType = + wrappedProperty->getPropertyWrapperInitValueInterfaceType() + ->getCanonicalType(); + bool isLocalContext = wrappedProperty->getDeclContext()->isLocalContext(); + GenericSignature sig = + TC.getGenericSignatureWithCapturedEnvironments(wrappedPropertyRef) + .genericSig; + + SmallVector params; + + // Create the wrappedValue param + params.emplace_back( + wrappedValueType, Identifier(), + ParameterTypeFlags().withOwnershipSpecifier(ParamSpecifier::LegacyOwned)); + + if (!isLocalContext) { + CanType selfType = wrappedProperty->getDeclContext() + ->getSelfInterfaceType() + ->getCanonicalType(); + // Create the self param + params.emplace_back( + selfType, Identifier(), + ParameterTypeFlags().withOwnershipSpecifier(ParamSpecifier::InOut)); + } + + CanType resultType = TupleType::getEmpty(TC.Context)->getCanonicalType(); + AnyFunctionType::CanParamArrayRef paramRef(params); + CanAnyFunctionType::ExtInfo extInfo; + return CanAnyFunctionType::get(getCanonicalSignatureOrNull(sig), paramRef, + resultType, extInfo); +} + /// Get the type of a destructor function. static CanAnyFunctionType getDestructorInterfaceType(DestructorDecl *dd, bool isDeallocating, @@ -4235,6 +4270,8 @@ CanAnyFunctionType TypeConverter::makeConstantInterfaceType(SILDeclRef c) { case SILDeclRef::Kind::PropertyWrapperBackingInitializer: case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: return getPropertyWrapperBackingInitializerInterfaceType(*this, c); + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: + return getPropertyWrappedFieldInitAccessorInterfaceType(*this, c); case SILDeclRef::Kind::IVarInitializer: return getIVarInitDestroyerInterfaceType(cast(vd), c.isForeign, false); @@ -4277,6 +4314,7 @@ TypeConverter::getGenericSignatureWithCapturedEnvironments(SILDeclRef c) { vd->getInnermostDeclContext(), captureInfo); } case SILDeclRef::Kind::PropertyWrapperBackingInitializer: + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: { // FIXME: It might be better to compute lowered local captures of // the property wrapper generator directly and collapse this into the @@ -4419,6 +4457,7 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) { case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: return CaptureInfo::empty(); default: diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 1c9d32f25c5d8..ca36191ae4a79 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -1433,6 +1433,9 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Result, } else if (!ParseState && Id.str() == "backinginit") { Kind = SILDeclRef::Kind::PropertyWrapperBackingInitializer; ParseState = 1; + } else if (!ParseState && Id.str() == "wrappedfieldinitaccessor") { + Kind = SILDeclRef::Kind::PropertyWrappedFieldInitAccessor; + ParseState = 1; } else if (!ParseState && Id.str() == "projectedvalueinit") { Kind = SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue; ParseState = 1; @@ -2046,32 +2049,6 @@ bool SILParser::parseSILDebugLocation(SILLocation &L, SILBuilder &B) { return false; } -static bool parseAssignByWrapperMode(AssignByWrapperInst::Mode &Result, - SILParser &P) { - StringRef Str; - // If we do not parse '[' ... ']', we have unknown. Set value and return. - if (!parseSILOptional(Str, P)) { - Result = AssignByWrapperInst::Unknown; - return false; - } - - // Then try to parse one of our other initialization kinds. We do not support - // parsing unknown here so we use that as our fail value. - auto Tmp = llvm::StringSwitch(Str) - .Case("init", AssignByWrapperInst::Initialization) - .Case("assign", AssignByWrapperInst::Assign) - .Case("assign_wrapped_value", AssignByWrapperInst::AssignWrappedValue) - .Default(AssignByWrapperInst::Unknown); - - // Thus return true (following the conventions in this file) if we fail. - if (Tmp == AssignByWrapperInst::Unknown) - return true; - - // Otherwise, assign Result and return false. - Result = Tmp; - return false; -} - static bool parseAssignOrInitMode(AssignOrInitInst::Mode &Result, SILParser &P) { StringRef Str; @@ -4653,32 +4630,6 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, break; } - case SILInstructionKind::AssignByWrapperInst: { - SILValue Src, DestAddr, InitFn, SetFn; - SourceLoc DestLoc; - AssignByWrapperInst::Mode mode; - - if (parseTypedValueRef(Src, B) || parseVerbatim("to") || - parseAssignByWrapperMode(mode, *this) || - parseTypedValueRef(DestAddr, DestLoc, 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; - - if (!DestAddr->getType().isAddress()) { - P.diagnose(DestLoc, diag::sil_operand_not_address, "destination", - OpcodeName); - return true; - } - - ResultVal = B.createAssignByWrapper(InstLoc, Src, DestAddr, - InitFn, SetFn, mode); - break; - } - case SILInstructionKind::MoveOnlyWrapperToCopyableAddrInst: { SILValue addrVal; SourceLoc addrLoc; diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 5ba6021668dc2..c4c569b79a10f 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -439,21 +439,6 @@ SILValue swift::isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI) { return Arg; } -bool swift::onlyUsedByAssignByWrapper(PartialApplyInst *PAI) { - bool usedByAssignByWrapper = false; - for (Operand *Op : PAI->getUses()) { - SILInstruction *User = Op->getUser(); - if (isa(User) && Op->getOperandNumber() >= 2) { - usedByAssignByWrapper = true; - continue; - } - if (isa(User)) - continue; - return false; - } - return usedByAssignByWrapper; -} - bool swift::onlyUsedByAssignOrInit(PartialApplyInst *PAI) { bool usedByAssignOrInit = false; for (Operand *Op : PAI->getUses()) { @@ -612,7 +597,6 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType) case SILInstructionKind::FixLifetimeInst: case SILInstructionKind::EndBorrowInst: case SILInstructionKind::AssignInst: - case SILInstructionKind::AssignByWrapperInst: case SILInstructionKind::AssignOrInitInst: case SILInstructionKind::MarkFunctionEscapeInst: case SILInstructionKind::EndLifetimeInst: diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index cdf04fa54bc79..7b19871f18020 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -2737,7 +2737,6 @@ void swift::visitAccessedAddress(SILInstruction *I, llvm_unreachable("unexpected memory access."); 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 c6a3f1826dd58..f52cad0ce0490 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -3141,47 +3141,6 @@ class SILVerifier : public SILVerifierBase { "initializer or setter has too many arguments"); } - void checkAssignByWrapperInst(AssignByWrapperInst *AI) { - SILValue Src = AI->getSrc(), Dest = AI->getDest(); - require(AI->getModule().getStage() == SILStage::Raw, - "assign instruction can only exist in raw SIL"); - require(Dest->getType().isAddress(), "Must store to an address dest"); - - SILValue initFn = AI->getInitializer(); - CanSILFunctionType initTy = initFn->getType().castTo(); - SILFunctionConventions initConv(initTy, AI->getModule()); - checkAssignByWrapperArgs(Src->getType(), initConv); - switch (initConv.getNumIndirectSILResults()) { - case 0: - require(initConv.getNumDirectSILResults() == 1, - "wrong number of init function results"); - requireSameType( - Dest->getType().getObjectType(), - *initConv.getDirectSILResultTypes(F.getTypeExpansionContext()) - .begin(), - "wrong init function result type"); - break; - case 1: - require(initConv.getNumDirectSILResults() == 0, - "wrong number of init function results"); - requireSameType( - Dest->getType(), - *initConv.getIndirectSILResultTypes(F.getTypeExpansionContext()) - .begin(), - "wrong indirect init function result type"); - break; - default: - require(false, "wrong number of indirect init function results"); - } - - SILValue setterFn = AI->getSetter(); - CanSILFunctionType setterTy = setterFn->getType().castTo(); - SILFunctionConventions setterConv(setterTy, AI->getModule()); - require(setterConv.getNumIndirectSILResults() == 0, - "set function has indirect results"); - checkAssignByWrapperArgs(Src->getType(), setterConv); - } - void checkAssigOrInitInstAccessorArgs(SILType argTy, SILFunctionConventions &conv) { unsigned argIdx = conv.getSILArgIndexOfFirstParam(); diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index b18b186c4d75d..9dac2728954c4 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1171,6 +1171,25 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { break; } + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: { + auto *var = cast(constant.getDecl()); + auto loc = RegularLocation::getAutoGeneratedLocation(var); + + preEmitFunction(constant, f, loc); + PrettyStackTraceSILFunction X("silgen propertyWrappedFieldInitAccessor", f); + f->createProfiler(constant); + + auto *init = constant.getInitializationExpr(); + assert(init); + + auto varDC = var->getInnermostDeclContext(); + SILGenFunction SGF(*this, *f, varDC); + SGF.emitPropertyWrappedFieldInitAccessor(constant, init, + /*EmitProfilerIncrement*/ true); + postEmitFunction(constant, f); + break; + } + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: { auto *var = cast(constant.getDecl()); @@ -1877,6 +1896,11 @@ emitPropertyWrapperBackingInitializer(VarDecl *var) { } } +void SILGenModule::emitPropertyWrappedFieldInitAccessor(VarDecl *var) { + SILDeclRef constant(var, SILDeclRef::Kind::PropertyWrappedFieldInitAccessor); + emitOrDelayFunction(constant); +} + SILFunction *SILGenModule::emitLazyGlobalInitializer(StringRef funcName, PatternBindingDecl *binding, unsigned pbdEntry) { diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 2e96cce09b59c..78261cf646ed5 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -340,6 +340,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// Emits the backing initializer for a property with an attached wrapper. void emitPropertyWrapperBackingInitializer(VarDecl *var); + /// Emits an init accessor that contains a call to the backing storage + /// initializer for a property with an attached property wrapper + void emitPropertyWrappedFieldInitAccessor(VarDecl *var); + /// Emits argument generators, including default argument generators and /// property wrapper argument generators, for the given parameter list. void emitArgumentGenerators(SILDeclRef::Loc decl, ParameterList *paramList); diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 071b1dc46c62e..0b0afee837948 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -1807,3 +1807,93 @@ void SILGenFunction::emitInitAccessor(AccessorDecl *accessor) { mergeCleanupBlocks(); } + +void SILGenFunction::emitPropertyWrappedFieldInitAccessor( + SILDeclRef function, Expr *value, bool EmitProfilerIncrement) { + auto vd = cast(function.getDecl()); + auto *declContext = function.getDecl()->getInnermostDeclContext(); + auto &ctx = getASTContext(); + + RegularLocation Loc(value); + Loc.markAutoGenerated(); + + auto loweredFuncDeclTy = F.getLoweredFunctionType(); + bool isLocalContext = vd->getDeclContext()->isLocalContext(); + + auto createArgument = [&](VarDecl *property, SILType type, + bool markUninitialized = false) { + auto *arg = ParamDecl::createImplicit( + getASTContext(), property->getBaseIdentifier(), + property->getBaseIdentifier(), property->getInterfaceType(), + declContext, ParamSpecifier::InOut); + + RegularLocation loc(property); + loc.markAutoGenerated(); + + SILValue argValue = F.begin()->createFunctionArgument(type, arg); + if (markUninitialized) { + argValue = B.createMarkUninitializedOut(loc, argValue); + } + + VarLocs[arg] = VarLoc(argValue, SILAccessEnforcement::Static); + InitAccessorArgumentMappings[property] = arg; + }; + + // Emit @out backing storage argument + auto backingStorage = vd->getPropertyWrapperBackingProperty(); + auto backingStorageTy = getSILTypeInContext( + loweredFuncDeclTy->getResults()[0], loweredFuncDeclTy); + createArgument(backingStorage, backingStorageTy, /*markUninitialized=*/true); + + // Emit `newValue` argument + ParameterList *params = nullptr; + auto newValueParam = new (ctx) + ParamDecl(SourceLoc(), SourceLoc(), ctx.getIdentifier("$input_value"), + SourceLoc(), ctx.getIdentifier("$input_value"), declContext); + newValueParam->setSpecifier(ParamSpecifier::LegacyOwned); + newValueParam->setImplicit(); + newValueParam->setInterfaceType( + vd->getPropertyWrapperInitValueInterfaceType()); + params = + ParameterList::create(ctx, SourceLoc(), {newValueParam}, SourceLoc()); + + // Nominal contexts have an extra trailing metatype argument, + // local contexts do not + auto numIgnoredParams = isLocalContext ? 0 : 1; + emitBasicProlog(declContext, params, + /*selfParam=*/nullptr, TupleType::getEmpty(F.getASTContext()), + /*errorType=*/std::nullopt, + /*throwsLoc=*/SourceLoc(), + /*ignored parameters*/ numIgnoredParams); + + // Emit the `self` argument. + if (!isLocalContext) + emitConstructorMetatypeArg(*this, vd); + + prepareEpilog(declContext, TupleType::getEmpty(F.getASTContext()), + std::nullopt, CleanupLocation(Loc)); + + // Create an opaque value binding that maps 'newValue' to the wrapper's + // wrappedValue AST placeholder. This makes the argument available when + // init(wrappedValue:) is emitted + std::optional opaqueValue; + auto initInfo = vd->getPropertyWrapperInitializerInfo(); + auto *placeholder = initInfo.getWrappedValuePlaceholder(); + opaqueValue.emplace( + *this, placeholder->getOpaqueValuePlaceholder(), + maybeEmitValueOfLocalVarDecl(newValueParam, AccessKind::Read)); + assert(value == initInfo.getInitFromWrappedValue()); + + // Prepare InitializationPtr for the @out return buffer + FullExpr scope(Cleanups, CleanupLocation(value)); + auto backingStorageArg = InitAccessorArgumentMappings[backingStorage]; + auto backingStorageAddr = VarLocs[backingStorageArg].value; + InitializationPtr init(new KnownAddressInitialization(backingStorageAddr)); + + // Intialize the @out buffer with the given expression + emitExprInto(value, init.get()); + + // Emit epilog/cleanups + emitEpilog(Loc); + mergeCleanupBlocks(); +} \ No newline at end of file diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 87009b948a8fe..8754d8cc4bbc3 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -1704,8 +1704,11 @@ void SILGenFunction::visitVarDecl(VarDecl *D) { if (D->getAttrs().hasAttribute()) { // Emit the property wrapper backing initializer if necessary. auto initInfo = D->getPropertyWrapperInitializerInfo(); - if (initInfo.hasInitFromWrappedValue()) + if (initInfo.hasInitFromWrappedValue()) { SGM.emitPropertyWrapperBackingInitializer(D); + // For local context emission + SGM.emitPropertyWrappedFieldInitAccessor(D); + } } // Emit lazy and property wrapper backing storage. diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index edab15a4b4980..37b5205224f8b 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -192,6 +192,8 @@ DeclName SILGenModule::getMagicFunctionName(SILDeclRef ref) { case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: return getMagicFunctionName(cast(ref.getDecl())->getDeclContext()); + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: + return getMagicFunctionName(cast(ref.getDecl())->getDeclContext()); case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: return getMagicFunctionName(cast(ref.getDecl())->getDeclContext()); case SILDeclRef::Kind::IVarInitializer: diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 5855251a41bc7..025869d92a85c 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1061,6 +1061,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// value assignment. void emitInitAccessor(AccessorDecl *accessor); + /// Generates code for the init accessor thunk that assigns the result of + /// calling a property wrapper's backing storage initializer into the backing + /// storage variable + void emitPropertyWrappedFieldInitAccessor(SILDeclRef function, Expr *value, + bool EmitProfilerIncrement); + /// Generates code to emit the given setter reference to the given base value. SILValue emitApplyOfSetterToBase(SILLocation loc, SILDeclRef setter, ManagedValue base, diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 2c906ddb64c0c..381375d7c7104 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1823,71 +1823,58 @@ namespace { isBackingVarVisible(cast(Storage), SGF.FunctionDC)) { // This is wrapped property. Instead of emitting a setter, emit an - // assign_by_wrapper with the allocating initializer function and the - // setter function as arguments. DefiniteInitialization will then decide - // between the two functions, depending if it's an initialization or a - // re-assignment. - // + // assign_or_init instruction with the allocating initializer function + // and the setter function as arguments. DefiniteInitialization will + // then decide between the two functions, depending if it's an + // initialization or a re-assignment. + VarDecl *field = cast(Storage); VarDecl *backingVar = field->getPropertyWrapperBackingProperty(); assert(backingVar); - auto FieldType = field->getValueInterfaceType(); - auto ValType = backingVar->getValueInterfaceType(); - if (!Substitutions.empty()) { - FieldType = FieldType.subst(Substitutions); - ValType = ValType.subst(Substitutions); - } - auto typeData = getLogicalStorageTypeData( - SGF.getTypeExpansionContext(), SGF.SGM, getTypeData().AccessKind, - ValType->getCanonicalType()); + // Create the init accessor thunk + SILDeclRef initConstant( + field, SILDeclRef::Kind::PropertyWrappedFieldInitAccessor); + SILValue initFRef = SGF.emitGlobalFunctionRef(loc, initConstant); - // Stores the address of the storage property. + // For nominal contexts, compute the self metatype + SILValue selfMetatype; ManagedValue proj; - - // TODO: revist minimal - SILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType( - TypeExpansionContext::minimal(), backingVar, - ValType->getCanonicalType()); - - if (!BaseFormalType) { + if (BaseFormalType) { + auto selfTy = base.getType().getASTType(); + auto metatypeTy = MetatypeType::get(selfTy); + if (selfTy->getClassOrBoundGenericClass()) { + selfMetatype = SGF.B + .createValueMetatype( + loc, SGF.getLoweredType(metatypeTy), base) + .getValue(); + } else { + assert(BaseFormalType->getStructOrBoundGenericStruct()); + selfMetatype = + SGF.B.createMetatype(loc, SGF.getLoweredType(metatypeTy)); + } + } else { // Dealing with a local context proj = SGF.maybeEmitValueOfLocalVarDecl(backingVar, AccessKind::Write); - } else if (BaseFormalType->mayHaveSuperclass()) { - RefElementComponent REC(backingVar, LValueOptions(), varStorageType, - typeData, /*actorIsolation=*/std::nullopt); - proj = std::move(REC).project(SGF, loc, base); - } else { - assert(BaseFormalType->getStructOrBoundGenericStruct()); - StructElementComponent SEC(backingVar, varStorageType, typeData, - /*actorIsolation=*/std::nullopt); - proj = std::move(SEC).project(SGF, loc, base); } - // The property wrapper backing initializer forms an instance of - // the backing storage type from a wrapped value. - SILDeclRef initConstant( - field, SILDeclRef::Kind::PropertyWrapperBackingInitializer); - SILValue initFRef = SGF.emitGlobalFunctionRef(loc, initConstant); - + auto argsPAI = BaseFormalType ? selfMetatype : ArrayRef(); PartialApplyInst *initPAI = - SGF.B.createPartialApply(loc, initFRef, - Substitutions, ArrayRef(), - ParameterConvention::Direct_Guaranteed); + SGF.B.createPartialApply(loc, initFRef, Substitutions, argsPAI, + ParameterConvention::Direct_Guaranteed); ManagedValue initFn = SGF.emitManagedRValueWithCleanup(initPAI); // Create the allocating setter function. It captures the base address. SILValue setterFn = SGF.emitApplyOfSetterToBase(loc, Accessor, base, Substitutions); - // Create the assign_by_wrapper with the initializer and setter. - + // Create the assign_or_init SIL instruction auto Mval = emitValue(SGF, loc, field, std::move(value), AccessorKind::Set); - - SGF.B.createAssignByWrapper(loc, Mval.forward(SGF), proj.forward(SGF), - initFn.getValue(), setterFn, - AssignByWrapperInst::Unknown); + auto selfOrLocal = selfMetatype ? base.getValue() : proj.forward(SGF); + SGF.B.createAssignOrInit(loc, field, selfOrLocal, Mval.forward(SGF), + initFn.getValue(), setterFn, + AssignOrInitInst::Unknown); return; } @@ -5697,10 +5684,6 @@ void SILGenFunction::emitAssignToLValue(SILLocation loc, RValue &&src, emitAssignToLValue(loc, ArgumentSource(loc, std::move(src)), std::move(dest)); } -namespace { - -} // end anonymous namespace - /// Checks if the last component of the LValue refers to the given var decl. static bool referencesVar(PathComponent const& comp, VarDecl *var) { if (comp.getKind() != PathComponent::RefElementKind) diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index be1bef8a79098..5de5f5a70b478 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -1537,6 +1537,8 @@ class SILGenType : public TypeMemberVisitor { auto initInfo = vd->getPropertyWrapperInitializerInfo(); if (initInfo.hasInitFromWrappedValue() && !vd->isStatic()) { SGM.emitPropertyWrapperBackingInitializer(vd); + // Output this unconditionally, SIL optimizer will remove it if not needed + SGM.emitPropertyWrappedFieldInitAccessor(vd); } visitAbstractStorageDecl(vd); diff --git a/lib/SILOptimizer/Analysis/RegionAnalysis.cpp b/lib/SILOptimizer/Analysis/RegionAnalysis.cpp index 28717628b29fd..c257a4b1d793f 100644 --- a/lib/SILOptimizer/Analysis/RegionAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/RegionAnalysis.cpp @@ -3657,7 +3657,6 @@ CONSTANT_TRANSLATION(DeallocPackMetadataInst, Asserting) // All of these instructions should be removed by DI which runs before us in the // pass pipeline. CONSTANT_TRANSLATION(AssignInst, Asserting) -CONSTANT_TRANSLATION(AssignByWrapperInst, Asserting) CONSTANT_TRANSLATION(AssignOrInitInst, Asserting) // We should never hit this since it can only appear as a final instruction in a diff --git a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp index 521508d4669cf..aa9ce8cb4f54f 100644 --- a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp +++ b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp @@ -285,7 +285,6 @@ static void checkUsesOfAccess(BeginAccessInst *access) { auto user = use->getUser(); assert(!isa(user)); assert(!isa(user) || - onlyUsedByAssignByWrapper(cast(user)) || onlyUsedByAssignOrInit(cast(user))); } #endif diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index e0c7bcd63af6d..3ca1f6c06661c 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -578,7 +578,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) || isa(Inst)) + if (isa(Inst)) return false; auto *F = Inst->getFunction(); @@ -851,15 +851,14 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { #include "swift/AST/ReferenceStorage.def" // Stores *to* the allocation are writes. - if ((isa(User) || isa(User) || - isa(User)) && + if ((isa(User) || isa(User)) && Op->getOperandNumber() == 1) { // Coming out of SILGen, we assume that raw stores are initializations, // unless they have trivial type (which we classify as InitOrAssign). DIUseKind Kind; if (InStructSubElement) Kind = DIUseKind::PartialStore; - else if (isa(User) || isa(User)) + else if (isa(User)) Kind = DIUseKind::InitOrAssign; else if (PointeeType.isTrivial(*User->getFunction())) Kind = DIUseKind::InitOrAssign; @@ -1141,8 +1140,6 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { } if (auto *PAI = dyn_cast(User)) { - if (onlyUsedByAssignByWrapper(PAI)) - continue; if (onlyUsedByAssignOrInit(PAI)) continue; @@ -1244,13 +1241,24 @@ ElementUseCollector::collectAssignOrInitUses(AssignOrInitInst *Inst, /// that the flag is dropped before calling \c addElementUses. llvm::SaveAndRestore X(IsSelfOfNonDelegatingInitializer, false); - auto *typeDC = Inst->getReferencedInitAccessor() - ->getDeclContext() - ->getSelfNominalTypeDecl(); + NominalTypeDecl *typeDC = nullptr; + if (auto declContext = Inst->getDeclContextOrNull()->getSelfNominalTypeDecl()) + typeDC = declContext; auto expansionContext = TypeExpansionContext(TheMemory.getFunction()); - - auto selfTy = Inst->getSelf()->getType(); + auto selfOrLocalTy = Inst->getSelfOrLocalOperand()->getType(); + + if (!typeDC) { + // Local context: objTy holds the projection value for the backing storage + // local + auto objTy = selfOrLocalTy.getObjectType(); + unsigned N = + getElementCountRec(expansionContext, Module, objTy, /*isSelf=*/false); + trackUse(DIMemoryUse(Inst, DIUseKind::InitOrAssign, + /*firstEltRelToObj=*/BaseEltNo, + /*NumElements=*/N)); + return; + } auto addUse = [&](VarDecl *property, DIUseKind useKind) { unsigned fieldIdx = 0; @@ -1260,15 +1268,14 @@ ElementUseCollector::collectAssignOrInitUses(AssignOrInitInst *Inst, fieldIdx += getElementCountRec( expansionContext, Module, - selfTy.getFieldType(VD, Module, expansionContext), false); + selfOrLocalTy.getFieldType(VD, Module, expansionContext), false); } - auto type = selfTy.getFieldType(property, Module, expansionContext); + auto type = selfOrLocalTy.getFieldType(property, Module, expansionContext); addElementUses(fieldIdx, type, Inst, useKind, property); }; - auto initializedElts = Inst->getInitializedProperties(); - if (initializedElts.empty()) { + if (Inst->getNumInitializedProperties() == 0) { auto initAccessorProperties = typeDC->getInitAccessorProperties(); auto initFieldAt = typeDC->getStoredProperties().size(); @@ -1286,8 +1293,8 @@ ElementUseCollector::collectAssignOrInitUses(AssignOrInitInst *Inst, ++initFieldAt; } } else { - for (auto *property : initializedElts) - addUse(property, DIUseKind::InitOrAssign); + Inst->forEachInitializedProperty( + [&](VarDecl *property) { addUse(property, DIUseKind::InitOrAssign); }); } for (auto *property : Inst->getAccessedProperties()) @@ -1704,8 +1711,6 @@ void ElementUseCollector::collectClassSelfUses( // If this is a partial application of self, then this is an escape point // for it. if (auto *PAI = dyn_cast(User)) { - if (onlyUsedByAssignByWrapper(PAI)) - continue; if (onlyUsedByAssignOrInit(PAI)) continue; diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index a9a161c816536..c33123af4644c 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -688,8 +688,7 @@ bool LifetimeChecker::shouldEmitError(const SILInstruction *Inst) { if (auto *PAI = isOnlyUsedByPartialApply(load)) { if (std::find_if(PAI->use_begin(), PAI->use_end(), [](auto PAIUse) { - return isa(PAIUse->getUser()) || - isa(PAIUse->getUser()); + return isa(PAIUse->getUser()); }) != PAI->use_end()) { return false; } @@ -1579,8 +1578,6 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { addr = moveAddr->getDest(); else if (auto *assign = dyn_cast(inst)) addr = assign->getDest(); - else if (auto *assign = dyn_cast(inst)) - addr = assign->getDest(); else return false; @@ -1637,13 +1634,6 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) { } 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::Set : DIUseKind::Assign; } else if (isFullyInitialized && isa(Use.Inst)) { auto allFieldsInitialized = getAnyUninitializedMemberAtInst(Use.Inst, 0, @@ -2486,8 +2476,7 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) { "delegating inits have a single elt"); // Lower Assign instructions if needed. - if (isa(Use.Inst) || isa(Use.Inst) || - isa(Use.Inst)) + if (isa(Use.Inst) || isa(Use.Inst)) NeedsUpdateForInitState.push_back(UseID); } else { // super.init also requires that all ivars are initialized before the @@ -2636,29 +2625,6 @@ 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::Initialization: - AI->setMode(AssignByWrapperInst::Initialization); - break; - case DIUseKind::Assign: - AI->setMode(AssignByWrapperInst::Assign); - break; - case DIUseKind::Set: - AI->setMode(AssignByWrapperInst::AssignWrappedValue); - break; - default: - llvm_unreachable("Wrong use kind for assign_by_wrapper"); - } - - return; - } - if (auto *TACI = dyn_cast(Inst)) { assert(!TACI->isInitializationOfDest() && "should not modify copy_addr that already knows it is initialized"); diff --git a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp index c24bf26979509..79e4a949e0141 100644 --- a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp +++ b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp @@ -180,88 +180,6 @@ static void emitInitAccessorInitialValueArgument( forProjections, forCleanup); } -static void -lowerAssignByWrapperInstruction(SILBuilderWithScope &b, - AssignByWrapperInst *inst, - llvm::SmallSetVector &toDelete) { - LLVM_DEBUG(llvm::dbgs() << " *** Lowering " << *inst << "\n"); - - ++numAssignRewritten; - - SILValue src = inst->getSrc(); - SILValue dest = inst->getDest(); - SILLocation loc = inst->getLoc(); - SILBuilderWithScope forCleanup(std::next(inst->getIterator())); - - switch (inst->getMode()) { - case AssignByWrapperInst::Unknown: - assert(b.getModule().getASTContext().hadError() && - "assign_by_wrapper must have a valid mode"); - // In case DefiniteInitialization already gave up with an error, just - // treat the assign_by_wrapper as an "init". - LLVM_FALLTHROUGH; - case AssignByWrapperInst::Initialization: - case AssignByWrapperInst::Assign: { - SILValue initFn = inst->getInitializer(); - CanSILFunctionType fTy = initFn->getType().castTo(); - SILFunctionConventions convention(fTy, inst->getModule()); - SmallVector args; - if (convention.hasIndirectSILResults()) { - if (inst->getMode() == AssignByWrapperInst::Assign) - b.createDestroyAddr(loc, dest); - - args.push_back(dest); - getAssignByWrapperArgs(args, src, convention, b, forCleanup); - b.createApply(loc, initFn, SubstitutionMap(), args); - } else { - getAssignByWrapperArgs(args, src, convention, b, forCleanup); - SILValue wrappedSrc = - b.createApply(loc, initFn, SubstitutionMap(), args); - if (inst->getMode() == AssignByWrapperInst::Initialization || - inst->getDest()->getType().isTrivial(*inst->getFunction())) { - b.createTrivialStoreOr(loc, wrappedSrc, dest, - StoreOwnershipQualifier::Init); - } else { - b.createStore(loc, wrappedSrc, dest, StoreOwnershipQualifier::Assign); - } - } - - // 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. - auto *setterPA = dyn_cast(inst->getSetter()); - if (setterPA && setterPA->getNumArguments() == 1) - toDelete.insert(setterPA->getArgument(0)); - break; - } - case AssignByWrapperInst::AssignWrappedValue: { - 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); - - // The destination address is not used. Remove it if it is a dead access - // marker. This is important, because also the setter function contains - // access marker. In case those markers are dynamic it would cause a - // nested access violation. - if (isa(dest)) - toDelete.insert(dest); - - // Again, we have to delete the unused dead closure. - toDelete.insert(inst->getInitializer()); - break; - } - } - inst->eraseFromParent(); -} - static void lowerAssignOrInitInstruction(SILBuilderWithScope &b, AssignOrInitInst *inst, @@ -279,34 +197,20 @@ lowerAssignOrInitInstruction(SILBuilderWithScope &b, 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". + // treat the assign_or_init with an "init" mode. LLVM_FALLTHROUGH; case AssignOrInitInst::Init: { SILValue initFn = inst->getInitializer(); CanSILFunctionType fTy = initFn->getType().castTo(); SILFunctionConventions convention(fTy, inst->getModule()); - auto selfValue = inst->getSelf(); - auto isRefSelf = selfValue->getType().getASTType()->mayHaveSuperclass(); - - SILValue selfRef; - bool needInsertEndAccess = false; - if (isRefSelf) { - selfRef = b.emitBeginBorrowOperation(loc, selfValue); - } else if (isa(selfValue)) { - // Don't insert an access scope if there is already one. This avoids - // inserting a dynamic access check when the parent is static (and therefore - // can be statically enforced). - selfRef = selfValue; - } else { - selfRef = b.createBeginAccess(loc, selfValue, SILAccessKind::Modify, - SILAccessEnforcement::Dynamic, - /*noNestedConflict=*/false, - /*fromBuiltin=*/false); - needInsertEndAccess = true; - } + auto inLocalContext = + inst->getProperty()->getDeclContext()->isLocalContext(); + auto selfOrLocalValue = inst->getSelfOrLocalOperand(); + bool isRefSelf = + selfOrLocalValue->getType().getASTType()->mayHaveSuperclass(); - auto emitFieldReference = [&](VarDecl *field, + auto emitFieldReference = [&](SILValue selfRef, VarDecl *field, bool emitDestroy = false) -> SILValue { SILValue fieldRef; if (isRefSelf) { @@ -325,13 +229,36 @@ lowerAssignOrInitInstruction(SILBuilderWithScope &b, // First, emit all of the properties listed in `initializes`. They // are passed as indirect results. - { - auto toInitialize = inst->getInitializedProperties(); - for (unsigned index : indices(toInitialize)) { + SILValue selfRef = nullptr; + bool needInsertEndAccess = false; + if (inLocalContext) { + // add the local projection which is for the _x backing local storage + arguments.push_back(selfOrLocalValue); + } else { + if (isRefSelf) { + selfRef = b.emitBeginBorrowOperation(loc, selfOrLocalValue); + } else if (isa(selfOrLocalValue)) { + // Don't insert an access scope if there is already one. This avoids + // inserting a dynamic access check when the parent is static (and therefore + // can be statically enforced). + selfRef = selfOrLocalValue; + } + else { + selfRef = + b.createBeginAccess(loc, selfOrLocalValue, SILAccessKind::Modify, + SILAccessEnforcement::Dynamic, + /*noNestedConflict=*/false, + /*fromBuiltin=*/false); + needInsertEndAccess = true; + } + + unsigned index = 0; + inst->forEachInitializedProperty([&](VarDecl *property) { arguments.push_back(emitFieldReference( - toInitialize[index], + selfRef, property, /*emitDestroy=*/inst->isPropertyAlreadyInitialized(index))); - } + index++; + }); } // Now emit `initialValue` which is the only argument specified @@ -341,15 +268,17 @@ lowerAssignOrInitInstruction(SILBuilderWithScope &b, // And finally, emit all of the `accesses` properties. for (auto *property : inst->getAccessedProperties()) - arguments.push_back(emitFieldReference(property)); + arguments.push_back(emitFieldReference(selfRef, property)); b.createApply(loc, initFn, SubstitutionMap(), arguments); - if (isRefSelf) { - if (selfRef != selfValue) - b.emitEndBorrowOperation(loc, selfRef); - } else if (needInsertEndAccess) { - b.createEndAccess(loc, selfRef, /*aborted=*/false); + if (selfRef) { + if (isRefSelf) { + if (selfRef != selfOrLocalValue) + b.emitEndBorrowOperation(loc, selfRef); + } else if (needInsertEndAccess) { + b.createEndAccess(loc, selfRef, /*aborted=*/false); + } } // The unused partial_apply violates memory lifetime rules in case "self" @@ -459,13 +388,6 @@ static bool lowerRawSILOperations(SILFunction &fn) { continue; } - if (auto *ai = dyn_cast(inst)) { - SILBuilderWithScope b(ai); - lowerAssignByWrapperInstruction(b, ai, toDelete); - changed = true; - continue; - } - if (auto *ai = dyn_cast(inst)) { SILBuilderWithScope b(ai); lowerAssignOrInitInstruction(b, ai, toDelete); diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index 69ed207bbe67e..db266c8085b23 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -289,7 +289,6 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::EndUnpairedAccessInst: case SILInstructionKind::StoreInst: case SILInstructionKind::AssignInst: - case SILInstructionKind::AssignByWrapperInst: case SILInstructionKind::AssignOrInitInst: case SILInstructionKind::MarkFunctionEscapeInst: case SILInstructionKind::DebugValueInst: diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index f55edfd1a32ea..8ed5e5faa6d7a 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -1080,7 +1080,6 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::ValueMetatypeInst: case SILInstructionKind::WitnessMethodInst: case SILInstructionKind::AssignInst: - case SILInstructionKind::AssignByWrapperInst: case SILInstructionKind::AssignOrInitInst: case SILInstructionKind::CheckedCastBranchInst: case SILInstructionKind::CheckedCastAddrBranchInst: diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 1b2a986b75f3b..7f31dea8d7f97 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -3024,7 +3024,6 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, getLocalValue(Builder.maybeGetFunction(), ValID2, addrType), qualifier); break; } - case SILInstructionKind::AssignByWrapperInst: case SILInstructionKind::AssignOrInitInst: llvm_unreachable("not supported"); case SILInstructionKind::BindMemoryInst: { diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 4463ca59ae1b2..9c987c8dddb67 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 = 958; // SILGlobalVariable markedAsUsed +const uint16_t SWIFTMODULE_VERSION_MINOR = 959; // Removed assign_by_wrapper SILGen instruction /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 49254e5106554..9f19c5128ebe9 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -2448,7 +2448,6 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { addValueRef(operand)); break; } - case SILInstructionKind::AssignByWrapperInst: case SILInstructionKind::AssignOrInitInst: llvm_unreachable("not supported"); case SILInstructionKind::BindMemoryInst: { diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index 65593d300a26d..6605837628398 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -377,6 +377,8 @@ $s18resilient_protocol24ResilientDerivedProtocolPxAA0c4BaseE0Tn --> associated c $s3red4testyAA3ResOyxSayq_GAEs5ErrorAAq_sAFHD1__HCg_GADyxq_GsAFR_r0_lF ---> red.test(red.Res) -> red.Res $s3red4testyAA7OurTypeOy4them05TheirD0Vy5AssocQzGAjE0F8ProtocolAAxAA0c7DerivedH0HD1_AA0c4BaseH0HI1_AieKHA2__HCg_GxmAaLRzlF ---> red.test(A.Type) -> red.OurType> $s17property_wrappers10WithTuplesV9fractionsSd_S2dtvpfP ---> property wrapper backing initializer of property_wrappers.WithTuples.fractions : (Swift.Double, Swift.Double, Swift.Double) +$s16property_wrapper8MyStructV1xSivpfF ---> property wrapped field init accessor of property_wrapper.MyStruct.x : Swift.Int +$s4main6myFuncyyF1xL_SivpfF ---> property wrapped field init accessor of x #1 : Swift.Int in main.myFunc() -> () $sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTOTA ---> {T:$sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTO} partial apply forwarder for @nonobjc __C.OS_dispatch_queue.sync(execute: () -> ()) -> () $s4main1gyySiXCvp ---> main.g : @convention(c) (Swift.Int) -> () $s4main1gyySiXBvp ---> main.g : @convention(block) (Swift.Int) -> () diff --git a/test/SILGen/assign_or_init_without_opaque_sil.swift b/test/SILGen/assign_or_init_without_opaque_sil.swift new file mode 100644 index 0000000000000..5fd3b6b7b26f4 --- /dev/null +++ b/test/SILGen/assign_or_init_without_opaque_sil.swift @@ -0,0 +1,35 @@ +// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types -disable-availability-checking -Xllvm -sil-full-demangle -primary-file %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-runtime + +// Extracted from opaque_values_closures.swift. +// This version uses the assign_or_init SILGen instruction instead of the previous assign_by_wrapper. +// Because init accessors do not currently support `-enable-sil-opaque-values`, +// this test runs without that flag to validate closure behavior in the local context. + +@propertyWrapper struct BoxWrapper { var wrappedValue: T } + +// CHECK-LABEL: sil {{.*}}[ossa] @$s33assign_or_init_without_opaque_sil35captureBoxNonopaqueOwnedNonescapingyyxyXElF : {{.*}} { +// CHECK: bb0([[GET:%[^,]+]] : +// CHECK: [[BOX:%[^,]+]] = alloc_box $<τ_0_0> { var BoxWrapper<τ_0_0> } , var +// CHECK: [[VAR:%[^,]+]] = mark_uninitialized [var] [[BOX]] +// CHECK: [[VAR_LIFETIME:%[^,]+]] = begin_borrow [lexical] [var_decl] [[VAR]] +// CHECK: [[VAR_ADDR:%[^,]+]] = project_box [[VAR_LIFETIME]] +// TODO: DETERMINE: Considering that captureCanEscape is false, should this mark_function_escape be emitted? +// CHECK: mark_function_escape [[VAR_ADDR]] +// CHECK: [[LOCAL:%[^,]+]] = function_ref @$s33assign_or_init_without_opaque_sil35captureBoxNonopaqueOwnedNonescapingyyxyXElF5localL_yylF +// CHECK: apply [[LOCAL]]([[VAR_LIFETIME]], [[GET]]) +// CHECK: end_borrow [[VAR_LIFETIME]] +// CHECK: destroy_value [[VAR]] +// CHECK-LABEL: } // end sil function '$s33assign_or_init_without_opaque_sil35captureBoxNonopaqueOwnedNonescapingyyxyXElF' + +// CHECK-LABEL: sil {{.*}}[ossa] @$s33assign_or_init_without_opaque_sil35captureBoxNonopaqueOwnedNonescapingyyxyXElF5localL_yylF : {{.*}} +// CHECK: bb0(%0 : @closureCapture @guaranteed $<τ_0_0> { var BoxWrapper<τ_0_0> } , +// CHECK-SAME: %1 : +// CHECK-LABEL: } // end sil function '$s33assign_or_init_without_opaque_sil35captureBoxNonopaqueOwnedNonescapingyyxyXElF5localL_yylF' +func captureBoxNonopaqueOwnedNonescaping(_ get: () -> U) { + @BoxWrapper var u: U + + func local() { + u = get() + } + local() +} diff --git a/test/SILGen/objc_properties.swift b/test/SILGen/objc_properties.swift index 09ef6d6a7fe27..f3c3ae8ab8f58 100644 --- a/test/SILGen/objc_properties.swift +++ b/test/SILGen/objc_properties.swift @@ -290,10 +290,10 @@ class SomeWrapperTests { // CHECK-LABEL: sil hidden [ossa] @$s15objc_properties16SomeWrapperTestsCyACSScfc : $@convention(method) (@owned String, @owned SomeWrapperTests) -> @owned SomeWrapperTests { // CHECK: [[M:%.*]] = function_ref @$s15objc_properties16SomeWrapperTestsC04someD0SivsTD // CHECK: [[C:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[M]]({{.*}}) -// CHECK: assign_by_wrapper {{%.*}}: $Int to {{%.*}} : $*SomeWrapper, init {{.*}} : $@callee_guaranteed (Int) -> SomeWrapper, set [[C]] : $@noescape @callee_guaranteed (Int) -> () +// CHECK: assign_or_init #SomeWrapperTests.someWrapper, self {{%.*}} : $SomeWrapperTests, value {{%.*}} : $Int, init {{%.*}} : $@callee_guaranteed (Int) -> @out SomeWrapper, set [[C]] : $@noescape @callee_guaranteed (Int) -> () // CHECK: [[M:%.*]] = function_ref @$s15objc_properties16SomeWrapperTestsC1sSSSgvsTD // CHECK: [[C:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[M]]( -// CHECK: assign_by_wrapper {{.*}} : $Optional to {{.*}} : $*W>, init {{.*}} : $@callee_guaranteed (@owned Optional) -> @owned W>, set [[C]] : $@noescape @callee_guaranteed (@owned Optional) -> () +// CHECK: assign_or_init #SomeWrapperTests.s, self {{.*}} : $SomeWrapperTests, value {{.*}} : $Optional, init {{.*}} : $@callee_guaranteed (@owned Optional) -> @out W>, set [[C]] : $@noescape @callee_guaranteed (@owned Optional) -> () init(_ s: String) { someWrapper = 1000 self.s = s diff --git a/test/SILGen/opaque_values_closures.swift b/test/SILGen/opaque_values_closures.swift index f38221a3c0c97..b470357628d2e 100644 --- a/test/SILGen/opaque_values_closures.swift +++ b/test/SILGen/opaque_values_closures.swift @@ -43,7 +43,6 @@ struct G {} struct MOG : ~Copyable {} func getMOG(_ t: T.Type) -> MOG { return MOG() } func borrow(_ t: T) {} -@propertyWrapper struct BoxWrapper { var wrappedValue: T } // CaptureKind::Immutable, canGuarantee=true, isPack=true // CHECK-LABEL: sil {{.*}}[ossa] @$s22opaque_values_closures30captureImmutablePackGuaranteed1tyxxQp_tRvzlF6$deferL_yyRvzlF : {{.*}} { @@ -357,32 +356,4 @@ func captureBoxNonopaqueOwnedEscaping() { } } func captureBoxNonopaqueOwnedEscaping_callee(_ t: T, _ c: @escaping (T) -> ()) {} -func captureBoxNonopaqueOwnedEscaping_vend() -> C { return C() } - -// CaptureKind::Box, non-opaque, canGuarantee=false, captureCanEscape=false -// CHECK-LABEL: sil {{.*}}[ossa] @$s22opaque_values_closures35captureBoxNonopaqueOwnedNonescapingyyxyXElF : {{.*}} { -// CHECK: bb0([[GET:%[^,]+]] : -// CHECK: [[BOX:%[^,]+]] = alloc_box $<τ_0_0> { var BoxWrapper<τ_0_0> } , var -// CHECK: [[VAR:%[^,]+]] = mark_uninitialized [var] [[BOX]] -// CHECK: [[VAR_LIFETIME:%[^,]+]] = begin_borrow [lexical] [var_decl] [[VAR]] -// CHECK: [[VAR_ADDR:%[^,]+]] = project_box [[VAR_LIFETIME]] -// TODO: DETERMINE: Considering that captureCanEscape is false, should this mark_function_escape be emitted? -// CHECK: mark_function_escape [[VAR_ADDR]] -// CHECK: [[LOCAL:%[^,]+]] = function_ref @$s22opaque_values_closures35captureBoxNonopaqueOwnedNonescapingyyxyXElF5localL_yylF -// CHECK: apply [[LOCAL]]([[VAR_LIFETIME]], [[GET]]) -// CHECK: end_borrow [[VAR_LIFETIME]] -// CHECK: destroy_value [[VAR]] -// CHECK-LABEL: } // end sil function '$s22opaque_values_closures35captureBoxNonopaqueOwnedNonescapingyyxyXElF' -// CHECK-LABEL: sil {{.*}}[ossa] @$s22opaque_values_closures35captureBoxNonopaqueOwnedNonescapingyyxyXElF5localL_yylF : {{.*}} -// CHECK: bb0(%0 : @closureCapture @guaranteed $<τ_0_0> { var BoxWrapper<τ_0_0> } , -// CHECK-SAME: %1 : -// CHECK-LABEL: } // end sil function '$s22opaque_values_closures35captureBoxNonopaqueOwnedNonescapingyyxyXElF5localL_yylF' -func captureBoxNonopaqueOwnedNonescaping(_ get: () -> U) { - @BoxWrapper var u: U - - func local() { - u = get() - } - local() -} - +func captureBoxNonopaqueOwnedEscaping_vend() -> C { return C() } \ No newline at end of file diff --git a/test/SILGen/property_wrapper_local.swift b/test/SILGen/property_wrapper_local.swift index 99c0a9d2142c0..72f697b6151f5 100644 --- a/test/SILGen/property_wrapper_local.swift +++ b/test/SILGen/property_wrapper_local.swift @@ -21,13 +21,13 @@ func testLocalWrapper() { // CHECK: [[P:%.*]] = project_box [[WLIFETIME]] : ${ var Wrapper } value = 10 - // CHECK: [[I:%.*]] = function_ref @$s22property_wrapper_local16testLocalWrapperyyF5valueL_SivpfP : $@convention(thin) (Int) -> Wrapper - // CHECK: [[IPA:%.*]] = partial_apply [callee_guaranteed] [[I]]() : $@convention(thin) (Int) -> Wrapper + // CHECK: [[I:%.*]] = function_ref @$s22property_wrapper_local16testLocalWrapperyyF5valueL_SivpfF : $@convention(thin) (Int) -> @out Wrapper + // CHECK: [[IPA:%.*]] = partial_apply [callee_guaranteed] [[I]]() : $@convention(thin) (Int) -> @out Wrapper // CHECK: [[S:%.*]] = function_ref @$s22property_wrapper_local16testLocalWrapperyyF5valueL_Sivs : $@convention(thin) (Int, @guaranteed { var Wrapper }) -> () // CHECK-NEXT: [[C:%.*]] = copy_value [[WLIFETIME]] : ${ var Wrapper } // CHECK-NOT: mark_function_escape // CHECK-NEXT: [[SPA:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[S]]([[C]]) : $@convention(thin) (Int, @guaranteed { var Wrapper }) -> () - // CHECK-NEXT: assign_by_wrapper {{%.*}} : $Int to [[P]] : $*Wrapper, init [[IPA]] : $@callee_guaranteed (Int) -> Wrapper, set [[SPA]] : $@noescape @callee_guaranteed (Int) -> () + // CHECK-NEXT: assign_or_init #value, local [[P]] : $*Wrapper, value {{%.*}} : $Int, init [[IPA]] : $@callee_guaranteed (Int) -> @out Wrapper, set [[SPA]] : $@noescape @callee_guaranteed (Int) -> () _ = value // CHECK: mark_function_escape [[P]] : $*Wrapper @@ -46,12 +46,12 @@ func testLocalWrapper() { // CHECK: [[OP:%.*]] = function_ref @$sSi2peoiyySiz_SitFZ : $@convention(method) (@inout Int, Int, @thin Int.Type) -> () // CHECK: apply [[OP]]({{%.*}}) : $@convention(method) (@inout Int, Int, @thin Int.Type) -> () // CHECK: [[RESULT:%.*]] = load [trivial] [[TMP]] : $*Int - // CHECK: assign_by_wrapper [[RESULT]] : $Int to [[P]] + // CHECK: assign_or_init #value, local [[P]] : $*Wrapper, value [[RESULT]] // Check local property wrapper backing initializer and accessors - // property wrapper backing initializer of value #1 in testLocalWrapper() - // CHECK-LABEL: sil private [ossa] @$s22property_wrapper_local16testLocalWrapperyyF5valueL_SivpfP : $@convention(thin) (Int) -> Wrapper { + // property wrapped field init accessor of value #1 in testLocalWrapper() + // CHECK-LABEL: sil private [ossa] @$s22property_wrapper_local16testLocalWrapperyyF5valueL_SivpfF : $@convention(thin) (Int) -> @out Wrapper { // getter of $value #1 in testLocalWrapper() // CHECK-LABEL: sil private [ossa] @$s22property_wrapper_local16testLocalWrapperyyF6$valueL_AA0F0VySiGvg : $@convention(thin) (@guaranteed { var Wrapper }) -> Wrapper { @@ -71,12 +71,12 @@ func testInitialValue() { value = 15 // CHECK: function_ref @$s22property_wrapper_local16testInitialValueyyF5valueL_Sivs : $@convention(thin) (Int, @guaranteed { var Wrapper }) -> () - // CHECK-NOT: assign_by_wrapper + // CHECK-NOT: assign_or_init value += 5 // CHECK: function_ref @$sSi2peoiyySiz_SitFZ : $@convention(method) (@inout Int, Int, @thin Int.Type) -> () // CHECK: function_ref @$s22property_wrapper_local16testInitialValueyyF5valueL_Sivs : $@convention(thin) (Int, @guaranteed { var Wrapper }) -> () - // CHECK-NOT: assign_by_wrapper + // CHECK-NOT: assign_or_init // CHECK: return } diff --git a/test/SILGen/property_wrappers.swift b/test/SILGen/property_wrappers.swift index f4f8ae18dab9e..33c33052cbbd4 100644 --- a/test/SILGen/property_wrappers.swift +++ b/test/SILGen/property_wrappers.swift @@ -738,8 +738,9 @@ public class TestClass { // CHECK-LABEL: sil [ossa] @$s17property_wrappers9TestClassC5valuexvpfP : $@convention(thin) (@in T) -> @out WrapperWithInitialValue // CHECK-LABEL: sil hidden [ossa] @$s17property_wrappers9TestClassC5value8protocolACyxGx_qd__tcAA0C8ProtocolRd__lufc - // CHECK: [[BACKING_INIT:%.*]] = function_ref @$s17property_wrappers9TestClassC5valuexvpfP : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out WrapperWithInitialValue<τ_0_0> - // CHECK-NEXT: partial_apply [callee_guaranteed] [[BACKING_INIT]]() + // CHECK: [[BACKING_INIT:%.*]] = function_ref @$s17property_wrappers9TestClassC5valuexvpfF : $@convention(thin) <τ_0_0> (@in τ_0_0, @thick TestClass<τ_0_0>.Type) -> @out WrapperWithInitialValue<τ_0_0> + // CHECK: [[METATYPE:%.*]] = value_metatype $@thick TestClass.Type + // CHECK: partial_apply [callee_guaranteed] [[BACKING_INIT]]([[METATYPE]]) init(value: T, protocol: U) { self.value = value } @@ -796,7 +797,7 @@ open class TestMyWrapper { extension UsesMyPublished { // CHECK-LABEL: sil hidden [ossa] @$s21property_wrapper_defs15UsesMyPublishedC0A9_wrappersE6setFooyySiF : $@convention(method) (Int, @guaranteed UsesMyPublished) -> () // CHECK: class_method %1 : $UsesMyPublished, #UsesMyPublished.foo!setter - // CHECK-NOT: assign_by_wrapper + // CHECK-NOT: assign_or_init // CHECK: return func setFoo(_ x: Int) { foo = x @@ -883,7 +884,7 @@ struct ObservedObject { // rdar://problem/60600911 -// Ensure assign_by_wrapper is emitted for initialization +// Ensure assign_or_init is emitted for initialization // of a property wrapper with a nonmutating set. Even though such setters // take `self` by-value. @propertyWrapper @@ -904,7 +905,7 @@ struct NonMutatingWrapperTestStruct { // CHECK-LABEL: sil hidden [ossa] @$s17property_wrappers28NonMutatingWrapperTestStructV3valACSi_tcfC : $@convention(method) (Int, @thin NonMutatingWrapperTestStruct.Type) -> NonMutatingWrapperTestStruct { // CHECK: %[[LOAD:[0-9]+]] = load [trivial] %[[SRC:[0-9]+]] : $*NonMutatingWrapperTestStruct // CHECK-NEXT: %[[SET_PA:[0-9]+]] = partial_apply [callee_guaranteed] [on_stack] %[[PW_SETTER:[0-9]+]](%[[LOAD]]) : $@convention(method) (Int, NonMutatingWrapperTestStruct) -> () - // CHECK-NEXT: assign_by_wrapper %[[SETVAL:[0-9]+]] : $Int to %[[ADDR:[0-9]+]] : $*NonMutatingSetterWrapper, init %[[INIT_PA:[0-9]+]] : $@callee_guaranteed (Int) -> NonMutatingSetterWrapper, set %[[SET_PA]] : $@noescape @callee_guaranteed (Int) -> () + // CHECK-NEXT: assign_or_init #NonMutatingWrapperTestStruct.SomeProp, self %[[ADDR:[0-9]+]] : $*NonMutatingWrapperTestStruct, value %[[SETVAL:[0-9]+]] : $Int, init %[[INIT_PA:[0-9]+]] : $@callee_guaranteed (Int) -> @out NonMutatingSetterWrapper, set %[[SET_PA]] : $@noescape @callee_guaranteed (Int) -> () @NonMutatingSetterWrapper var SomeProp: Int init(val: Int) { SomeProp = val diff --git a/test/SILGen/resilient_assign_by_wrapper.swift b/test/SILGen/resilient_assign_or_init.swift similarity index 68% rename from test/SILGen/resilient_assign_by_wrapper.swift rename to test/SILGen/resilient_assign_or_init.swift index bebf7154d4683..6661938b2c428 100644 --- a/test/SILGen/resilient_assign_by_wrapper.swift +++ b/test/SILGen/resilient_assign_or_init.swift @@ -25,22 +25,23 @@ public class AddressOnlySetter { @WrapGod var value: AddressOnlyEnum = .value(nil) init() { - // CHECK-LABEL: sil hidden [ossa] @$s27resilient_assign_by_wrapper17AddressOnlySetterCACycfc + // CHECK-LABEL: sil hidden [ossa] @$s24resilient_assign_or_init17AddressOnlySetterCACycfc // CHECK: [[E1:%.*]] = alloc_stack $AddressOnlyEnum // CHECK: [[W:%.*]] = alloc_stack $WrapGod - // CHECK: [[I:%.*]] = function_ref @$s27resilient_assign_by_wrapper17AddressOnlySetterC5valueAA0eF4EnumOvpfP : $@convention(thin) (@in AddressOnlyEnum) -> @out WrapGod + // property wrapper backing initializer of AddressOnlySetter.value + // CHECK: [[I:%.*]] = function_ref @$s24resilient_assign_or_init17AddressOnlySetterC5valueAA0eF4EnumOvpfP : $@convention(thin) (@in AddressOnlyEnum) -> @out WrapGod // CHECK: apply [[I]]([[W]], [[E1]]) // CHECK: [[E2:%.*]] = alloc_stack $AddressOnlyEnum // CHECK-NEXT: inject_enum_addr [[E2]] : $*AddressOnlyEnum, #AddressOnlyEnum.some!enumelt // CHECK: [[S:%.*]] = partial_apply [callee_guaranteed] [on_stack] {{%.*}}({{%.*}}) : $@convention(method) (@in AddressOnlyEnum, @guaranteed AddressOnlySetter) -> () - // CHECK: assign_by_wrapper [[E2]] : $*AddressOnlyEnum + // CHECK: assign_or_init #AddressOnlySetter.value, self {{%.*}} : $AddressOnlySetter, value [[E2]] : $*AddressOnlyEnum // CHECK-SAME: set [[S]] : $@noescape @callee_guaranteed (@in AddressOnlyEnum) -> () self.value = .some } func testAssignment() { - // CHECK-LABEL: sil hidden [ossa] @$s27resilient_assign_by_wrapper17AddressOnlySetterC14testAssignmentyyF + // CHECK-LABEL: sil hidden [ossa] @$s24resilient_assign_or_init17AddressOnlySetterC14testAssignmentyyF // CHECK: [[E:%.*]] = alloc_stack $AddressOnlyEnum // CHECK: inject_enum_addr [[E]] : $*AddressOnlyEnum, #AddressOnlyEnum.some!enumelt // CHECK: [[S:%.*]] = class_method %0 : $AddressOnlySetter, #AddressOnlySetter.value!setter : (AddressOnlySetter) -> (AddressOnlyEnum) -> (), $@convention(method) (@in AddressOnlyEnum, @guaranteed AddressOnlySetter) -> () @@ -55,10 +56,9 @@ public struct SubstitutedSetter { extension SubstitutedSetter where T == Bool { init() { - // CHECK-LABEL: hidden [ossa] @$s27resilient_assign_by_wrapper17SubstitutedSetterVAASbRszlEACySbGycfC - // CHECK: [[W:%.*]] = struct_element_addr {{%.*}} : $*SubstitutedSetter, #SubstitutedSetter._value + // CHECK-LABEL: hidden [ossa] @$s24resilient_assign_or_init17SubstitutedSetterVAASbRszlEACySbGycfC // CHECK: [[B:%.*]] = alloc_stack $Bool - // CHECK: assign_by_wrapper [[B]] : $*Bool to [[W]] : $*WrapGod + // CHECK: assign_or_init #SubstitutedSetter.value, self {{%.*}} : $*SubstitutedSetter, value [[B]] : $*Bool // CHECK-SAME: init {{%.*}} : $@callee_guaranteed (@in Bool) -> @out WrapGod // CHECK-SAME: set {{%.*}} : $@noescape @callee_guaranteed (@in Bool) -> () self.value = true @@ -71,14 +71,13 @@ public struct ReabstractedSetter { extension ReabstractedSetter where T == Int { init() { - // CHECK-LABEL: hidden [ossa] @$s27resilient_assign_by_wrapper18ReabstractedSetterVAASiRszlEACySiGycfC - // CHECK: [[F:%.*]] = function_ref @$s27resilient_assign_by_wrapper18ReabstractedSetterVAASiRszlEACySiGycfcySicfU_ : $@convention(thin) (Int) -> () + // CHECK-LABEL: hidden [ossa] @$s24resilient_assign_or_init18ReabstractedSetterVAASiRszlEACySiGycfC + // CHECK: [[F:%.*]] = function_ref @$s24resilient_assign_or_init18ReabstractedSetterVAASiRszlEACySiGycfcySicfU_ : $@convention(thin) (Int) -> () // CHECK: [[TH_F:%.*]] = thin_to_thick_function [[F]] : $@convention(thin) (Int) -> () to $@callee_guaranteed (Int) -> () // CHECK: [[THUNK_REF:%.*]] = function_ref @$sSiIegy_SiIegn_TR : $@convention(thin) (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> ()) -> () // CHECK: [[CF:%.*]] = partial_apply [callee_guaranteed] [[THUNK_REF]]([[TH_F]]) : $@convention(thin) (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> ()) -> () // CHECK: [[CF2:%.*]] = convert_function [[CF]] - // CHECK: assign_by_wrapper [[CF2]] - // CHECK-SAME: to {{%.*}} : $*WrapGod<(Int) -> ()> + // CHECK: assign_or_init #ReabstractedSetter.value, self {{%.*}} : $*ReabstractedSetter, value [[CF2]] self.value = { x in } } } @@ -91,10 +90,9 @@ public class SomeObject {} extension ObjectifiedSetter where T == SomeObject { init() { - // CHECK-LABEL: sil hidden [ossa] @$s27resilient_assign_by_wrapper17ObjectifiedSetterV5valuexvs : $@convention(method) (@owned T, @inout ObjectifiedSetter) -> () { + // CHECK-LABEL: sil hidden [ossa] @$s24resilient_assign_or_init17ObjectifiedSetterV5valuexvs : $@convention(method) (@owned T, @inout ObjectifiedSetter) -> () { // CHECK: [[OBJ:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick SomeObject.Type) -> @owned SomeObject - // CHECK: [[STORAGE:%.*]] = struct_element_addr {{%.*}} : $*ObjectifiedSetter, #ObjectifiedSetter._value - // CHECK: assign_by_wrapper [[OBJ]] : $SomeObject to [[STORAGE]] : $*WrapGod + // CHECK: assign_or_init #ObjectifiedSetter.value, self {{%.*}} : $*ObjectifiedSetter, value [[OBJ]] // CHECK-SAME: init {{%.*}} : $@callee_guaranteed (@owned SomeObject) -> @out WrapGod // CHECK-SAME: set {{%.*}} : $@noescape @callee_guaranteed (@owned SomeObject) -> () self.value = SomeObject() diff --git a/test/SILOptimizer/raw_sil_inst_lowering.sil b/test/SILOptimizer/raw_sil_inst_lowering.sil index 04818feebabf2..4c249b88d13d5 100644 --- a/test/SILOptimizer/raw_sil_inst_lowering.sil +++ b/test/SILOptimizer/raw_sil_inst_lowering.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -raw-sil-inst-lowering | %FileCheck %s +// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all -definite-init -raw-sil-inst-lowering %s | %FileCheck %s sil_stage raw @@ -79,139 +79,303 @@ bb0(%0 : $*SomeClass, %1 : @owned $SomeClass): } @propertyWrapper struct Wrapper { - @_hasStorage var wrappedValue: T + @_hasStorage var wrappedValue: T { get set } init(wrappedValue initialValue: T) } struct RefStruct { - @_hasStorage var _wrapped: Wrapper + @_hasStorage var _wrapped: Wrapper { get set } + var wrapped: SomeClass { + @storageRestrictions(initializes: _wrapped) init + get + set + } + init(inputVal: SomeClass) } - -sil @init_closure : $@convention(thin) (@owned SomeClass) -> @owned Wrapper -sil @init_closure_indirect : $@convention(thin) (@owned SomeClass) -> @out Wrapper -sil @set_closure : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - -// CHECK-LABEL: sil [ossa] @assign_by_wrapper_initialization_direct -// CHECK: [[E:%[0-9]+]] = struct_element_addr -// CHECK: [[C:%[0-9]+]] = function_ref @init_closure -// CHECK: [[P:%[0-9]+]] = partial_apply [callee_guaranteed] [[C]]() -// CHECK-NOT: partial_apply -// CHECK: [[R:%[0-9]+]] = apply [[P]](%0) -// CHECK: store [[R]] to [init] [[E]] -// CHECK: } // end sil function 'assign_by_wrapper_initialization_direct' -sil [ossa] @assign_by_wrapper_initialization_direct : $@convention(method) (@owned SomeClass) -> @owned RefStruct { -bb0(%0 : @owned $SomeClass): - %1 = alloc_stack $RefStruct - %7 = struct_element_addr %1 : $*RefStruct, #RefStruct._wrapped - %8 = function_ref @init_closure : $@convention(thin) (@owned SomeClass) -> @owned Wrapper - %9 = partial_apply [callee_guaranteed] %8() : $@convention(thin) (@owned SomeClass) -> @owned Wrapper - %10 = function_ref @set_closure : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - %11 = partial_apply [callee_guaranteed] %10(%1) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - assign_by_wrapper %0 : $SomeClass to [init] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () - destroy_value %11 : $@callee_guaranteed (@owned SomeClass) -> () - destroy_value %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper - %16 = load [take] %1 : $*RefStruct - dealloc_stack %1 : $*RefStruct - return %16 : $RefStruct +// Wrapper.init(wrappedValue:) +sil hidden [ossa] @$s4main7WrapperV12wrappedValueACyxGx_tcfC : $@convention(method) (@in T, @thin Wrapper.Type) -> @out Wrapper { +bb0(%0 : $*Wrapper, %1 : $*T, %2 : $@thin Wrapper.Type): + %3 = alloc_box $<τ_0_0> { var Wrapper<τ_0_0> } , var, name "self" + %4 = mark_uninitialized [rootself] %3 : $<τ_0_0> { var Wrapper<τ_0_0> } + %5 = begin_borrow [lexical] [var_decl] %4 : $<τ_0_0> { var Wrapper<τ_0_0> } + %6 = project_box %5 : $<τ_0_0> { var Wrapper<τ_0_0> } , 0 + debug_value %1 : $*T, let, name "initialValue", argno 1, expr op_deref + %8 = alloc_stack $T + copy_addr %1 to [init] %8 : $*T + %10 = begin_access [modify] [unknown] %6 : $*Wrapper + %11 = struct_element_addr %10 : $*Wrapper, #Wrapper.wrappedValue + copy_addr [take] %8 to %11 : $*T + end_access %10 : $*Wrapper + dealloc_stack %8 : $*T + copy_addr %6 to [init] %0 : $*Wrapper + destroy_addr %1 : $*T + end_borrow %5 : $<τ_0_0> { var Wrapper<τ_0_0> } + destroy_value %4 : $<τ_0_0> { var Wrapper<τ_0_0> } + %19 = tuple () + return %19 : $() } -// CHECK-LABEL: sil [ossa] @assign_by_wrapper_initialization_indirect -// CHECK: [[E:%[0-9]+]] = struct_element_addr -// CHECK: [[C:%[0-9]+]] = function_ref @init_closure_indirect -// CHECK: [[P:%[0-9]+]] = partial_apply [callee_guaranteed] [[C]]() -// CHECK-NOT: partial_apply -// CHECK-NOT: destroy_addr -// CHECK: [[R:%[0-9]+]] = apply [[P]]([[E]], %0) -// CHECK: } // end sil function 'assign_by_wrapper_initialization_indirect' -sil [ossa] @assign_by_wrapper_initialization_indirect : $@convention(method) (@owned SomeClass) -> @owned RefStruct { -bb0(%0 : @owned $SomeClass): - %1 = alloc_stack $RefStruct - %7 = struct_element_addr %1 : $*RefStruct, #RefStruct._wrapped - %8 = function_ref @init_closure_indirect : $@convention(thin) (@owned SomeClass) -> @out Wrapper - %9 = partial_apply [callee_guaranteed] %8() : $@convention(thin) (@owned SomeClass) -> @out Wrapper - %10 = function_ref @set_closure : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - %11 = partial_apply [callee_guaranteed] %10(%1) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - assign_by_wrapper %0 : $SomeClass to [init] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () - destroy_value %11 : $@callee_guaranteed (@owned SomeClass) -> () - destroy_value %9 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper - %16 = load [take] %1 : $*RefStruct - dealloc_stack %1 : $*RefStruct - return %16 : $RefStruct +// property wrapped field init accessor of RefStruct.wrapped +sil hidden [ossa] @$s4main9RefStructV7wrappedAA9SomeClassCvpfF : $@convention(thin) (@owned SomeClass, @thin RefStruct.Type) -> @out Wrapper { +bb0(%0 : $*Wrapper, %1 : @owned $SomeClass, %2 : $@thin RefStruct.Type): + %3 = mark_uninitialized [out] %0 : $*Wrapper + debug_value %1 : $SomeClass, let, name "$input_value", argno 1 + %5 = metatype $@thin Wrapper.Type + %6 = copy_value %1 : $SomeClass + %7 = alloc_stack $SomeClass + store %6 to [init] %7 : $*SomeClass + %9 = function_ref @$s4main7WrapperV12wrappedValueACyxGx_tcfC : $@convention(method) <τ_0_0> (@in τ_0_0, @thin Wrapper<τ_0_0>.Type) -> @out Wrapper<τ_0_0> // user: %10 + %10 = apply %9(%3, %7, %5) : $@convention(method) <τ_0_0> (@in τ_0_0, @thin Wrapper<τ_0_0>.Type) -> @out Wrapper<τ_0_0> + dealloc_stack %7 : $*SomeClass + destroy_value %1 : $SomeClass + %13 = tuple () + return %13 : $() +} + +// RefStruct.wrapped.setter +sil hidden [ossa] @$s4main9RefStructV7wrappedAA9SomeClassCvs : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () { +bb0(%0 : @owned $SomeClass, %1 : $*RefStruct): + debug_value %0 : $SomeClass, let, name "value", argno 1 + debug_value %1 : $*RefStruct, var, name "self", argno 2, expr op_deref + %4 = begin_borrow %0 : $SomeClass + %5 = copy_value %4 : $SomeClass + %6 = begin_access [modify] [unknown] %1 : $*RefStruct + %7 = struct_element_addr %6 : $*RefStruct, #RefStruct._wrapped + %8 = struct_element_addr %7 : $*Wrapper, #Wrapper.wrappedValue + assign %5 to %8 : $*SomeClass + end_access %6 : $*RefStruct + end_borrow %4 : $SomeClass + destroy_value %0 : $SomeClass + %13 = tuple () + return %13 : $() +} + +// property wrapped field init accessor of GenericRefStruct.wrapped +// Isolation: unspecified +sil hidden [ossa] @$s4main16GenericRefStructV7wrappedxvpfF : $@convention(thin) (@in T, @thin GenericRefStruct.Type) -> @out Wrapper { +bb0(%0 : $*Wrapper, %1 : $*T, %2 : $@thin GenericRefStruct.Type): + %3 = mark_uninitialized [out] %0 : $*Wrapper // user: %9 + debug_value %1 : $*T, let, name "$input_value", argno 1, expr op_deref // id: %4 + %5 = metatype $@thin Wrapper.Type // user: %9 + %6 = alloc_stack $T // users: %10, %9, %7 + copy_addr %1 to [init] %6 : $*T // id: %7 + // function_ref Wrapper.init(wrappedValue:) + %8 = function_ref @$s4main7WrapperV12wrappedValueACyxGx_tcfC : $@convention(method) <τ_0_0> (@in τ_0_0, @thin Wrapper<τ_0_0>.Type) -> @out Wrapper<τ_0_0> // user: %9 + %9 = apply %8(%3, %6, %5) : $@convention(method) <τ_0_0> (@in τ_0_0, @thin Wrapper<τ_0_0>.Type) -> @out Wrapper<τ_0_0> + dealloc_stack %6 : $*T // id: %10 + destroy_addr %1 : $*T // id: %11 + %12 = tuple () // user: %13 + return %12 : $() // id: %13 +} // end sil function '$s4main16GenericRefStructV7wrappedxvpfF' + +// GenericRefStruct.wrapped.setter +// Isolation: unspecified +sil hidden [ossa] @$s4main16GenericRefStructV7wrappedxvs : $@convention(method) (@in T, @inout GenericRefStruct) -> () { +// %0 "value" // users: %12, %5, %2 +// %1 "self" // users: %6, %3 +bb0(%0 : $*T, %1 : $*GenericRefStruct): + debug_value %0 : $*T, let, name "value", argno 1, expr op_deref // id: %2 + debug_value %1 : $*GenericRefStruct, var, name "self", argno 2, expr op_deref // id: %3 + %4 = alloc_stack $T // users: %11, %9, %5 + copy_addr %0 to [init] %4 : $*T // id: %5 + %6 = begin_access [modify] [unknown] %1 : $*GenericRefStruct // users: %10, %7 + %7 = struct_element_addr %6 : $*GenericRefStruct, #GenericRefStruct._wrapped // user: %8 + %8 = struct_element_addr %7 : $*Wrapper, #Wrapper.wrappedValue // user: %9 + copy_addr [take] %4 to %8 : $*T // id: %9 + end_access %6 : $*GenericRefStruct // id: %10 + dealloc_stack %4 : $*T // id: %11 + destroy_addr %0 : $*T // id: %12 + %13 = tuple () // user: %14 + return %13 : $() // id: %14 } -// CHECK-LABEL: sil [ossa] @assign_by_wrapper_assign_direct -// CHECK: [[E:%[0-9]+]] = struct_element_addr -// CHECK: [[C:%[0-9]+]] = function_ref @init_closure -// CHECK: [[P:%[0-9]+]] = partial_apply [callee_guaranteed] [[C]]() -// CHECK-NOT: partial_apply -// CHECK: [[R:%[0-9]+]] = apply [[P]](%0) -// CHECK: store [[R]] to [assign] [[E]] -// CHECK: } // end sil function 'assign_by_wrapper_assign_direct' -sil [ossa] @assign_by_wrapper_assign_direct : $@convention(method) (@owned SomeClass, @owned Wrapper) -> @owned RefStruct { -bb0(%0 : @owned $SomeClass, %1 : @owned $Wrapper): - %2 = alloc_stack $RefStruct - %7 = struct_element_addr %2 : $*RefStruct, #RefStruct._wrapped - store %1 to [init] %7 : $*Wrapper - %8 = function_ref @init_closure : $@convention(thin) (@owned SomeClass) -> @owned Wrapper - %9 = partial_apply [callee_guaranteed] %8() : $@convention(thin) (@owned SomeClass) -> @owned Wrapper - %10 = function_ref @set_closure : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - %11 = partial_apply [callee_guaranteed] %10(%2) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - assign_by_wrapper %0 : $SomeClass to [assign] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () - destroy_value %11 : $@callee_guaranteed (@owned SomeClass) -> () - destroy_value %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper - %16 = load [take] %2 : $*RefStruct - dealloc_stack %2 : $*RefStruct - return %16 : $RefStruct +// ====== CASE 01 - Init Indirect ========= +// [Init] mode chosen because backing storage ._wrapped is uninitialized +// The initializer closure (an init accessor thunk) has an indirect return type +// Therefore, ._wrapped gets initialized indirectly + +// RefStruct.init(inputVal:) +// CHECK-LABEL: sil hidden [ossa] @$assign_or_init_init_indirect +// CHECK: [[INITIAL_VAL:%.*]] = copy_value {{%.*}} : $SomeClass +// CHECK: [[SELF_REF:%.*]] = begin_access [modify] [unknown] {{%.*}} : $*RefStruct +// CHECK: [[INIT_REF:%.*]] = function_ref @$s4main9RefStructV7wrappedAA9SomeClassCvpfF : $@convention(thin) (@owned SomeClass, @thin RefStruct.Type) -> @out Wrapper +// CHECK: [[INIT_PAI:%.*]] = partial_apply [callee_guaranteed] [[INIT_REF]]({{%.*}}) +// CHECK: [[FIELD_REF:%.*]] = struct_element_addr [[SELF_REF]] : $*RefStruct, #RefStruct._wrapped +// CHECK-NEXT: {{%.*}} = apply [[INIT_PAI]]([[FIELD_REF]], [[INITIAL_VAL]]) : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper +sil hidden [ossa] @$assign_or_init_init_indirect : $@convention(method) (@owned SomeClass, @thin RefStruct.Type) -> @owned RefStruct { +bb0(%0 : @owned $SomeClass, %1 : $@thin RefStruct.Type): + %2 = alloc_box ${ var RefStruct }, var, name "self" + %3 = mark_uninitialized [rootself] %2 : ${ var RefStruct } + %4 = begin_borrow [lexical] [var_decl] %3 : ${ var RefStruct } + %5 = project_box %4 : ${ var RefStruct }, 0 + debug_value %0 : $SomeClass, let, name "inputVal", argno 1 + %7 = begin_borrow %0 : $SomeClass + %8 = copy_value %7 : $SomeClass + %9 = begin_access [modify] [unknown] %5 : $*RefStruct + %10 = function_ref @$s4main9RefStructV7wrappedAA9SomeClassCvpfF : $@convention(thin) (@owned SomeClass, @thin RefStruct.Type) -> @out Wrapper // user: %12 + %11 = metatype $@thin RefStruct.Type + %12 = partial_apply [callee_guaranteed] %10(%11) : $@convention(thin) (@owned SomeClass, @thin RefStruct.Type) -> @out Wrapper // users: %18, %15 + %13 = function_ref @$s4main9RefStructV7wrappedAA9SomeClassCvs : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () // user: %14 + %14 = partial_apply [callee_guaranteed] [on_stack] %13(%9) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () // users: %17, %15 + assign_or_init #RefStruct.wrapped, + self %9 : $*RefStruct, + value %8 : $SomeClass, + init %12 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper, + set %14 : $@noescape @callee_guaranteed (@owned SomeClass) -> () // id: %15 + end_access %9 : $*RefStruct + destroy_value %14 : $@noescape @callee_guaranteed (@owned SomeClass) -> () + destroy_value %12 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper + end_borrow %7 : $SomeClass + %20 = load [copy] %5 : $*RefStruct + destroy_value %0 : $SomeClass + end_borrow %4 : ${ var RefStruct } + destroy_value %3 : ${ var RefStruct } + return %20 : $RefStruct } -// CHECK-LABEL: sil [ossa] @assign_by_wrapper_assign_indirect -// CHECK: [[E:%[0-9]+]] = struct_element_addr -// CHECK: [[C:%[0-9]+]] = function_ref @init_closure_indirect -// CHECK: [[P:%[0-9]+]] = partial_apply [callee_guaranteed] [[C]]() -// CHECK-NOT: partial_apply -// CHECK: destroy_addr [[E]] -// CHECK: [[R:%[0-9]+]] = apply [[P]]([[E]], %0) -// CHECK: } // end sil function 'assign_by_wrapper_assign_indirect' -sil [ossa] @assign_by_wrapper_assign_indirect : $@convention(method) (@owned SomeClass, @owned Wrapper) -> @owned RefStruct { -bb0(%0 : @owned $SomeClass, %1 : @owned $Wrapper): - %2 = alloc_stack $RefStruct - %7 = struct_element_addr %2 : $*RefStruct, #RefStruct._wrapped - store %1 to [init] %7 : $*Wrapper - %8 = function_ref @init_closure_indirect : $@convention(thin) (@owned SomeClass) -> @out Wrapper - %9 = partial_apply [callee_guaranteed] %8() : $@convention(thin) (@owned SomeClass) -> @out Wrapper - %10 = function_ref @set_closure : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - %11 = partial_apply [callee_guaranteed] %10(%2) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - assign_by_wrapper %0 : $SomeClass to [assign] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () - destroy_value %11 : $@callee_guaranteed (@owned SomeClass) -> () - destroy_value %9 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper - %16 = load [take] %2 : $*RefStruct - dealloc_stack %2 : $*RefStruct - return %16 : $RefStruct +// ====== CASE 02 - Assign Direct ============ +// RefStruct is already initialized, so DI selects [assign] mode +// Because the property type (SomeClass) is loadable, the setter can consume the value directly +// This test verifies that the setter path is taken + +// RefStruct.init(inputVal:) +// CHECK-LABEL: sil hidden [ossa] @$assign_or_init_assign_direct +// CHECK: [[FIELD_REF:%.*]] = struct_element_addr {{.*}} : $*RefStruct, #RefStruct._wrapped +// CHECK-NEXT: {{.*}} = apply [[INIT_PAI]]([[FIELD_REF]], [[INITIAL_VAL]]) : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper +// CHECK: [[INITIAL_VAL2:%.*]] = copy_value {{%.*}} : $SomeClass +// CHECK: [[SELF_REF:%.*]] = begin_access [modify] [unknown] {{%.*}} : $*RefStruct +// CHECK: [[SETTER_REF:%.*]] = function_ref @$s4main9RefStructV7wrappedAA9SomeClassCvs : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () +// CHECK-NEXT: [[SETTER_PAI:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[SETTER_REF]]([[SELF_REF]]) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () +// CHECK-NEXT: {{.*}} = apply [[SETTER_PAI]]([[INITIAL_VAL2]]) : $@noescape @callee_guaranteed (@owned SomeClass) -> () +sil hidden [ossa] @$assign_or_init_assign_direct : $@convention(method) (@owned SomeClass, @thin RefStruct.Type) -> @owned RefStruct { +bb0(%0 : @owned $SomeClass, %1 : $@thin RefStruct.Type): + %2 = alloc_box ${ var RefStruct }, var, name "self" // user: %3 + %3 = mark_uninitialized [rootself] %2 : ${ var RefStruct } // users: %36, %4 + %4 = begin_borrow [lexical] [var_decl] %3 : ${ var RefStruct } // users: %35, %5 + %5 = project_box %4 : ${ var RefStruct }, 0 // users: %33, %22, %9 + debug_value %0 : $SomeClass, let, name "inputVal", argno 1 // id: %6 + %7 = begin_borrow %0 : $SomeClass // users: %19, %8 + %8 = copy_value %7 : $SomeClass // user: %15 + %9 = begin_access [modify] [unknown] %5 : $*RefStruct // users: %16, %15, %14 + + // Initialization + %10 = function_ref @$s4main9RefStructV7wrappedAA9SomeClassCvpfF : $@convention(thin) (@owned SomeClass, @thin RefStruct.Type) -> @out Wrapper // user: %12 + %11 = metatype $@thin RefStruct.Type // user: %12 + %12 = partial_apply [callee_guaranteed] %10(%11) : $@convention(thin) (@owned SomeClass, @thin RefStruct.Type) -> @out Wrapper // users: %18, %15 + %13 = function_ref @$s4main9RefStructV7wrappedAA9SomeClassCvs : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () // user: %14 + %14 = partial_apply [callee_guaranteed] [on_stack] %13(%9) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () // users: %17, %15 + assign_or_init #RefStruct.wrapped, + self %9 : $*RefStruct, + value %8 : $SomeClass, + init %12 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper, + set %14 : $@noescape @callee_guaranteed (@owned SomeClass) -> () // id: %15 + end_access %9 : $*RefStruct // id: %16 + destroy_value %14 : $@noescape @callee_guaranteed (@owned SomeClass) -> () // id: %17 + destroy_value %12 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper // id: %18 + end_borrow %7 : $SomeClass // id: %19 + %20 = begin_borrow %0 : $SomeClass // users: %32, %21 + %21 = copy_value %20 : $SomeClass // user: %28 + %22 = begin_access [modify] [unknown] %5 : $*RefStruct // users: %29, %28, %27 + + // Assignment + %23 = function_ref @$s4main9RefStructV7wrappedAA9SomeClassCvpfF : $@convention(thin) (@owned SomeClass, @thin RefStruct.Type) -> @out Wrapper // user: %25 + %24 = metatype $@thin RefStruct.Type // user: %25 + %25 = partial_apply [callee_guaranteed] %23(%24) : $@convention(thin) (@owned SomeClass, @thin RefStruct.Type) -> @out Wrapper // users: %31, %28 + %26 = function_ref @$s4main9RefStructV7wrappedAA9SomeClassCvs : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () // user: %27 + %27 = partial_apply [callee_guaranteed] [on_stack] %26(%22) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () // users: %30, %28 + assign_or_init #RefStruct.wrapped, + self %22 : $*RefStruct, + value %21 : $SomeClass, + init %25 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper, + set %27 : $@noescape @callee_guaranteed (@owned SomeClass) -> () + end_access %22 : $*RefStruct // id: %29 + destroy_value %27 : $@noescape @callee_guaranteed (@owned SomeClass) -> () + destroy_value %25 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper + end_borrow %20 : $SomeClass + %33 = load [copy] %5 : $*RefStruct + destroy_value %0 : $SomeClass + end_borrow %4 : ${ var RefStruct } + destroy_value %3 : ${ var RefStruct } + return %33 : $RefStruct } -// CHECK-LABEL: sil [ossa] @assign_by_wrapper_assign_wrapped_value -// CHECK: [[A:%[0-9]+]] = alloc_stack $RefStruct -// CHECK: [[E:%[0-9]+]] = struct_element_addr -// CHECK-NOT: partial_apply -// CHECK: [[C:%[0-9]+]] = function_ref @set_closure -// CHECK: [[P:%[0-9]+]] = partial_apply [callee_guaranteed] [[C]]([[A]]) -// CHECK: apply [[P]](%0) -// CHECK: } // end sil function 'assign_by_wrapper_assign_wrapped_value' -sil [ossa] @assign_by_wrapper_assign_wrapped_value : $@convention(method) (@owned SomeClass, @owned RefStruct) -> @owned RefStruct { -bb0(%0 : @owned $SomeClass, %1 : @owned $RefStruct): - %2 = alloc_stack $RefStruct - store %1 to [init] %2 : $*RefStruct - %7 = struct_element_addr %2 : $*RefStruct, #RefStruct._wrapped - %8 = function_ref @init_closure : $@convention(thin) (@owned SomeClass) -> @owned Wrapper - %9 = partial_apply [callee_guaranteed] %8() : $@convention(thin) (@owned SomeClass) -> @owned Wrapper - %10 = function_ref @set_closure : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - %11 = partial_apply [callee_guaranteed] %10(%2) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - assign_by_wrapper %0 : $SomeClass to [assign_wrapped_value] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () - destroy_value %11 : $@callee_guaranteed (@owned SomeClass) -> () - destroy_value %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper - %16 = load [take] %2 : $*RefStruct - dealloc_stack %2 : $*RefStruct - return %16 : $RefStruct +// ====== CASE 03 - Assign Indirect ========== +// RefStruct is already initialized, so DI selects [assign] mode +// The wrapped property type is address-only (e.g. a generic T), so values +// are passed indirectly instead of by value +// This test verifies that the setter uses the indirect assignment path + +struct GenericRefStruct { + @_hasStorage var _wrapped: Wrapper { get set } + var wrapped: T { + @storageRestrictions(initializes: _wrapped) init + get + set + } + init(inputVal: T) } +// GenericRefStruct.init(inputVal:) +// CHECK-LABEL: sil hidden [ossa] @$assign_or_init_assign_indirect +// CHECK: [[TADDR1:%.*]] = alloc_stack $T +// CHECK: copy_addr {{%.*}} to [init] [[TADDR1]] : $*T +// CHECK: [[INIT_REF:%.*]] = function_ref @$s4main16GenericRefStructV7wrappedxvpfF : $@convention(thin) <{{.*}}> (@in {{.*}}, @thin GenericRefStruct<{{.*}}>.Type) -> @out Wrapper<{{.*}}> +// CHECK: [[META1:%.*]] = metatype $@thin GenericRefStruct<{{.*}}>.Type +// CHECK: [[INIT_PAI:%.*]] = partial_apply [callee_guaranteed] [[INIT_REF]]<{{.*}}>([[META1]]) +// CHECK: {{%.*}} = struct_element_addr %{{.*}} : $*GenericRefStruct<{{.*}}>, #GenericRefStruct._wrapped +// CHECK-NEXT: {{%.*}} = apply [[INIT_PAI]](%{{.*}}, [[TADDR1]]) : $@callee_guaranteed (@in {{.*}}) -> @out Wrapper<{{.*}}> +// CHECK: [[TADDR2:%.*]] = alloc_stack $T +// CHECK: copy_addr {{%.*}} to [init] [[TADDR2]] : $*T +// CHECK: [[SELF2:%.*]] = begin_access [modify] {{\[.*\]}} %{{.*}} : $*GenericRefStruct<{{.*}}> +// CHECK: [[SET_REF:%.*]] = function_ref @$s4main16GenericRefStructV7wrappedxvs : $@convention(method) <{{.*}}> (@in {{.*}}, @inout GenericRefStruct<{{.*}}>) -> () +// CHECK: [[SET_PAI:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[SET_REF]]<{{.*}}>([[SELF2]]) +// CHECK-NEXT: {{%.*}} = apply [[SET_PAI]]([[TADDR2]]) : $@noescape @callee_guaranteed (@in {{.*}}) -> () +sil hidden [ossa] @$assign_or_init_assign_indirect : $@convention(method) (@in T, @thin GenericRefStruct.Type) -> @out GenericRefStruct { +bb0(%0 : $*GenericRefStruct, %1 : $*T, %2 : $@thin GenericRefStruct.Type): + %3 = alloc_box $<τ_0_0> { var GenericRefStruct<τ_0_0> } , var, name "self" // user: %4 + %4 = mark_uninitialized [rootself] %3 : $<τ_0_0> { var GenericRefStruct<τ_0_0> } // users: %37, %5 + %5 = begin_borrow [lexical] [var_decl] %4 : $<τ_0_0> { var GenericRefStruct<τ_0_0> } // users: %36, %6 + %6 = project_box %5 : $<τ_0_0> { var GenericRefStruct<τ_0_0> } , 0 // users: %34, %23, %10 + debug_value %1 : $*T, let, name "inputVal", argno 1, expr op_deref // id: %7 + %8 = alloc_stack $T // users: %20, %16, %9 + copy_addr %1 to [init] %8 : $*T // id: %9 + %10 = begin_access [modify] [unknown] %6 : $*GenericRefStruct // users: %17, %16, %15 + + // Initialization + %11 = function_ref @$s4main16GenericRefStructV7wrappedxvpfF : $@convention(thin) <τ_0_0> (@in τ_0_0, @thin GenericRefStruct<τ_0_0>.Type) -> @out Wrapper<τ_0_0> // user: %13 + %12 = metatype $@thin GenericRefStruct.Type // user: %13 + %13 = partial_apply [callee_guaranteed] %11(%12) : $@convention(thin) <τ_0_0> (@in τ_0_0, @thin GenericRefStruct<τ_0_0>.Type) -> @out Wrapper<τ_0_0> // users: %19, %16 + %14 = function_ref @$s4main16GenericRefStructV7wrappedxvs : $@convention(method) <τ_0_0> (@in τ_0_0, @inout GenericRefStruct<τ_0_0>) -> () // user: %15 + %15 = partial_apply [callee_guaranteed] [on_stack] %14(%10) : $@convention(method) <τ_0_0> (@in τ_0_0, @inout GenericRefStruct<τ_0_0>) -> () // users: %18, %16 + assign_or_init #GenericRefStruct.wrapped, + self %10 : $*GenericRefStruct, + value %8 : $*T, + init %13 : $@callee_guaranteed (@in T) -> @out Wrapper, + set %15 : $@noescape @callee_guaranteed (@in T) -> () + end_access %10 : $*GenericRefStruct // id: %17 + destroy_value %15 : $@noescape @callee_guaranteed (@in T) -> () // id: %18 + destroy_value %13 : $@callee_guaranteed (@in T) -> @out Wrapper // id: %19 + dealloc_stack %8 : $*T // id: %20 + %21 = alloc_stack $T // users: %33, %29, %22 + copy_addr %1 to [init] %21 : $*T // id: %22 + %23 = begin_access [modify] [unknown] %6 : $*GenericRefStruct // users: %30, %29, %28 + + // Assignment + %24 = function_ref @$s4main16GenericRefStructV7wrappedxvpfF : $@convention(thin) <τ_0_0> (@in τ_0_0, @thin GenericRefStruct<τ_0_0>.Type) -> @out Wrapper<τ_0_0> // user: %26 + %25 = metatype $@thin GenericRefStruct.Type // user: %26 + %26 = partial_apply [callee_guaranteed] %24(%25) : $@convention(thin) <τ_0_0> (@in τ_0_0, @thin GenericRefStruct<τ_0_0>.Type) -> @out Wrapper<τ_0_0> // users: %32, %29 + %27 = function_ref @$s4main16GenericRefStructV7wrappedxvs : $@convention(method) <τ_0_0> (@in τ_0_0, @inout GenericRefStruct<τ_0_0>) -> () // user: %28 + %28 = partial_apply [callee_guaranteed] [on_stack] %27(%23) : $@convention(method) <τ_0_0> (@in τ_0_0, @inout GenericRefStruct<τ_0_0>) -> () // users: %31, %29 + assign_or_init #GenericRefStruct.wrapped, self %23 : $*GenericRefStruct, value %21 : $*T, init %26 : $@callee_guaranteed (@in T) -> @out Wrapper, set %28 : $@noescape @callee_guaranteed (@in T) -> () // id: %29 + end_access %23 : $*GenericRefStruct // id: %30 + destroy_value %28 : $@noescape @callee_guaranteed (@in T) -> () // id: %31 + destroy_value %26 : $@callee_guaranteed (@in T) -> @out Wrapper // id: %32 + dealloc_stack %21 : $*T // id: %33 + copy_addr %6 to [init] %0 : $*GenericRefStruct // id: %34 + destroy_addr %1 : $*T // id: %35 + end_borrow %5 : $<τ_0_0> { var GenericRefStruct<τ_0_0> } // id: %36 + destroy_value %4 : $<τ_0_0> { var GenericRefStruct<τ_0_0> } // id: %37 + %38 = tuple () // user: %39 + return %38 : $() // id: %39 +}