Skip to content

Commit a53e351

Browse files
committed
[silgenpattern] Fix a leak in tuple pattern emission.
Today SILGenPattern maintains the following invariants: 1. All values passed into a switch must be either TakeAlways or BorrowAlways and loadable input values will be loaded. 2. Loadable types passed to a subtree must be loaded. 3. TakeOnSuccess can only occur with address only types and only in subtrees. 4. A TakeOnSuccess value must go through "fake borrowing" (i.e. forward/unforwarding) to ensure that along failing cases, we properly re-enable the cleanup on the aggregate. This means that TakeOnSuccess can never be handled as a loadable value with ownership enabled and that any take_on_success value since the original cleanup on the parent value was disabled must be at +1. 5. We use BorrowAlways instead of TakeOnSuccess for objects to express the notion that the object is not owned by the sub-tree. The bug in this tuple code occured at the a place in the code where we go from an address only parent type to a loadable subtype via TakeOnSuccess. I was trying to follow (5) and thus I converted the subvalue to use a {load_borrow, BorrowAlways}. The problem with this is that I lost the cleanup from the tuple subvalue since take_on_success is just simulating +0 and thus entails having a cleanup for each of the underlying tuple values. The fix here was to: * Create a cleanup for the loadable subvalue leaving it in memory. This address version of the subvalue we use for the purposes of unforwarding. This avoids (4). * load_borrow the subvalue and pass that off to the subtree. This ensures that we respect (2), (3), (4). * Unforward in the failure case the in memory subvalue. This gives us both characteristics as well as in the future the possibility of enforcing via the ownership verifier that the borrow ends before the destroy_addr. I also sprinkled some assertions liberally to maintain invariants. rdar://47034816
1 parent 7b73135 commit a53e351

File tree

3 files changed

+315
-101
lines changed

3 files changed

+315
-101
lines changed

lib/SILGen/SILGenPattern.cpp

