From f4f99ba4ff1dbcc32a1b7d609563ed2b40c3a696 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 26 Sep 2025 15:39:38 -0700 Subject: [PATCH] [SIL] Allow init accessor declarations in constrained extensions While emitting a function type for init accessor reference "self" parameter should always be mapped into the context. When referencing an init accessor in an constrained extension context, substitution map for the original property cannot always be used when applying "self" parameter, emission logic eeds to check whether context makes the init accessor reference "concrete" and if so, avoid using the substitution map when forming partial application for "self" parameter. Resolves: rdar://160816474 --- lib/SIL/IR/SILFunctionType.cpp | 6 +- lib/SILGen/SILGenFunction.cpp | 18 +++-- ...t_accessor_in_constrained_extensions.swift | 73 +++++++++++++++++++ 3 files changed, 88 insertions(+), 9 deletions(-) create mode 100644 test/SILOptimizer/init_accessor_in_constrained_extensions.swift diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index a32423aeb18a9..0fc26a1c02f70 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2955,8 +2955,10 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor( // Make a new 'self' parameter. if (!declContext->isLocalContext()) { - auto selfInterfaceType = - MetatypeType::get(declContext->getSelfInterfaceType()); + auto selfInterfaceType = MetatypeType::get( + (genericSig) + ? declContext->getSelfInterfaceType()->getReducedType(genericSig) + : declContext->getSelfTypeInContext()); AbstractionPattern origSelfType(genericSig, selfInterfaceType->getCanonicalType()); auto loweredSelfType = TC.getLoweredType( diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 37b5205224f8b..e39ebbfc0b8e5 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -1931,11 +1931,6 @@ void SILGenFunction::emitAssignOrInit(SILLocation loc, ManagedValue selfValue, auto initTy = initFRef->getType().castTo(); - // If there are substitutions we need to emit partial apply to - // apply substitutions to the init accessor reference type. - initTy = initTy->substGenericArgs(SGM.M, substitutions, - getTypeExpansionContext()); - // Emit partial apply with self metatype argument to produce a substituted // init accessor reference. auto selfTy = selfValue.getType().getASTType(); @@ -1949,8 +1944,9 @@ void SILGenFunction::emitAssignOrInit(SILLocation loc, ManagedValue selfValue, selfMetatype = B.createMetatype(loc, getLoweredType(metatypeTy)); } - auto expectedSelfTy = initAccessor->getDeclContext()->getSelfInterfaceType() - .subst(substitutions); + auto expectedSelfTy = + initAccessor->getDeclContext()->getSelfInterfaceType().subst( + substitutions); // This should only happen in the invalid case where we attempt to initialize // superclass storage from a subclass initializer. However, we shouldn't @@ -1960,6 +1956,14 @@ void SILGenFunction::emitAssignOrInit(SILLocation loc, ManagedValue selfValue, selfMetatype = B.createUpcast(loc, selfMetatype, getLoweredType(MetatypeType::get(expectedSelfTy))); } + + if (auto invocationSig = initTy->getInvocationGenericSignature()) { + if (invocationSig->areAllParamsConcrete()) + substitutions = SubstitutionMap(); + } else { + substitutions = SubstitutionMap(); + } + PartialApplyInst *initPAI = B.createPartialApply(loc, initFRef, substitutions, selfMetatype, ParameterConvention::Direct_Guaranteed, diff --git a/test/SILOptimizer/init_accessor_in_constrained_extensions.swift b/test/SILOptimizer/init_accessor_in_constrained_extensions.swift new file mode 100644 index 0000000000000..a8337ee955b4b --- /dev/null +++ b/test/SILOptimizer/init_accessor_in_constrained_extensions.swift @@ -0,0 +1,73 @@ +// RUN: %target-swift-frontend -Xllvm -sil-print-types -Xllvm -sil-print-after=definite-init -emit-sil -module-name assign_or_init_lowering %s -o /dev/null 2>&1 | %FileCheck %s + +struct S1 { +} + +extension S1 where T == Int { + class Test { + var test1: Int { + init(initialValue) { } + set {} + get { 0 } + } + + var test2: T { + init(initialValue) { } + set {} + get { 0 } + } + + // CHECK-LABEL: sil hidden [ossa] @$s23assign_or_init_lowering2S1VAASiRszlE4TestCAEySi_Gycfc : $@convention(method) (@owned S1.Test) -> @owned S1.Test + // + // CHECK: [[TEST1_INIT_REF:%.*]] = function_ref @$s23assign_or_init_lowering2S1VAASiRszlE4TestC5test1Sivi : $@convention(thin) (Int, @thick S1.Test.Type) -> () + // CHECK-NEXT: [[SELF:%.*]] = value_metatype $@thick S1.Test.Type, {{.*}} : $S1.Test + // CHECK-NEXT: [[TEST1_INIT_REF_WITH_SELF_APPLIED:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[TEST1_INIT_REF]]([[SELF]]) : $@convention(thin) (Int, @thick S1.Test.Type) -> () + // CHECK: assign_or_init [init] #S1.Test.test1, self {{.*}} : $S1.Test, value {{.*}} : $Int, init [[TEST1_INIT_REF_WITH_SELF_APPLIED]] : $@noescape @callee_guaranteed (Int) -> (), set {{.*}} : $@noescape @callee_guaranteed (Int) -> () + // + // CHECK: [[TEST2_INIT_REF:%.*]] = function_ref @$s23assign_or_init_lowering2S1VAASiRszlE4TestC5test2Sivi : $@convention(thin) (Int, @thick S1.Test.Type) -> () + // CHECK-NEXT: [[SELF:%.*]] = value_metatype $@thick S1.Test.Type, {{.*}} : $S1.Test + // CHECK-NEXT: [[TEST2_INIT_REF_WITH_SELF_APPLIED:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[TEST2_INIT_REF]]([[SELF]]) : $@convention(thin) (Int, @thick S1.Test.Type) -> () + // CHECK: assign_or_init [init] #S1.Test.test2, self {{.*}} : $S1.Test, value {{.*}} : $Int, init [[TEST2_INIT_REF_WITH_SELF_APPLIED]] : $@noescape @callee_guaranteed (Int) -> (), set {{.*}} : $@noescape @callee_guaranteed (Int) -> () + // CHECK: } // end sil function '$s23assign_or_init_lowering2S1VAASiRszlE4TestCAEySi_Gycfc' + init() { + test1 = 0 + test2 = 1 + } + } +} + +struct S2 { +} + +extension S2 where T == Int { + class Test { + var test1: T { + init(initialValue) { } + set {} + get { 0 } + } + + var test2: U { + init(initialValue) { } + set {} + get { fatalError() } + } + + // CHECK-LABEL: sil hidden [ossa] @$s23assign_or_init_lowering2S2VAASiRszrlE4TestC1uAEySiq__Gq__tcfc : $@convention(method) (@in U, @owned S2.Test) -> @owned S2.Test { + // + // CHECK: [[TEST1_INIT_REF:%.*]] = function_ref @$s23assign_or_init_lowering2S2VAASiRszrlE4TestC5test1Sivi : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == Int> (Int, @thick S2.Test.Type) -> () + // CHECK-NEXT: [[SELF:%.*]] = value_metatype $@thick S2.Test.Type, {{.*}} : $S2.Test + // CHECK-NEXT: [[TEST1_INIT_REF_WITH_SELF_APPLIED:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[TEST1_INIT_REF]]([[SELF]]) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == Int> (Int, @thick S2.Test.Type) -> () + // CHECK: assign_or_init [init] #S2.Test.test1, self {{.*}} : $S2.Test, value {{.*}} : $Int, init [[TEST1_INIT_REF_WITH_SELF_APPLIED]] : $@noescape @callee_guaranteed (Int) -> (), set {{.*}} : $@noescape @callee_guaranteed (Int) -> () + // + // CHECK: [[TEST2_INIT_REF:%.*]] = function_ref @$s23assign_or_init_lowering2S2VAASiRszrlE4TestC5test2q_vi : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == Int> (@in τ_0_1, @thick S2.Test.Type) -> () + // CHECK-NEXT: [[SELF:%.*]] = value_metatype $@thick S2.Test.Type, {{.*}} : $S2.Test + // CHECK-NEXT: [[TEST2_INIT_REF_WITH_SELF_APPLIED:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[TEST2_INIT_REF]]([[SELF]]) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == Int> (@in τ_0_1, @thick S2.Test.Type) -> () + // CHECK: assign_or_init [init] #S2.Test.test2, self {{.*}} : $S2.Test, value {{.*}} : $*U, init [[TEST2_INIT_REF_WITH_SELF_APPLIED]] : $@noescape @callee_guaranteed (@in U) -> (), set {{.*}} : $@noescape @callee_guaranteed (@in U) -> () + // CHECK: } // end sil function '$s23assign_or_init_lowering2S2VAASiRszrlE4TestC1uAEySiq__Gq__tcfc' + init(u: U) { + test1 = 0 + test2 = u + } + } +}