Skip to content

Commit e06f811

Browse files
committed
FieldSensitivePrunedLiveness: inject_enum_addr only initializes the tag of enums.
Fixes a crash when optional-chaining a move-only function that produces an address-only value; FSPL would previously see the apply that initializes the payload and the `inject_enum_tag` as overlapping defs, leading to mishandling when trying to maximize the lifetime of the enum.
1 parent 66801fd commit e06f811

File tree

2 files changed

+48
-3
lines changed

2 files changed

+48
-3
lines changed

include/swift/SIL/FieldSensitivePrunedLiveness.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -315,12 +315,27 @@ struct TypeTreeLeafTypeRange {
315315
return std::nullopt;
316316

317317
// A drop_deinit only consumes the deinit bit of its operand.
318-
auto *ddi = dyn_cast<DropDeinitInst>(op->getUser());
319-
if (ddi) {
318+
if (isa<DropDeinitInst>(op->getUser())) {
320319
auto upperBound = *startEltOffset + TypeSubElementCount(projectedValue);
321320
return {{upperBound - 1, upperBound}};
322321
}
323322

323+
// An `inject_enum_addr` only initializes the enum tag.
324+
if (auto inject = dyn_cast<InjectEnumAddrInst>(op->getUser())) {
325+
auto upperBound = *startEltOffset + TypeSubElementCount(projectedValue);
326+
unsigned payloadUpperBound = 0;
327+
if (inject->getElement()->hasAssociatedValues()) {
328+
auto payloadTy = projectedValue->getType()
329+
.getEnumElementType(inject->getElement(), op->getFunction());
330+
331+
payloadUpperBound = *startEltOffset
332+
+ TypeSubElementCount(payloadTy, op->getFunction());
333+
}
334+
// TODO: account for deinit component if enum has deinit.
335+
assert(!projectedValue->getType().isValueTypeWithDeinit());
336+
return {{payloadUpperBound, upperBound}};
337+
}
338+
324339
// Uses that borrow a value do not involve the deinit bit.
325340
//
326341
// FIXME: This shouldn't be limited to applies.
@@ -984,7 +999,7 @@ class FieldSensitivePrunedLiveness {
984999
return *&iter->second;
9851000
}
9861001

987-
/// If \p user has had uses recored, return a pointer to the InterestingUser
1002+
/// If \p user has had uses recorded, return a pointer to the InterestingUser
9881003
/// where they've been recorded.
9891004
InterestingUser const *getInterestingUser(SILInstruction *user) const {
9901005
auto iter = users.find(user);

test/SILGen/moveonly_optional_operations.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ struct NC: ~Copyable {
2323
borrowing func b() {}
2424
mutating func m() {}
2525
consuming func c() {}
26+
27+
consuming func c2() -> NC { c2() } // expected-warning{{}}
28+
consuming func c3() -> NCAO { c3() } // expected-warning{{}}
2629
}
2730

2831
struct NCAO: ~Copyable {
@@ -31,6 +34,9 @@ struct NCAO: ~Copyable {
3134
borrowing func b() {}
3235
mutating func m() {}
3336
consuming func c() {}
37+
38+
consuming func c2() -> NC { c2() } // expected-warning{{}}
39+
consuming func c3() -> NCAO { c3() } // expected-warning{{}}
3440
}
3541

3642
func borrowingChains(nc: borrowing NC?, // expected-error{{'nc' is borrowed and cannot be consumed}}
@@ -140,3 +146,27 @@ func consumingChains2(nc: consuming NC?,
140146
ncao?.m()
141147
ncao?.c()
142148
}
149+
150+
func consumingSwitchSubject(nc: consuming NC?,
151+
ncao: consuming NCAO?) {
152+
switch nc?.c2() {
153+
default:
154+
break
155+
}
156+
switch ncao?.c2() {
157+
default:
158+
break
159+
}
160+
}
161+
162+
func consumingSwitchSubject2(nc: consuming NC?,
163+
ncao: consuming NCAO?) {
164+
switch nc?.c3() {
165+
default:
166+
break
167+
}
168+
switch ncao?.c3() {
169+
default:
170+
break
171+
}
172+
}

0 commit comments

Comments
 (0)