Lines changed: 135 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,7 @@ forwardIntoSubtree(SILGenFunction &SGF, SILLocation loc,
743743
(void)consumptionKind;
744744

745745
// If we have an object and it is take always, we need to borrow the value
746-
// since we do not own the value at this point.
746+
// since our subtree does not own the value.
747747
if (outerMV.getType().isObject()) {
748748
assert(consumptionKind == CastConsumptionKind::TakeAlways &&
749749
"Object without cleanup that is not take_always?!");
@@ -1429,45 +1429,59 @@ emitTupleDispatch(ArrayRef<RowToSpecialize> rows, ConsumableManagedValue src,
14291429

14301430
// If our source is an address that is loadable, perform a load_borrow.
14311431
if (src.getType().isAddress() && src.getType().isLoadable(SGF.getModule())) {
1432+
// We should only see take_on_success if we have a base type that is address
1433+
// only.
1434+
assert(src.getFinalConsumption() != CastConsumptionKind::TakeOnSuccess &&
1435+
"Can only occur if base type is address only?!");
14321436
src = {SGF.B.createLoadBorrow(loc, src.getFinalManagedValue()),
14331437
CastConsumptionKind::BorrowAlways};
14341438
}
14351439

14361440
// Then if we have an object...
14371441
if (src.getType().isObject()) {
1438-
// Make sure that if we ahve a copy_on_success, non-trivial value that we do
1442+
// Make sure that if we have a copy_on_success, non-trivial value that we do
14391443
// not have a value with @owned ownership.
14401444
assert((!src.getType().isTrivial(SGF.getModule()) ||
14411445
src.getFinalConsumption() != CastConsumptionKind::CopyOnSuccess ||
14421446
src.getOwnershipKind() != ValueOwnershipKind::Owned) &&
14431447
"@owned value without cleanup + copy_on_success");
14441448

1445-
// If we have are asked to perform TakeOnSuccess, borrow the value instead.
1446-
//
1447-
// The reason why do this for TakeOnSuccess is that we want to not have to
1448-
// deal with unforwarding of aggregate tuples in failing cases since that
1449-
// causes ownership invariants to be violated since we already forwarded the
1450-
// aggregate to create cleanups on its elements.
1451-
//
1452-
// In contrast, we do still want to allow for TakeAlways variants to not
1453-
// need to borrow, so we do not borrow if we take always.
1454-
if (!src.getType().isTrivial(SGF.getModule()) &&
1455-
src.getFinalConsumption() == CastConsumptionKind::TakeOnSuccess) {
1456-
src = {src.getFinalManagedValue().borrow(SGF, loc),
1457-
CastConsumptionKind::BorrowAlways};
1458-
}
1449+
// We should only see take_on_success if we have a base type that is address
1450+
// only.
1451+
assert(src.getFinalConsumption() != CastConsumptionKind::TakeOnSuccess &&
1452+
"Can only occur if base type is address only?!");
14591453

14601454
// Then perform a forward or reborrow destructure on the object.
14611455
return emitTupleObjectDispatch(rows, src, handleCase, outerFailure);
14621456
}
14631457

1458+
// Construct the specialized rows.
1459+
SmallVector<SpecializedRow, 4> specializedRows;
1460+
specializedRows.resize(rows.size());
1461+
for (unsigned i = 0, e = rows.size(); i != e; ++i) {
1462+
specializedRows[i].RowIndex = rows[i].RowIndex;
1463+
1464+
auto pattern = cast<TuplePattern>(rows[i].Pattern);
1465+
for (auto &elt : pattern->getElements()) {
1466+
specializedRows[i].Patterns.push_back(elt.getPattern());
1467+
}
1468+
}
1469+
14641470
// At this point we know that we must have an address only type, since we
14651471
// would have loaded it earlier.
14661472
SILValue v = src.getFinalManagedValue().forward(SGF);
14671473
assert(v->getType().isAddressOnly(SGF.getModule()) &&
14681474
"Loadable values were handled earlier");
14691475

1470-
SmallVector<ConsumableManagedValue, 4> destructured;
1476+
// The destructured tuple that we pass off to our sub pattern. This may
1477+
// contain values that we have performed a load_borrow from subsequent to
1478+
// "performing a SILGenPattern borrow".
1479+
SmallVector<ConsumableManagedValue, 4> subPatternArgs;
1480+
1481+
// An array of values that have the same underlying values as our
1482+
// subPatternArgs, but may have a different cleanup and final consumption
1483+
// kind. These are at +1 and are unforwarded.
1484+
SmallVector<ConsumableManagedValue, 4> unforwardArgs;
14711485

14721486
// Break down the values.
14731487
auto tupleSILTy = v->getType();
@@ -1476,65 +1490,87 @@ emitTupleDispatch(ArrayRef<RowToSpecialize> rows, ConsumableManagedValue src,
14761490
auto &fieldTL = SGF.getTypeLowering(fieldTy);
14771491

14781492
SILValue member = SGF.B.createTupleElementAddr(loc, v, i, fieldTy);
1479-
ConsumableManagedValue memberCMV;
1493+
// Inline constructor.
1494+
auto memberCMV = ([&]() -> ConsumableManagedValue {
1495+
if (!fieldTL.isLoadable()) {
1496+
// If we have an address only type, just get the managed
1497+
// subobject.
1498+
return getManagedSubobject(SGF, member, fieldTL,
1499+
src.getFinalConsumption());
1500+
}
14801501

1481-
// If we have a loadable sub-type of our tuple...
1482-
if (fieldTL.isLoadable()) {
1502+
// If we have a loadable type, then we have a loadable sub-type of the
1503+
// underlying address only tuple.
1504+
auto memberMV = ManagedValue::forUnmanaged(member);
14831505
switch (src.getFinalConsumption()) {
14841506
case CastConsumptionKind::TakeAlways: {
1485-
// and our consumption is take always, perform a load [take] and
1486-
// continue.
1487-
auto memberMV = ManagedValue::forUnmanaged(member);
1488-
memberCMV = {SGF.B.createLoadTake(loc, memberMV),
1489-
CastConsumptionKind::TakeAlways};
1490-
break;
1507+
// If our original source value is take always, perform a load [take].
1508+
return {SGF.B.createLoadTake(loc, memberMV),
1509+
CastConsumptionKind::TakeAlways};
1510+
}
1511+
case CastConsumptionKind::TakeOnSuccess: {
1512+
// If we have a take_on_success, we propagate down the member as a +1
1513+
// address value and do not load.
1514+
//
1515+
// DISCUSSION: Unforwarding objects violates ownership since
1516+
// unforwarding relies on forwarding an aggregate into subvalues and
1517+
// on failure disabling the subvalue cleanups and re-enabling the
1518+
// cleanup for the aggregate (which was already destroyed). So we are
1519+
// forced to use an address here so we can forward/unforward this
1520+
// value. We maintain our invariants that loadable types are always
1521+
// loaded and are never take on success by passing down to our
1522+
// subPattern a borrow of this value. See below.
1523+
return getManagedSubobject(SGF, member, fieldTL,
1524+
src.getFinalConsumption());
14911525
}
1492-
case CastConsumptionKind::TakeOnSuccess:
14931526
case CastConsumptionKind::CopyOnSuccess: {
1494-
// otherwise we have take on success or copy on success perform a
1495-
// load_borrow.
1527+
// We translate copy_on_success => borrow_always.
14961528
auto memberMV = ManagedValue::forUnmanaged(member);
1497-
memberCMV = {SGF.B.createLoadBorrow(loc, memberMV),
1498-
CastConsumptionKind::BorrowAlways};
1499-
break;
1529+
return {SGF.B.createLoadBorrow(loc, memberMV),
1530+
CastConsumptionKind::BorrowAlways};
15001531
}
1501-
case CastConsumptionKind::BorrowAlways:
1502-
llvm_unreachable("Borrow always can not be used on objects");
1532+
case CastConsumptionKind::BorrowAlways: {
1533+
llvm_unreachable(
1534+
"Borrow always can only occur along object only code paths");
15031535
}
1504-
} else {
1505-
// Otherwise, if we have an address only type, just get the managed
1506-
// subobject.
1507-
memberCMV =
1508-
getManagedSubobject(SGF, member, fieldTL, src.getFinalConsumption());
1509-
}
1510-
1511-
destructured.push_back(memberCMV);
1512-
}
1513-
1514-
// Construct the specialized rows.
1515-
SmallVector<SpecializedRow, 4> specializedRows;
1516-
specializedRows.resize(rows.size());
1517-
for (unsigned i = 0, e = rows.size(); i != e; ++i) {
1518-
specializedRows[i].RowIndex = rows[i].RowIndex;
1536+
}
1537+
}());
15191538

1520-
auto pattern = cast<TuplePattern>(rows[i].Pattern);
1521-
for (auto &elt : pattern->getElements()) {
1522-
specializedRows[i].Patterns.push_back(elt.getPattern());
1539+
// If we aren't loadable, add to the unforward array.
1540+
if (!fieldTL.isLoadable()) {
1541+
unforwardArgs.push_back(memberCMV);
1542+
} else {
1543+
// If we have a loadable type that we didn't load, we must have had a
1544+
// take_on_success address. This means that our parent cleanup is
1545+
// currently persistently active, so we needed to propagate an active +1
1546+
// cleanup on our address so we can take if we actually succeed. That
1547+
// being said, we do not want to pass objects with take_on_success into
1548+
// the actual subtree. So we perform a load_borrow at this point. This
1549+
// will ensure that we will always finish the end_borrow before we jumped
1550+
// to a failure point, but at the same time the original +1 value will be
1551+
// appropriately destroyed/forwarded around.
1552+
if (memberCMV.getType().isAddress()) {
1553+
unforwardArgs.push_back(memberCMV);
1554+
auto val = memberCMV.getFinalManagedValue();
1555+
memberCMV = {SGF.B.createLoadBorrow(loc, val),
1556+
CastConsumptionKind::BorrowAlways};
1557+
}
15231558
}
1559+
subPatternArgs.push_back(memberCMV);
15241560
}
15251561

15261562
// Maybe revert to the original cleanups during failure branches.
15271563
const FailureHandler *innerFailure = &outerFailure;
15281564
FailureHandler specializedFailure = [&](SILLocation loc) {
15291565
ArgUnforwarder unforwarder(SGF);
1530-
unforwarder.unforwardBorrowedValues(src, destructured);
1566+
unforwarder.unforwardBorrowedValues(src, unforwardArgs);
15311567
outerFailure(loc);
15321568
};
15331569
if (ArgUnforwarder::requiresUnforwarding(SGF, src))
15341570
innerFailure = &specializedFailure;
15351571

15361572
// Recurse.
1537-
handleCase(destructured, specializedRows, *innerFailure);
1573+
handleCase(subPatternArgs, specializedRows, *innerFailure);
15381574
}
15391575

15401576
static CanType getTargetType(const RowToSpecialize &row) {
@@ -1564,46 +1600,53 @@ emitCastOperand(SILGenFunction &SGF, SILLocation loc,
15641600
return src;
15651601
}
15661602

1603+
// We know that we must have a loadable type at this point since address only
1604+
// types do not need reabstraction and are addresses. So we should have exited
1605+
// above already.
1606+
assert(src.getType().isLoadable(SGF.getModule()) &&
1607+
"Should have a loadable value at this point");
1608+
1609+
// Since our finalValue is loadable, we could not have had a take_on_success
1610+
// here.
1611+
assert(src.getFinalConsumption() != CastConsumptionKind::TakeOnSuccess &&
1612+
"Loadable types can not have take_on_success?!");
1613+
15671614
std::unique_ptr<TemporaryInitialization> init;
15681615
SGFContext ctx;
15691616
if (requiresAddress) {
15701617
init = SGF.emitTemporary(loc, srcAbstractTL);
15711618
ctx = SGFContext(init.get());
15721619
}
15731620

1574-
ManagedValue substValue = SGF.getManagedValue(loc, src);
1575-
ManagedValue finalValue;
1621+
// This will always produce a +1 take always value no matter what src's
1622+
// ownership is.
1623+
ManagedValue finalValue = SGF.getManagedValue(loc, src);
15761624
if (hasAbstraction) {
1577-
assert(src.getType().isObject() &&
1578-
"address-only type with abstraction difference?");
1579-
// Produce the value at +1.
1580-
finalValue = SGF.emitSubstToOrigValue(loc, substValue,
1581-
abstraction, sourceType, ctx);
1582-
} else {
1583-
finalValue = substValue;
1584-
}
1585-
1586-
if (requiresAddress) {
1587-
if (finalValue.getOwnershipKind() == ValueOwnershipKind::Guaranteed)
1588-
finalValue = finalValue.copy(SGF, loc);
1589-
SGF.B.emitStoreValueOperation(loc, finalValue.forward(SGF),
1590-
init->getAddress(),
1591-
StoreOwnershipQualifier::Init);
1592-
init->finishInitialization(SGF);
1593-
1594-
// If we had borrow_always, we need to switch to copy_on_success since
1595-
// that is the address only variant of borrow_always.
1596-
auto consumption = src.getFinalConsumption();
1597-
if (consumption == CastConsumptionKind::BorrowAlways)
1598-
consumption = CastConsumptionKind::CopyOnSuccess;
1599-
ConsumableManagedValue result = {init->getManagedAddress(), consumption};
1600-
if (ArgUnforwarder::requiresUnforwarding(SGF, src))
1601-
borrowedValues.push_back(result);
1602-
1603-
return result;
1604-
}
1605-
1606-
return ConsumableManagedValue::forOwned(finalValue);
1625+
// Reabstract the value if we need to. This should produce a +1 value as
1626+
// well.
1627+
finalValue =
1628+
SGF.emitSubstToOrigValue(loc, finalValue, abstraction, sourceType, ctx);
1629+
}
1630+
assert(finalValue.isPlusOne(SGF));
1631+
1632+
// If we at this point do not require an address, return final value. We know
1633+
// that it is a +1 take always value.
1634+
if (!requiresAddress) {
1635+
return ConsumableManagedValue::forOwned(finalValue);
1636+
}
1637+
1638+
// At this point, we know that we have a non-address only type since we are
1639+
// materializing an object into memory and addresses can not be stored into
1640+
// memory.
1641+
SGF.B.emitStoreValueOperation(loc, finalValue.forward(SGF),
1642+
init->getAddress(),
1643+
StoreOwnershipQualifier::Init);
1644+
init->finishInitialization(SGF);
1645+
1646+
// We know that either our initial value was already take_always or we made a
1647+
// copy of the underlying value. In either case, we now have a take_always +1
1648+
// value.
1649+
return ConsumableManagedValue::forOwned(init->getManagedAddress());
16071650
}
16081651

16091652
/// Perform specialized dispatch for a sequence of IsPatterns.
@@ -1938,27 +1981,18 @@ void PatternMatchEmission::emitEnumElementDispatch(
19381981

19391982
// If our source is an address that is loadable, perform a load_borrow.
19401983
if (src.getType().isAddress() && src.getType().isLoadable(SGF.getModule())) {
1984+
assert(src.getFinalConsumption() != CastConsumptionKind::TakeOnSuccess &&
1985+
"Can only have take_on_success with address only values");
19411986
src = {SGF.B.createLoadBorrow(loc, src.getFinalManagedValue()),
19421987
CastConsumptionKind::BorrowAlways};
19431988
}
19441989

19451990
// If we have an object...
19461991
if (src.getType().isObject()) {
1947-
// And we have a non-trivial object type that we are asked to perform take
1948-
// on success for, borrow the value instead.
1949-
//
1950-
// The reason why do this for TakeOnSuccess is that we want to not have to
1951-
// deal with unforwarding of aggregate tuples in failing cases since that
1952-
// causes ownership invariants to be violated since we already forwarded the
1953-
// aggregate to create cleanups on its elements.
1954-
//
1955-
// In contrast, we do still want to allow for TakeAlways variants to not
1956-
// need to borrow, so we do not borrow if we take always.
1957-
if (!src.getType().isTrivial(SGF.getModule()) &&
1958-
src.getFinalConsumption() == CastConsumptionKind::TakeOnSuccess) {
1959-
src = {src.getFinalManagedValue().borrow(SGF, loc),
1960-
CastConsumptionKind::BorrowAlways};
1961-
}
1992+
// Do a quick assert that we do not have take_on_success. This should only
1993+
// be passed take_on_success if src is an address only type.
1994+
assert(src.getFinalConsumption() != CastConsumptionKind::TakeOnSuccess &&
1995+
"Can only have take_on_success with address only values");
19621996

19631997
// Finally perform the enum element dispatch.
19641998
return emitEnumElementObjectDispatch(rows, src, handleCase, outerFailure,

test/Interpreter/switch.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,58 @@ SwitchTestSuite.test("GenericVar") {
168168
expectTrue(l === Gesture.pinch(l).valueVar as! LifetimeTracked)
169169
}
170170

171+
SwitchTestSuite.test("TupleUnforwarding") {
172+
// None of these switches should leak.
173+
do {
174+
let l = LifetimeTracked(0)
175+
let r: Optional<Any> = LifetimeTracked(0) as Any
176+
177+
switch (l, r) {
178+
case (_, _):
179+
break
180+
default:
181+
break
182+
}
183+
}
184+
185+
do {
186+
let l = LifetimeTracked(0)
187+
let r: Optional<Any> = LifetimeTracked(0) as Any
188+
switch (l, r) {
189+
case let (x, _):
190+
break
191+
case let (_, y as AnyObject):
192+
break
193+
default:
194+
break
195+
}
196+
}
197+
198+
do {
199+
let l: Optional<LifetimeTracked> = LifetimeTracked(0)
200+
let r: Optional<Any> = LifetimeTracked(0) as Any
201+
switch (l, r) {
202+
case let (_, _):
203+
break
204+
case let (_, y as AnyObject):
205+
break
206+
default:
207+
break
208+
}
209+
}
210+
211+
do {
212+
let l = LifetimeTracked(0)
213+
let r: Optional<Any> = LifetimeTracked(0) as Any
214+
switch (l, r) {
215+
case let (_, y as AnyObject):
216+
break
217+
case let (x as AnyObject, _):
218+
break
219+
default:
220+
break
221+
}
222+
}
223+
}
224+
171225
runAllTests()

0 commit comments

Comments
 (0)