Skip to content

Commit 8bb3082

Browse files
committed
[Property Wrappers] Lower assign_by_wrapper to re-assignment of the backing
property wrapper before all of self is initialized if the wrapper has already been initialized.
1 parent 4ab7933 commit 8bb3082

File tree

5 files changed

+108
-27
lines changed

5 files changed

+108
-27
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3937,6 +3937,16 @@ class AssignByWrapperInst
39373937
: public AssignInstBase<SILInstructionKind::AssignByWrapperInst, 4> {
39383938
friend SILBuilder;
39393939

3940+
public:
3941+
/// The assignment destination for the property wrapper
3942+
enum class Destination {
3943+
BackingWrapper,
3944+
WrappedValue,
3945+
};
3946+
3947+
private:
3948+
Destination AssignDest = Destination::WrappedValue;
3949+
39403950
AssignByWrapperInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest,
39413951
SILValue Initializer, SILValue Setter,
39423952
AssignOwnershipQualifier Qualifier =
@@ -3951,8 +3961,17 @@ class AssignByWrapperInst
39513961
return AssignOwnershipQualifier(
39523962
SILInstruction::Bits.AssignByWrapperInst.OwnershipQualifier);
39533963
}
3954-
void setOwnershipQualifier(AssignOwnershipQualifier qualifier) {
3964+
3965+
Destination getAssignDestination() const { return AssignDest; }
3966+
3967+
void setAssignInfo(AssignOwnershipQualifier qualifier, Destination dest) {
3968+
using Qualifier = AssignOwnershipQualifier;
3969+
assert(qualifier == Qualifier::Init && dest == Destination::BackingWrapper ||
3970+
qualifier == Qualifier::Reassign && dest == Destination::BackingWrapper ||
3971+
qualifier == Qualifier::Reassign && dest == Destination::WrappedValue);
3972+
39553973
SILInstruction::Bits.AssignByWrapperInst.OwnershipQualifier = unsigned(qualifier);
3974+
AssignDest = dest;
39563975
}
39573976
};
39583977

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ enum DIUseKind {
254254
/// value.
255255
Assign,
256256

257+
/// The instruction is an assignment of a wrapped value with an already initialized
258+
/// backing property wrapper.
259+
AssignWrappedValue,
260+
257261
/// The instruction is a store to a member of a larger struct value.
258262
PartialStore,
259263

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,7 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory,
533533
case DIUseKind::Escape:
534534
continue;
535535
case DIUseKind::Assign:
536+
case DIUseKind::AssignWrappedValue:
536537
case DIUseKind::IndirectIn:
537538
case DIUseKind::InitOrAssign:
538539
case DIUseKind::InOutArgument:
@@ -750,6 +751,7 @@ void LifetimeChecker::doIt() {
750751
continue;
751752

752753
case DIUseKind::Assign:
754+
case DIUseKind::AssignWrappedValue:
753755
// Instructions classified as assign are only generated when lowering
754756
// InitOrAssign instructions in regions known to be initialized. Since
755757
// they are already known to be definitely init, don't reprocess them.
@@ -1047,16 +1049,15 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) {
10471049
// an initialization or assign in the uses list so that clients know about it.
10481050
if (isFullyUninitialized) {
10491051
Use.Kind = DIUseKind::Initialization;
1052+
} else if (isFullyInitialized && isa<AssignByWrapperInst>(Use.Inst)) {
1053+
// If some fields are uninitialized, re-write assign_by_wrapper to assignment
1054+
// of the backing wrapper. If all fields are initialized, assign to the wrapped
1055+
// value.
1056+
auto allFieldsInitialized =
1057+
getAnyUninitializedMemberAtInst(Use.Inst, 0, TheMemory.getNumElements()) == -1;
1058+
Use.Kind = allFieldsInitialized ? DIUseKind::AssignWrappedValue : DIUseKind::Assign;
10501059
} else if (isFullyInitialized) {
1051-
// Only re-write assign_by_wrapper to assignment if all fields have been
1052-
// initialized.
1053-
if (isa<AssignByWrapperInst>(Use.Inst) &&
1054-
getAnyUninitializedMemberAtInst(Use.Inst, 0,
1055-
TheMemory.getNumElements()) != -1) {
1056-
Use.Kind = DIUseKind::Initialization;
1057-
} else {
1058-
Use.Kind = DIUseKind::Assign;
1059-
}
1060+
Use.Kind = DIUseKind::Assign;
10601061
} else {
10611062
// If it is initialized on some paths, but not others, then we have an
10621063
// inconsistent initialization, which needs dynamic control logic in the
@@ -1909,7 +1910,7 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
19091910
Use.Kind == DIUseKind::SelfInit)
19101911
InitKind = IsInitialization;
19111912
else {
1912-
assert(Use.Kind == DIUseKind::Assign);
1913+
assert(Use.Kind == DIUseKind::Assign || Use.Kind == DIUseKind::AssignWrappedValue);
19131914
InitKind = IsNotInitialization;
19141915
}
19151916

@@ -1958,14 +1959,21 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
19581959
Use.Inst = nullptr;
19591960
NonLoadUses.erase(Inst);
19601961

1961-
if (TheMemory.isClassInitSelf() &&
1962-
Use.Kind == DIUseKind::SelfInit) {
1963-
assert(InitKind == IsInitialization);
1964-
AI->setOwnershipQualifier(AssignOwnershipQualifier::Reinit);
1965-
} else {
1966-
AI->setOwnershipQualifier((InitKind == IsInitialization
1967-
? AssignOwnershipQualifier::Init
1968-
: AssignOwnershipQualifier::Reassign));
1962+
switch (Use.Kind) {
1963+
case DIUseKind::Initialization:
1964+
AI->setAssignInfo(AssignOwnershipQualifier::Init,
1965+
AssignByWrapperInst::Destination::BackingWrapper);
1966+
break;
1967+
case DIUseKind::Assign:
1968+
AI->setAssignInfo(AssignOwnershipQualifier::Reassign,
1969+
AssignByWrapperInst::Destination::BackingWrapper);
1970+
break;
1971+
case DIUseKind::AssignWrappedValue:
1972+
AI->setAssignInfo(AssignOwnershipQualifier::Reassign,
1973+
AssignByWrapperInst::Destination::WrappedValue);
1974+
break;
1975+
default:
1976+
llvm_unreachable("Wrong use kind for assign_by_wrapper");
19691977
}
19701978

19711979
return;

lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,8 @@ static void lowerAssignByWrapperInstruction(SILBuilderWithScope &b,
179179
SILLocation loc = inst->getLoc();
180180
SILBuilderWithScope forCleanup(std::next(inst->getIterator()));
181181

182-
switch (inst->getOwnershipQualifier()) {
183-
case AssignOwnershipQualifier::Init: {
182+
switch (inst->getAssignDestination()) {
183+
case AssignByWrapperInst::Destination::BackingWrapper: {
184184
SILValue initFn = inst->getInitializer();
185185
CanSILFunctionType fTy = initFn->getType().castTo<SILFunctionType>();
186186
SILFunctionConventions convention(fTy, inst->getModule());
@@ -193,15 +193,18 @@ static void lowerAssignByWrapperInstruction(SILBuilderWithScope &b,
193193
getAssignByWrapperArgs(args, src, convention, b, forCleanup);
194194
SILValue wrappedSrc = b.createApply(loc, initFn, SubstitutionMap(),
195195
args);
196-
b.createTrivialStoreOr(loc, wrappedSrc, dest,
197-
StoreOwnershipQualifier::Init);
196+
if (inst->getOwnershipQualifier() == AssignOwnershipQualifier::Init ||
197+
inst->getDest()->getType().isTrivial(*inst->getFunction())) {
198+
b.createTrivialStoreOr(loc, wrappedSrc, dest, StoreOwnershipQualifier::Init);
199+
} else {
200+
b.createStore(loc, wrappedSrc, dest, StoreOwnershipQualifier::Assign);
201+
}
198202
}
199203
// TODO: remove the unused setter function, which usually is a dead
200204
// partial_apply.
201205
break;
202206
}
203-
case AssignOwnershipQualifier::Unknown:
204-
case AssignOwnershipQualifier::Reassign: {
207+
case AssignByWrapperInst::Destination::WrappedValue: {
205208
SILValue setterFn = inst->getSetter();
206209
CanSILFunctionType fTy = setterFn->getType().castTo<SILFunctionType>();
207210
SILFunctionConventions convention(fTy, inst->getModule());
@@ -220,8 +223,6 @@ static void lowerAssignByWrapperInstruction(SILBuilderWithScope &b,
220223
// partial_apply.
221224
break;
222225
}
223-
case AssignOwnershipQualifier::Reinit:
224-
llvm_unreachable("wrong qualifier for assign_by_wrapper");
225226
}
226227
inst->eraseFromParent();
227228
}

test/SILOptimizer/di_property_wrappers.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,54 @@ func testSR_12341() {
534534
_ = SR_12341(condition: true)
535535
}
536536

537+
class TestLeak: CustomStringConvertible {
538+
let val: Int
539+
init(val: Int) { self.val = val }
540+
deinit { print(" .. \(self).deinit") }
541+
var description: String { "TestLeak(\(val))" }
542+
}
543+
544+
struct SR_13495 {
545+
@Wrapper var wrapped: TestLeak
546+
var str: String
547+
548+
init() {
549+
wrapped = TestLeak(val: 42)
550+
str = ""
551+
wrapped = TestLeak(val: 27)
552+
}
553+
554+
init(conditionalInit: Bool) {
555+
if (conditionalInit) {
556+
wrapped = TestLeak(val: 42)
557+
}
558+
str = ""
559+
wrapped = TestLeak(val: 27)
560+
}
561+
}
562+
563+
func testSR_13495() {
564+
// CHECK: ## SR_13495
565+
print("\n## SR_13495")
566+
567+
// CHECK-NEXT: .. init TestLeak(42)
568+
// CHECK-NEXT: .. TestLeak(42).deinit
569+
// CHECK-NEXT: .. set TestLeak(27)
570+
// CHECK-NEXT: .. TestLeak(27).deinit
571+
_ = SR_13495()
572+
573+
// CHECK-NEXT: .. init TestLeak(42)
574+
// CHECK-NEXT: .. TestLeak(42).deinit
575+
// CHECK-NEXT: .. init TestLeak(27)
576+
// CHECK-NEXT: .. TestLeak(27).deinit
577+
_ = SR_13495(conditionalInit: true)
578+
579+
// CHECK-NEXT: .. init TestLeak(27)
580+
// CHECK-NEXT: .. TestLeak(27).deinit
581+
_ = SR_13495(conditionalInit: false)
582+
}
583+
584+
537585
@propertyWrapper
538586
struct NonMutatingSetterWrapper<Value> {
539587
var value: Value
@@ -572,4 +620,5 @@ testDefaultNilOptIntStruct()
572620
testComposed()
573621
testWrapperInitWithDefaultArg()
574622
testSR_12341()
623+
testSR_13495()
575624
testNonMutatingSetterStruct()

0 commit comments

Comments
 (0)