Skip to content

Commit 11f1b08

Browse files
committed
[DI] Properties with init accessors without "initializes" act as stored
Adjust DI to treat init accessor properties that have only 'accesses' or no restrictions as if they are stored properties, this means that if such property doesn't have a default initializer users would have to reference it explicitly in their custom initializers. We also need to suppress default init synthesis for such cases which would be done in a followup commit. Resolves: rdar://113401979
1 parent 281207f commit 11f1b08

File tree

5 files changed

+174
-15
lines changed

5 files changed

+174
-15
lines changed

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ static unsigned getElementCountRec(TypeExpansionContext context,
9595
for (auto *VD : NTD->getStoredProperties())
9696
NumElements += getElementCountRec(
9797
context, Module, T.getFieldType(VD, Module, context), false);
98+
for (auto *P : NTD->getInitAccessorProperties()) {
99+
auto *init = P->getAccessor(AccessorKind::Init);
100+
if (init->getInitializedProperties().empty())
101+
++NumElements;
102+
}
98103
return NumElements;
99104
}
100105
}
@@ -451,6 +456,15 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element,
451456
Element -= NumFieldElements;
452457
}
453458

459+
for (auto *property : NTD->getInitAccessorProperties()) {
460+
auto *init = property->getAccessor(AccessorKind::Init);
461+
if (init->getInitializedProperties().empty()) {
462+
if (Element == 0)
463+
return property;
464+
--Element;
465+
}
466+
}
467+
454468
// If we do not have any stored properties, we have nothing of interest.
455469
if (!HasStoredProperty)
456470
return nullptr;
@@ -497,6 +511,15 @@ bool DIMemoryObjectInfo::isElementLetProperty(unsigned Element) const {
497511
Element -= NumFieldElements;
498512
}
499513

