Skip to content

[DI] Properties with init accessors without "initializes" act as stored #67763

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 39 additions & 4 deletions lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ static unsigned getElementCountRec(TypeExpansionContext context,
for (auto *VD : NTD->getStoredProperties())
NumElements += getElementCountRec(
context, Module, T.getFieldType(VD, Module, context), false);
for (auto *P : NTD->getInitAccessorProperties()) {
auto *init = P->getAccessor(AccessorKind::Init);
if (init->getInitializedProperties().empty())
++NumElements;
}
return NumElements;
}
}
Expand Down Expand Up @@ -451,6 +456,15 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element,
Element -= NumFieldElements;
}

for (auto *property : NTD->getInitAccessorProperties()) {
auto *init = property->getAccessor(AccessorKind::Init);
if (init->getInitializedProperties().empty()) {
if (Element == 0)
return property;
--Element;
}
}

// If we do not have any stored properties, we have nothing of interest.
if (!HasStoredProperty)
return nullptr;
Expand Down Expand Up @@ -497,6 +511,15 @@ bool DIMemoryObjectInfo::isElementLetProperty(unsigned Element) const {
Element -= NumFieldElements;
}

for (auto *property : NTD->getInitAccessorProperties()) {
auto *init = property->getAccessor(AccessorKind::Init);
if (init->getInitializedProperties().empty()) {
if (Element == 0)
return !property->getAccessor(AccessorKind::Set);
--Element;
}
}

