Skip to content

Commit 73e34f0

Browse files
committed
[rbi] Teach SendNonSendable how to more aggressively suppress sending errors around obfuscated Sendable functions
Specifically the type checker to work around interface types not having isolation introduces casts into the AST that enrich the AST with isolation information. Part of that information is Sendable. This means that we can sometimes lose due to conversions that a function is actually Sendable. To work around this, we today suppress those errors when they are emitted (post 6.2, we should just change their classification as being Sendable... but I don't want to make that change now). This change just makes the pattern matching for these conversions handle more cases so that transfernonsendable_closureliterals_isolationinference.swift now passes.
1 parent ea4d044 commit 73e34f0

File tree

2 files changed

+31
-6
lines changed

2 files changed

+31
-6
lines changed

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,9 +1601,32 @@ struct PartitionOpEvaluator {
16011601
}
16021602

16031603
private:
1604-
bool isConvertFunctionFromSendableType(SILValue equivalenceClassRep) const {
1604+
/// To work around not having isolation in interface types, the type checker
1605+
/// inserts casts and other AST nodes that are used to enrich the AST with
1606+
/// isolation information. This results in Sendable functions being
1607+
/// wrapped/converted/etc in ways that hide the Sendability. This helper looks
1608+
/// through these conversions/wrappers/thunks to see if the original
1609+
/// underlying function is Sendable.
1610+
///
1611+
/// The two ways this can happen is that we either get an actual function_ref
1612+
/// that is Sendable or we get a convert function with a Sendable operand.
1613+
bool isHiddenSendableFunctionType(SILValue equivalenceClassRep) const {
16051614
SILValue valueToTest = equivalenceClassRep;
16061615
while (true) {
1616+
if (auto *pai = dyn_cast<PartialApplyInst>(valueToTest)) {
1617+
if (auto *calleeFunction = pai->getCalleeFunction()) {
1618+
if (pai->getNumArguments() >= 1 &&
1619+
pai->getArgument(0)->getType().isFunction() &&
1620+
calleeFunction->isThunk()) {
1621+
valueToTest = pai->getArgument(0);
1622+
continue;
1623+
}
1624+
1625+
if (calleeFunction->getLoweredFunctionType()->isSendable())
1626+
return true;
1627+
}
1628+
}
1629+
16071630
if (auto *i = dyn_cast<ThinToThickFunctionInst>(valueToTest)) {
16081631
valueToTest = i->getOperand();
16091632
continue;
@@ -1615,6 +1638,9 @@ struct PartitionOpEvaluator {
16151638
break;
16161639
}
16171640

1641+
if (auto *fn = dyn_cast<FunctionRefInst>(valueToTest))
1642+
return fn->getReferencedFunction()->getLoweredFunctionType()->isSendable();
1643+
16181644
auto *cvi = dyn_cast<ConvertFunctionInst>(valueToTest);
16191645
if (!cvi)
16201646
return false;
@@ -1647,7 +1673,7 @@ struct PartitionOpEvaluator {
16471673

16481674
// See if we have a convert function from a `@Sendable` type. In this
16491675
// case, we want to squelch the error.
1650-
if (isConvertFunctionFromSendableType(equivalenceClassRep))
1676+
if (isHiddenSendableFunctionType(equivalenceClassRep))
16511677
return;
16521678
}
16531679

@@ -1692,7 +1718,7 @@ struct PartitionOpEvaluator {
16921718

16931719
// See if we have a convert function from a `@Sendable` type. In this
16941720
// case, we want to squelch the error.
1695-
if (isConvertFunctionFromSendableType(equivalenceClassRep))
1721+
if (isHiddenSendableFunctionType(equivalenceClassRep))
16961722
return;
16971723
}
16981724
}

test/Concurrency/transfernonsendable_global_actor_sending.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,8 @@ func useValue<T>(_ t: T) {}
3434
@MainActor func testGlobalFakeInit() {
3535
let ns = NonSendableKlass()
3636

37-
// Will be resolved once @MainActor is @Sendable.
38-
Task.fakeInit { @MainActor in // expected-error {{passing closure as a 'sending' parameter risks causing data races between main actor-isolated code and concurrent execution of the closure}}
39-
print(ns) // expected-note {{closure captures 'ns' which is accessible to main actor-isolated code}}
37+
Task.fakeInit { @MainActor in
38+
print(ns)
4039
}
4140

4241
useValue(ns)

0 commit comments

Comments
 (0)