Skip to content

Commit 40cfbee

Browse files
committed
Fix compiler crashes with consuming and borrowing keywords.
Without this fix, the new 'consuming' and 'borrowing' keywords cannot be used with trivial types. Which means, for example, they can't be used in macro expansions that work on various types. Fixes patterns like: public func test1(i: consuming Int) -> Int { takeClosure { [i = copy i] in i } } public func test2(i: borrowing Int) -> Int { takeClosure { [i = copy i] in i } } public func test3(i: consuming Int) -> Int { takeClosure { i } } // Sadly, test4 is still incorrectly diagnosed. public func test4(i: borrowing Int) -> Int { takeClosure { i } } Fixes rdar://112795074 (Crash compiling function that has a macro annotation and uses `consuming`) (cherry picked from commit 9c24933)
1 parent a51f98d commit 40cfbee

File tree

6 files changed

+126
-43
lines changed

6 files changed

+126
-43
lines changed

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -281,17 +281,24 @@ void releasePartialApplyCapturedArg(
281281
SILParameterInfo paramInfo,
282282
InstModCallbacks callbacks = InstModCallbacks());
283283

284-
/// Insert destroys of captured arguments of partial_apply [stack].
284+
/// Insert destroys of captured arguments of partial_apply [stack]. \p builder
285+
/// indicates a position at which the closure's lifetime ends.
286+
///
287+
/// The \p getValueToDestroy callback allows the caller to handle some captured
288+
/// arguments specially. For example, ClosureLifetimeFixup generates borrow
289+
/// scopes for captured arguments; each getValueToDestroy callback then inserts
290+
/// the corresponding end_borrow and returns the owned operand of the borrow,
291+
/// which will then be destroyed as usual.
285292
void insertDestroyOfCapturedArguments(
286293
PartialApplyInst *pai, SILBuilder &builder,
287-
llvm::function_ref<bool(SILValue)> shouldInsertDestroy =
288-
[](SILValue arg) -> bool { return true; },
294+
llvm::function_ref<SILValue(SILValue)> getValueToDestroy =
295+
[](SILValue arg) -> SILValue { return arg; },
289296
SILLocation loc = RegularLocation::getAutoGeneratedLocation());
290297

291298
void insertDeallocOfCapturedArguments(PartialApplyInst *pai,
292299
DominanceInfo *domInfo,
293-
llvm::function_ref<bool(SILValue)> shouldInsertDestroy =
294-
[](SILValue arg) -> bool { return true; });
300+
llvm::function_ref<SILValue(SILValue)> getAddressToDealloc =
301+
[](SILValue arg) -> SILValue { return arg; });
295302

296303
/// This iterator 'looks through' one level of builtin expect users exposing all
297304
/// users of the looked through builtin expect instruction i.e it presents a

lib/SILOptimizer/Mandatory/CapturePromotion.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1555,7 +1555,9 @@ processPartialApplyInst(SILOptFunctionBuilder &funcBuilder,
15551555
builder.setInsertionPoint(std::next(SILBasicBlock::iterator(dsi)));
15561556
insertDestroyOfCapturedArguments(
15571557
newPAI, builder,
1558-
[&](SILValue arg) -> bool { return newCaptures.count(arg); });
1558+
[&](SILValue arg) -> SILValue {
1559+
return newCaptures.count(arg) ? arg : SILValue();
1560+
});
15591561
}
15601562
}
15611563
// Map the mark dependence arguments.

lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -889,12 +889,28 @@ static SILValue tryRewriteToPartialApplyStack(
889889
destroy->dump();
890890
*/
891891
SILBuilderWithScope builder(std::next(destroy->getIterator()));
892-
insertDestroyOfCapturedArguments(newPA, builder,
893-
[&](SILValue arg) -> bool {
894-
// Don't need to destroy if we borrowed
895-
// in place .
896-
return !borrowedOriginals.count(arg);
897-
},
892+
// This getCapturedArg hack attempts to perfectly compensate for all the
893+
// other hacks involved in gathering new arguments above.
894+
auto getArgToDestroy = [&](SILValue argValue) -> SILValue {
895+
// A MoveOnlyWrapperToCopyableValueInst may produce a trivial value. Be
896+
// careful not to emit an extra destroy of the original.
897+
if (argValue->getType().isTrivial(argValue->getFunction()))
898+
return SILValue();
899+
900+
// We may have inserted a new begin_borrow->moveonlywrapper_to_copyvalue
901+
// when creating the new arguments. Now we need to end that borrow.
902+
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(argValue))
903+
if (m->hasGuaranteedInitialKind())
904+
argValue = m->getOperand();
905+
auto *argBorrow = dyn_cast<BeginBorrowInst>(argValue);
906+
if (argBorrow) {
907+
argValue = argBorrow->getOperand();
908+
builder.createEndBorrow(newPA->getLoc(), argBorrow);
909+
}
910+
// Don't need to destroy if we borrowed in place .
911+
return borrowedOriginals.count(argValue) ? SILValue() : argValue;
912+
};
913+
insertDestroyOfCapturedArguments(newPA, builder, getArgToDestroy,
898914
newPA->getLoc());
899915
}
900916
/* DEBUG
@@ -919,13 +935,17 @@ static SILValue tryRewriteToPartialApplyStack(
919935
if (unreachableBlocks.count(newPA->getParent()))
920936
return closureOp;
921937

938+
auto getAddressToDealloc = [&](SILValue argAddress) -> SILValue {
939+
if (auto moveWrapper =
940+
dyn_cast<MoveOnlyWrapperToCopyableAddrInst>(argAddress)) {
941+
argAddress = moveWrapper->getOperand();
942+
}
943+
// Don't need to destroy if we borrowed in place .
944+
return borrowedOriginals.count(argAddress) ? SILValue() : argAddress;
945+
};
922946
insertDeallocOfCapturedArguments(
923947
newPA, dominanceAnalysis->get(closureUser->getFunction()),
924-
[&](SILValue arg) -> bool {
925-
// Don't need to destroy if we borrowed
926-
// in place.
927-
return !borrowedOriginals.count(arg);
928-
});
948+
getAddressToDealloc);
929949

930950
return closureOp;
931951
}

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -902,13 +902,14 @@ void swift::emitDestroyOperation(SILBuilder &builder, SILLocation loc,
902902
callbacks.createdNewInst(u.get<ReleaseValueInst *>());
903903
}
904904

905-
// *HEY YOU, YES YOU, PLEASE READ*. Even though a textual partial apply is
906-
// printed with the convention of the closed over function upon it, all
907-
// non-inout arguments to a partial_apply are passed at +1. This includes
908-
// arguments that will eventually be passed as guaranteed or in_guaranteed to
909-
// the closed over function. This is because the partial apply is building up a
910-
// boxed aggregate to send off to the closed over function. Of course when you
911-
// call the function, the proper conventions will be used.
905+
// NOTE: The ownership of the partial_apply argument does not match the
906+
// convention of the closed over function. All non-inout arguments to a
907+
// partial_apply are passed at +1 for regular escaping closures and +0 for
908+
// closures that have been promoted to partial_apply [on_stack]. An escaping
909+
// partial apply stores each capture in an owned box, even for guaranteed and
910+
// in_guaranteed argument convention. A non-escaping/on-stack closure either
911+
// borrows its arguments or takes an inout_aliasable address. Non-escaping
912+
// closures do not support owned arguments.
912913
void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc,
913914
SILValue arg,
914915
SILParameterInfo paramInfo,
@@ -1468,7 +1469,7 @@ swift::findLocalApplySites(FunctionRefBaseInst *fri) {
14681469
/// Insert destroys of captured arguments of partial_apply [stack].
14691470
void swift::insertDestroyOfCapturedArguments(
14701471
PartialApplyInst *pai, SILBuilder &builder,
1471-
llvm::function_ref<bool(SILValue)> shouldInsertDestroy,
1472+
llvm::function_ref<SILValue(SILValue)> getValueToDestroy,
14721473
SILLocation origLoc) {
14731474
assert(pai->isOnStack());
14741475

@@ -1477,27 +1478,25 @@ void swift::insertDestroyOfCapturedArguments(
14771478
pai->getModule());
14781479
auto loc = CleanupLocation(origLoc);
14791480
for (auto &arg : pai->getArgumentOperands()) {
1480-
SILValue argValue = arg.get();
1481-
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(argValue))
1482-
if (m->hasGuaranteedInitialKind())
1483-
argValue = m->getOperand();
1484-
auto *argBorrow = dyn_cast<BeginBorrowInst>(argValue);
1485-
if (argBorrow) {
1486-
argValue = argBorrow->getOperand();
1487-
builder.createEndBorrow(loc, argBorrow);
1488-
}
1489-
if (!shouldInsertDestroy(argValue))
1481+
SILValue argValue = getValueToDestroy(arg.get());
1482+
if (!argValue)
14901483
continue;
1484+
1485+
assert(!pai->getFunction()->hasOwnership()
1486+
|| (argValue->getOwnershipKind().isCompatibleWith(
1487+
OwnershipKind::Owned)));
1488+
14911489
unsigned calleeArgumentIndex = site.getCalleeArgIndex(arg);
14921490
assert(calleeArgumentIndex >= calleeConv.getSILArgIndexOfFirstParam());
14931491
auto paramInfo = calleeConv.getParamInfoForSILArg(calleeArgumentIndex);
14941492
releasePartialApplyCapturedArg(builder, loc, argValue, paramInfo);
14951493
}
14961494
}
14971495

1498-
void swift::insertDeallocOfCapturedArguments(PartialApplyInst *pai,
1499-
DominanceInfo *domInfo,
1500-
llvm::function_ref<bool(SILValue)> shouldInsertDestroy)
1496+
void swift::insertDeallocOfCapturedArguments(
1497+
PartialApplyInst *pai,
1498+
DominanceInfo *domInfo,
1499+
llvm::function_ref<SILValue(SILValue)> getAddressToDealloc)
15011500
{
15021501
assert(pai->isOnStack());
15031502

@@ -1511,13 +1510,10 @@ void swift::insertDeallocOfCapturedArguments(PartialApplyInst *pai,
15111510
if (!paramInfo.isIndirectInGuaranteed())
15121511
continue;
15131512

1514-
SILValue argValue = arg.get();
1515-
if (!shouldInsertDestroy(argValue)) {
1513+
SILValue argValue = getAddressToDealloc(arg.get());
1514+
if (!argValue) {
15161515
continue;
15171516
}
1518-
if (auto moveWrapper =
1519-
dyn_cast<MoveOnlyWrapperToCopyableAddrInst>(argValue))
1520-
argValue = moveWrapper->getOperand();
15211517

15221518
SmallVector<SILBasicBlock *, 4> boundary;
15231519
auto *asi = cast<AllocStackInst>(argValue);

test/SILOptimizer/closure-lifetime-fixup.sil

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,41 @@ bb3(%25 : @owned $any Error):
310310
bb4(%29 : @owned $any Error):
311311
unreachable
312312
}
313+
314+
sil @testTrivialBorrowClosure : $@convention(thin) (Int) -> Int
315+
sil @testTrivialBorrowTakeClosure : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> Int) -> Int
316+
317+
// rdar://112795074 (Crash compiling function that has a macro annotation and uses `consuming`)
318+
//
319+
// CHECK-LABEL: sil [ossa] @testTrivialBorrow : $@convention(thin) (Int) -> Int {
320+
// CHECK: [[ARG:%.*]] = moveonlywrapper_to_copyable [guaranteed]
321+
// CHECK: [[PA:%.*]] = partial_apply [callee_guaranteed] [on_stack] %{{.*}}([[ARG]]) : $@convention(thin) (Int) -> Int
322+
// CHECK-NOT: destroy
323+
// CHECK: destroy_value [[PA]] : $@noescape @callee_guaranteed () -> Int
324+
// CHECK-NOT: destroy
325+
// CHECK: destroy_addr
326+
// CHECK-NEXT: dealloc_stack
327+
// CHECK-NEXT: return
328+
// CHECK-LABEL: } // end sil function 'testTrivialBorrow'
329+
sil [ossa] @testTrivialBorrow : $@convention(thin) (Int) -> Int {
330+
bb0(%0 : @noImplicitCopy @_eagerMove $Int):
331+
%1 = alloc_stack $@moveOnly Int, var, name "i"
332+
%2 = mark_must_check [consumable_and_assignable] %1 : $*@moveOnly Int
333+
%3 = moveonlywrapper_to_copyable_addr %2 : $*@moveOnly Int
334+
store %0 to [trivial] %3 : $*Int
335+
%5 = begin_access [read] [static] %2 : $*@moveOnly Int
336+
%6 = load_borrow %5 : $*@moveOnly Int
337+
%7 = moveonlywrapper_to_copyable [guaranteed] %6 : $@moveOnly Int
338+
end_borrow %6 : $@moveOnly Int
339+
end_access %5 : $*@moveOnly Int
340+
%10 = function_ref @testTrivialBorrowClosure : $@convention(thin) (Int) -> Int
341+
%11 = partial_apply [callee_guaranteed] %10(%7) : $@convention(thin) (Int) -> Int
342+
%12 = convert_escape_to_noescape [not_guaranteed] %11 : $@callee_guaranteed () -> Int to $@noescape @callee_guaranteed () -> Int
343+
%13 = function_ref @testTrivialBorrowTakeClosure : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> Int) -> Int
344+
%14 = apply %13(%12) : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> Int) -> Int
345+
destroy_value %12 : $@noescape @callee_guaranteed () -> Int
346+
destroy_value %11 : $@callee_guaranteed () -> Int
347+
destroy_addr %2 : $*@moveOnly Int
348+
dealloc_stack %1 : $*@moveOnly Int
349+
return %14 : $Int
350+
}

test/SILOptimizer/noimplicitcopy_trivial.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,3 +1078,23 @@ public func noImplicitCopyReturnUse(_ x: Int) -> Int {
10781078
let _ = z
10791079
return y // expected-note {{consumed again here}}
10801080
}
1081+
1082+
func takeClosure(_ f: ()->Int) -> Int { f() }
1083+
1084+
public func test1(i: consuming Int) -> Int {
1085+
takeClosure { [i = copy i] in i }
1086+
}
1087+
1088+
public func test2(i: borrowing Int) -> Int {
1089+
takeClosure { [i = copy i] in i }
1090+
}
1091+
1092+
public func test3(i: consuming Int) -> Int {
1093+
takeClosure { i }
1094+
}
1095+
1096+
// TODO: incorrect diagnostic:
1097+
// error: 'i' cannot be captured by an escaping closure since it is a borrowed parameter
1098+
// public func test4(i: borrowing Int) -> Int {
1099+
// takeClosure { i }
1100+
// }

0 commit comments

Comments
 (0)