Skip to content

Commit 04ddca3

Browse files
authored
Merge pull request swiftlang#33923 from hborla/assign-by-wrapper-lowering
[Property Wrappers] Refine assign_by_wrapper lowering
2 parents 58f2ae6 + a6d8425 commit 04ddca3

File tree

5 files changed

+99
-27
lines changed

5 files changed

+99
-27
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3950,6 +3950,16 @@ class AssignByWrapperInst
39503950
: public AssignInstBase<SILInstructionKind::AssignByWrapperInst, 4> {
39513951
friend SILBuilder;
39523952

3953+
public:
3954+
/// The assignment destination for the property wrapper
3955+
enum class Destination {
3956+
BackingWrapper,
3957+
WrappedValue,
3958+
};
3959+
3960+
private:
3961+
Destination AssignDest = Destination::WrappedValue;
3962+
39533963
AssignByWrapperInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest,
39543964
SILValue Initializer, SILValue Setter,
39553965
AssignOwnershipQualifier Qualifier =
@@ -3964,8 +3974,17 @@ class AssignByWrapperInst
39643974
return AssignOwnershipQualifier(
39653975
SILInstruction::Bits.AssignByWrapperInst.OwnershipQualifier);
39663976
}
3967-
void setOwnershipQualifier(AssignOwnershipQualifier qualifier) {
3977+
3978+
Destination getAssignDestination() const { return AssignDest; }
3979+
3980+
void setAssignInfo(AssignOwnershipQualifier qualifier, Destination dest) {
3981+
using Qualifier = AssignOwnershipQualifier;
3982+
assert(qualifier == Qualifier::Init && dest == Destination::BackingWrapper ||
3983+
qualifier == Qualifier::Reassign && dest == Destination::BackingWrapper ||
3984+
qualifier == Qualifier::Reassign && dest == Destination::WrappedValue);
3985+
39683986
SILInstruction::Bits.AssignByWrapperInst.OwnershipQualifier = unsigned(qualifier);
3987+
AssignDest = dest;
39693988
}
39703989
};
39713990

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.
@@ -1048,16 +1050,15 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) {
10481050
// an initialization or assign in the uses list so that clients know about it.
10491051
if (isFullyUninitialized) {
10501052
Use.Kind = DIUseKind::Initialization;
1053+
} else if (isFullyInitialized && isa<AssignByWrapperInst>(Use.Inst)) {
1054+
// If some fields are uninitialized, re-write assign_by_wrapper to assignment
1055+
// of the backing wrapper. If all fields are initialized, assign to the wrapped
1056+
// value.
1057+
auto allFieldsInitialized =
1058+
getAnyUninitializedMemberAtInst(Use.Inst, 0, TheMemory.getNumElements()) == -1;
1059+
Use.Kind = allFieldsInitialized ? DIUseKind::AssignWrappedValue : DIUseKind::Assign;
10511060
} else if (isFullyInitialized) {
1052-
// Only re-write assign_by_wrapper to assignment if all fields have been
1053-
// initialized.
1054-
if (isa<AssignByWrapperInst>(Use.Inst) &&
1055-
getAnyUninitializedMemberAtInst(Use.Inst, 0,
1056-
TheMemory.getNumElements()) != -1) {
1057-
Use.Kind = DIUseKind::Initialization;
1058-
} else {
1059-
Use.Kind = DIUseKind::Assign;
1060-
}
1061+
Use.Kind = DIUseKind::Assign;
10611062
} else {
10621063
// If it is initialized on some paths, but not others, then we have an
10631064
// inconsistent initialization, which needs dynamic control logic in the
@@ -1918,7 +1919,7 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
19181919
Use.Kind == DIUseKind::SelfInit)
19191920
InitKind = IsInitialization;
19201921
else {
1921-
assert(Use.Kind == DIUseKind::Assign);
1922+
assert(Use.Kind == DIUseKind::Assign || Use.Kind == DIUseKind::AssignWrappedValue);
19221923
InitKind = IsNotInitialization;
19231924
}
19241925

@@ -1967,14 +1968,21 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
19671968
Use.Inst = nullptr;
19681969
NonLoadUses.erase(Inst);
19691970

1970-
if (TheMemory.isClassInitSelf() &&
1971-
Use.Kind == DIUseKind::SelfInit) {
1972-
assert(InitKind == IsInitialization);
1973-
AI->setOwnershipQualifier(AssignOwnershipQualifier::Reinit);
1974-
} else {
1975-
AI->setOwnershipQualifier((InitKind == IsInitialization
1976-
? AssignOwnershipQualifier::Init
1977-
: AssignOwnershipQualifier::Reassign));
1971+
switch (Use.Kind) {
1972+
case DIUseKind::Initialization:
1973+
AI->setAssignInfo(AssignOwnershipQualifier::Init,
1974+
AssignByWrapperInst::Destination::BackingWrapper);
1975+
break;
1976+
case DIUseKind::Assign:
1977+
AI->setAssignInfo(AssignOwnershipQualifier::Reassign,
1978+
AssignByWrapperInst::Destination::BackingWrapper);
1979+
break;
1980+
case DIUseKind::AssignWrappedValue:
1981+
AI->setAssignInfo(AssignOwnershipQualifier::Reassign,
1982+
AssignByWrapperInst::Destination::WrappedValue);
1983+
break;
1984+
default:
1985+
llvm_unreachable("Wrong use kind for assign_by_wrapper");
19781986
}
19791987

19801988
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
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -o %t/a.out
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %target-run %t/a.out
5+
6+
// REQUIRES: executable_test
7+
8+
import StdlibUnittest
9+
10+
@propertyWrapper
11+
struct Wrapper<T> {
12+
var wrappedValue: T
13+
}
14+
15+
struct TestWrappedValueLeak {
16+
@Wrapper var wrapped: LifetimeTracked = LifetimeTracked(0)
17+
var str: String
18+
19+
init() {
20+
wrapped = LifetimeTracked(42)
21+
str = ""
22+
wrapped = LifetimeTracked(27)
23+
}
24+
25+
init(conditionalInit: Bool) {
26+
if (conditionalInit) {
27+
wrapped = LifetimeTracked(42)
28+
}
29+
str = ""
30+
wrapped = LifetimeTracked(27)
31+
}
32+
}
33+
34+
TestSuite("Property Wrapper DI").test("test wrapped value leak") {
35+
_ = TestWrappedValueLeak()
36+
_ = TestWrappedValueLeak(conditionalInit: true)
37+
_ = TestWrappedValueLeak(conditionalInit: false)
38+
}
39+
40+
runAllTests()

0 commit comments

Comments
 (0)