Skip to content

Commit 749f319

Browse files
authored
Merge pull request #21745 from gottesmm/pr-7badf1ef083f46a99dde5777619f7f410168e8a5
2 parents a779f9b + a53e351 commit 749f319

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)