Skip to content

Commit 2a10ee0

Browse files
committed
[SILGen] InitAccessors: Make sure that assign_or_init always directly references self
`nonmutating set` gets a copy of "self" in `GetterSetterComponent` which is expected for partial application of the setter but doesn't work for "self" reference that `assign_or_init` instruction needs to emit references to stored properties during lowering. We need to make sure that "self" is always a reference to rootself of the constructor before passing it to `assign_or_init`. Resolves: #67827 Resolves: rdar://114433261 (cherry picked from commit 10947de)
1 parent fae30fb commit 2a10ee0

File tree

3 files changed

+120
-3
lines changed

3 files changed

+120
-3
lines changed

lib/SILGen/SILGenFunction.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1859,6 +1859,19 @@ void SILGenFunction::emitAssignOrInit(SILLocation loc, ManagedValue selfValue,
18591859
setterFRef = SILUndef::get(initFRef->getType(), F);
18601860
}
18611861

1862-
B.createAssignOrInit(loc, selfValue.getValue(), newValue.forward(*this),
1862+
auto isValueSelf = !selfValue.getType().getASTType()->mayHaveSuperclass();
1863+
// If we are emitting `assign_or_init` instruction for a value
1864+
// type, we need to make sure that "self" is always a l-value
1865+
// reference to "rootself" because `nonmutating set` loads "self"
1866+
// and referencing `selfValue` in such case is incorrect because
1867+
// it's a copy which is going to be skipped by DI.
1868+
auto selfRef = selfValue;
1869+
if (isValueSelf && !selfRef.isLValue()) {
1870+
auto *ctor = cast<ConstructorDecl>(FunctionDC->getAsDecl());
1871+
selfRef = maybeEmitValueOfLocalVarDecl(ctor->getImplicitSelfDecl(),
1872+
AccessKind::ReadWrite);
1873+
}
1874+
1875+
B.createAssignOrInit(loc, selfRef.getValue(), newValue.forward(*this),
18631876
initFRef, setterFRef, AssignOrInitInst::Unknown);
18641877
}

