Skip to content

Commit 3640de3

Browse files
committed
[Property Wrappers] Check for ignored assign_by_wrapper loads in
LifetimeChecker::shouldEmitError in order to avoid incorrectly populating EmittedErrorLocs when no error is emitted. This fixes a few corner cases where DI would error out or crash while assigning to a wrapped property with a nonmutating setter.
1 parent e75b7ef commit 3640de3

File tree

4 files changed

+80
-43
lines changed

4 files changed

+80
-43
lines changed

lib/SILGen/SILGenLValue.cpp

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,21 +1494,19 @@ namespace {
14941494

14951495
if (setterConv.getSILArgumentConvention(argIdx).isInoutConvention()) {
14961496
capturedBase = base.getValue();
1497+
} else if (base.getType().isAddress() &&
1498+
base.getType().getObjectType() ==
1499+
setterConv.getSILArgumentType(argIdx,
1500+
SGF.getTypeExpansionContext())) {
1501+
// If the base is a reference and the setter expects a value, emit a
1502+
// load. This pattern is emitted for property wrappers with a
1503+
// nonmutating setter, for example.
1504+
capturedBase = SGF.B.createTrivialLoadOr(
1505+
loc, base.getValue(), LoadOwnershipQualifier::Copy);
14971506
} else {
14981507
capturedBase = base.copy(SGF, loc).forward(SGF);
14991508
}
15001509

1501-
// If the base is a reference and the setter expects a value, emit a
1502-
// load. This pattern is emitted for property wrappers with a
1503-
// nonmutating setter, for example.
1504-
if (base.getType().isAddress() &&
1505-
base.getType().getObjectType() ==
1506-
setterConv.getSILArgumentType(argIdx,
1507-
SGF.getTypeExpansionContext())) {
1508-
capturedBase = SGF.B.createTrivialLoadOr(
1509-
loc, capturedBase, LoadOwnershipQualifier::Take);
1510-
}
1511-
15121510
capturedArgs.push_back(capturedBase);
15131511
}
15141512

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,22 @@ bool LifetimeChecker::shouldEmitError(const SILInstruction *Inst) {
625625
}))
626626
return false;
627627

