From 988a3dcf2571e0020c6f38b30389859bab1887ee Mon Sep 17 00:00:00 2001 From: JanBaig Date: Tue, 29 Jul 2025 21:16:11 -0400 Subject: [PATCH 01/39] [SIL] Introduce new SILDeclRef --- include/swift/AST/ASTMangler.h | 3 +++ include/swift/Demangling/DemangleNodes.def | 1 + include/swift/SIL/SILDeclRef.h | 4 ++++ lib/Demangling/Demangler.cpp | 5 +++++ lib/Demangling/NodePrinter.cpp | 6 ++++++ lib/Demangling/OldRemangler.cpp | 5 +++++ lib/Demangling/Remangler.cpp | 10 ++++++++++ lib/IRGen/GenObjC.cpp | 1 + lib/SIL/IR/SILDeclRef.cpp | 8 ++++++++ lib/SIL/IR/SILPrinter.cpp | 3 +++ lib/SIL/Parser/ParseSIL.cpp | 3 +++ lib/SILGen/SILGen.cpp | 4 ++++ lib/SILGen/SILGenFunction.cpp | 2 ++ 13 files changed, 55 insertions(+) diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 8f292e19ab20b..0d3db0635bf9c 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/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index d19a66d34659f..2869967206a0b 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 initializer + PropertyWrappedFieldInitAccessor }; /// Represents the variants of a back deployable function. diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 88837089d4f2c..37d214f5f4f32 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -2135,6 +2135,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; @@ -4129,6 +4130,10 @@ NodePointer Demangler::demangleFunctionEntity() { case 'P': Args = None; Kind = Node::Kind::PropertyWrapperBackingInitializer; + break; + case 'F': + Args = None; + Kind = Node::Kind::PropertyWrappedFieldInitAccessor; break; case 'W': Args = None; diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 476425c5e3a92..9f41983694758 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,6 +3541,7 @@ 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::PropertyWrappedFieldInitAccessor || Entity->getKind() == Node::Kind::PropertyWrapperInitFromProjectedValue) { Printer << " of "; } else { diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 58a55afb69b35..d41c3f3117257 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..00bdc3076426f 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 0dd45be382692..fca723504334e 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/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 07cd8f84365b5..7e09c1bad0a38 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -303,6 +303,8 @@ bool SILDeclRef::hasUserWrittenCode() const { // Non-implicit decls generally have user-written code. if (!isImplicit()) { switch (kind) { + case Kind::PropertyWrappedFieldInitAccessor: + llvm_unreachable("SILDeclRef unimplemented"); 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 +388,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 +524,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 @@ -1378,6 +1382,9 @@ 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); @@ -1733,6 +1740,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/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 4ff43ed0c14ec..46cf30f48721a 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; diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 890f0e7ee6e49..64e94cf180e3f 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; diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 148a90ffa3f2f..569045b1bf912 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1142,6 +1142,10 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { break; } + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: { + llvm_unreachable("SILDeclRef not handled"); + } + case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: { auto *var = cast(constant.getDecl()); diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 9637fab88d835..5cdd16b0f0073 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: From 0dbbc8ece0d7398dffd3a9e1b1f98273ddbe1bb9 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Thu, 31 Jul 2025 22:18:00 -0400 Subject: [PATCH 02/39] [SIL] SILDeclRef Mangling Added --- lib/AST/ASTMangler.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 0fb823b4c1433..004ad98082691 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); From b2fad4bc151658ee77ca7e6cb5a8d5ace8fe0137 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 8 Aug 2025 18:12:55 -0400 Subject: [PATCH 03/39] [TypeLowering] Implement the interface type for the `SILDeclRef` --- include/swift/SIL/SILDeclRef.h | 2 +- lib/SIL/IR/TypeLowering.cpp | 38 ++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 2869967206a0b..ae4b6701f1528 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -169,7 +169,7 @@ struct SILDeclRef { AsyncEntryPoint, /// An init accessor that calls a propery wrapped field's - /// backing initializer + /// backing storage initializer PropertyWrappedFieldInitAccessor }; diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index 032c0424c4d11..4d116cb86a7a7 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -3964,6 +3964,40 @@ static CanAnyFunctionType getPropertyWrapperBackingInitializerInterfaceType( resultType, info); } +static CanAnyFunctionType getPropertyWrappedFieldInitAccessorInterfaceType(TypeConverter &TC, + SILDeclRef wrappedPropertyRef) { + // TODO: May need to handle projected value input type + 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, @@ -4207,6 +4241,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); @@ -4249,6 +4285,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 @@ -4391,6 +4428,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: From 2b648eaf43101a06fe9e4718ead2a70d66156b11 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 8 Aug 2025 18:39:43 -0400 Subject: [PATCH 04/39] [SIL] Mark `Self` operand as optional for `AssignOrInitInst` --- include/swift/SIL/SILBuilder.h | 2 +- include/swift/SIL/SILCloner.h | 6 ++++-- include/swift/SIL/SILInstruction.h | 15 ++++++++++++--- lib/SIL/IR/SILInstructions.cpp | 7 +++++-- lib/SIL/IR/SILPrinter.cpp | 11 ++++++++--- .../Mandatory/DIMemoryUseCollector.cpp | 12 ++++++++---- lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp | 2 +- 7 files changed, 39 insertions(+), 16 deletions(-) diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 9603c48bba284..4288bff74a8c1 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -987,7 +987,7 @@ class SILBuilder { AssignOrInitInst *createAssignOrInit(SILLocation Loc, VarDecl *Property, - SILValue Self, + std::optional Self, SILValue Src, SILValue Initializer, SILValue Setter, diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index fb6b0506a0e14..846c02fc2642f 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1539,11 +1539,14 @@ void SILCloner::visitAssignByWrapperInst(AssignByWrapperInst *Inst) { template void SILCloner::visitAssignOrInitInst(AssignOrInitInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + auto selfArg = Inst->getOptionalSelfOperand(); + if (selfArg) + selfArg = getOpValue(*selfArg); recordClonedInstruction( Inst, getBuilder().createAssignOrInit( getOpLocation(Inst->getLoc()), Inst->getProperty(), - getOpValue(Inst->getSelf()), + selfArg, getOpValue(Inst->getSrc()), getOpValue(Inst->getInitializer()), getOpValue(Inst->getSetter()), Inst->getMode())); @@ -3539,7 +3542,6 @@ visitSwitchEnumAddrInst(SwitchEnumAddrInst *Inst) { DefaultBB, CaseBBs)); } - template void diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 5cc1b87bf63c7..960929e69f9d0 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -5260,7 +5260,7 @@ class AssignByWrapperInst } }; -/// 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. /// @@ -5284,6 +5284,8 @@ class AssignOrInitInst /// lowering to emit destroys. llvm::BitVector Assignments; + bool HasSelfOperand; + public: enum Mode { /// The mode is not decided yet (by DefiniteInitialization). @@ -5297,16 +5299,23 @@ class AssignOrInitInst }; private: - AssignOrInitInst(SILDebugLocation DebugLoc, VarDecl *P, SILValue Self, + AssignOrInitInst(SILDebugLocation DebugLoc, VarDecl *P, std::optional Self, 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(); } + std::optional getOptionalSelfOperand() const { + return HasSelfOperand ? std::optional(getOperand(1)) + : std::nullopt; + } + SILValue getSelfOperand() const { + assert(HasSelfOperand); + return Operands[0].get(); + } Mode getMode() const { return Mode(sharedUInt8().AssignOrInitInst.mode); diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index da4c39e2e3872..dd48b3b46f694 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1325,15 +1325,18 @@ AssignByWrapperInst::AssignByWrapperInst(SILDebugLocation Loc, } AssignOrInitInst::AssignOrInitInst(SILDebugLocation Loc, VarDecl *P, - SILValue Self, SILValue Src, + std::optional Self, SILValue Src, SILValue Initializer, SILValue Setter, AssignOrInitInst::Mode Mode) : InstructionBase(Loc), - Operands(this, Self, Src, Initializer, Setter), Property(P) { + Operands(this, + Self.has_value() ? *Self : SILUndef::get(Src->getFunction(), Src->getType()), + Src, Initializer, Setter), Property(P) { assert(Initializer->getType().is()); sharedUInt8().AssignOrInitInst.mode = uint8_t(Mode); Assignments.resize(getNumInitializedProperties()); + HasSelfOperand = Self.has_value(); } void AssignOrInitInst::markAsInitialized(VarDecl *property) { diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 46cf30f48721a..f5506f8723182 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2011,10 +2011,15 @@ class SILPrinter : public SILInstructionVisitor { } *this << "#"; - printFullContext(AI->getProperty()->getDeclContext(), PrintState.OS); + auto declContext = AI->getProperty()->getDeclContext(); + // TODO: represent local context differently. Currently outputs: "#" + if (!declContext->isLocalContext()) + printFullContext(AI->getProperty()->getDeclContext(), PrintState.OS); *this << AI->getPropertyName(); - - *this << ", self " << getIDAndType(AI->getSelf()); + if (AI->getOptionalSelfOperand()) + *this << ", self " << getIDAndType(AI->getSelfOperand()); + else + *this << ", self nil"; *this << ", value " << getIDAndType(AI->getSrc()); *this << ", init " << getIDAndType(AI->getInitializer()) << ", set " << getIDAndType(AI->getSetter()); diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index e0c7bcd63af6d..a5fd2b1473451 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -1244,13 +1244,17 @@ ElementUseCollector::collectAssignOrInitUses(AssignOrInitInst *Inst, /// that the flag is dropped before calling \c addElementUses. llvm::SaveAndRestore X(IsSelfOfNonDelegatingInitializer, false); - auto *typeDC = Inst->getReferencedInitAccessor() - ->getDeclContext() - ->getSelfNominalTypeDecl(); + // TODO: Change to work for local contexts + NominalTypeDecl *typeDC; + if (auto accessorDecl = Inst->getReferencedInitAccessor()) { + typeDC = accessorDecl ->getDeclContext()->getSelfNominalTypeDecl(); + } else { + typeDC = Inst->getProperty()->getDeclContext()->getSelfNominalTypeDecl(); + } auto expansionContext = TypeExpansionContext(TheMemory.getFunction()); - auto selfTy = Inst->getSelf()->getType(); + auto selfTy = Inst->getSelfOperand()->getType(); auto addUse = [&](VarDecl *property, DIUseKind useKind) { unsigned fieldIdx = 0; diff --git a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp index 24ccbb0842762..ab6f76ab0f72c 100644 --- a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp +++ b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp @@ -286,7 +286,7 @@ lowerAssignOrInitInstruction(SILBuilderWithScope &b, CanSILFunctionType fTy = initFn->getType().castTo(); SILFunctionConventions convention(fTy, inst->getModule()); - auto selfValue = inst->getSelf(); + auto selfValue = inst->getSelfOperand(); auto isRefSelf = selfValue->getType().getASTType()->mayHaveSuperclass(); SILValue selfRef; From c84cc3491e66774d537000c7f6d057c96e7e6069 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 8 Aug 2025 18:46:56 -0400 Subject: [PATCH 05/39] [SIL] Emit `CanSILFunctionType` for the new `SILDeclRef` --- lib/SIL/IR/SILDeclRef.cpp | 1 - lib/SIL/IR/SILFunctionType.cpp | 61 ++++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 7e09c1bad0a38..5e15f5789c734 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -304,7 +304,6 @@ bool SILDeclRef::hasUserWrittenCode() const { if (!isImplicit()) { switch (kind) { case Kind::PropertyWrappedFieldInitAccessor: - llvm_unreachable("SILDeclRef unimplemented"); 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 diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 1e916de1b565c..be49401854dbb 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2868,8 +2868,15 @@ 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) { + AccessorDecl *accessor = nullptr; + VarDecl *varDecl = nullptr; + if (auto *accessorTmp = dyn_cast(constant.getDecl())) { + accessor = accessorTmp; + } else { + varDecl = cast(constant.getDecl()); + } + auto declContext = accessor ? accessor->getDeclContext() : varDecl->getDeclContext(); CanGenericSignature genericSig = substAccessorType.getOptGenericSignature(); @@ -2899,8 +2906,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(); @@ -2911,27 +2920,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 (accessor) { + 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 declContext = accessor ? accessor->getDeclContext() : varDecl->getDeclContext(); + 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 (accessor) { + for (auto *property : accessor->getInitializedProperties()) { + results.push_back(SILResultInfo(getLoweredTypeOfProperty(property), + ResultConvention::Indirect)); + } + } else { + auto backingStorage = varDecl->getPropertyWrapperBackingProperty(); + results.push_back(SILResultInfo(getLoweredTypeOfProperty(backingStorage), ResultConvention::Indirect)); } @@ -3246,6 +3266,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: { @@ -4113,6 +4137,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: @@ -4411,6 +4436,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; @@ -4817,6 +4843,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: From 544aa3b06ad2cddf66b885f8972c739e80d95f20 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 8 Aug 2025 19:05:27 -0400 Subject: [PATCH 06/39] [SILGen] SILGen body emission for the new `SILDeclRef` works for nominal types --- lib/SIL/IR/SILInstructions.cpp | 9 +++- lib/SILGen/SILGen.cpp | 23 ++++++++- lib/SILGen/SILGen.h | 4 ++ lib/SILGen/SILGenConstructor.cpp | 84 ++++++++++++++++++++++++++++++++ lib/SILGen/SILGenFunction.h | 5 ++ lib/SILGen/SILGenType.cpp | 2 + 6 files changed, 125 insertions(+), 2 deletions(-) diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index dd48b3b46f694..d88cef9499bb4 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1374,7 +1374,14 @@ unsigned AssignOrInitInst::getNumInitializedProperties() const { ArrayRef AssignOrInitInst::getInitializedProperties() const { if (auto *accessor = getReferencedInitAccessor()) return accessor->getInitializedProperties(); - return {}; + else { + // Dealing wtih an init accessor thunk, only initializes backing property + // FIXME: Tempory solution below, cannot allocate each time this function is called + SmallVector res; + auto *backingVar = Property->getPropertyWrapperBackingProperty(); + res.push_back(backingVar); + return Property->getASTContext().AllocateCopy(res); + } } ArrayRef AssignOrInitInst::getAccessedProperties() const { diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 569045b1bf912..4bac521de016a 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1143,7 +1143,22 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { } case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: { - llvm_unreachable("SILDeclRef not handled"); + + 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: { @@ -1852,6 +1867,12 @@ 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 6b75a571c2d89..2ff37a02a9468 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -335,6 +335,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 169b6b961a8e7..ba59009edd819 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -1788,3 +1788,87 @@ 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()); + + emitBasicProlog(declContext, params, + /*selfParam=*/nullptr, TupleType::getEmpty(F.getASTContext()), + /*errorType=*/std::nullopt, + /*throwsLoc=*/SourceLoc(), + /*ignored parameters*/ + 1); + + // 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()); + + // Emit the initialization expression directly into the @out return buffer + emitReturnExpr(Loc, value); + + // Emit epilog/cleanups + emitEpilog(Loc); + mergeCleanupBlocks(); +} \ No newline at end of file diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 6ad90ae8b9d84..b47a3ee3189f8 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1078,6 +1078,11 @@ 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/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 78038a71f08d0..6c1b571d68aba 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -1533,6 +1533,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); From 2b8e635325e80633e3d4c10c0fbf1670e5890ffb Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 8 Aug 2025 19:07:38 -0400 Subject: [PATCH 07/39] [SILGen] Use DI-tracked backing storage for indirect results --- lib/SILGen/SILGenStmt.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 88db501de104a..6e865a681a0bc 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -639,8 +639,25 @@ prepareIndirectResultInit(SILGenFunction &SGF, SILLocation loc, // If it's indirect, we should be emitting into an argument. InitializationPtr init; if (SGF.silConv.isSILIndirect(result)) { + + auto resolveIndirectResultAddr = [&]() -> SILValue { + auto declRef = SGF.F.getDeclRef(); + if (declRef.kind != SILDeclRef::Kind::PropertyWrappedFieldInitAccessor) + return indirectResultAddrs.front(); + + // Use the DI-tracked backing storage address (from mark_uninitialized) + // instead of the raw result argument + auto *varDecl = dyn_cast(declRef.getDecl()); + assert(varDecl); + + auto *backingStorage = varDecl->getPropertyWrapperBackingProperty(); + auto *outParamDecl = SGF.InitAccessorArgumentMappings[backingStorage]; + return SGF.VarLocs[outParamDecl].value; + }; + + SILValue addr = resolveIndirectResultAddr(); + // Pull off the next indirect result argument. - SILValue addr = indirectResultAddrs.front(); indirectResultAddrs = indirectResultAddrs.slice(1); init = createIndirectResultInit(SGF, addr, origResultType, resultType, From 51dca5a6f7deef9a02a051eb1d21447c052f4155 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 8 Aug 2025 19:11:21 -0400 Subject: [PATCH 08/39] [SILGen] Emit `assign_or_init` for ambiguous assignments to property-wrapper variables --- lib/SILGen/SILGenLValue.cpp | 79 ++++++++++++------------------------- 1 file changed, 26 insertions(+), 53 deletions(-) diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 501855b2bf90f..d297555e3199a 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1823,71 +1823,48 @@ 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 + // 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()); - - // Stores the address of the storage property. - ManagedValue proj; - - // TODO: revist minimal - SILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType( - TypeExpansionContext::minimal(), backingVar, - ValType->getCanonicalType()); - - if (!BaseFormalType) { - 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); + // Create the init accessor thunk + SILDeclRef initConstant(field, SILDeclRef::Kind::PropertyWrappedFieldInitAccessor); SILValue initFRef = SGF.emitGlobalFunctionRef(loc, initConstant); + // For nominal contexts, compute the self metatype + SILValue selfMetatype; + 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)); + } + } + + auto argsPAI = BaseFormalType ? selfMetatype : ArrayRef(); PartialApplyInst *initPAI = SGF.B.createPartialApply(loc, initFRef, - Substitutions, ArrayRef(), + 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); + SILValue setterFn = SGF.emitApplyOfSetterToBase(loc, Accessor, base, Substitutions); - // Create the assign_by_wrapper with the initializer and setter. - - 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); + // Create the assign_or_init SIL instruction + auto Mval = emitValue(SGF, loc, field, std::move(value), AccessorKind::Set); + auto selfArg = BaseFormalType ? std::optional{base.getValue()} : std::nullopt; + SGF.B.createAssignOrInit(loc, field, selfArg, Mval.forward(SGF), + initFn.getValue(), setterFn, AssignOrInitInst::Unknown); return; } @@ -5706,10 +5683,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) From ff894c4d0195abe7ab709fe7c621ea770cd90b8b Mon Sep 17 00:00:00 2001 From: JanBaig Date: Sat, 9 Aug 2025 12:46:18 -0400 Subject: [PATCH 09/39] [Format] Apply clang-format to recent changes --- include/swift/AST/ASTMangler.h | 4 +- include/swift/SIL/SILBuilder.h | 6 +-- include/swift/SIL/SILCloner.h | 12 ++--- include/swift/SIL/SILDeclRef.h | 2 +- include/swift/SIL/SILInstruction.h | 12 ++--- lib/Demangling/Demangler.cpp | 8 ++-- lib/Demangling/NodePrinter.cpp | 11 +++-- lib/Demangling/OldRemangler.cpp | 6 +-- lib/Demangling/Remangler.cpp | 2 +- lib/SIL/IR/SILDeclRef.cpp | 7 +-- lib/SIL/IR/SILFunctionType.cpp | 20 ++++---- lib/SIL/IR/SILInstructions.cpp | 16 ++++--- lib/SIL/IR/SILPrinter.cpp | 7 +-- lib/SIL/IR/TypeLowering.cpp | 38 ++++++++------- lib/SILGen/SILGen.cpp | 12 ++--- lib/SILGen/SILGen.h | 4 +- lib/SILGen/SILGenConstructor.cpp | 48 +++++++++---------- lib/SILGen/SILGenFunction.cpp | 2 +- lib/SILGen/SILGenFunction.h | 5 +- lib/SILGen/SILGenLValue.cpp | 45 +++++++++-------- lib/SILGen/SILGenStmt.cpp | 8 ++-- .../Mandatory/DIMemoryUseCollector.cpp | 4 +- 22 files changed, 145 insertions(+), 134 deletions(-) diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 0d3db0635bf9c..c4933fc4009fd 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -246,8 +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 manglePropertyWrappedFieldInitAccessorEntity( + const VarDecl *var, SymbolKind SKind = SymbolKind::Default); std::string mangleInitFromProjectedValueEntity(const VarDecl *var, SymbolKind SKind = SymbolKind::Default); diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 4288bff74a8c1..69647730536d2 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -985,11 +985,9 @@ class SILBuilder { getSILDebugLocation(Loc), Src, Dest, Initializer, Setter, mode)); } - AssignOrInitInst *createAssignOrInit(SILLocation Loc, - VarDecl *Property, + AssignOrInitInst *createAssignOrInit(SILLocation Loc, VarDecl *Property, std::optional Self, - SILValue Src, - SILValue Initializer, + SILValue Src, SILValue Initializer, SILValue Setter, AssignOrInitInst::Mode Mode) { return insert(new (getModule()) diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 846c02fc2642f..3f23989856b0f 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1540,15 +1540,12 @@ template void SILCloner::visitAssignOrInitInst(AssignOrInitInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); auto selfArg = Inst->getOptionalSelfOperand(); - if (selfArg) + if (selfArg) selfArg = getOpValue(*selfArg); recordClonedInstruction( Inst, getBuilder().createAssignOrInit( - getOpLocation(Inst->getLoc()), - Inst->getProperty(), - selfArg, - getOpValue(Inst->getSrc()), - getOpValue(Inst->getInitializer()), + getOpLocation(Inst->getLoc()), Inst->getProperty(), selfArg, + getOpValue(Inst->getSrc()), getOpValue(Inst->getInitializer()), getOpValue(Inst->getSetter()), Inst->getMode())); } @@ -3541,8 +3538,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 ae4b6701f1528..0815bd8787274 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -168,7 +168,7 @@ struct SILDeclRef { /// The asynchronous main entry-point function. AsyncEntryPoint, - /// An init accessor that calls a propery wrapped field's + /// An init accessor that calls a propery wrapped field's /// backing storage initializer PropertyWrappedFieldInitAccessor }; diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 960929e69f9d0..b82d15ae211f9 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -5299,9 +5299,9 @@ class AssignOrInitInst }; private: - AssignOrInitInst(SILDebugLocation DebugLoc, VarDecl *P, std::optional Self, - SILValue Src, SILValue Initializer, SILValue Setter, - Mode mode); + AssignOrInitInst(SILDebugLocation DebugLoc, VarDecl *P, + std::optional Self, SILValue Src, + SILValue Initializer, SILValue Setter, Mode mode); public: VarDecl *getProperty() const { return Property; } @@ -5309,12 +5309,12 @@ class AssignOrInitInst SILValue getInitializer() const { return Operands[2].get(); } SILValue getSetter() { return Operands[3].get(); } std::optional getOptionalSelfOperand() const { - return HasSelfOperand ? std::optional(getOperand(1)) + return HasSelfOperand ? std::optional(getOperand(1)) : std::nullopt; } - SILValue getSelfOperand() const { + SILValue getSelfOperand() const { assert(HasSelfOperand); - return Operands[0].get(); + return Operands[0].get(); } Mode getMode() const { diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 37d214f5f4f32..e42474744c030 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -4130,10 +4130,10 @@ NodePointer Demangler::demangleFunctionEntity() { case 'P': Args = None; Kind = Node::Kind::PropertyWrapperBackingInitializer; - break; - case 'F': - Args = None; - Kind = Node::Kind::PropertyWrappedFieldInitAccessor; + break; + case 'F': + Args = None; + Kind = Node::Kind::PropertyWrappedFieldInitAccessor; break; case 'W': Args = None; diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 9f41983694758..ae9bd05ddcce1 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -1584,10 +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::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, @@ -3542,7 +3542,8 @@ NodePointer NodePrinter::printEntity(NodePointer Entity, unsigned depth, Entity->getKind() == Node::Kind::Initializer || Entity->getKind() == Node::Kind::PropertyWrapperBackingInitializer || Entity->getKind() == Node::Kind::PropertyWrappedFieldInitAccessor || - Entity->getKind() == Node::Kind::PropertyWrapperInitFromProjectedValue) { + Entity->getKind() == + Node::Kind::PropertyWrapperInitFromProjectedValue) { Printer << " of "; } else { Printer << " in "; diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index d41c3f3117257..d09cfc884521d 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -1189,9 +1189,9 @@ ManglingError Remangler::manglePropertyWrapperBackingInitializer( } ManglingError Remangler::manglePropertyWrappedFieldInitAccessor( - Node *node,EntityContext &ctx, unsigned depth) { - return mangleSimpleEntity(node, 'I', "F", ctx, depth + 1); -} + Node *node, EntityContext &ctx, unsigned depth) { + return mangleSimpleEntity(node, 'I', "F", ctx, depth + 1); +} ManglingError Remangler::manglePropertyWrapperInitFromProjectedValue( Node *node, EntityContext &ctx, unsigned depth) { diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 00bdc3076426f..05c0fc5391e46 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -2355,7 +2355,7 @@ Remangler::manglePropertyWrapperBackingInitializer(Node *node, unsigned depth) { return ManglingError::Success; } -ManglingError +ManglingError Remangler::manglePropertyWrappedFieldInitAccessor(Node *node, unsigned depth) { RETURN_IF_ERROR(mangleChildNodes(node, depth + 1)); Buffer << "fF"; diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 5e15f5789c734..0bacc82797012 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -303,7 +303,7 @@ bool SILDeclRef::hasUserWrittenCode() const { // Non-implicit decls generally have user-written code. if (!isImplicit()) { switch (kind) { - case Kind::PropertyWrappedFieldInitAccessor: + 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 @@ -1381,8 +1381,9 @@ 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::PropertyWrappedFieldInitAccessor: + return mangler.manglePropertyWrappedFieldInitAccessorEntity( + cast(getDecl()), SKind); case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: return mangler.mangleInitFromProjectedValueEntity(cast(getDecl()), diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index be49401854dbb..d60df90d99e56 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2876,7 +2876,8 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor( } else { varDecl = cast(constant.getDecl()); } - auto declContext = accessor ? accessor->getDeclContext() : varDecl->getDeclContext(); + auto declContext = + accessor ? accessor->getDeclContext() : varDecl->getDeclContext(); CanGenericSignature genericSig = substAccessorType.getOptGenericSignature(); @@ -2928,10 +2929,11 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor( } // Make a new 'self' parameter. - if (!declContext->isLocalContext()) { - auto declContext = accessor ? accessor->getDeclContext() : varDecl->getDeclContext(); - auto selfInterfaceType = MetatypeType::get( - declContext->getSelfInterfaceType()); + if (!declContext->isLocalContext()) { + auto declContext = + accessor ? accessor->getDeclContext() : varDecl->getDeclContext(); + auto selfInterfaceType = + MetatypeType::get(declContext->getSelfInterfaceType()); AbstractionPattern origSelfType(genericSig, selfInterfaceType->getCanonicalType()); auto loweredSelfType = TC.getLoweredType( @@ -2951,7 +2953,7 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor( } } else { auto backingStorage = varDecl->getPropertyWrapperBackingProperty(); - results.push_back(SILResultInfo(getLoweredTypeOfProperty(backingStorage), + results.push_back(SILResultInfo(getLoweredTypeOfProperty(backingStorage), ResultConvention::Indirect)); } @@ -3266,10 +3268,10 @@ static CanSILFunctionType getNativeSILFunctionType( case SILDeclRef::Kind::IVarDestroyer: return getSILFunctionTypeForConventions( DefaultConventions(NormalParameterConvention::Guaranteed)); - case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: return getSILFunctionTypeForInitAccessor( - TC, context, origType, substInterfaceType, extInfoBuilder, - DefaultSetterConventions(), *constant); + TC, context, origType, substInterfaceType, extInfoBuilder, + DefaultSetterConventions(), *constant); case SILDeclRef::Kind::Deallocator: return getSILFunctionTypeForConventions(DeallocatorConventions()); case SILDeclRef::Kind::IsolatedDeallocator: { diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index d88cef9499bb4..759a6802c1c36 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1330,13 +1330,16 @@ AssignOrInitInst::AssignOrInitInst(SILDebugLocation Loc, VarDecl *P, AssignOrInitInst::Mode Mode) : InstructionBase(Loc), - Operands(this, - Self.has_value() ? *Self : SILUndef::get(Src->getFunction(), Src->getType()), - Src, Initializer, Setter), Property(P) { + Operands(this, + Self.has_value() + ? *Self + : SILUndef::get(Src->getFunction(), Src->getType()), + Src, Initializer, Setter), + Property(P) { assert(Initializer->getType().is()); sharedUInt8().AssignOrInitInst.mode = uint8_t(Mode); Assignments.resize(getNumInitializedProperties()); - HasSelfOperand = Self.has_value(); + HasSelfOperand = Self.has_value(); } void AssignOrInitInst::markAsInitialized(VarDecl *property) { @@ -1375,8 +1378,9 @@ ArrayRef AssignOrInitInst::getInitializedProperties() const { if (auto *accessor = getReferencedInitAccessor()) return accessor->getInitializedProperties(); else { - // Dealing wtih an init accessor thunk, only initializes backing property - // FIXME: Tempory solution below, cannot allocate each time this function is called + // Dealing wtih an init accessor thunk, only initializes backing property + // FIXME: Tempory solution below, cannot allocate each time this function is + // called SmallVector res; auto *backingVar = Property->getPropertyWrapperBackingProperty(); res.push_back(backingVar); diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index f5506f8723182..335e2d412d001 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -441,7 +441,7 @@ void SILDeclRef::print(raw_ostream &OS) const { case SILDeclRef::Kind::PropertyWrapperBackingInitializer: OS << "!backinginit"; break; - case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: OS << "!wrappedfieldinitaccessor"; break; case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: @@ -2012,13 +2012,14 @@ class SILPrinter : public SILInstructionVisitor { *this << "#"; auto declContext = AI->getProperty()->getDeclContext(); - // TODO: represent local context differently. Currently outputs: "#" + // TODO: represent local context differently. Currently outputs: "#" if (!declContext->isLocalContext()) printFullContext(AI->getProperty()->getDeclContext(), PrintState.OS); *this << AI->getPropertyName(); if (AI->getOptionalSelfOperand()) *this << ", self " << getIDAndType(AI->getSelfOperand()); - else + else *this << ", self nil"; *this << ", value " << getIDAndType(AI->getSrc()); *this << ", init " << getIDAndType(AI->getInitializer()) diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index 4d116cb86a7a7..21694781b2982 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -3964,38 +3964,40 @@ static CanAnyFunctionType getPropertyWrapperBackingInitializerInterfaceType( resultType, info); } -static CanAnyFunctionType getPropertyWrappedFieldInitAccessorInterfaceType(TypeConverter &TC, - SILDeclRef wrappedPropertyRef) { - // TODO: May need to handle projected value input type +static CanAnyFunctionType getPropertyWrappedFieldInitAccessorInterfaceType( + TypeConverter &TC, SILDeclRef wrappedPropertyRef) { + // TODO: May need to handle projected value input type auto *wrappedProperty = cast(wrappedPropertyRef.getDecl()); - CanType wrappedValueType = wrappedProperty - ->getPropertyWrapperInitValueInterfaceType() - ->getCanonicalType(); + CanType wrappedValueType = + wrappedProperty->getPropertyWrapperInitValueInterfaceType() + ->getCanonicalType(); bool isLocalContext = wrappedProperty->getDeclContext()->isLocalContext(); - GenericSignature sig = TC.getGenericSignatureWithCapturedEnvironments(wrappedPropertyRef).genericSig; + GenericSignature sig = + TC.getGenericSignatureWithCapturedEnvironments(wrappedPropertyRef) + .genericSig; SmallVector params; // Create the wrappedValue param params.emplace_back( - wrappedValueType, Identifier(), - ParameterTypeFlags().withOwnershipSpecifier(ParamSpecifier::LegacyOwned)); + wrappedValueType, Identifier(), + ParameterTypeFlags().withOwnershipSpecifier(ParamSpecifier::LegacyOwned)); if (!isLocalContext) { CanType selfType = wrappedProperty->getDeclContext() - ->getSelfInterfaceType() - ->getCanonicalType(); + ->getSelfInterfaceType() + ->getCanonicalType(); // Create the self param params.emplace_back( - selfType, Identifier(), - ParameterTypeFlags().withOwnershipSpecifier(ParamSpecifier::InOut)); + 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); + CanAnyFunctionType::ExtInfo extInfo; + return CanAnyFunctionType::get(getCanonicalSignatureOrNull(sig), paramRef, + resultType, extInfo); } /// Get the type of a destructor function. @@ -4241,7 +4243,7 @@ CanAnyFunctionType TypeConverter::makeConstantInterfaceType(SILDeclRef c) { case SILDeclRef::Kind::PropertyWrapperBackingInitializer: case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: return getPropertyWrapperBackingInitializerInterfaceType(*this, c); - case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: return getPropertyWrappedFieldInitAccessorInterfaceType(*this, c); case SILDeclRef::Kind::IVarInitializer: return getIVarInitDestroyerInterfaceType(cast(vd), diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 4bac521de016a..40821bc396177 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1143,20 +1143,21 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { } 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); + SGF.emitPropertyWrappedFieldInitAccessor(constant, init, + /*EmitProfilerIncrement*/ true); postEmitFunction(constant, f); break; } @@ -1867,8 +1868,7 @@ emitPropertyWrapperBackingInitializer(VarDecl *var) { } } -void SILGenModule:: -emitPropertyWrappedFieldInitAccessor(VarDecl *var) { +void SILGenModule::emitPropertyWrappedFieldInitAccessor(VarDecl *var) { SILDeclRef constant(var, SILDeclRef::Kind::PropertyWrappedFieldInitAccessor); emitOrDelayFunction(constant); } diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 2ff37a02a9468..c5c9e33ee96e8 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -335,8 +335,8 @@ 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 + /// 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 diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index ba59009edd819..688bcada8e436 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -1789,12 +1789,12 @@ void SILGenFunction::emitInitAccessor(AccessorDecl *accessor) { mergeCleanupBlocks(); } -void SILGenFunction::emitPropertyWrappedFieldInitAccessor(SILDeclRef function, - Expr* value, bool EmitProfilerIncrement) { +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(); @@ -1802,11 +1802,11 @@ void SILGenFunction::emitPropertyWrappedFieldInitAccessor(SILDeclRef function, bool isLocalContext = vd->getDeclContext()->isLocalContext(); auto createArgument = [&](VarDecl *property, SILType type, - bool markUninitialized = false) { + bool markUninitialized = false) { auto *arg = ParamDecl::createImplicit( getASTContext(), property->getBaseIdentifier(), - property->getBaseIdentifier(), property->getInterfaceType(), declContext, - ParamSpecifier::InOut); + property->getBaseIdentifier(), property->getInterfaceType(), + declContext, ParamSpecifier::InOut); RegularLocation loc(property); loc.markAutoGenerated(); @@ -1815,44 +1815,42 @@ void SILGenFunction::emitPropertyWrappedFieldInitAccessor(SILDeclRef function, 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); + 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); + 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()); - + newValueParam->setInterfaceType( + vd->getPropertyWrapperInitValueInterfaceType()); + params = + ParameterList::create(ctx, SourceLoc(), {newValueParam}, SourceLoc()); + emitBasicProlog(declContext, params, /*selfParam=*/nullptr, TupleType::getEmpty(F.getASTContext()), /*errorType=*/std::nullopt, /*throwsLoc=*/SourceLoc(), /*ignored parameters*/ 1); - + // Emit the `self` argument. - if (!isLocalContext) - emitConstructorMetatypeArg(*this, vd); + if (!isLocalContext) + emitConstructorMetatypeArg(*this, vd); - prepareEpilog(declContext, - TupleType::getEmpty(F.getASTContext()), - std::nullopt, - CleanupLocation(Loc)); + 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 @@ -1866,7 +1864,7 @@ void SILGenFunction::emitPropertyWrappedFieldInitAccessor(SILDeclRef function, assert(value == initInfo.getInitFromWrappedValue()); // Emit the initialization expression directly into the @out return buffer - emitReturnExpr(Loc, value); + emitReturnExpr(Loc, value); // Emit epilog/cleanups emitEpilog(Loc); diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 5cdd16b0f0073..410aa5d60610a 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -192,7 +192,7 @@ DeclName SILGenModule::getMagicFunctionName(SILDeclRef ref) { case SILDeclRef::Kind::StoredPropertyInitializer: case SILDeclRef::Kind::PropertyWrapperBackingInitializer: return getMagicFunctionName(cast(ref.getDecl())->getDeclContext()); - case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: + case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: return getMagicFunctionName(cast(ref.getDecl())->getDeclContext()); case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: return getMagicFunctionName(cast(ref.getDecl())->getDeclContext()); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index b47a3ee3189f8..5701dfd1d5774 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1079,9 +1079,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction 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 + /// calling a property wrapper's backing storage initializer into the backing /// storage variable - void emitPropertyWrappedFieldInitAccessor(SILDeclRef function, Expr* value, bool EmitProfilerIncrement); + 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, diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index d297555e3199a..0171bfa2f75db 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1823,48 +1823,55 @@ namespace { isBackingVarVisible(cast(Storage), SGF.FunctionDC)) { // This is wrapped property. Instead of emitting a setter, emit an - // 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. + // 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); - // Create the init accessor thunk - SILDeclRef initConstant(field, SILDeclRef::Kind::PropertyWrappedFieldInitAccessor); + // Create the init accessor thunk + SILDeclRef initConstant( + field, SILDeclRef::Kind::PropertyWrappedFieldInitAccessor); SILValue initFRef = SGF.emitGlobalFunctionRef(loc, initConstant); // For nominal contexts, compute the self metatype SILValue selfMetatype; if (BaseFormalType) { - auto selfTy = base.getType().getASTType(); + auto selfTy = base.getType().getASTType(); auto metatypeTy = MetatypeType::get(selfTy); if (selfTy->getClassOrBoundGenericClass()) { - selfMetatype = SGF.B.createValueMetatype(loc, SGF.getLoweredType(metatypeTy), - base).getValue(); + selfMetatype = SGF.B + .createValueMetatype( + loc, SGF.getLoweredType(metatypeTy), base) + .getValue(); } else { assert(BaseFormalType->getStructOrBoundGenericStruct()); - selfMetatype = SGF.B.createMetatype(loc, SGF.getLoweredType(metatypeTy)); + selfMetatype = + SGF.B.createMetatype(loc, SGF.getLoweredType(metatypeTy)); } - } + } auto argsPAI = BaseFormalType ? selfMetatype : ArrayRef(); PartialApplyInst *initPAI = - SGF.B.createPartialApply(loc, initFRef, - Substitutions, argsPAI, - 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); + SILValue setterFn = + SGF.emitApplyOfSetterToBase(loc, Accessor, base, Substitutions); // Create the assign_or_init SIL instruction - auto Mval = emitValue(SGF, loc, field, std::move(value), AccessorKind::Set); - auto selfArg = BaseFormalType ? std::optional{base.getValue()} : std::nullopt; - SGF.B.createAssignOrInit(loc, field, selfArg, Mval.forward(SGF), - initFn.getValue(), setterFn, AssignOrInitInst::Unknown); + auto Mval = + emitValue(SGF, loc, field, std::move(value), AccessorKind::Set); + auto selfArg = + BaseFormalType ? std::optional{base.getValue()} : std::nullopt; + SGF.B.createAssignOrInit(loc, field, selfArg, Mval.forward(SGF), + initFn.getValue(), setterFn, + AssignOrInitInst::Unknown); return; } diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 6e865a681a0bc..25ef2f1c296b5 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -642,10 +642,10 @@ prepareIndirectResultInit(SILGenFunction &SGF, SILLocation loc, auto resolveIndirectResultAddr = [&]() -> SILValue { auto declRef = SGF.F.getDeclRef(); - if (declRef.kind != SILDeclRef::Kind::PropertyWrappedFieldInitAccessor) - return indirectResultAddrs.front(); - - // Use the DI-tracked backing storage address (from mark_uninitialized) + if (declRef.kind != SILDeclRef::Kind::PropertyWrappedFieldInitAccessor) + return indirectResultAddrs.front(); + + // Use the DI-tracked backing storage address (from mark_uninitialized) // instead of the raw result argument auto *varDecl = dyn_cast(declRef.getDecl()); assert(varDecl); diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index a5fd2b1473451..522dca0d6d20d 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -1244,10 +1244,10 @@ ElementUseCollector::collectAssignOrInitUses(AssignOrInitInst *Inst, /// that the flag is dropped before calling \c addElementUses. llvm::SaveAndRestore X(IsSelfOfNonDelegatingInitializer, false); - // TODO: Change to work for local contexts + // TODO: Change to work for local contexts NominalTypeDecl *typeDC; if (auto accessorDecl = Inst->getReferencedInitAccessor()) { - typeDC = accessorDecl ->getDeclContext()->getSelfNominalTypeDecl(); + typeDC = accessorDecl->getDeclContext()->getSelfNominalTypeDecl(); } else { typeDC = Inst->getProperty()->getDeclContext()->getSelfNominalTypeDecl(); } From 3d1123428c081fe6637ae987736790bb9b0a6390 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Sat, 9 Aug 2025 16:59:30 -0400 Subject: [PATCH 10/39] [SIL] Refactor to allow `Self` operand to hold local projections --- include/swift/SIL/SILBuilder.h | 11 +++++------ include/swift/SIL/SILCloner.h | 6 ++---- include/swift/SIL/SILInstruction.h | 26 ++++++++++++-------------- lib/SIL/IR/SILInstructions.cpp | 10 ++-------- lib/SIL/IR/SILPrinter.cpp | 17 ++++++++--------- 5 files changed, 29 insertions(+), 41 deletions(-) diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 69647730536d2..49c0fcb8c287b 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -986,13 +986,12 @@ class SILBuilder { } AssignOrInitInst *createAssignOrInit(SILLocation Loc, VarDecl *Property, - std::optional Self, - SILValue Src, SILValue Initializer, - SILValue Setter, + 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 3f23989856b0f..35a6f41d1286b 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1539,12 +1539,10 @@ void SILCloner::visitAssignByWrapperInst(AssignByWrapperInst *Inst) { template void SILCloner::visitAssignOrInitInst(AssignOrInitInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); - auto selfArg = Inst->getOptionalSelfOperand(); - if (selfArg) - selfArg = getOpValue(*selfArg); recordClonedInstruction( Inst, getBuilder().createAssignOrInit( - getOpLocation(Inst->getLoc()), Inst->getProperty(), selfArg, + getOpLocation(Inst->getLoc()), Inst->getProperty(), + getOpValue(Inst->getSelfOrLocalOperand()), getOpValue(Inst->getSrc()), getOpValue(Inst->getInitializer()), getOpValue(Inst->getSetter()), Inst->getMode())); } diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index b82d15ae211f9..86f9dbfd3a3d9 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -5284,8 +5284,6 @@ class AssignOrInitInst /// lowering to emit destroys. llvm::BitVector Assignments; - bool HasSelfOperand; - public: enum Mode { /// The mode is not decided yet (by DefiniteInitialization). @@ -5299,23 +5297,23 @@ class AssignOrInitInst }; private: - AssignOrInitInst(SILDebugLocation DebugLoc, VarDecl *P, - std::optional Self, SILValue Src, - SILValue Initializer, SILValue Setter, Mode mode); + AssignOrInitInst(SILDebugLocation DebugLoc, VarDecl *P, SILValue SelfOrLocal, + SILValue Src, SILValue Initializer, SILValue Setter, + Mode mode); public: VarDecl *getProperty() const { return Property; } SILValue getSrc() const { return Operands[1].get(); } SILValue getInitializer() const { return Operands[2].get(); } - SILValue getSetter() { return Operands[3].get(); } - std::optional getOptionalSelfOperand() const { - return HasSelfOperand ? std::optional(getOperand(1)) - : std::nullopt; - } - SILValue getSelfOperand() const { - assert(HasSelfOperand); - return Operands[0].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); diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 759a6802c1c36..e50b3e398f63c 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1325,21 +1325,15 @@ AssignByWrapperInst::AssignByWrapperInst(SILDebugLocation Loc, } AssignOrInitInst::AssignOrInitInst(SILDebugLocation Loc, VarDecl *P, - std::optional Self, SILValue Src, + SILValue SelfOrLocal, SILValue Src, SILValue Initializer, SILValue Setter, AssignOrInitInst::Mode Mode) : InstructionBase(Loc), - Operands(this, - Self.has_value() - ? *Self - : SILUndef::get(Src->getFunction(), Src->getType()), - 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()); - HasSelfOperand = Self.has_value(); } void AssignOrInitInst::markAsInitialized(VarDecl *property) { diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 335e2d412d001..32d4dd60dbd2e 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2012,15 +2012,14 @@ class SILPrinter : public SILInstructionVisitor { *this << "#"; auto declContext = AI->getProperty()->getDeclContext(); - // TODO: represent local context differently. Currently outputs: "#" - if (!declContext->isLocalContext()) - printFullContext(AI->getProperty()->getDeclContext(), PrintState.OS); - *this << AI->getPropertyName(); - if (AI->getOptionalSelfOperand()) - *this << ", self " << getIDAndType(AI->getSelfOperand()); - else - *this << ", self nil"; + + 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()); From b939bdc31a67348d4f6a7738dd9e4ddbc1eaeed9 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Sat, 9 Aug 2025 19:36:52 -0400 Subject: [PATCH 11/39] [SIL] Initial Implemention of thunk support for local contexts --- lib/SILGen/SILGenConstructor.cpp | 6 +- lib/SILGen/SILGenDecl.cpp | 5 +- lib/SILGen/SILGenLValue.cpp | 9 ++- .../Mandatory/DIMemoryUseCollector.cpp | 22 ++++++-- .../Mandatory/RawSILInstLowering.cpp | 55 ++++++++++++------- 5 files changed, 64 insertions(+), 33 deletions(-) diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 688bcada8e436..1a2a4e840c292 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -1838,12 +1838,14 @@ void SILGenFunction::emitPropertyWrappedFieldInitAccessor( 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*/ - 1); + /*ignored parameters*/ numIgnoredParams); // Emit the `self` argument. if (!isLocalContext) diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index 0cd8ef002ca68..d7c316ea1f158 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -1690,8 +1690,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/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 0171bfa2f75db..7ac87425ff379 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1839,6 +1839,7 @@ namespace { // For nominal contexts, compute the self metatype SILValue selfMetatype; + ManagedValue proj; if (BaseFormalType) { auto selfTy = base.getType().getASTType(); auto metatypeTy = MetatypeType::get(selfTy); @@ -1852,6 +1853,9 @@ namespace { selfMetatype = SGF.B.createMetatype(loc, SGF.getLoweredType(metatypeTy)); } + } else { // Dealing with a local context + proj = + SGF.maybeEmitValueOfLocalVarDecl(backingVar, AccessKind::Write); } auto argsPAI = BaseFormalType ? selfMetatype : ArrayRef(); @@ -1867,9 +1871,8 @@ namespace { // Create the assign_or_init SIL instruction auto Mval = emitValue(SGF, loc, field, std::move(value), AccessorKind::Set); - auto selfArg = - BaseFormalType ? std::optional{base.getValue()} : std::nullopt; - SGF.B.createAssignOrInit(loc, field, selfArg, Mval.forward(SGF), + auto selfOrLocal = selfMetatype ? base.getValue() : proj.forward(SGF); + SGF.B.createAssignOrInit(loc, field, selfOrLocal, Mval.forward(SGF), initFn.getValue(), setterFn, AssignOrInitInst::Unknown); return; diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 522dca0d6d20d..a6704d295bb5d 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -1244,8 +1244,7 @@ ElementUseCollector::collectAssignOrInitUses(AssignOrInitInst *Inst, /// that the flag is dropped before calling \c addElementUses. llvm::SaveAndRestore X(IsSelfOfNonDelegatingInitializer, false); - // TODO: Change to work for local contexts - NominalTypeDecl *typeDC; + NominalTypeDecl *typeDC = nullptr; if (auto accessorDecl = Inst->getReferencedInitAccessor()) { typeDC = accessorDecl->getDeclContext()->getSelfNominalTypeDecl(); } else { @@ -1253,8 +1252,19 @@ ElementUseCollector::collectAssignOrInitUses(AssignOrInitInst *Inst, } auto expansionContext = TypeExpansionContext(TheMemory.getFunction()); - - auto selfTy = Inst->getSelfOperand()->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; @@ -1264,10 +1274,10 @@ 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); }; diff --git a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp index ab6f76ab0f72c..91a51e1b0fc56 100644 --- a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp +++ b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp @@ -286,17 +286,23 @@ lowerAssignOrInitInstruction(SILBuilderWithScope &b, CanSILFunctionType fTy = initFn->getType().castTo(); SILFunctionConventions convention(fTy, inst->getModule()); - auto selfValue = inst->getSelfOperand(); - auto isRefSelf = selfValue->getType().getASTType()->mayHaveSuperclass(); - - SILValue selfRef; - if (isRefSelf) { - selfRef = b.emitBeginBorrowOperation(loc, selfValue); - } else { - selfRef = b.createBeginAccess(loc, selfValue, SILAccessKind::Modify, - SILAccessEnforcement::Dynamic, - /*noNestedConflict=*/false, - /*fromBuiltin=*/false); + auto inLocalContext = + inst->getProperty()->getDeclContext()->isLocalContext(); + auto selfOrLocalValue = inst->getSelfOrLocalOperand(); + bool isRefSelf = + selfOrLocalValue->getType().getASTType()->mayHaveSuperclass(); + + SILValue selfRef = nullptr; + if (!inLocalContext) { + if (isRefSelf) { + selfRef = b.emitBeginBorrowOperation(loc, selfOrLocalValue); + } else { + selfRef = + b.createBeginAccess(loc, selfOrLocalValue, SILAccessKind::Modify, + SILAccessEnforcement::Dynamic, + /*noNestedConflict=*/false, + /*fromBuiltin=*/false); + } } auto emitFieldReference = [&](VarDecl *field, @@ -319,11 +325,16 @@ 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)) { - arguments.push_back(emitFieldReference( - toInitialize[index], - /*emitDestroy=*/inst->isPropertyAlreadyInitialized(index))); + if (inLocalContext) { + // add the local projection which is for the _x backing local storage + arguments.push_back(selfOrLocalValue); + } else { + auto toInitialize = inst->getInitializedProperties(); + for (unsigned index : indices(toInitialize)) { + arguments.push_back(emitFieldReference( + toInitialize[index], + /*emitDestroy=*/inst->isPropertyAlreadyInitialized(index))); + } } } @@ -338,11 +349,13 @@ lowerAssignOrInitInstruction(SILBuilderWithScope &b, b.createApply(loc, initFn, SubstitutionMap(), arguments); - if (isRefSelf) { - if (selfRef != selfValue) - b.emitEndBorrowOperation(loc, selfRef); - } else { - b.createEndAccess(loc, selfRef, /*aborted=*/false); + if (!inLocalContext) { + if (isRefSelf) { + if (selfRef != selfOrLocalValue) + b.emitEndBorrowOperation(loc, selfRef); + } else { + b.createEndAccess(loc, selfRef, /*aborted=*/false); + } } // The unused partial_apply violates memory lifetime rules in case "self" From b793db7bfaa49535522004e33da29c9955448ab1 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Tue, 12 Aug 2025 11:21:11 -0400 Subject: [PATCH 12/39] [SILGen] Inline `InitializationPtr` setup for init accessor @out buffer --- lib/SILGen/SILGenConstructor.cpp | 12 ++++++++++-- lib/SILGen/SILGenStmt.cpp | 19 +------------------ 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 1a2a4e840c292..db5c857bc36b5 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -1865,8 +1865,16 @@ void SILGenFunction::emitPropertyWrappedFieldInitAccessor( maybeEmitValueOfLocalVarDecl(newValueParam, AccessKind::Read)); assert(value == initInfo.getInitFromWrappedValue()); - // Emit the initialization expression directly into the @out return buffer - emitReturnExpr(Loc, value); + // Prepare InitializationPtr for the @out return buffer + FullExpr scope(Cleanups, CleanupLocation(value)); + auto backingStorageArg = InitAccessorArgumentMappings[backingStorage]; + auto backingStorageAddr = VarLocs[backingStorageArg].value; + auto &TL = getTypeLowering(backingStorageAddr->getType()); + auto tmp = useBufferAsTemporary(backingStorageAddr, TL); + InitializationPtr init(tmp.release()); + + // Intialize the @out buffer with the given expression + emitExprInto(value, init.get()); // Emit epilog/cleanups emitEpilog(Loc); diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 25ef2f1c296b5..88db501de104a 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -639,25 +639,8 @@ prepareIndirectResultInit(SILGenFunction &SGF, SILLocation loc, // If it's indirect, we should be emitting into an argument. InitializationPtr init; if (SGF.silConv.isSILIndirect(result)) { - - auto resolveIndirectResultAddr = [&]() -> SILValue { - auto declRef = SGF.F.getDeclRef(); - if (declRef.kind != SILDeclRef::Kind::PropertyWrappedFieldInitAccessor) - return indirectResultAddrs.front(); - - // Use the DI-tracked backing storage address (from mark_uninitialized) - // instead of the raw result argument - auto *varDecl = dyn_cast(declRef.getDecl()); - assert(varDecl); - - auto *backingStorage = varDecl->getPropertyWrapperBackingProperty(); - auto *outParamDecl = SGF.InitAccessorArgumentMappings[backingStorage]; - return SGF.VarLocs[outParamDecl].value; - }; - - SILValue addr = resolveIndirectResultAddr(); - // Pull off the next indirect result argument. + SILValue addr = indirectResultAddrs.front(); indirectResultAddrs = indirectResultAddrs.slice(1); init = createIndirectResultInit(SGF, addr, origResultType, resultType, From 836e20a253d32953dd506fdd2f6946f9262f9b18 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Sun, 17 Aug 2025 14:05:02 -0400 Subject: [PATCH 13/39] [SILGen] Refactor `InitializationPtr` usage --- lib/SILGen/SILGenConstructor.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index db5c857bc36b5..3a07ba8036218 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -1869,9 +1869,7 @@ void SILGenFunction::emitPropertyWrappedFieldInitAccessor( FullExpr scope(Cleanups, CleanupLocation(value)); auto backingStorageArg = InitAccessorArgumentMappings[backingStorage]; auto backingStorageAddr = VarLocs[backingStorageArg].value; - auto &TL = getTypeLowering(backingStorageAddr->getType()); - auto tmp = useBufferAsTemporary(backingStorageAddr, TL); - InitializationPtr init(tmp.release()); + InitializationPtr init(new KnownAddressInitialization(backingStorageAddr)); // Intialize the @out buffer with the given expression emitExprInto(value, init.get()); From 8ca5c83c51c48e1fa1e78a288991e61029f5a6eb Mon Sep 17 00:00:00 2001 From: JanBaig Date: Sun, 17 Aug 2025 23:53:29 -0400 Subject: [PATCH 14/39] [Test] Extract closure test to support init accessor thunk without opaque values --- test/SILGen/opaque_values_closures.swift | 31 +------------------- test/SILGen/opaque_values_closures2.swift | 35 +++++++++++++++++++++++ 2 files changed, 36 insertions(+), 30 deletions(-) create mode 100644 test/SILGen/opaque_values_closures2.swift 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/opaque_values_closures2.swift b/test/SILGen/opaque_values_closures2.swift new file mode 100644 index 0000000000000..56a214c039759 --- /dev/null +++ b/test/SILGen/opaque_values_closures2.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] @$s23opaque_values_closures235captureBoxNonopaqueOwnedNonescapingyyxyXElF : {{.*}} { +// 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 @$s23opaque_values_closures235captureBoxNonopaqueOwnedNonescapingyyxyXElF5localL_yylF +// CHECK: apply [[LOCAL]]([[VAR_LIFETIME]], [[GET]]) +// CHECK: end_borrow [[VAR_LIFETIME]] +// CHECK: destroy_value [[VAR]] +// CHECK-LABEL: } // end sil function '$s23opaque_values_closures235captureBoxNonopaqueOwnedNonescapingyyxyXElF' + +// CHECK-LABEL: sil {{.*}}[ossa] @$s23opaque_values_closures235captureBoxNonopaqueOwnedNonescapingyyxyXElF5localL_yylF : {{.*}} +// CHECK: bb0(%0 : @closureCapture @guaranteed $<τ_0_0> { var BoxWrapper<τ_0_0> } , +// CHECK-SAME: %1 : +// CHECK-LABEL: } // end sil function '$s23opaque_values_closures235captureBoxNonopaqueOwnedNonescapingyyxyXElF5localL_yylF' +func captureBoxNonopaqueOwnedNonescaping(_ get: () -> U) { + @BoxWrapper var u: U + + func local() { + u = get() + } + local() +} From 54cc1beb10c50a9279125558a1477240c9daf9a1 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Tue, 19 Aug 2025 18:29:57 -0400 Subject: [PATCH 15/39] [Test] Refactor property wrapper tests to work with new init accessor thunk --- test/SILGen/objc_properties.swift | 4 ++-- test/SILGen/property_wrapper_local.swift | 16 ++++++++-------- test/SILGen/property_wrappers.swift | 11 ++++++----- ...pper.swift => resilient_assign_or_init.swift} | 11 ++++------- 4 files changed, 20 insertions(+), 22 deletions(-) rename test/SILGen/{resilient_assign_by_wrapper.swift => resilient_assign_or_init.swift} (88%) 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/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 88% rename from test/SILGen/resilient_assign_by_wrapper.swift rename to test/SILGen/resilient_assign_or_init.swift index bebf7154d4683..b9d41aa48e661 100644 --- a/test/SILGen/resilient_assign_by_wrapper.swift +++ b/test/SILGen/resilient_assign_or_init.swift @@ -34,7 +34,7 @@ public class AddressOnlySetter { // 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 } @@ -56,9 +56,8 @@ 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: [[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 @@ -77,8 +76,7 @@ extension ReabstractedSetter where T == 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 } } } @@ -93,8 +91,7 @@ extension ObjectifiedSetter where T == SomeObject { init() { // CHECK-LABEL: sil hidden [ossa] @$s27resilient_assign_by_wrapper17ObjectifiedSetterV5valuexvs : $@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() From 2499116bc05e31912e6e07badccc255f161c35b3 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Wed, 20 Aug 2025 00:40:32 -0400 Subject: [PATCH 16/39] [Test] Refactor function manglings to reflect file name change --- test/SILGen/resilient_assign_or_init.swift | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/SILGen/resilient_assign_or_init.swift b/test/SILGen/resilient_assign_or_init.swift index b9d41aa48e661..6661938b2c428 100644 --- a/test/SILGen/resilient_assign_or_init.swift +++ b/test/SILGen/resilient_assign_or_init.swift @@ -25,10 +25,11 @@ 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 @@ -40,7 +41,7 @@ public class AddressOnlySetter { } 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,7 +56,7 @@ public struct SubstitutedSetter { extension SubstitutedSetter where T == Bool { init() { - // CHECK-LABEL: hidden [ossa] @$s27resilient_assign_by_wrapper17SubstitutedSetterVAASbRszlEACySbGycfC + // CHECK-LABEL: hidden [ossa] @$s24resilient_assign_or_init17SubstitutedSetterVAASbRszlEACySbGycfC // CHECK: [[B:%.*]] = alloc_stack $Bool // CHECK: assign_or_init #SubstitutedSetter.value, self {{%.*}} : $*SubstitutedSetter, value [[B]] : $*Bool // CHECK-SAME: init {{%.*}} : $@callee_guaranteed (@in Bool) -> @out WrapGod @@ -70,8 +71,8 @@ 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) -> ()) -> () @@ -89,7 +90,7 @@ 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: assign_or_init #ObjectifiedSetter.value, self {{%.*}} : $*ObjectifiedSetter, value [[OBJ]] // CHECK-SAME: init {{%.*}} : $@callee_guaranteed (@owned SomeObject) -> @out WrapGod From 30d678046dfcca9a699c7e76e2d466efb1e259ff Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 22 Aug 2025 10:45:08 -0400 Subject: [PATCH 17/39] [TBD] Add init accessor thunk to TBD gen --- lib/SIL/IR/SILSymbolVisitor.cpp | 2 ++ 1 file changed, 2 insertions(+) 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); From c2850c33c93d4e390b6c488fdee284803a692f63 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 22 Aug 2025 23:06:59 -0400 Subject: [PATCH 18/39] [SIL] Remove `AssignByWrapper` definition and registration --- .../Sources/SIL/Instruction.swift | 2 - .../Sources/SIL/Registration.swift | 1 - include/swift/SIL/SILInstruction.h | 45 ------------------- include/swift/SIL/SILNode.h | 2 - include/swift/SIL/SILNodes.def | 2 - lib/SIL/Utils/InstructionUtils.cpp | 16 ------- 6 files changed, 68 deletions(-) diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index 7383e91ecae50..be968ee655444 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -310,8 +310,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 d8f352762e129..d664877cd9c43 100644 --- a/SwiftCompilerSources/Sources/SIL/Registration.swift +++ b/SwiftCompilerSources/Sources/SIL/Registration.swift @@ -38,7 +38,6 @@ public 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/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 86f9dbfd3a3d9..a4c526e2ee342 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -5215,51 +5215,6 @@ 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 an init accessor /// or a setter, which may either be an initialization or a store sequence. /// This is only valid in Raw SIL. 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/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 19005d5e8220b..ccd6b11ed8abd 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: From 8469740859e8a758930a4ed90d836dddd56e0e5b Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 22 Aug 2025 23:13:30 -0400 Subject: [PATCH 19/39] [SIL] Remove `AssignByWrapper` cloning and builder support --- include/swift/SIL/SILBuilder.h | 9 --------- include/swift/SIL/SILCloner.h | 11 ----------- 2 files changed, 20 deletions(-) diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 49c0fcb8c287b..e92f311b6d984 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -976,15 +976,6 @@ 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 SelfOrLocal, SILValue Src, SILValue Initializer, SILValue Setter, diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 35a6f41d1286b..124958361e5af 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1525,17 +1525,6 @@ 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())); From 4c61096be74902fe9eb46ea0e5a56c63dff511c9 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 22 Aug 2025 23:15:16 -0400 Subject: [PATCH 20/39] [SIL] Remove `AssignByWrapper` handling from analysis and utils --- .../Sources/Optimizer/Utilities/AddressUtils.swift | 2 +- include/swift/SIL/AddressWalker.h | 6 +++--- lib/SIL/IR/OperandOwnership.cpp | 11 ----------- lib/SIL/Utils/MemAccessUtils.cpp | 1 - lib/SILOptimizer/Analysis/RegionAnalysis.cpp | 1 - 5 files changed, 4 insertions(+), 17 deletions(-) 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/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/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/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 56bde823a2cca..bfc513617694c 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/SILOptimizer/Analysis/RegionAnalysis.cpp b/lib/SILOptimizer/Analysis/RegionAnalysis.cpp index 88e4e0fe3516e..3a4e586ab99e5 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 From 6f1ee54885b5c00eeb3b9a5f500085cf34640a69 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 22 Aug 2025 23:17:54 -0400 Subject: [PATCH 21/39] [SIL] Remove `AssignByWrapper` suport from SIL parsing and serialization --- lib/SIL/IR/SILInstructions.cpp | 9 ---- lib/SIL/IR/SILPrinter.cpp | 21 -------- lib/SIL/Parser/ParseSIL.cpp | 52 ------------------- .../UtilityPasses/SerializeSILPass.cpp | 1 - lib/Serialization/DeserializeSIL.cpp | 1 - 5 files changed, 84 deletions(-) diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index e50b3e398f63c..1a8c50074deea 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1315,15 +1315,6 @@ 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 SelfOrLocal, SILValue Src, SILValue Initializer, SILValue Setter, diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 32d4dd60dbd2e..225afb5bf0024 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -1970,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: diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 64e94cf180e3f..ce922828f64fb 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -2049,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; @@ -4656,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/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/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index cecc81c427666..5c1269da61678 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -3023,7 +3023,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: { From 52895fefc35c38d4c88aca3a6dca1e299442d600 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 22 Aug 2025 23:20:53 -0400 Subject: [PATCH 22/39] [SIL] Remove `AssignByWrapper` references from SIL passes --- include/swift/SIL/InstructionUtils.h | 4 - lib/IRGen/IRGenSIL.cpp | 3 - lib/SIL/Verifier/SILVerifier.cpp | 41 --------- .../Mandatory/AccessEnforcementSelection.cpp | 1 - .../Mandatory/DIMemoryUseCollector.cpp | 11 +-- .../Mandatory/DefiniteInitialization.cpp | 38 +------- .../Mandatory/RawSILInstLowering.cpp | 89 ------------------- lib/SILOptimizer/Utils/SILInliner.cpp | 1 - lib/Serialization/SerializeSIL.cpp | 1 - 9 files changed, 5 insertions(+), 184 deletions(-) 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/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 34bdb94ff7a8a..612ae689c52a0 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/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index d271355b5e644..1acf851b7da16 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/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 a6704d295bb5d..f4389c6d5b285 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; @@ -1718,8 +1715,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 a385a2475d442..46fc0f51160ba 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 91a51e1b0fc56..e6f1492e0a7d2 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, @@ -465,13 +383,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/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/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 787c71f81e9e0..5772493a9b02a 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: { From ba1fd9f2425af9f753e8ac5ae7bb86224aef708b Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 22 Aug 2025 23:24:36 -0400 Subject: [PATCH 23/39] Minor Comment Cleanup --- lib/SIL/IR/TypeLowering.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index 21694781b2982..bb127e08b78f6 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -3966,7 +3966,6 @@ static CanAnyFunctionType getPropertyWrapperBackingInitializerInterfaceType( static CanAnyFunctionType getPropertyWrappedFieldInitAccessorInterfaceType( TypeConverter &TC, SILDeclRef wrappedPropertyRef) { - // TODO: May need to handle projected value input type auto *wrappedProperty = cast(wrappedPropertyRef.getDecl()); CanType wrappedValueType = wrappedProperty->getPropertyWrapperInitValueInterfaceType() From 95cf42b516d96865286709af5a487be0c561ae90 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Sat, 23 Aug 2025 10:38:53 -0400 Subject: [PATCH 24/39] [Docs] Add `assign_or_init` documentation and remove `assign_by_wrapper` --- docs/SIL/Instructions.md | 80 +++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/docs/SIL/Instructions.md b/docs/SIL/Instructions.md index 38063fbd38927..d326671beb29f 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 From d3c40c05796376ac26bceb3828d211ad20ec39da Mon Sep 17 00:00:00 2001 From: JanBaig Date: Sat, 23 Aug 2025 11:03:57 -0400 Subject: [PATCH 25/39] [Docs] Document mangling for the init accessor thunk --- docs/ABI/Mangling.rst | 1 + test/Demangle/Inputs/manglings.txt | 1 + 2 files changed, 2 insertions(+) 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/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index 65593d300a26d..f47f31e2fc2b0 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -377,6 +377,7 @@ $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 $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) -> () From 288c6d9553c5b965bbf14084c755aa6c89e60602 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Sat, 23 Aug 2025 11:06:34 -0400 Subject: [PATCH 26/39] Update `SWIFTMODULE_VERSION_MINOR` --- lib/Serialization/ModuleFormat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 4b93691c9b1c1..faa936de3a642 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 = 956; // nonisolated(nonsending) isolation was moved +const uint16_t SWIFTMODULE_VERSION_MINOR = 959; // nonisolated(nonsending) isolation was moved /// A standard hash seed used for all string hashes in a serialized module. /// From 63888e34d93ab7580ce973234d7a8b8934069872 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Mon, 25 Aug 2025 22:01:29 -0400 Subject: [PATCH 27/39] Update `SWIFTMODULE_VERSION_MINOR` comment --- lib/SILGen/SILGen.cpp | 1 - lib/Serialization/ModuleFormat.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 00c2f40e326d2..3a9edbf6bc513 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1143,7 +1143,6 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { } case SILDeclRef::Kind::PropertyWrappedFieldInitAccessor: { - auto *var = cast(constant.getDecl()); auto loc = RegularLocation::getAutoGeneratedLocation(var); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index ecdb61eae0874..16fffde8249fb 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -59,7 +59,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// 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 = 959; // 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. /// From 66b38a63b7c59d32804ee2d1114711e3b15d5ecc Mon Sep 17 00:00:00 2001 From: JanBaig Date: Tue, 26 Aug 2025 00:21:04 -0400 Subject: [PATCH 28/39] Refactor `lowerAssignOrInitInstruction` --- .../Mandatory/RawSILInstLowering.cpp | 50 +++++++++---------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp index e6f1492e0a7d2..0719ccd26ef41 100644 --- a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp +++ b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp @@ -197,7 +197,7 @@ 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(); @@ -210,20 +210,7 @@ lowerAssignOrInitInstruction(SILBuilderWithScope &b, bool isRefSelf = selfOrLocalValue->getType().getASTType()->mayHaveSuperclass(); - SILValue selfRef = nullptr; - if (!inLocalContext) { - if (isRefSelf) { - selfRef = b.emitBeginBorrowOperation(loc, selfOrLocalValue); - } else { - selfRef = - b.createBeginAccess(loc, selfOrLocalValue, SILAccessKind::Modify, - SILAccessEnforcement::Dynamic, - /*noNestedConflict=*/false, - /*fromBuiltin=*/false); - } - } - - auto emitFieldReference = [&](VarDecl *field, + auto emitFieldReference = [&](SILValue selfRef, VarDecl *field, bool emitDestroy = false) -> SILValue { SILValue fieldRef; if (isRefSelf) { @@ -242,17 +229,26 @@ lowerAssignOrInitInstruction(SILBuilderWithScope &b, // First, emit all of the properties listed in `initializes`. They // are passed as indirect results. - { - if (inLocalContext) { - // add the local projection which is for the _x backing local storage - arguments.push_back(selfOrLocalValue); + SILValue selfRef = nullptr; + 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 { - auto toInitialize = inst->getInitializedProperties(); - for (unsigned index : indices(toInitialize)) { - arguments.push_back(emitFieldReference( - toInitialize[index], - /*emitDestroy=*/inst->isPropertyAlreadyInitialized(index))); - } + selfRef = + b.createBeginAccess(loc, selfOrLocalValue, SILAccessKind::Modify, + SILAccessEnforcement::Dynamic, + /*noNestedConflict=*/false, + /*fromBuiltin=*/false); + } + + auto toInitialize = inst->getInitializedProperties(); + for (unsigned index : indices(toInitialize)) { + arguments.push_back(emitFieldReference( + selfRef, toInitialize[index], + /*emitDestroy=*/inst->isPropertyAlreadyInitialized(index))); } } @@ -263,11 +259,11 @@ 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 (!inLocalContext) { + if (selfRef) { if (isRefSelf) { if (selfRef != selfOrLocalValue) b.emitEndBorrowOperation(loc, selfRef); From afe83fe951b49609b1d60d22134ab28e7afd72ad Mon Sep 17 00:00:00 2001 From: JanBaig Date: Tue, 26 Aug 2025 00:28:05 -0400 Subject: [PATCH 29/39] Simple refactors to `getSILFunctionTypeForInitAccessor` --- lib/SIL/IR/SILFunctionType.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 0a7b8f3428210..5bbb7c889037e 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2868,16 +2868,7 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor( AbstractionPattern origType, CanAnyFunctionType substAccessorType, SILExtInfoBuilder extInfoBuilder, const Conventions &conventions, SILDeclRef constant) { - AccessorDecl *accessor = nullptr; - VarDecl *varDecl = nullptr; - if (auto *accessorTmp = dyn_cast(constant.getDecl())) { - accessor = accessorTmp; - } else { - varDecl = cast(constant.getDecl()); - } - auto declContext = - accessor ? accessor->getDeclContext() : varDecl->getDeclContext(); - + auto *declContext = constant.getInnermostDeclContext(); CanGenericSignature genericSig = substAccessorType.getOptGenericSignature(); std::optional contextRAII; @@ -2920,7 +2911,7 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor( // accessed properties appear as `inout` parameters because they could be // read from and modified. - if (accessor) { + if (auto *accessor = dyn_cast(constant.getDecl())) { for (auto *property : accessor->getAccessedProperties()) { inputs.push_back(SILParameterInfo(getLoweredTypeOfProperty(property), ParameterConvention::Indirect_Inout)); @@ -2929,8 +2920,6 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor( // Make a new 'self' parameter. if (!declContext->isLocalContext()) { - auto declContext = - accessor ? accessor->getDeclContext() : varDecl->getDeclContext(); auto selfInterfaceType = MetatypeType::get(declContext->getSelfInterfaceType()); AbstractionPattern origSelfType(genericSig, @@ -2945,12 +2934,13 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor( // initialized properties appear as `@out` results because they are // initialized by the accessor. - if (accessor) { + 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)); From f4d9c7bb04425d85b5d3e5334ac413505011adf5 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Tue, 26 Aug 2025 00:48:48 -0400 Subject: [PATCH 30/39] Add `getDeclContextOrNull` to `AssignOrInitInst` --- include/swift/SIL/SILInstruction.h | 1 + lib/SIL/IR/SILInstructions.cpp | 6 ++++++ lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp | 7 ++----- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 1e789b5969002..3fc7979b8d8d9 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -5310,6 +5310,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/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 90c67f69088c1..6ef31e443c978 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1355,6 +1355,12 @@ 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(); } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index f4389c6d5b285..2bf2b6615df46 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -1242,11 +1242,8 @@ ElementUseCollector::collectAssignOrInitUses(AssignOrInitInst *Inst, llvm::SaveAndRestore X(IsSelfOfNonDelegatingInitializer, false); NominalTypeDecl *typeDC = nullptr; - if (auto accessorDecl = Inst->getReferencedInitAccessor()) { - typeDC = accessorDecl->getDeclContext()->getSelfNominalTypeDecl(); - } else { - typeDC = Inst->getProperty()->getDeclContext()->getSelfNominalTypeDecl(); - } + if (auto declContext = Inst->getDeclContextOrNull()->getSelfNominalTypeDecl()) + typeDC = declContext; auto expansionContext = TypeExpansionContext(TheMemory.getFunction()); auto selfOrLocalTy = Inst->getSelfOrLocalOperand()->getType(); From be6e6e50a7c681ff228ce3ae39a188de9551a1bc Mon Sep 17 00:00:00 2001 From: JanBaig Date: Tue, 26 Aug 2025 08:16:04 -0400 Subject: [PATCH 31/39] Use callback to avoid repeated allocation when collecting initialized properties --- include/swift/SIL/SILInstruction.h | 3 +- lib/SIL/IR/SILInstructions.cpp | 34 +++++++++---------- .../Mandatory/DIMemoryUseCollector.cpp | 7 ++-- .../Mandatory/RawSILInstLowering.cpp | 9 ++--- 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 3fc7979b8d8d9..ee059d57f4ba0 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -5302,7 +5302,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(); } diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 6ef31e443c978..4a0974af6d38a 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1328,13 +1328,13 @@ AssignOrInitInst::AssignOrInitInst(SILDebugLocation Loc, VarDecl *P, } 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) { @@ -1362,20 +1362,20 @@ DeclContext *AssignOrInitInst::getDeclContextOrNull() const { } 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(); - else { - // Dealing wtih an init accessor thunk, only initializes backing property - // FIXME: Tempory solution below, cannot allocate each time this function is - // called - SmallVector res; +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(); - res.push_back(backingVar); - return Property->getASTContext().AllocateCopy(res); + callback(backingVar); } } diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index 2bf2b6615df46..3ca1f6c06661c 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -1275,8 +1275,7 @@ ElementUseCollector::collectAssignOrInitUses(AssignOrInitInst *Inst, 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(); @@ -1294,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()) diff --git a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp index 0719ccd26ef41..27eeca28cab05 100644 --- a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp +++ b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp @@ -244,12 +244,13 @@ lowerAssignOrInitInstruction(SILBuilderWithScope &b, /*fromBuiltin=*/false); } - auto toInitialize = inst->getInitializedProperties(); - for (unsigned index : indices(toInitialize)) { + unsigned index = 0; + inst->forEachInitializedProperty([&](VarDecl *property) { arguments.push_back(emitFieldReference( - selfRef, toInitialize[index], + selfRef, property, /*emitDestroy=*/inst->isPropertyAlreadyInitialized(index))); - } + index++; + }); } // Now emit `initialValue` which is the only argument specified From 78fc5cdc2c47708032edd0ac91763dfdb3b23a43 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Tue, 26 Aug 2025 08:17:13 -0400 Subject: [PATCH 32/39] Add local context test for demangling --- test/Demangle/Inputs/manglings.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index f47f31e2fc2b0..6605837628398 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -378,6 +378,7 @@ $s3red4testyAA3ResOyxSayq_GAEs5ErrorAAq_sAFHD1__HCg_GADyxq_GsAFR_r0_lF ---> red. $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) -> () From 4a3938bf9cfa7d10bb8a0c9eb776720ef80e32ab Mon Sep 17 00:00:00 2001 From: JanBaig Date: Tue, 26 Aug 2025 14:23:21 -0400 Subject: [PATCH 33/39] Fix: avoid `getInnermostDeclContext()` --- lib/SIL/IR/SILFunctionType.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 5bbb7c889037e..c752983a8a052 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2868,7 +2868,10 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor( AbstractionPattern origType, CanAnyFunctionType substAccessorType, SILExtInfoBuilder extInfoBuilder, const Conventions &conventions, SILDeclRef constant) { - auto *declContext = constant.getInnermostDeclContext(); + auto *decl = constant.getDecl(); + DeclContext *declContext = isa(decl) + ? cast(decl)->getDeclContext() + : cast(decl)->getDeclContext(); CanGenericSignature genericSig = substAccessorType.getOptGenericSignature(); std::optional contextRAII; From 3a604026710c3ddd4e09ccb48112fdbaa1f150db Mon Sep 17 00:00:00 2001 From: JanBaig Date: Tue, 26 Aug 2025 14:35:17 -0400 Subject: [PATCH 34/39] Rename `opaque_values_closures2` test file --- ...es_closures2.swift => assign_or_init_without_opaque_sil.swift} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/SILGen/{opaque_values_closures2.swift => assign_or_init_without_opaque_sil.swift} (100%) diff --git a/test/SILGen/opaque_values_closures2.swift b/test/SILGen/assign_or_init_without_opaque_sil.swift similarity index 100% rename from test/SILGen/opaque_values_closures2.swift rename to test/SILGen/assign_or_init_without_opaque_sil.swift From a1f0b8b98d052b34578691457a0eeb58f2e0d08a Mon Sep 17 00:00:00 2001 From: JanBaig Date: Tue, 26 Aug 2025 16:27:35 -0400 Subject: [PATCH 35/39] Refactor `declContext` extraction --- lib/SIL/IR/SILFunctionType.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index c752983a8a052..aefa64734f876 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2868,10 +2868,7 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor( AbstractionPattern origType, CanAnyFunctionType substAccessorType, SILExtInfoBuilder extInfoBuilder, const Conventions &conventions, SILDeclRef constant) { - auto *decl = constant.getDecl(); - DeclContext *declContext = isa(decl) - ? cast(decl)->getDeclContext() - : cast(decl)->getDeclContext(); + auto *declContext = constant.getDecl()->getDeclContext(); CanGenericSignature genericSig = substAccessorType.getOptGenericSignature(); std::optional contextRAII; From 4effb83bc0f12cc63d4fb352353cf8e1c4b531ea Mon Sep 17 00:00:00 2001 From: JanBaig Date: Wed, 27 Aug 2025 20:21:37 -0400 Subject: [PATCH 36/39] Refactor SILOptimizer test to use `assign_or_init` --- test/SILOptimizer/raw_sil_inst_lowering.sil | 406 ++++++++++++++------ 1 file changed, 285 insertions(+), 121 deletions(-) diff --git a/test/SILOptimizer/raw_sil_inst_lowering.sil b/test/SILOptimizer/raw_sil_inst_lowering.sil index 04818feebabf2..f7ff7c2160c86 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: [[INIT_REF:%.*]] = function_ref @$s4main9RefStructV7wrappedAA9SomeClassCvpfF : $@convention(thin) (@owned SomeClass, @thin RefStruct.Type) -> @out Wrapper +// CHECK: [[INIT_PAI:%.*]] = partial_apply [callee_guaranteed] [[INIT_REF]]({{%.*}}) +// CHECK: [[SELF_REF:%.*]] = begin_access [modify] [dynamic] {{%.*}} : $*RefStruct +// CHECK-NEXT: [[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 +} From d71cde993b884217c84207a2e8f4812b7dbe3a96 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Wed, 27 Aug 2025 20:32:16 -0400 Subject: [PATCH 37/39] Update mangling due to file name change --- test/SILGen/assign_or_init_without_opaque_sil.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/SILGen/assign_or_init_without_opaque_sil.swift b/test/SILGen/assign_or_init_without_opaque_sil.swift index 56a214c039759..5fd3b6b7b26f4 100644 --- a/test/SILGen/assign_or_init_without_opaque_sil.swift +++ b/test/SILGen/assign_or_init_without_opaque_sil.swift @@ -7,7 +7,7 @@ @propertyWrapper struct BoxWrapper { var wrappedValue: T } -// CHECK-LABEL: sil {{.*}}[ossa] @$s23opaque_values_closures235captureBoxNonopaqueOwnedNonescapingyyxyXElF : {{.*}} { +// 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]] @@ -15,16 +15,16 @@ // 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 @$s23opaque_values_closures235captureBoxNonopaqueOwnedNonescapingyyxyXElF5localL_yylF +// 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 '$s23opaque_values_closures235captureBoxNonopaqueOwnedNonescapingyyxyXElF' +// CHECK-LABEL: } // end sil function '$s33assign_or_init_without_opaque_sil35captureBoxNonopaqueOwnedNonescapingyyxyXElF' -// CHECK-LABEL: sil {{.*}}[ossa] @$s23opaque_values_closures235captureBoxNonopaqueOwnedNonescapingyyxyXElF5localL_yylF : {{.*}} +// 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 '$s23opaque_values_closures235captureBoxNonopaqueOwnedNonescapingyyxyXElF5localL_yylF' +// CHECK-LABEL: } // end sil function '$s33assign_or_init_without_opaque_sil35captureBoxNonopaqueOwnedNonescapingyyxyXElF5localL_yylF' func captureBoxNonopaqueOwnedNonescaping(_ get: () -> U) { @BoxWrapper var u: U From ac8d630efb3ed9f05f6b8e2cafcd62d11797e2f5 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Fri, 29 Aug 2025 16:48:19 -0400 Subject: [PATCH 38/39] Remove newline --- lib/Serialization/ModuleFormat.h | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 16fffde8249fb..9c987c8dddb67 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,6 @@ 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 = 959; // Removed assign_by_wrapper SILGen instruction /// A standard hash seed used for all string hashes in a serialized module. From 48cedb5e72a954ae6d1bc4dba2a547f3f22d7693 Mon Sep 17 00:00:00 2001 From: JanBaig Date: Wed, 3 Sep 2025 10:30:57 -0400 Subject: [PATCH 39/39] Fix `RawSILInstLowering` test --- test/SILOptimizer/raw_sil_inst_lowering.sil | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/SILOptimizer/raw_sil_inst_lowering.sil b/test/SILOptimizer/raw_sil_inst_lowering.sil index f7ff7c2160c86..4c249b88d13d5 100644 --- a/test/SILOptimizer/raw_sil_inst_lowering.sil +++ b/test/SILOptimizer/raw_sil_inst_lowering.sil @@ -198,10 +198,10 @@ bb0(%0 : $*T, %1 : $*GenericRefStruct): // 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: [[SELF_REF:%.*]] = begin_access [modify] [dynamic] {{%.*}} : $*RefStruct -// CHECK-NEXT: [[FIELD_REF:%.*]] = struct_element_addr [[SELF_REF]] : $*RefStruct, #RefStruct._wrapped +// 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):