test/Interpreter/init_accessors.swift

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,3 +846,77 @@ do {
846846
// CHECK-NEXT: TestMixedDefaultInitalizable(a: nil, b: Optional(""))
847847
// CHECK-NEXT: TestMixedDefaultInitalizable(a: nil, b: Optional("Hello"))
848848
// CHECK-NEXT: TestMixedDefaultInitalizable(a: nil, b: Optional(""))
849+
850+
do {
851+
struct TestNonMutatingSetDefault {
852+
var _count: Int
853+
854+
var count: Int = 42 {
855+
@storageRestrictions(initializes: _count)
856+
init {
857+
_count = newValue
858+
}
859+
860+
get { _count }
861+
nonmutating set {}
862+
}
863+
}
864+
865+
struct TestNonMutatingSetNoDefault {
866+
var _count: Int
867+
868+
var count: Int {
869+
@storageRestrictions(initializes: _count)
870+
init {
871+
print("init accessor is called: \(newValue)")
872+
_count = newValue
873+
}
874+
875+
get { _count }
876+
877+
nonmutating set {
878+
print("nonmutating set called: \(newValue)")
879+
}
880+
}
881+
882+
init(value: Int) {
883+
self.count = value
884+
self.count = value + 1
885+
}
886+
}
887+
888+
struct TestNonMutatingSetCustom {
889+
var _count: Int
890+
891+
var count: Int = 42 {
892+
@storageRestrictions(initializes: _count)
893+
init {
894+
print("init accessor is called: \(newValue)")
895+
_count = newValue
896+
}
897+
898+
get { _count }
899+
900+
nonmutating set {
901+
print("nonmutating set called: \(newValue)")
902+
}
903+
}
904+
905+
init(custom: Int) {
906+
count = custom
907+
}
908+
}
909+
910+
print("test-nonmutating-set-1: \(TestNonMutatingSetDefault())")
911+
print("test-nonmutating-set-2: \(TestNonMutatingSetDefault(count: 0))")
912+
print("test-nonmutating-set-3: \(TestNonMutatingSetNoDefault(value: -1))")
913+
print("test-nonmutating-set-4: \(TestNonMutatingSetCustom(custom: 0))")
914+
}
915+
// CHECK: test-nonmutating-set-1: TestNonMutatingSetDefault(_count: 42)
916+
// CHECK-NEXT: test-nonmutating-set-2: TestNonMutatingSetDefault(_count: 0)
917+
// CHECK-NEXT: init accessor is called: -1
918+
// CHECK-NEXT: nonmutating set called: 0
919+
// CHECK-NEXT: test-nonmutating-set-3: TestNonMutatingSetNoDefault(_count: -1)
920+
// CHECK-NEXT: init accessor is called: 42
921+
// CHECK-NEXT: nonmutating set called: 0
922+
// CHECK-NEXT: test-nonmutating-set-4: TestNonMutatingSetCustom(_count: 42)

test/SILOptimizer/init_accessor_raw_sil_lowering.swift

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ func test_default_inits() {
225225
// CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test1L_V1xSivi : $@convention(thin) (Int) -> @out Int
226226
// CHECK: [[SETTER_REF:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test1L_V1xSivs : $@convention(method) (Int, @inout Test1) -> ()
227227
// CHECK-NEXT: [[SETTER:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]([[SELF_REF]]) : $@convention(method) (Int, @inout Test1) -> ()
228-
// 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) -> ()
228+
// CHECK-NEXT: assign_or_init [init] self %1 : $*Test1, value [[INIT_VAL]] : $Int, init [[INIT_ACCESSOR]] : $@convention(thin) (Int) -> @out Int, set [[SETTER]] : $@callee_guaranteed (Int) -> ()
229229
// CHECK-NEXT: end_access [[SELF_REF]] : $*Test1
230230
// CHECK-NEXT: destroy_value [[SETTER]] : $@callee_guaranteed (Int) -> ()
231231
init() {
@@ -238,7 +238,7 @@ func test_default_inits() {
238238
// CHECK: [[INIT_ACCESSOR:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test1L_V1xSivi : $@convention(thin) (Int) -> @out Int
239239
// CHECK: [[SETTER_REF:%.*]] = function_ref @$s23assign_or_init_lowering18test_default_initsyyF5Test1L_V1xSivs : $@convention(method) (Int, @inout Test1) -> ()
240240
// CHECK-NEXT: [[SETTER:%.*]] = partial_apply [callee_guaranteed] [[SETTER_REF]]([[SELF_REF]]) : $@convention(method) (Int, @inout Test1) -> ()
241-
// 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) -> ()
241+
// CHECK-NEXT: assign_or_init [init] self %2 : $*Test1, value [[INIT_VAL]] : $Int, init [[INIT_ACCESSOR]] : $@convention(thin) (Int) -> @out Int, set [[SETTER]] : $@callee_guaranteed (Int) -> ()
242242
// CHECK-NEXT: end_access [[SELF_REF]] : $*Test1
243243
// CHECK-NEXT: destroy_value [[SETTER]] : $@callee_guaranteed (Int) -> ()
244244
//
@@ -396,3 +396,33 @@ func test_handling_of_superclass_properties() {
396396
}
397397
}
398398
}
399+
400+
func test_handling_of_nonmutating_set() {
401+
struct Test {
402+
private var _count: Int
403+
404+
var count: Int = 42 {
405+
@storageRestrictions(initializes: _count)
406+
init {
407+
_count = newValue
408+
}
409+
get {
410+
_count
411+
}
412+
nonmutating set {
413+
// Update store
414+
}
415+
}
416+
417+
// CHECK-LABEL: sil private [ossa] @$s23assign_or_init_lowering32test_handling_of_nonmutating_setyyF4TestL_V5countADSi_tcfC : $@convention(method) (Int, @thin Test.Type) -> Test
418+
// CHECK: [[INIT_VALUE:%.*]] = function_ref @$s23assign_or_init_lowering32test_handling_of_nonmutating_setyyF4TestL_V5countSivpfi : $@convention(thin) () -> Int
419+
// CHECK-NEXT: [[VALUE:%.*]] = apply [[INIT_VALUE]]() : $@convention(thin) () -> Int
420+
// CHECK: assign_or_init [init] self %2 : $*Test, value [[VALUE]] : $Int, init {{.*}} : $@convention(thin) (Int) -> @out Int, set {{.*}} : $@callee_guaranteed (Int) -> ()
421+
// CHECK: assign_or_init [set] self %2 : $*Test, value %0 : $Int, init {{.*}} : $@convention(thin) (Int) -> @out Int, set {{.*}} : $@callee_guaranteed (Int) -> ()
422+
// CHECK: assign_or_init [set] self %2 : $*Test, value [[ZERO:%.*]] : $Int, init {{.*}} : $@convention(thin) (Int) -> @out Int, set {{.*}} : $@callee_guaranteed (Int) -> ()
423+
init(count: Int) {
424+
self.count = count
425+
self.count = 0
426+
}
427+
}
428+
}

0 commit comments

Comments
 (0)