Skip to content

Commit ffeae0e

Browse files
committed
[SILGenConstructor] InitAccessors: Make sure that accessed fields are initialized before init accessors
Initializations for all of the fields accessed by init accessor should be emitted before init accessor property even if they are declared after it in the source order. Resolves: rdar://112984795 (cherry picked from commit f6017cc)
1 parent bce62a7 commit ffeae0e

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

lib/SILGen/SILGenConstructor.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,14 +1577,28 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc,
15771577
NominalTypeDecl *nominal) {
15781578
auto subs = getSubstitutionsForPropertyInitializer(dc, nominal);
15791579

1580+
llvm::SmallPtrSet<PatternBindingDecl *, 4> alreadyInitialized;
15801581
for (auto member : nominal->getImplementationContext()->getAllMembers()) {
15811582
// Find instance pattern binding declarations that have initializers.
15821583
if (auto pbd = dyn_cast<PatternBindingDecl>(member)) {
15831584
if (pbd->isStatic()) continue;
15841585

1586+
if (alreadyInitialized.count(pbd))
1587+
continue;
1588+
15851589
// Emit default initialization for an init accessor property.
15861590
if (auto *var = pbd->getSingleVar()) {
15871591
if (var->hasInitAccessor()) {
1592+
auto initAccessor = var->getAccessor(AccessorKind::Init);
1593+
1594+
// Make sure that initializations for the accessed properties
1595+
// are emitted before the init accessor that uses them.
1596+
for (auto *property : initAccessor->getAccessedProperties()) {
1597+
auto *PBD = property->getParentPatternBinding();
1598+
if (alreadyInitialized.insert(PBD).second)
1599+
emitMemberInitializer(dc, selfDecl, PBD, subs);
1600+
}
1601+
15881602
emitMemberInitializationViaInitAccessor(dc, selfDecl, pbd, subs);
15891603
continue;
15901604
}

test/SILOptimizer/init_accessor_raw_sil_lowering.swift

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,115 @@
22

33
// REQUIRES: asserts
44

5+
protocol Storage<T> {
6+
associatedtype T
7+
8+
func getValue<V>(_: KeyPath<T, V>) -> V
9+
func setValue<V>(_: KeyPath<T, V>, _: V)
10+
}
11+
12+
struct NoopStorage<T>: Storage {
13+
init() {}
14+
15+
func getValue<V>(_: KeyPath<T, V>) -> V { fatalError() }
16+
func setValue<V>(_: KeyPath<T, V>, _: V) {}
17+
}
18+
19+
final class TestIndirectionThroughStorage {
20+
var name: String = "item1" {
21+
@storageRestrictions(accesses: _storage)
22+
init {
23+
_storage.setValue(\.name, newValue)
24+
}
25+
get { _storage.getValue(\.name) }
26+
set { }
27+
}
28+
29+
var age: Int? = nil {
30+
@storageRestrictions(accesses: _storage)
31+
init {
32+
_storage.setValue(\.age, newValue)
33+
}
34+
35+
get { _storage.getValue(\.age) }
36+
set { }
37+
}
38+
39+
private var _storage: any Storage<TestIndirectionThroughStorage> = NoopStorage()
40+
41+
var storage: any Storage<TestIndirectionThroughStorage> {
42+
get { _storage }
43+
set { _storage = newValue }
44+
}
45+
46+
// CHECK-LABEL: sil hidden [ossa] @$s23assign_or_init_lowering29TestIndirectionThroughStorageC4name3ageACSS_Sitcfc : $@convention(method) (@owned String, Int, @owned TestIndirectionThroughStorage) -> @owned TestIndirectionThroughStorage
47+
// CHECK: [[STORAGE_REF:%.*]] = ref_element_addr {{.*}} : $TestIndirectionThroughStorage, #TestIndirectionThroughStorage._storage
48+
// CHECK: [[STORAGE_INIT:%.*]] = function_ref @$s23assign_or_init_lowering29TestIndirectionThroughStorageC8_storage33_DE106275C2F16FB3D05881E72FBD87C8LLAA0H0_pAC1TAaFPRts_XPvpfi : $@convention(thin) () -> @out any Storage<TestIndirectionThroughStorage>
49+
// CHECK-NEXT: {{.*}} = apply [[STORAGE_INIT]]([[STORAGE_REF]]) : $@convention(thin) () -> @out any Storage<TestIndirectionThroughStorage>
50+
// Initialization:
51+
// CHECK: assign_or_init [set] self %2 : $TestIndirectionThroughStorage, value {{.*}} : $String, init {{.*}} : $@convention(thin) (@owned String, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (@owned String) -> ()
52+
// CHECK: assign_or_init [set] self %2 : $TestIndirectionThroughStorage, value {{.*}} : $Optional<Int>, init {{.*}} : $@convention(thin) (Optional<Int>, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (Optional<Int>) -> (
53+
// Explicit set:
54+
// CHECK: assign_or_init [set] self %2 : $TestIndirectionThroughStorage, value {{.*}} : $String, init {{.*}} : $@convention(thin) (@owned String, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (@owned String) -> ()
55+
// CHECK: assign_or_init [set] self %2 : $TestIndirectionThroughStorage, value {{.*}} : $Optional<Int>, init {{.*}} : $@convention(thin) (Optional<Int>, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (Optional<Int>) -> (
56+
init(name: String, age: Int) {
57+
self.name = name
58+
self.age = age
59+
}
60+
61+
// CHECK-LABEL: sil hidden [ossa] @$s23assign_or_init_lowering29TestIndirectionThroughStorageC7storageAcA0H0_pAC1TAaEPRts_XP_tcfc : $@convention(method) (@in any Storage<TestIndirectionThroughStorage>, @owned TestIndirectionThroughStorage) -> @owned TestIndirectionThroughStorage
62+
// CHECK: [[STORAGE_REF:%.*]] = ref_element_addr {{.*}} : $TestIndirectionThroughStorage, #TestIndirectionThroughStorage._storage
63+
// CHECK: [[STORAGE_INIT:%.*]] = function_ref @$s23assign_or_init_lowering29TestIndirectionThroughStorageC8_storage33_DE106275C2F16FB3D05881E72FBD87C8LLAA0H0_pAC1TAaFPRts_XPvpfi : $@convention(thin) () -> @out any Storage<TestIndirectionThroughStorage>
64+
// CHECK-NEXT: {{.*}} = apply [[STORAGE_INIT]]([[STORAGE_REF]]) : $@convention(thin) () -> @out any Storage<TestIndirectionThroughStorage>
65+
// Initialization:
66+
// CHECK: assign_or_init [set] self %1 : $TestIndirectionThroughStorage, value {{.*}} : $String, init {{.*}} : $@convention(thin) (@owned String, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (@owned String) -> ()
67+
// CHECK: assign_or_init [set] self %1 : $TestIndirectionThroughStorage, value {{.*}} : $Optional<Int>, init {{.*}} : $@convention(thin) (Optional<Int>, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (Optional<Int>) -> ()
68+
// Explicit set:
69+
// CHECK: [[STORAGE_SETTER:%.*]] = function_ref @$s23assign_or_init_lowering29TestIndirectionThroughStorageC7storageAA0H0_pAC1TAaEPRts_XPvs : $@convention(method) (@in any Storage<TestIndirectionThroughStorage>, @guaranteed TestIndirectionThroughStorage) -> ()
70+
// CHECK-NEXT: {{.*}} = apply [[STORAGE_SETTER]]({{.*}}, %1) : $@convention(method) (@in any Storage<TestIndirectionThroughStorage>, @guaranteed TestIndirectionThroughStorage) -> ()
71+
init(storage: any Storage<TestIndirectionThroughStorage>) {
72+
self.storage = storage
73+
}
74+
}
75+
76+
struct TestAccessOfOnePatternVars {
77+
var data: (Int, String) = (0, "a") {
78+
@storageRestrictions(accesses: x, y)
79+
init {
80+
}
81+
82+
get { (x, y) }
83+
set {
84+
x = newValue.0
85+
y = newValue.1
86+
}
87+
}
88+
89+
var other: Bool = false {
90+
@storageRestrictions(accesses: x)
91+
init {}
92+
get { x != 0 }
93+
set {}
94+
}
95+
96+
var x: Int = 1, y: String = ""
97+
98+
// CHECK-LABEL: sil hidden [ossa] @$s23assign_or_init_lowering26TestAccessOfOnePatternVarsV1x1yACSi_SStcfC : $@convention(method) (Int, @owned String, @thin TestAccessOfOnePatternVars.Type) -> @owned TestAccessOfOnePatternVars
99+
// CHECK: [[X_REF:%.*]] = struct_element_addr {{.*}} : $*TestAccessOfOnePatternVars, #TestAccessOfOnePatternVars.x
100+
// CHECK: [[X_INIT:%.*]] = function_ref @$s23assign_or_init_lowering26TestAccessOfOnePatternVarsV1xSivpfi : $@convention(thin) () -> Int
101+
// CHECK-NEXT: {{.*}} = apply [[X_INIT]]() : $@convention(thin) () -> Int
102+
// CHECK: [[Y_REF:%.*]] = struct_element_addr {{.*}} : $*TestAccessOfOnePatternVars, #TestAccessOfOnePatternVars.y
103+
// CHECK: [[Y_INIT:%.*]] = function_ref @$s23assign_or_init_lowering26TestAccessOfOnePatternVarsV1ySSvpfi : $@convention(thin) () -> @owned String
104+
// CHECK-NEXT: {{.*}} = apply [[Y_INIT]]() : $@convention(thin) () -> @owned String
105+
// CHECK-NOT: [[X_REF:%.*]] = struct_element_addr %3 : $*TestAccessOfOnePatternVars, #TestAccessOfOnePatternVars.x
106+
// CHECK-NOT: [[Y_REF:%.*]] = struct_element_addr {{.*}} : $*TestAccessOfOnePatternVars, #TestAccessOfOnePatternVars.y
107+
// CHECK: assign_or_init [set] self {{.*}} : $*TestAccessOfOnePatternVars, value {{.*}} : $(Int, String), init {{.*}} : $@convention(thin) (Int, @owned String, @inout Int, @inout String) -> (), set {{.*}} : $@callee_guaranteed (Int, @owned String) -> ()
108+
// CHECK: assign_or_init [set] self {{.*}} : $*TestAccessOfOnePatternVars, value {{.*}} : $Bool, init {{.*}} : $@convention(thin) (Bool, @inout Int) -> (), set {{.*}} : $@callee_guaranteed (Bool) -> ()
109+
init(x: Int, y: String) {
110+
self.x = x
111+
self.y = y
112+
}
113+
}
5114

6115
struct Test1 {
7116
var _a: Int

0 commit comments

Comments
 (0)