Skip to content

Commit 1bb7b37

Browse files
committed
Teach static exclusivity verification about withoutActuallyEscaping.
DiagnoseStaticExclusivity no longer asserts as follows when non-escaping closures are passed to withoutActuallyEscaping: Applied argument must be @NoEscape function type: ... A partial_apply with @inout_aliasable may only be used as a @NoEscape function type argument. Subsequent commits will improve diagnostics to detect actual conflicts in these situations. Fixes <rdar://problem/43059088> Assertion in DiagnoseStaticExclusivity.
1 parent c9033ed commit 1bb7b37

File tree

3 files changed

+137
-2
lines changed

3 files changed

+137
-2
lines changed

include/swift/SIL/InstructionUtils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ SILValue stripBorrow(SILValue V);
8686
/// copies the value of its first operand, possibly changing its type or
8787
/// ownership state, but otherwise having no effect.
8888
///
89+
/// The returned instruction may have additional "incidental" operands;
90+
/// mark_dependence for example.
91+
///
8992
/// This is useful for checking all users of a value to verify that the value is
9093
/// only used in recognizable patterns without otherwise "escaping". These are
9194
/// instructions that the use-visitor can recurse into. Note that the value's

lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -967,12 +967,24 @@ static void checkNoEscapePartialApplyUse(Operand *oper, FollowUse followUses) {
967967
if (isIncidentalUse(user) || onlyAffectsRefCount(user))
968968
return;
969969

970+
// Before checking conversions in general below (getSingleValueCopyOrCast),
971+
// check for convert_function to [without_actually_escaping]. Assume such
972+
// conversion are not actually escaping without following their uses.
973+
if (auto *CFI = dyn_cast<ConvertFunctionInst>(user)) {
974+
if (CFI->withoutActuallyEscaping())
975+
return;
976+
}
977+
970978
// Look through copies, borrows, and conversions.
971979
//
972980
// Note: This handles ConversionInst, which already includes everything in
973981
// swift::stripConvertFunctions.
974982
if (SingleValueInstruction *copy = getSingleValueCopyOrCast(user)) {
975-
followUses(copy);
983+
// Only follow the copied operand. Other operands are incidental,
984+
// as in the second operand of mark_dependence.
985+
if (oper->getOperandNumber() == 0)
986+
followUses(copy);
987+
976988
return;
977989
}
978990

@@ -1017,6 +1029,10 @@ static void checkNoEscapePartialApplyUse(Operand *oper, FollowUse followUses) {
10171029
}
10181030
break;
10191031

1032+
case SILInstructionKind::IsEscapingClosureInst:
1033+
// May be generated by withoutActuallyEscaping.
1034+
return;
1035+
10201036
case SILInstructionKind::PartialApplyInst: {
10211037
// Recurse through partial_apply to handle special cases before handling
10221038
// ApplySites in general below.
@@ -1036,7 +1052,10 @@ static void checkNoEscapePartialApplyUse(Operand *oper, FollowUse followUses) {
10361052
// thunk as "escaping", but as long as the thunk is only used as a
10371053
// '@noescape" type then it is safe.
10381054
if (isPartialApplyOfReabstractionThunk(PAI)) {
1039-
followUses(PAI);
1055+
// Don't follow thunks that were generated by withoutActuallyEscaping.
1056+
SILFunction *thunkDef = PAI->getReferencedFunction();
1057+
if (!thunkDef->isWithoutActuallyEscapingThunk())
1058+
followUses(PAI);
10401059
return;
10411060
}
10421061
// Handle this use like a normal applied argument.

test/SILOptimizer/exclusivity_static_diagnostics.sil

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,3 +1285,116 @@ bb3(%pa3 : @owned $@callee_guaranteed (@inout Int) -> ()):
12851285
%v = tuple ()
12861286
return %v : $()
12871287
}
1288+
1289+
// -----------------------------------------------------------------------------
1290+
// Test withoutActuallyEscaping thunk.
1291+
// <rdar://problem/43059088> Assertion in DiagnoseStaticExclusivity
1292+
// Noescape closure verification should not assert.
1293+
1294+
sil @takeEscapingClosure : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> ()
1295+
1296+
sil private @nestedInWithoutActuallyEscapingVerify : $@convention(thin) (@inout_aliasable Int) -> () {
1297+
bb0(%0 : @trivial $*Int):
1298+
%a = begin_access [modify] [unknown] %0 : $*Int
1299+
end_access %a : $*Int
1300+
%v = tuple ()
1301+
return %v : $()
1302+
}
1303+
1304+
// CHECK-LABEL: sil hidden @testWithoutActuallyEscapingVerify : $@convention(thin) (@inout Int) -> () {
1305+
sil hidden @testWithoutActuallyEscapingVerify : $@convention(thin) (@inout Int) -> () {
1306+
bb0(%0 : @trivial $*Int):
1307+
%2 = function_ref @nestedInWithoutActuallyEscapingVerify : $@convention(thin) (@inout_aliasable Int) -> ()
1308+
%3 = partial_apply [callee_guaranteed] %2(%0) : $@convention(thin) (@inout_aliasable Int) -> ()
1309+
1310+
%4 = function_ref @thunkForWithoutActuallyEscapingVerify : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> ()
1311+
%5 = partial_apply [callee_guaranteed] %4(%3) : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> ()
1312+
%6 = mark_dependence %5 : $@callee_guaranteed () -> () on %3 : $@callee_guaranteed () -> ()
1313+
1314+
%8 = function_ref @closureForWithoutActuallyEscapingVerify : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> ()
1315+
%9 = apply %8(%6) : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> ()
1316+
%10 = is_escaping_closure %6 : $@callee_guaranteed () -> ()
1317+
cond_fail %10 : $Builtin.Int1
1318+
destroy_value %6 : $@callee_guaranteed () -> ()
1319+
%14 = tuple ()
1320+
return %14 : $()
1321+
}
1322+
1323+
// CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] [without_actually_escaping] @thunkForWithoutActuallyEscapingVerify : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () {
1324+
sil shared [transparent] [serializable] [reabstraction_thunk] [without_actually_escaping] @thunkForWithoutActuallyEscapingVerify : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () {
1325+
bb0(%0 : @guaranteed $@callee_guaranteed () -> ()):
1326+
%1 = apply %0() : $@callee_guaranteed () -> ()
1327+
return %1 : $()
1328+
}
1329+
1330+
sil private @closureForWithoutActuallyEscapingVerify : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () {
1331+
bb0(%0 : @guaranteed $@callee_guaranteed () -> ()):
1332+
%2 = function_ref @takeEscapingClosure : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> ()
1333+
%3 = apply %2(%0) : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> ()
1334+
%4 = tuple ()
1335+
return %4 : $()
1336+
}
1337+
1338+
// -----------------------------------------------------------------------------
1339+
// Test withoutActuallyEscaping convert_function.
1340+
// <rdar://problem/43059088> Assertion in DiagnoseStaticExclusivity
1341+
// Noescape closure verification should not assert.
1342+
1343+
// CHECK-LABEL: sil hidden @testWithoutActuallyEscapingBlockVerify : $@convention(thin) (Int) -> () {
1344+
// CHECK: convert_function %{{.*}} : $@convention(block) () -> () to [without_actually_escaping] $@convention(block) () -> ()
1345+
sil hidden @testWithoutActuallyEscapingBlockVerify : $@convention(thin) (Int) -> () {
1346+
bb0(%0 : @trivial $Int):
1347+
%box = alloc_box ${ var Int }, var, name "localVal"
1348+
%projbox = project_box %box : ${ var Int }, 0
1349+
store %0 to [trivial] %projbox : $*Int
1350+
%closureF = function_ref @testWithoutActuallyEscapingBlockVerifyClosure : $@convention(thin) (@inout_aliasable Int) -> ()
1351+
%closure = partial_apply [callee_guaranteed] %closureF(%projbox) : $@convention(thin) (@inout_aliasable Int) -> ()
1352+
%block = alloc_stack $@block_storage @callee_guaranteed () -> ()
1353+
%blockproj = project_block_storage %block : $*@block_storage @callee_guaranteed () -> ()
1354+
store %closure to [init] %blockproj : $*@callee_guaranteed () -> ()
1355+
// function_ref thunk for @escaping @callee_guaranteed () -> ()
1356+
%thunkF = function_ref @$SIeg_IeyB_TR : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> ()
1357+
%initblock = init_block_storage_header %block : $*@block_storage @callee_guaranteed () -> (), invoke %thunkF : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> (), type $@convention(block) () -> ()
1358+
destroy_addr %blockproj : $*@callee_guaranteed () -> ()
1359+
dealloc_stack %block : $*@block_storage @callee_guaranteed () -> ()
1360+
%borrow = begin_borrow %initblock : $@convention(block) () -> ()
1361+
%copy = copy_value %borrow : $@convention(block) () -> ()
1362+
%escapingF = convert_function %copy : $@convention(block) () -> () to [without_actually_escaping] $@convention(block) () -> ()
1363+
%clauseF = function_ref @testWithoutActuallyEscapingBlockVerifyClause : $@convention(thin) (@guaranteed @convention(block) () -> ()) -> ()
1364+
%call = apply %clauseF(%escapingF) : $@convention(thin) (@guaranteed @convention(block) () -> ()) -> ()
1365+
destroy_value %escapingF : $@convention(block) () -> ()
1366+
end_borrow %borrow from %initblock : $@convention(block) () -> (), $@convention(block) () -> ()
1367+
destroy_value %initblock : $@convention(block) () -> ()
1368+
destroy_value %box : ${ var Int }
1369+
%v = tuple ()
1370+
return %v : $()
1371+
}
1372+
1373+
sil @testWithoutActuallyEscapingBlockVerifyClosure : $@convention(thin) (@inout_aliasable Int) -> ()
1374+
1375+
// thunk for @escaping @callee_guaranteed () -> ()
1376+
sil shared [transparent] [serializable] [reabstraction_thunk] @$SIeg_IeyB_TR : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> () {
1377+
// %0
1378+
bb0(%0 : @trivial $*@block_storage @callee_guaranteed () -> ()):
1379+
%1 = project_block_storage %0 : $*@block_storage @callee_guaranteed () -> ()
1380+
%2 = load [copy] %1 : $*@callee_guaranteed () -> ()
1381+
%3 = begin_borrow %2 : $@callee_guaranteed () -> ()
1382+
%4 = apply %3() : $@callee_guaranteed () -> ()
1383+
end_borrow %3 from %2 : $@callee_guaranteed () -> (), $@callee_guaranteed () -> ()
1384+
%6 = tuple ()
1385+
destroy_value %2 : $@callee_guaranteed () -> ()
1386+
return %6 : $()
1387+
} // end sil function '$SIeg_IeyB_TR'
1388+
1389+
sil private @testWithoutActuallyEscapingBlockVerifyClause : $@convention(thin) (@guaranteed @convention(block) () -> ()) -> () {
1390+
bb0(%0 : @guaranteed $@convention(block) () -> ()):
1391+
%1 = copy_block %0 : $@convention(block) () -> ()
1392+
%3 = begin_borrow %1 : $@convention(block) () -> ()
1393+
%4 = copy_value %3 : $@convention(block) () -> ()
1394+
%5 = apply %4() : $@convention(block) () -> ()
1395+
destroy_value %4 : $@convention(block) () -> ()
1396+
end_borrow %3 from %1 : $@convention(block) () -> (), $@convention(block) () -> ()
1397+
destroy_value %1 : $@convention(block) () -> ()
1398+
%v = tuple ()
1399+
return %v : $()
1400+
}

0 commit comments

Comments
 (0)