SILGen: Route the cleanup state management through the failure chain of IsPatterns.

Failure paths need to maintain a consistent view of the cleanup state, so we can't just branch to a common exit point and fixup the cleanup state later. Fixes rdar://problem/21087371.

Swift SVN r29287
This commit is contained in:
Joe Groff
2015-06-04 01:03:36 +00:00
parent 58111522c5
commit 515983bcfa
3 changed files with 80 additions and 23 deletions

View File

@@ -1225,6 +1225,44 @@ void PatternMatchEmission::emitSpecializedDispatch(ClauseMatrix &clauses,
unsigned &lastRow,
unsigned column,
const FailureHandler &failure) {
// HEY! LISTEN!
//
// When a pattern specializes its submatrix (like an 'as' or enum element
// pattern), it *must* chain the FailureHandler for its inner submatrixes
// through our `failure` handler if it manipulates any cleanup state.
// Here's an example from emitEnumElementDispatch:
//
// const FailureHandler *innerFailure = &failure;
// FailureHandler specializedFailure = [&](SILLocation loc) {
// ArgUnforwarder unforwarder(SGF);
// unforwarder.unforwardBorrowedValues(src, origCMV);
// failure(loc);
// };
//
// if (ArgUnforwarder::requiresUnforwarding(src))
// innerFailure = &specializedFailure;
//
// Note that the inner failure handler either is exactly the outer failure
// or performs the work necessary to clean up after the failed specialized
// decision tree immediately before chaining onto the outer failure.
// It is specifically NOT correct to do something like this:
//
// /* DON'T DO THIS */
// ExitableFullExpr scope;
// FailureHandler innerFailure = [&](SILLocation loc) {
// emitBranchAndCleanups(scope, loc);
// };
// ...
// /* DON'T DO THIS */
// scope.exit();
// ArgUnforwarder unforwarder(SGF);
// unforwarder.unforwardBorrowedValues(src, origCMV);
// failure(loc);
// /* DON'T DO THIS */
//
// since the cleanup state changes performed by ArgUnforwarder will
// occur too late.
unsigned firstRow = lastRow;
// Collect the rows to specialize.
@@ -1553,39 +1591,30 @@ void PatternMatchEmission::emitIsDispatch(ArrayRef<RowToSpecialize> rows,
}
SILLocation loc = rows[0].Pattern;
auto cleanupLoc = CleanupLocation::get(loc);
ConsumableManagedValue castOperand = operand.asBorrowedOperand();
// Enter an exitable scope. On failure, we'll just go on to the next case.
ExitableFullExpr scope(SGF, cleanupLoc);
FailureHandler innerFailure = [&](SILLocation loc) {
// The cleanup for the cast operand was pushed outside of this
// jump dest, so we don't have to undo any cleanup-splitting here.
SGF.Cleanups.emitBranchAndCleanups(scope.getExitDest(), loc);
// Chain inner failures onto the outer failure.
const FailureHandler *innerFailure = &failure;
FailureHandler specializedFailure = [&](SILLocation loc) {
ArgUnforwarder unforwarder(SGF);
unforwarder.unforwardBorrowedValues(src, borrowedValues);
failure(loc);
};
if (ArgUnforwarder::requiresUnforwarding(src))
innerFailure = &specializedFailure;
// Perform a conditional cast branch.
SGF.emitCheckedCastBranch(loc, castOperand,
sourceType, targetType, SGFContext(),
// Success block: recurse.
[&](ManagedValue castValue) {
handleCase(ConsumableManagedValue::forOwned(castValue),
specializedRows, innerFailure);
specializedRows, *innerFailure);
assert(!SGF.B.hasValidInsertionPoint() && "did not end block");
},
// Failure block: branch out to the continuation block.
[&] { innerFailure(loc); });
// Dispatch continues on the "false" block.
scope.exit();
ArgUnforwarder unforwarder(SGF);
if (ArgUnforwarder::requiresUnforwarding(src)) {
unforwarder.unforwardBorrowedValues(src, borrowedValues);
}
failure(rows.back().Pattern);
[&] { (*innerFailure)(loc); });
}
/// Perform specialized dispatch for a sequence of EnumElementPattern or an