628+
// Ignore loads used only by an assign_by_wrapper setter. This
629+
// is safe to ignore because assign_by_wrapper will only be
630+
// re-written to use the setter if the value is fully initialized.
631+
if (auto *load = dyn_cast<SingleValueInstruction>(Inst)) {
632+
if (auto Op = load->getSingleUse()) {
633+
if (auto PAI = dyn_cast<PartialApplyInst>(Op->getUser())) {
634+
if (std::find_if(PAI->use_begin(), PAI->use_end(),
635+
[](auto PAIUse) {
636+
return isa<AssignByWrapperInst>(PAIUse->getUser());
637+
}) != PAI->use_end()) {
638+
return false;
639+
}
640+
}
641+
}
642+
}
643+
628644
EmittedErrorLocs.push_back(InstLoc);
629645
return true;
630646
}
@@ -1841,20 +1857,6 @@ void LifetimeChecker::handleLoadUseFailure(const DIMemoryUse &Use,
18411857
TheMemory.isAnyInitSelf() && !TheMemory.isClassInitSelf()) {
18421858
if (!shouldEmitError(Inst)) return;
18431859

1844-
// Ignore loads used only for a set-by-value (nonmutating) setter
1845-
// since it will be deleted by lowering anyway.
1846-
auto load = cast<SingleValueInstruction>(Inst);
1847-
if (auto Op = load->getSingleUse()) {
1848-
if (auto PAI = dyn_cast<PartialApplyInst>(Op->getUser())) {
1849-
if (std::find_if(PAI->use_begin(), PAI->use_end(),
1850-
[](auto PAIUse) {
1851-
return isa<AssignByWrapperInst>(PAIUse->getUser());
1852-
}) != PAI->use_end()) {
1853-
return;
1854-
}
1855-
}
1856-
}
1857-
18581860
diagnose(Module, Inst->getLoc(), diag::use_of_self_before_fully_init);
18591861
noteUninitializedMembers(Use);
18601862
return;

test/SILOptimizer/di_property_wrappers.swift

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -537,30 +537,66 @@ func testSR_12341() {
537537

538538
@propertyWrapper
539539
struct NonMutatingSetterWrapper<Value> {
540-
var value: Value
541-
init(wrappedValue: Value) {
542-
value = wrappedValue
543-
}
544-
var wrappedValue: Value {
545-
get { value }
546-
nonmutating set {
547-
print(" .. nonmutatingSet \(newValue)")
548-
}
540+
var value: Value
541+
init(wrappedValue: Value) {
542+
print(" .. init \(wrappedValue)")
543+
value = wrappedValue
544+
}
545+
var wrappedValue: Value {
546+
get { value }
547+
nonmutating set {
548+
print(" .. nonmutatingSet \(newValue)")
549549
}
550+
}
550551
}
551552

552553
struct NonMutatingWrapperTestStruct {
553-
@NonMutatingSetterWrapper var SomeProp: Int
554-
init(val: Int) {
555-
SomeProp = val
556-
}
554+
@NonMutatingSetterWrapper var SomeProp: Int
555+
init(val: Int) {
556+
SomeProp = val
557+
SomeProp = val + 1
558+
}
557559
}
558560

559561
func testNonMutatingSetterStruct() {
560562
// CHECK: ## NonMutatingSetterWrapper
561563
print("\n## NonMutatingSetterWrapper")
562564
let A = NonMutatingWrapperTestStruct(val: 11)
563-
// CHECK-NEXT: .. nonmutatingSet 11
565+
// CHECK-NEXT: .. init 11
566+
// CHECK-NEXT: .. nonmutatingSet 12
567+
}
568+
569+
@propertyWrapper
570+
final class ClassWrapper<T> {
571+
private var _wrappedValue: T
572+
var wrappedValue: T {
573+
get { _wrappedValue }
574+
set {
575+
print(" .. set \(newValue)")
576+
_wrappedValue = newValue
577+
}
578+
}
579+
580+
init(wrappedValue: T) {
581+
print(" .. init \(wrappedValue)")
582+
self._wrappedValue = wrappedValue
583+
}
584+
}
585+
586+
struct StructWithClassWrapper<T> {
587+
@ClassWrapper private var _storage: T?
588+
589+
init(value: T?) {
590+
_storage = value
591+
}
592+
}
593+
594+
func testStructWithClassWrapper() {
595+
// CHECK: ## StructWithClassWrapper
596+
print("\n## StructWithClassWrapper")
597+
let _ = StructWithClassWrapper<Int>(value: 10)
598+
// CHECK-NEXT: .. init nil
599+
// CHECK-NEXT: .. set Optional(10)
564600
}
565601

566602
testIntStruct()
@@ -574,3 +610,4 @@ testComposed()
574610
testWrapperInitWithDefaultArg()
575611
testSR_12341()
576612
testNonMutatingSetterStruct()
613+
testStructWithClassWrapper()

test/SILOptimizer/di_property_wrappers_errors.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,22 @@ struct IntStructWithClassWrapper {
2323
@ClassWrapper var wrapped: Int
2424

2525
init() {
26-
wrapped = 42 // expected-error{{variable 'self.wrapped' used before being initialized}}
26+
wrapped = 42
2727
}
2828

2929
init(conditional b: Bool) {
3030
if b {
3131
self._wrapped = ClassWrapper(wrappedValue: 32)
3232
} else {
33-
wrapped = 42 // expected-error{{variable 'self.wrapped' used before being initialized}}
33+
wrapped = 42
3434
}
3535
}
3636

3737
init(dynamic b: Bool) {
3838
if b {
39-
wrapped = 42 // expected-error{{variable 'self.wrapped' used before being initialized}}
39+
wrapped = 42
4040
}
41-
wrapped = 27 // expected-error{{variable 'self.wrapped' used before being initialized}}
41+
wrapped = 27
4242
}
4343
}
4444

0 commit comments

Comments
 (0)