// Otherwise, we miscounted elements?
assert(Element == 0 && "Element count problem");
return false;
Expand Down Expand Up @@ -1221,10 +1244,22 @@ ElementUseCollector::collectAssignOrInitUses(AssignOrInitInst *Inst,

auto initializedElts = Inst->getInitializedProperties();
if (initializedElts.empty()) {
// Add a placeholder use that doesn't touch elements to make sure that
// the `assign_or_init` instruction gets the kind set when `initializes`
// list is empty.
trackUse(DIMemoryUse(Inst, DIUseKind::InitOrAssign, BaseEltNo, 0));
auto initAccessorProperties = typeDC->getInitAccessorProperties();
auto initFieldAt = typeDC->getStoredProperties().size();

for (auto *property : initAccessorProperties) {
auto initAccessor = property->getAccessor(AccessorKind::Init);
if (!initAccessor->getInitializedProperties().empty())
continue;

if (property == Inst->getProperty()) {
trackUse(DIMemoryUse(Inst, DIUseKind::InitOrAssign, initFieldAt,
/*NumElements=*/1));
break;
}

++initFieldAt;
}
} else {
for (auto *property : initializedElts)
addUse(property, DIUseKind::InitOrAssign);
Expand Down
51 changes: 51 additions & 0 deletions test/Interpreter/init_accessors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -750,3 +750,54 @@ func test_inheritance() {
test_inheritance()
// CHECK: test-inheritance-1: Person(firstName: <<unknown>>, age: 0)
// CHECK-NEXT: test-inheritance-2: Person(firstName: Q, age: 42)

do {
class BackingData<T> {
var data: [PartialKeyPath<T>: Any] = [:]

func get<V>(_ key: KeyPath<T, V>) -> V { data[key] as! V }
func set<V>(_ key: KeyPath<T, V>, _ value: V) {
data[key] = value
}
}

class Person : CustomStringConvertible {
var description: String {
"Person(name: \(name))"
}

private var backingData: BackingData<Person> = BackingData()

private var _name: Int

var name: String {
@storageRestrictions(accesses: backingData, initializes: _name)
init(newValue) {
self.backingData.set(\.name, newValue)
self._name = 0
}

get { self.backingData.get(\.name) }
set { self.backingData.set(\.name, newValue) }
}

init(name: String) {
self.name = name
}

init(backingData: BackingData<Person>) {
self.backingData = backingData
self._name = 0
}
}

let person = Person(name: "P")
print(person)

let localData = BackingData<Person>()
localData.set(\.name, "O")

print(Person(backingData: localData))
}
// CHECK: Person(name: P)
// CHECK-NEXT: Person(name: O)
70 changes: 70 additions & 0 deletions test/SILOptimizer/init_accessor_definite_init_diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,73 @@ do {
}
}
}

// Test that init accessor without "initializes" is required
do {
struct Test {
var a: Int {
init {}
get { 42 }
set {}
}
var b: Int { // expected-note 2 {{'self' not initialized}}
init {}
get { 0 }
set { }
}

init(a: Int) {
self.a = a
} // expected-error {{return from initializer without initializing all stored properties}}

init(a: Int, b: Int) {
if a == 0 {
self.a = a
self.b = b
} else {
self.a = 0
}
} // expected-error {{return from initializer without initializing all stored properties}}

init() { // Ok
self.a = 0
self.b = 0
}
}

struct TestWithStored {
var _value: Int = 0

var a: Int {
@storageRestrictions(accesses: _value)
init {}
get { _value }
set { _value = newValue }
}
}

_ = TestWithStored(a: 42) // Ok
_ = TestWithStored(_value: 1, a: 42) // Ok

class TestWithStoredExplicit {
var _value: Int = 0
var _other: String = ""

var a: Int { // expected-note 2 {{'self' not initialized}}
@storageRestrictions(accesses: _value)
init {}
get { _value }
set { _value = newValue }
}

init(data: Int) {
self._value = data
} // expected-error {{return from initializer without initializing all stored properties}}

init() {} // expected-error {{return from initializer without initializing all stored properties}}

init(a: Int) {
self.a = a // Ok
}
}
}
16 changes: 8 additions & 8 deletions test/SILOptimizer/init_accessor_raw_sil_lowering.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ final class TestIndirectionThroughStorage {
// CHECK: [[STORAGE_INIT:%.*]] = function_ref @$s23assign_or_init_lowering29TestIndirectionThroughStorageC8_storage33_DE106275C2F16FB3D05881E72FBD87C8LLAA0H0_pAC1TAaFPRts_XPvpfi : $@convention(thin) () -> @out any Storage<TestIndirectionThroughStorage>
// CHECK-NEXT: {{.*}} = apply [[STORAGE_INIT]]([[STORAGE_REF]]) : $@convention(thin) () -> @out any Storage<TestIndirectionThroughStorage>
// Initialization:
// 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) -> ()
// 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>) -> (
// 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) -> ()
// 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>) -> ()
// Explicit set:
// 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) -> ()
// 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>) -> (
// 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>) -> ()
init(name: String, age: Int) {
self.name = name
self.age = age
Expand All @@ -61,8 +61,8 @@ final class TestIndirectionThroughStorage {
// CHECK: [[STORAGE_INIT:%.*]] = function_ref @$s23assign_or_init_lowering29TestIndirectionThroughStorageC8_storage33_DE106275C2F16FB3D05881E72FBD87C8LLAA0H0_pAC1TAaFPRts_XPvpfi : $@convention(thin) () -> @out any Storage<TestIndirectionThroughStorage>
// CHECK-NEXT: {{.*}} = apply [[STORAGE_INIT]]([[STORAGE_REF]]) : $@convention(thin) () -> @out any Storage<TestIndirectionThroughStorage>
// Initialization:
// 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) -> ()
// 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>) -> ()
// 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) -> ()
// 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>) -> ()
// Explicit set:
// CHECK: [[STORAGE_SETTER:%.*]] = function_ref @$s23assign_or_init_lowering29TestIndirectionThroughStorageC7storageAA0H0_pAC1TAaEPRts_XPvs : $@convention(method) (@in any Storage<TestIndirectionThroughStorage>, @guaranteed TestIndirectionThroughStorage) -> ()
// CHECK-NEXT: {{.*}} = apply [[STORAGE_SETTER]]({{.*}}, %1) : $@convention(method) (@in any Storage<TestIndirectionThroughStorage>, @guaranteed TestIndirectionThroughStorage) -> ()
Expand Down Expand Up @@ -102,8 +102,8 @@ struct TestAccessOfOnePatternVars {
// CHECK-NEXT: {{.*}} = apply [[Y_INIT]]() : $@convention(thin) () -> @owned String
// CHECK-NOT: [[X_REF:%.*]] = struct_element_addr %3 : $*TestAccessOfOnePatternVars, #TestAccessOfOnePatternVars.x
// CHECK-NOT: [[Y_REF:%.*]] = struct_element_addr {{.*}} : $*TestAccessOfOnePatternVars, #TestAccessOfOnePatternVars.y
// 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) -> ()
// CHECK: assign_or_init [set] #TestAccessOfOnePatternVars.other, self {{.*}} : $*TestAccessOfOnePatternVars, value {{.*}} : $Bool, init {{.*}} : $@convention(thin) (Bool, @inout Int) -> (), set {{.*}} : $@callee_guaranteed (Bool) -> ()
// 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) -> ()
// CHECK: assign_or_init [init] #TestAccessOfOnePatternVars.other, self {{.*}} : $*TestAccessOfOnePatternVars, value {{.*}} : $Bool, init {{.*}} : $@convention(thin) (Bool, @inout Int) -> (), set {{.*}} : $@callee_guaranteed (Bool) -> ()
init(x: Int, y: String) {
self.x = x
self.y = y
Expand Down Expand Up @@ -196,7 +196,7 @@ struct Test {
// CHECK-LABEL: sil hidden [ossa] @$s23assign_or_init_lowering4TestV1vACSi_tcfC : $@convention(method) (Int, @thin Test.Type) -> Test
init(v: Int) {
// CHECK: [[INIT_REF:%.*]] = function_ref @$s23assign_or_init_lowering4TestV4testSivi : $@convention(thin) (Int) -> ()
// CHECK: assign_or_init [set] #Test.test, self {{.*}}, value %0 : $Int, init [[INIT_REF]] : $@convention(thin) (Int) -> (), set {{.*}} : $@callee_guaranteed (Int) -> ()
// CHECK: assign_or_init [init] #Test.test, self {{.*}}, value %0 : $Int, init [[INIT_REF]] : $@convention(thin) (Int) -> (), set {{.*}} : $@callee_guaranteed (Int) -> ()
self.test = v
}
}
Expand Down
9 changes: 6 additions & 3 deletions test/SILOptimizer/init_accessors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,12 @@ struct TestSetter {
}

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