Skip to content

Commit aa52e42

Browse files
committed
[SILGen] InitAccessors: Start emitting init property initialization expressions
Similar to regular stored properties emit initialization expressions for properties with init accessors at constructor's prolog.
1 parent 1f87ee8 commit aa52e42

File tree

4 files changed

+268
-3
lines changed

4 files changed

+268
-3
lines changed

lib/SILGen/SILGenConstructor.cpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,49 @@ emitAndStoreInitialValueInto(SILGenFunction &SGF,
14591459
std::move(result).forwardInto(SGF, loc, init);
14601460
}
14611461

1462+
void SILGenFunction::emitMemberInitializationViaInitAccessor(
1463+
DeclContext *dc, VarDecl *selfDecl, PatternBindingDecl *member,
1464+
SubstitutionMap subs) {
1465+
auto *var = member->getSingleVar();
1466+
assert(var->hasInitAccessor());
1467+
1468+
auto init = member->getExecutableInit(0);
1469+
if (!init)
1470+
return;
1471+
1472+
auto *varPattern = member->getPattern(0);
1473+
1474+
// Cleanup after this initialization.
1475+
FullExpr scope(Cleanups, varPattern);
1476+
1477+
auto resultType =
1478+
getInitializationTypeInContext(member->getDeclContext(), dc, varPattern);
1479+
1480+
RValue initResult = emitApplyOfStoredPropertyInitializer(
1481+
init, var, subs, resultType.second, resultType.first, SGFContext());
1482+
1483+
SILLocation loc(init);
1484+
loc.markAutoGenerated();
1485+
1486+
auto selfValue = emitSelfForMemberInit(*this, varPattern, selfDecl);
1487+
1488+
ManagedValue selfRef = selfValue;
1489+
if (selfValue.isLValue()) {
1490+
auto accessToSelf =
1491+
B.createBeginAccess(loc, selfValue.getValue(), SILAccessKind::Modify,
1492+
SILAccessEnforcement::Unknown,
1493+
/*noNestedConflict=*/false,
1494+
/*fromBuiltin=*/false);
1495+
selfRef = ManagedValue::forUnmanaged(accessToSelf);
1496+
}
1497+
1498+
emitAssignOrInit(loc, selfRef, var,
1499+
std::move(initResult).getAsSingleValue(*this, loc), subs);
1500+
1501+
if (selfValue.isLValue())
1502+
B.createEndAccess(loc, selfRef.getValue(), /*aborted=*/false);
1503+
}
1504+
14621505
void SILGenFunction::emitMemberInitializers(DeclContext *dc,
14631506
VarDecl *selfDecl,
14641507
NominalTypeDecl *nominal) {
@@ -1469,11 +1512,12 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc,
14691512
if (auto pbd = dyn_cast<PatternBindingDecl>(member)) {
14701513
if (pbd->isStatic()) continue;
14711514

1472-
// Skip properties with init accessors, they could only be used
1473-
// explicitly and in memberwise initializers.
1515+
// Emit default initialization for an init accessor property.
14741516
if (auto *var = pbd->getSingleVar()) {
1475-
if (var->hasInitAccessor())
1517+
if (var->hasInitAccessor()) {
1518+
emitMemberInitializationViaInitAccessor(dc, selfDecl, pbd, subs);
14761519
continue;
1520+
}
14771521
}
14781522

14791523
for (auto i : range(pbd->getNumPatternEntries())) {

lib/SILGen/SILGenFunction.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
802802
void emitMemberInitializers(DeclContext *dc, VarDecl *selfDecl,
803803
NominalTypeDecl *nominal);
804804

805+
void emitMemberInitializationViaInitAccessor(DeclContext *dc,
806+
VarDecl *selfDecl,
807+
PatternBindingDecl *member,
808+
SubstitutionMap subs);
809+
805810
/// Emit a method that initializes the ivars of a class.
806811
void emitIVarInitializer(SILDeclRef ivarInitializer);
807812

test/Interpreter/init_accessors.swift

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,3 +586,104 @@ func test_memberwise_without_stored_properties() {
586586
test_memberwise_without_stored_properties()
587587
// CHECK: no-stored: a = 1
588588
// CHECK-NEXT: no-stored: b = 2
589+
590+
protocol P {
591+
static var initialValue: Self { get }
592+
}
593+
594+
func test_properties_with_inits() {
595+
struct S: P, CustomStringConvertible {
596+
var x: Int
597+
598+
static var initialValue: S { S(x: 42) }
599+
600+
var description: String {
601+
"S(x: \(x))"
602+
}
603+
}
604+
605+
final class K: P, CustomStringConvertible {
606+
var v: [String]
607+
static var initialValue: K { K(v: ["question"]) }
608+
609+
var description: String {
610+
"K(v: \(v))"
611+
}
612+
613+
init(v: [String]) {
614+
self.v = v
615+
}
616+
}
617+
618+
class Test<T: P> {
619+
var _x: T
620+
621+
var x: T = T.initialValue {
622+
@storageRestrictions(initializes: _x)
623+
init {
624+
_x = newValue
625+
}
626+
get { _x }
627+
set { _x = newValue }
628+
}
629+
630+
init() {}
631+
}
632+
633+
print("test-init-expr-1: \(Test<S>().x)")
634+
635+
struct TestPair<T: P, U: P> {
636+
var _data: (T, U)
637+
638+
var data: (T, U) = (T.initialValue, U.initialValue) {
639+
@storageRestrictions(initializes: _data)
640+
init(initialValue) {
641+
_data = initialValue
642+
}
643+
644+
get { _data }
645+
set { _data = newValue }
646+
}
647+
648+
init() {
649+
}
650+
651+
init(x: T, y: U) {
652+
self.data = (x, y)
653+
}
654+
}
655+
656+
print("test-init-expr-2: \(TestPair<S, K>().data)")
657+
print("test-init-expr-2: \(TestPair<S, K>(x: S(x: 0), y: K(v: ["a", "b", "c"])).data)")
658+
659+
struct TestAssign<T: P> {
660+
var _x: T
661+
var x: T = T.initialValue {
662+
@storageRestrictions(initializes: _x)
663+
init {
664+
_x = newValue
665+
print("TestAssign in x.init: self.x = \(_x)")
666+
}
667+
get { _x }
668+
set { _x = newValue }
669+
}
670+
var y: Int
671+
672+
init(x1: T, x2: T, y: Int) {
673+
self.x = x1
674+
self.y = y
675+
self.x = x2
676+
print("TestAssign: self.x = \(self.x)")
677+
}
678+
}
679+
680+
_ = TestAssign(x1: S(x: 0), x2: S(x: -3), y: 2)
681+
}
682+
683+
test_properties_with_inits()
684+
// CHECK: test-init-expr-1: S(x: 42)
685+
// CHECK-NEXT: test-init-expr-2: (S(x: 42), K(v: ["question"]))
686+
// CHECK-NEXT: test-init-expr-2: (S(x: 0), K(v: ["a", "b", "c"]))
687+
// CHECK-NEXT: TestAssign in x.init: self.x = S(x: 42)
688+
// CHECK-NEXT: TestAssign in x.init: self.x = S(x: 0)
689+
// CHECK-NEXT: TestAssign: self.x = S(x: -3)

test/SILOptimizer/init_accessor_raw_sil_lowering.swift

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,118 @@ struct Test {
9292
self.test = v
9393
}
9494
}
95+
96+
protocol Initializable {
97+
static var initialValue: Self { get }
98+
}
99+
100+
func test_default_inits() {
101+
struct Test1 {
102+
var _x: Int
103+
104+
var x: Int = 0 {
105+
@storageRestrictions(initializes: _x)
106+
init {
107+
_x = newValue
108+
}
109+
get { _x }
110+
set { _x = newValue }
111+
}
112+
113+
// CHECK-LABEL: sil private [ossa] @$s23assign_or_init_lowering18test_default_initsyyF5Test1L_VADycfC : $@convention(method) (@thin Test1.Type) -> Test1
114+
// CHECK: [[DEFAULT:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test1L_V1xSivpfi : $@convention(thin) () -> Int
115+
// CHECK-NEXT: [[INIT_VAL:%.*]] = apply [[DEFAULT]]() : $@convention(thin) () -> Int
116+
// CHECK-NEXT: [[SELF_REF:%.*]] = begin_access [modify] [static] %1 : $*Test1
117+
// CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test1L_V1xSivi : $@convention(thin) (Int) -> @out Int
118+
// CHECK: [[SETTER_REF:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test1L_V1xSivs : $@convention(method) (Int, @inout Test1) -> ()
119+
// CHECK-NEXT: [[SETTER:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]([[SELF_REF]]) : $@convention(method) (Int, @inout Test1) -> ()
120+
// CHECK-NEXT: assign_or_init [init] self [[SELF_REF]] : $*Test1, value [[INIT_VAL]] : $Int, init [[INIT_ACCESSOR]] : $@convention(thin) (Int) -> @out Int, set [[SETTER]] : $@callee_guaranteed (Int) -> ()
121+
// CHECK-NEXT: end_access [[SELF_REF]] : $*Test1
122+
// CHECK-NEXT: destroy_value [[SETTER]] : $@callee_guaranteed (Int) -> ()
123+
init() {
124+
}
125+
126+
// CHECK-LABEL: sil private [ossa] @$s23assign_or_init_lowering18test_default_initsyyF5Test1L_V1xADSi_tcfC : $@convention(method) (Int, @thin Test1.Type) -> Test1
127+
// CHECK: [[DEFAULT:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test1L_V1xSivpfi : $@convention(thin) () -> Int
128+
// CHECK-NEXT: [[INIT_VAL:%.*]] = apply [[DEFAULT]]() : $@convention(thin) () -> Int
129+
// CHECK-NEXT: [[SELF_REF:%.*]] = begin_access [modify] [static] %2 : $*Test1
130+
// CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test1L_V1xSivi : $@convention(thin) (Int) -> @out Int
131+
// CHECK: [[SETTER_REF:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test1L_V1xSivs : $@convention(method) (Int, @inout Test1) -> ()
132+
// CHECK-NEXT: [[SETTER:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]([[SELF_REF]]) : $@convention(method) (Int, @inout Test1) -> ()
133+
// CHECK-NEXT: assign_or_init [init] self [[SELF_REF]] : $*Test1, value [[INIT_VAL]] : $Int, init [[INIT_ACCESSOR]] : $@convention(thin) (Int) -> @out Int, set [[SETTER]] : $@callee_guaranteed (Int) -> ()
134+
// CHECK-NEXT: end_access [[SELF_REF]] : $*Test1
135+
// CHECK-NEXT: destroy_value [[SETTER]] : $@callee_guaranteed (Int) -> ()
136+
//
137+
// CHECK: assign_or_init [set] self {{.*}} : $*Test1, value %0 : $Int, init {{.*}} : $@convention(thin) (Int) -> @out Int, set {{.*}} : $@callee_guaranteed (Int) -> ()
138+
init(x: Int) {
139+
self.x = x
140+
}
141+
}
142+
143+
class Test2<T: Initializable> {
144+
var _x: (T, String)
145+
146+
var x: (T, String) = (T.initialValue, "test") {
147+
@storageRestrictions(initializes: _x)
148+
init {
149+
_x = newValue
150+
}
151+
get { _x }
152+
set { _x = newValue }
153+
}
154+
155+
var y: Int
156+
157+
// CHECK-LABEL: sil private [ossa] @$s23assign_or_init_lowering18test_default_initsyyF5Test2L_CADyxGycfc : $@convention(method) <T where T : Initializable> (@owned Test2<T>) -> @owned Test2<T>
158+
// CHECK: [[DEFAULT:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test2L_C1xx_SStvpfi : $@convention(thin) <τ_0_0 where τ_0_0 : Initializable> () -> (@out τ_0_0, @owned String)
159+
// CHECK-NEXT: [[T:%.*]] = alloc_stack $T
160+
// CHECK-NEXT: [[STR:%.*]] = apply [[DEFAULT]]<T>([[T]]) : $@convention(thin) <τ_0_0 where τ_0_0 : Initializable> () -> (@out τ_0_0, @owned String)
161+
// CHECK-NEXT: [[NEW_VALUE:%.*]] = alloc_stack $(T, String)
162+
// CHECK-NEXT: [[NEW_VALUE_0:%.*]] = tuple_element_addr [[NEW_VALUE]] : $*(T, String), 0
163+
// CHECK-NEXT: [[NEW_VALUE_1:%.*]] = tuple_element_addr [[NEW_VALUE]] : $*(T, String), 1
164+
// CHECK-NEXT: copy_addr [take] [[T]] to [init] [[NEW_VALUE_0]] : $*T
165+
// CHECK-NEXT: store [[STR]] to [init] [[NEW_VALUE_1]] : $*String
166+
// CHECK: [[INIT_ACCESSOR_REF:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test2L_C1xx_SStvi : $@convention(thin) <τ_0_0 where τ_0_0 : Initializable> (@in τ_0_0, @owned String) -> @out (τ_0_0, String)
167+
// CHECK-NEXT: [[INIT_ACCESSOR:%.*]] = partial_apply [callee_guaranteed] [[INIT_ACCESSOR_REF]]<T>() : $@convention(thin) <τ_0_0 where τ_0_0 : Initializable> (@in τ_0_0, @owned String) -> @out (τ_0_0, String)
168+
// CHECK: [[SETTER_REF:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test2L_C1xx_SStvs : $@convention(method) <τ_0_0 where τ_0_0 : Initializable> (@in τ_0_0, @owned String, @guaranteed Test2<τ_0_0>) -> ()
169+
// CHECK-NEXT: [[SELF:%.*]] = copy_value %0 : $Test2<T>
170+
// CHECK-NEXT: [[SETTER:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]<T>([[SELF]]) : $@convention(method) <τ_0_0 where τ_0_0 : Initializable> (@in τ_0_0, @owned String, @guaranteed Test2<τ_0_0>) -> ()
171+
// CHECK-NEXT: assign_or_init [init] self %0 : $Test2<T>, value [[NEW_VALUE]] : $*(T, String), init [[INIT_ACCESSOR]] : $@callee_guaranteed (@in T, @owned String) -> @out (T, String), set [[SETTER]] : $@callee_guaranteed (@in T, @owned String) -> ()
172+
// CHECK-NEXT: destroy_value [[SETTER]] : $@callee_guaranteed (@in T, @owned String) -> ()
173+
// CHECK-NEXT: destroy_value [[INIT_ACCESSOR]] : $@callee_guaranteed (@in T, @owned String) -> @out (T, String)
174+
// CHECK-NEXT: dealloc_stack [[NEW_VALUE]] : $*(T, String)
175+
// CHECK-NEXT: dealloc_stack [[T]] : $*T
176+
init() {
177+
y = 10
178+
}
179+
180+
// CHECK-LABEL: sil private [ossa] @$s23assign_or_init_lowering18test_default_initsyyF5Test2L_C1x1yADyxGx_SSt_Sitcfc : $@convention(method) <T where T : Initializable> (@in T, @owned String, Int, @owned Test2<T>) -> @owned Test2<T>
181+
// CHECK: [[DEFAULT:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test2L_C1xx_SStvpfi : $@convention(thin) <τ_0_0 where τ_0_0 : Initializable> () -> (@out τ_0_0, @owned String)
182+
// CHECK-NEXT: [[T:%.*]] = alloc_stack $T
183+
// CHECK-NEXT: [[STR:%.*]] = apply [[DEFAULT]]<T>([[T]]) : $@convention(thin) <τ_0_0 where τ_0_0 : Initializable> () -> (@out τ_0_0, @owned String)
184+
// CHECK-NEXT: [[NEW_VALUE:%.*]] = alloc_stack $(T, String)
185+
// CHECK-NEXT: [[NEW_VALUE_0:%.*]] = tuple_element_addr [[NEW_VALUE]] : $*(T, String), 0
186+
// CHECK-NEXT: [[NEW_VALUE_1:%.*]] = tuple_element_addr [[NEW_VALUE]] : $*(T, String), 1
187+
// CHECK-NEXT: copy_addr [take] [[T]] to [init] [[NEW_VALUE_0]] : $*T
188+
// CHECK-NEXT: store [[STR]] to [init] [[NEW_VALUE_1]] : $*String
189+
// CHECK: [[INIT_ACCESSOR_REF:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test2L_C1xx_SStvi : $@convention(thin) <τ_0_0 where τ_0_0 : Initializable> (@in τ_0_0, @owned String) -> @out (τ_0_0, String)
190+
// CHECK-NEXT: [[INIT_ACCESSOR:%.*]] = partial_apply [callee_guaranteed] [[INIT_ACCESSOR_REF]]<T>() : $@convention(thin) <τ_0_0 where τ_0_0 : Initializable> (@in τ_0_0, @owned String) -> @out (τ_0_0, String)
191+
// CHECK: [[SETTER_REF:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test2L_C1xx_SStvs : $@convention(method) <τ_0_0 where τ_0_0 : Initializable> (@in τ_0_0, @owned String, @guaranteed Test2<τ_0_0>) -> ()
192+
// CHECK-NEXT: [[SELF:%.*]] = copy_value %3 : $Test2<T>
193+
// CHECK-NEXT: [[SETTER:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]<T>([[SELF]]) : $@convention(method) <τ_0_0 where τ_0_0 : Initializable> (@in τ_0_0, @owned String, @guaranteed Test2<τ_0_0>) -> ()
194+
// CHECK-NEXT: assign_or_init [init] self %3 : $Test2<T>, value [[NEW_VALUE]] : $*(T, String), init [[INIT_ACCESSOR]] : $@callee_guaranteed (@in T, @owned String) -> @out (T, String), set [[SETTER]] : $@callee_guaranteed (@in T, @owned String) -> ()
195+
// CHECK-NEXT: destroy_value [[SETTER]] : $@callee_guaranteed (@in T, @owned String) -> ()
196+
// CHECK-NEXT: destroy_value [[INIT_ACCESSOR]] : $@callee_guaranteed (@in T, @owned String) -> @out (T, String)
197+
// CHECK-NEXT: dealloc_stack [[NEW_VALUE]] : $*(T, String)
198+
// CHECK-NEXT: dealloc_stack [[T]] : $*T
199+
//
200+
// CHECK: assign_or_init [init] [assign=0] self %3 : $Test2<T>, value {{.*}} : $*(T, String), init {{.*}} : $@callee_guaranteed (@in T, @owned String) -> @out (T, String), set {{.*}} : $@callee_guaranteed (@in T, @owned String) -> ()
201+
// CHECK: assign %2 to [init] [[Y_REF:%.*]] : $*Int
202+
// CHECK: assign_or_init [set] self %3 : $Test2<T>, value {{.*}} : $*(T, String), init {{.*}} : $@callee_guaranteed (@in T, @owned String) -> @out (T, String), set {{.*}} : $@callee_guaranteed (@in T, @owned String) -> ()
203+
init(x: (T, String), y: Int) {
204+
self.x = x
205+
self.y = y
206+
self.x = x
207+
}
208+
}
209+
}

0 commit comments

Comments
 (0)