Skip to content

Commit 9c24933

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`)
1 parent 9b5c398 commit 9c24933

File tree

6 files changed

+118
-42
lines changed

6 files changed

+118
-42
lines changed

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,14 +284,14 @@ void releasePartialApplyCapturedArg(
284284
/// Insert destroys of captured arguments of partial_apply [stack].
285285
void insertDestroyOfCapturedArguments(
286286
PartialApplyInst *pai, SILBuilder &builder,
287-
llvm::function_ref<bool(SILValue)> shouldInsertDestroy =
288-
[](SILValue arg) -> bool { return true; },
287+
llvm::function_ref<SILValue(SILValue)> getValueToDestroy =
288+
[](SILValue arg) -> SILValue { return arg; },
289289
SILLocation loc = RegularLocation::getAutoGeneratedLocation());
290290

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

296296
/// This iterator 'looks through' one level of builtin expect users exposing all
297297
/// 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: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@ static SILValue tryRewriteToPartialApplyStack(
619619
if (!argTy.isAddress() && !argTy.isTrivial(*cvt->getFunction())) {
620620
SILValue argValue = arg.get();
621621
bool foundNoImplicitCopy = false;
622+
//!!!
622623
if (auto *mmci = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(argValue)) {
623624
if (mmci->hasOwnedInitialKind() && mmci->hasOneUse()) {
624625
foundNoImplicitCopy = true;
@@ -889,12 +890,28 @@ static SILValue tryRewriteToPartialApplyStack(
889890
destroy->dump();
890891
*/
891892
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-
},
893+
// This getCapturedArg hack attempts to perfectly compensate for all the
894+
// other hacks involved in gathering new arguments above.
895+
auto getArgToDestroy = [&](SILValue argValue) -> SILValue {
896+
// A MoveOnlyWrapperToCopyableValueInst may produce a trivial value. Be
897+
// careful not to emit an extra destroy of the original.
898+
if (argValue->getType().isTrivial(argValue->getFunction()))
899+
return SILValue();
900+
901+
// We may have inserted a new begin_borrow->moveonlywrapper_to_copyvalue
902+
// when creating the new arguments. Now we need to end that borrow.
903+
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(argValue))
904+
if (m->hasGuaranteedInitialKind())
905+
argValue = m->getOperand();
906+
auto *argBorrow = dyn_cast<BeginBorrowInst>(argValue);
907+
if (argBorrow) {
908+
argValue = argBorrow->getOperand();
909+
builder.createEndBorrow(newPA->getLoc(), argBorrow);
910+
}
911+
// Don't need to destroy if we borrowed in place .
912+
return borrowedOriginals.count(argValue) ? SILValue() : argValue;
913+
};
914+
insertDestroyOfCapturedArguments(newPA, builder, getArgToDestroy,
898915
newPA->getLoc());
899916
}
900917
/* DEBUG
@@ -919,13 +936,17 @@ static SILValue tryRewriteToPartialApplyStack(
919936
if (unreachableBlocks.count(newPA->getParent()))
920937
return closureOp;
921938

939+
auto getAddressToDealloc = [&](SILValue argAddress) -> SILValue {
940+
if (auto moveWrapper =
941+
dyn_cast<MoveOnlyWrapperToCopyableAddrInst>(argAddress)) {
942+
argAddress = moveWrapper->getOperand();
943+
}
944+
// Don't need to destroy if we borrowed in place .
945+
return borrowedOriginals.count(argAddress) ? SILValue() : argAddress;
946+
};
922947
insertDeallocOfCapturedArguments(
923948
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-
});
949+
getAddressToDealloc);
929950

930951
return closureOp;
931952
}

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -902,13 +902,13 @@ 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 is building up a boxed aggregate to send off to the closed over
910+
// function, even for guaranteed and in_guaranteed convention. Of course, when
911+
// you call the function, the proper conventions will be used.
912912
void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc,
913913
SILValue arg,
914914
SILParameterInfo paramInfo,
@@ -1479,7 +1479,7 @@ swift::findLocalApplySites(FunctionRefBaseInst *fri) {
14791479
/// Insert destroys of captured arguments of partial_apply [stack].
14801480
void swift::insertDestroyOfCapturedArguments(
14811481
PartialApplyInst *pai, SILBuilder &builder,
1482-
llvm::function_ref<bool(SILValue)> shouldInsertDestroy,
1482+
llvm::function_ref<SILValue(SILValue)> getValueToDestroy,
14831483
SILLocation origLoc) {
14841484
assert(pai->isOnStack());
14851485

@@ -1488,27 +1488,25 @@ void swift::insertDestroyOfCapturedArguments(
14881488
pai->getModule());
14891489
auto loc = CleanupLocation(origLoc);
14901490
for (auto &arg : pai->getArgumentOperands()) {
1491-
SILValue argValue = arg.get();
1492-
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(argValue))
1493-
if (m->hasGuaranteedInitialKind())
1494-
argValue = m->getOperand();
1495-
auto *argBorrow = dyn_cast<BeginBorrowInst>(argValue);
1496-
if (argBorrow) {
1497-
argValue = argBorrow->getOperand();
1498-
builder.createEndBorrow(loc, argBorrow);
1499-
}
1500-
if (!shouldInsertDestroy(argValue))
1491+
SILValue argValue = getValueToDestroy(arg.get());
1492+
if (!argValue)
15011493
continue;
1494+
1495+
assert(!argValue->getFunction()->hasOwnership()
1496+
|| (argValue->getOwnershipKind().isCompatibleWith(
1497+
OwnershipKind::Owned)));
1498+
15021499
unsigned calleeArgumentIndex = site.getCalleeArgIndex(arg);
15031500
assert(calleeArgumentIndex >= calleeConv.getSILArgIndexOfFirstParam());
15041501
auto paramInfo = calleeConv.getParamInfoForSILArg(calleeArgumentIndex);
15051502
releasePartialApplyCapturedArg(builder, loc, argValue, paramInfo);
15061503
}
15071504
}
15081505

1509-
void swift::insertDeallocOfCapturedArguments(PartialApplyInst *pai,
1510-
DominanceInfo *domInfo,
1511-
llvm::function_ref<bool(SILValue)> shouldInsertDestroy)
1506+
void swift::insertDeallocOfCapturedArguments(
1507+
PartialApplyInst *pai,
1508+
DominanceInfo *domInfo,
1509+
llvm::function_ref<SILValue(SILValue)> getAddressToDealloc)
15121510
{
15131511
assert(pai->isOnStack());
15141512

@@ -1522,13 +1520,10 @@ void swift::insertDeallocOfCapturedArguments(PartialApplyInst *pai,
15221520
if (!paramInfo.isIndirectInGuaranteed())
15231521
continue;
15241522

1525-
SILValue argValue = arg.get();
1526-
if (!shouldInsertDestroy(argValue)) {
1523+
SILValue argValue = getAddressToDealloc(arg.get());
1524+
if (!argValue) {
15271525
continue;
15281526
}
1529-
if (auto moveWrapper =
1530-
dyn_cast<MoveOnlyWrapperToCopyableAddrInst>(argValue))
1531-
argValue = moveWrapper->getOperand();
15321527

15331528
SmallVector<SILBasicBlock *, 4> boundary;
15341529
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)