514+
for (auto *property : NTD->getInitAccessorProperties()) {
515+
auto *init = property->getAccessor(AccessorKind::Init);
516+
if (init->getInitializedProperties().empty()) {
517+
if (Element == 0)
518+
return !property->getAccessor(AccessorKind::Set);
519+
--Element;
520+
}
521+
}
522+
500523
// Otherwise, we miscounted elements?
501524
assert(Element == 0 && "Element count problem");
502525
return false;
@@ -1221,10 +1244,22 @@ ElementUseCollector::collectAssignOrInitUses(AssignOrInitInst *Inst,
12211244

12221245
auto initializedElts = Inst->getInitializedProperties();
12231246
if (initializedElts.empty()) {
1224-
// Add a placeholder use that doesn't touch elements to make sure that
1225-
// the `assign_or_init` instruction gets the kind set when `initializes`
1226-
// list is empty.
1227-
trackUse(DIMemoryUse(Inst, DIUseKind::InitOrAssign, BaseEltNo, 0));
1247+
auto initAccessorProperties = typeDC->getInitAccessorProperties();
1248+
auto initFieldAt = typeDC->getStoredProperties().size();
1249+
1250+
for (auto *property : initAccessorProperties) {
1251+
auto initAccessor = property->getAccessor(AccessorKind::Init);
1252+
if (!initAccessor->getInitializedProperties().empty())
1253+
continue;
1254+
1255+
if (property == Inst->getProperty()) {
1256+
trackUse(DIMemoryUse(Inst, DIUseKind::InitOrAssign, initFieldAt,
1257+
/*NumElements=*/1));
1258+
break;
1259+
}
1260+
1261+
++initFieldAt;
1262+
}
12281263
} else {
12291264
for (auto *property : initializedElts)
12301265
addUse(property, DIUseKind::InitOrAssign);

test/Interpreter/init_accessors.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,3 +750,54 @@ func test_inheritance() {
750750
test_inheritance()
751751
// CHECK: test-inheritance-1: Person(firstName: <<unknown>>, age: 0)
752752
// CHECK-NEXT: test-inheritance-2: Person(firstName: Q, age: 42)
753+
754+
do {
755+
class BackingData<T> {
756+
var data: [PartialKeyPath<T>: Any] = [:]
757+
758+
func get<V>(_ key: KeyPath<T, V>) -> V { data[key] as! V }
759+
func set<V>(_ key: KeyPath<T, V>, _ value: V) {
760+
data[key] = value
761+
}
762+
}
763+
764+
class Person : CustomStringConvertible {
765+
var description: String {
766+
"Person(name: \(name))"
767+
}
768+
769+
private var backingData: BackingData<Person> = BackingData()
770+
771+
private var _name: Int
772+
773+
var name: String {
774+
@storageRestrictions(accesses: backingData, initializes: _name)
775+
init(newValue) {
776+
self.backingData.set(\.name, newValue)
777+
self._name = 0
778+
}
779+
780+
get { self.backingData.get(\.name) }
781+
set { self.backingData.set(\.name, newValue) }
782+
}
783+
784+
init(name: String) {
785+
self.name = name
786+
}
787+
788+
init(backingData: BackingData<Person>) {
789+
self.backingData = backingData
790+
self._name = 0
791+
}
792+
}
793+
794+
let person = Person(name: "P")
795+
print(person)
796+
797+
let localData = BackingData<Person>()
798+
localData.set(\.name, "O")
799+
800+
print(Person(backingData: localData))
801+
}
802+
// CHECK: Person(name: P)
803+
// CHECK-NEXT: Person(name: O)

test/SILOptimizer/init_accessor_definite_init_diagnostics.swift

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,73 @@ do {
268268
}
269269
}
270270
}
271+
272+
// Test that init accessor without "initializes" is required
273+
do {
274+
struct Test {
275+
var a: Int {
276+
init {}
277+
get { 42 }
278+
set {}
279+
}
280+
var b: Int { // expected-note 2 {{'self' not initialized}}
281+
init {}
282+
get { 0 }
283+
set { }
284+
}
285+
286+
init(a: Int) {
287+
self.a = a
288+
} // expected-error {{return from initializer without initializing all stored properties}}
289+
290+
init(a: Int, b: Int) {
291+
if a == 0 {
292+
self.a = a
293+
self.b = b
294+
} else {
295+
self.a = 0
296+
}
297+
} // expected-error {{return from initializer without initializing all stored properties}}
298+
299+
init() { // Ok
300+
self.a = 0
301+
self.b = 0
302+
}
303+
}
304+
305+
struct TestWithStored {
306+
var _value: Int = 0
307+
308+
var a: Int {
309+
@storageRestrictions(accesses: _value)
310+
init {}
311+
get { _value }
312+
set { _value = newValue }
313+
}
314+
}
315+
316+
_ = TestWithStored(a: 42) // Ok
317+
_ = TestWithStored(_value: 1, a: 42) // Ok
318+
319+
class TestWithStoredExplicit {
320+
var _value: Int = 0
321+
var _other: String = ""
322+
323+
var a: Int { // expected-note 2 {{'self' not initialized}}
324+
@storageRestrictions(accesses: _value)
325+
init {}
326+
get { _value }
327+
set { _value = newValue }
328+
}
329+
330+
init(data: Int) {
331+
self._value = data
332+
} // expected-error {{return from initializer without initializing all stored properties}}
333+
334+
init() {} // expected-error {{return from initializer without initializing all stored properties}}
335+
336+
init(a: Int) {
337+
self.a = a // Ok
338+
}
339+
}
340+
}

test/SILOptimizer/init_accessor_raw_sil_lowering.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ final class TestIndirectionThroughStorage {
4646
// CHECK: [[STORAGE_INIT:%.*]] = function_ref @$s23assign_or_init_lowering29TestIndirectionThroughStorageC8_storage33_DE106275C2F16FB3D05881E72FBD87C8LLAA0H0_pAC1TAaFPRts_XPvpfi : $@convention(thin) () -> @out any Storage<TestIndirectionThroughStorage>
4747
// CHECK-NEXT: {{.*}} = apply [[STORAGE_INIT]]([[STORAGE_REF]]) : $@convention(thin) () -> @out any Storage<TestIndirectionThroughStorage>
4848
// Initialization:
49-
// CHECK: assign_or_init [set] #TestIndirectionThroughStorage.name, self %2 : $TestIndirectionThroughStorage, value {{.*}} : $String, init {{.*}} : $@convention(thin) (@owned String, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (@owned String) -> ()
50-
// CHECK: assign_or_init [set] #TestIndirectionThroughStorage.age, self %2 : $TestIndirectionThroughStorage, value {{.*}} : $Optional<Int>, init {{.*}} : $@convention(thin) (Optional<Int>, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (Optional<Int>) -> (
49+
// CHECK: assign_or_init [init] #TestIndirectionThroughStorage.name, self %2 : $TestIndirectionThroughStorage, value {{.*}} : $String, init {{.*}} : $@convention(thin) (@owned String, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (@owned String) -> ()
50+
// CHECK: assign_or_init [init] #TestIndirectionThroughStorage.age, self %2 : $TestIndirectionThroughStorage, value {{.*}} : $Optional<Int>, init {{.*}} : $@convention(thin) (Optional<Int>, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (Optional<Int>) -> ()
5151
// Explicit set:
5252
// CHECK: assign_or_init [set] #TestIndirectionThroughStorage.name, self %2 : $TestIndirectionThroughStorage, value {{.*}} : $String, init {{.*}} : $@convention(thin) (@owned String, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (@owned String) -> ()
53-
// CHECK: assign_or_init [set] #TestIndirectionThroughStorage.age, self %2 : $TestIndirectionThroughStorage, value {{.*}} : $Optional<Int>, init {{.*}} : $@convention(thin) (Optional<Int>, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (Optional<Int>) -> (
53+
// CHECK: assign_or_init [set] #TestIndirectionThroughStorage.age, self %2 : $TestIndirectionThroughStorage, value {{.*}} : $Optional<Int>, init {{.*}} : $@convention(thin) (Optional<Int>, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (Optional<Int>) -> ()
5454
init(name: String, age: Int) {
5555
self.name = name
5656
self.age = age
@@ -61,8 +61,8 @@ final class TestIndirectionThroughStorage {
6161
// CHECK: [[STORAGE_INIT:%.*]] = function_ref @$s23assign_or_init_lowering29TestIndirectionThroughStorageC8_storage33_DE106275C2F16FB3D05881E72FBD87C8LLAA0H0_pAC1TAaFPRts_XPvpfi : $@convention(thin) () -> @out any Storage<TestIndirectionThroughStorage>
6262
// CHECK-NEXT: {{.*}} = apply [[STORAGE_INIT]]([[STORAGE_REF]]) : $@convention(thin) () -> @out any Storage<TestIndirectionThroughStorage>
6363
// Initialization:
64-
// CHECK: assign_or_init [set] #TestIndirectionThroughStorage.name, self %1 : $TestIndirectionThroughStorage, value {{.*}} : $String, init {{.*}} : $@convention(thin) (@owned String, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (@owned String) -> ()
65-
// CHECK: assign_or_init [set] #TestIndirectionThroughStorage.age, self %1 : $TestIndirectionThroughStorage, value {{.*}} : $Optional<Int>, init {{.*}} : $@convention(thin) (Optional<Int>, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (Optional<Int>) -> ()
64+
// CHECK: assign_or_init [init] #TestIndirectionThroughStorage.name, self %1 : $TestIndirectionThroughStorage, value {{.*}} : $String, init {{.*}} : $@convention(thin) (@owned String, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (@owned String) -> ()
65+
// CHECK: assign_or_init [init] #TestIndirectionThroughStorage.age, self %1 : $TestIndirectionThroughStorage, value {{.*}} : $Optional<Int>, init {{.*}} : $@convention(thin) (Optional<Int>, @inout any Storage<TestIndirectionThroughStorage>) -> (), set {{.*}} : $@callee_guaranteed (Optional<Int>) -> ()
6666
// Explicit set:
6767
// CHECK: [[STORAGE_SETTER:%.*]] = function_ref @$s23assign_or_init_lowering29TestIndirectionThroughStorageC7storageAA0H0_pAC1TAaEPRts_XPvs : $@convention(method) (@in any Storage<TestIndirectionThroughStorage>, @guaranteed TestIndirectionThroughStorage) -> ()
6868
// CHECK-NEXT: {{.*}} = apply [[STORAGE_SETTER]]({{.*}}, %1) : $@convention(method) (@in any Storage<TestIndirectionThroughStorage>, @guaranteed TestIndirectionThroughStorage) -> ()
@@ -102,8 +102,8 @@ struct TestAccessOfOnePatternVars {
102102
// CHECK-NEXT: {{.*}} = apply [[Y_INIT]]() : $@convention(thin) () -> @owned String
103103
// CHECK-NOT: [[X_REF:%.*]] = struct_element_addr %3 : $*TestAccessOfOnePatternVars, #TestAccessOfOnePatternVars.x
104104
// CHECK-NOT: [[Y_REF:%.*]] = struct_element_addr {{.*}} : $*TestAccessOfOnePatternVars, #TestAccessOfOnePatternVars.y
105-
// CHECK: assign_or_init [set] #TestAccessOfOnePatternVars.data, self {{.*}} : $*TestAccessOfOnePatternVars, value {{.*}} : $(Int, String), init {{.*}} : $@convention(thin) (Int, @owned String, @inout Int, @inout String) -> (), set {{.*}} : $@callee_guaranteed (Int, @owned String) -> ()
106-
// CHECK: assign_or_init [set] #TestAccessOfOnePatternVars.other, self {{.*}} : $*TestAccessOfOnePatternVars, value {{.*}} : $Bool, init {{.*}} : $@convention(thin) (Bool, @inout Int) -> (), set {{.*}} : $@callee_guaranteed (Bool) -> ()
105+
// CHECK: assign_or_init [init] #TestAccessOfOnePatternVars.data, self {{.*}} : $*TestAccessOfOnePatternVars, value {{.*}} : $(Int, String), init {{.*}} : $@convention(thin) (Int, @owned String, @inout Int, @inout String) -> (), set {{.*}} : $@callee_guaranteed (Int, @owned String) -> ()
106+
// CHECK: assign_or_init [init] #TestAccessOfOnePatternVars.other, self {{.*}} : $*TestAccessOfOnePatternVars, value {{.*}} : $Bool, init {{.*}} : $@convention(thin) (Bool, @inout Int) -> (), set {{.*}} : $@callee_guaranteed (Bool) -> ()
107107
init(x: Int, y: String) {
108108
self.x = x
109109
self.y = y
@@ -196,7 +196,7 @@ struct Test {
196196
// CHECK-LABEL: sil hidden [ossa] @$s23assign_or_init_lowering4TestV1vACSi_tcfC : $@convention(method) (Int, @thin Test.Type) -> Test
197197
init(v: Int) {
198198
// CHECK: [[INIT_REF:%.*]] = function_ref @$s23assign_or_init_lowering4TestV4testSivi : $@convention(thin) (Int) -> ()
199-
// CHECK: assign_or_init [set] #Test.test, self {{.*}}, value %0 : $Int, init [[INIT_REF]] : $@convention(thin) (Int) -> (), set {{.*}} : $@callee_guaranteed (Int) -> ()
199+
// CHECK: assign_or_init [init] #Test.test, self {{.*}}, value %0 : $Int, init [[INIT_REF]] : $@convention(thin) (Int) -> (), set {{.*}} : $@callee_guaranteed (Int) -> ()
200200
self.test = v
201201
}
202202
}

test/SILOptimizer/init_accessors.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,12 @@ struct TestSetter {
7373
}
7474

7575
// CHECK-LABEL: sil hidden [ossa] @$s14init_accessors10TestSetterV1x1yACSi_SitcfC : $@convention(method) (Int, Int, @thin TestSetter.Type) -> TestSetter
76-
// CHECK: [[SETTER_REF:%.*]] = function_ref @$s14init_accessors10TestSetterV5pointSi_Sitvs : $@convention(method) (Int, Int, @inout TestSetter) -> ()
77-
// CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]([[SELF_VALUE:%.*]]) : $@convention(method) (Int, Int, @inout TestSetter) -> ()
78-
// CHECK: {{.*}} = apply [[SETTER_CLOSURE]]({{.*}}) : $@callee_guaranteed (Int, Int) -> ()
76+
// CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s14init_accessors10TestSetterV5pointSi_Sitvi : $@convention(thin) (Int, Int, @inout Int, @inout Int) -> ()
77+
// CHECK: [[SELF:%.*]] = begin_access [modify] [dynamic] %14 : $*TestSetter
78+
// CHECK-NEXT: ([[X:%.*]], [[Y:%.*]]) = destructure_tuple {{.*}} : $(Int, Int)
79+
// CHECK-NEXT: [[X_REF:%.*]] = struct_element_addr [[SELF]] : $*TestSetter, #TestSetter.x
80+
// CHECK-NEXT: [[Y_REF:%.*]] = struct_element_addr [[SELF]] : $*TestSetter, #TestSetter.y
81+
// CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR]]([[X]], [[Y]], [[X_REF]], [[Y_REF]]) : $@convention(thin) (Int, Int, @inout Int, @inout Int) -> ()
7982
init(x: Int, y: Int) {
8083
self.x = x
8184
self.y = y

0 commit comments

Comments
 (0)