mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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.
This commit is contained in:
@@ -1601,9 +1601,32 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool isConvertFunctionFromSendableType(SILValue equivalenceClassRep) const {
|
||||
/// To work around not having isolation in interface types, the type checker
|
||||
/// inserts casts and other AST nodes that are used to enrich the AST with
|
||||
/// isolation information. This results in Sendable functions being
|
||||
/// wrapped/converted/etc in ways that hide the Sendability. This helper looks
|
||||
/// through these conversions/wrappers/thunks to see if the original
|
||||
/// underlying function is Sendable.
|
||||
///
|
||||
/// The two ways this can happen is that we either get an actual function_ref
|
||||
/// that is Sendable or we get a convert function with a Sendable operand.
|
||||
bool isHiddenSendableFunctionType(SILValue equivalenceClassRep) const {
|
||||
SILValue valueToTest = equivalenceClassRep;
|
||||
while (true) {
|
||||
if (auto *pai = dyn_cast<PartialApplyInst>(valueToTest)) {
|
||||
if (auto *calleeFunction = pai->getCalleeFunction()) {
|
||||
if (pai->getNumArguments() >= 1 &&
|
||||
pai->getArgument(0)->getType().isFunction() &&
|
||||
calleeFunction->isThunk()) {
|
||||
valueToTest = pai->getArgument(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (calleeFunction->getLoweredFunctionType()->isSendable())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto *i = dyn_cast<ThinToThickFunctionInst>(valueToTest)) {
|
||||
valueToTest = i->getOperand();
|
||||
continue;
|
||||
@@ -1615,6 +1638,9 @@ private:
|
||||
break;
|
||||
}
|
||||
|
||||
if (auto *fn = dyn_cast<FunctionRefInst>(valueToTest))
|
||||
return fn->getReferencedFunction()->getLoweredFunctionType()->isSendable();
|
||||
|
||||
auto *cvi = dyn_cast<ConvertFunctionInst>(valueToTest);
|
||||
if (!cvi)
|
||||
return false;
|
||||
@@ -1647,7 +1673,7 @@ private:
|
||||
|
||||
// See if we have a convert function from a `@Sendable` type. In this
|
||||
// case, we want to squelch the error.
|
||||
if (isConvertFunctionFromSendableType(equivalenceClassRep))
|
||||
if (isHiddenSendableFunctionType(equivalenceClassRep))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1692,7 +1718,7 @@ private:
|
||||
|
||||
// See if we have a convert function from a `@Sendable` type. In this
|
||||
// case, we want to squelch the error.
|
||||
if (isConvertFunctionFromSendableType(equivalenceClassRep))
|
||||
if (isHiddenSendableFunctionType(equivalenceClassRep))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,9 +34,8 @@ func useValue<T>(_ t: T) {}
|
||||
@MainActor func testGlobalFakeInit() {
|
||||
let ns = NonSendableKlass()
|
||||
|
||||
// Will be resolved once @MainActor is @Sendable.
|
||||
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}}
|
||||
print(ns) // expected-note {{closure captures 'ns' which is accessible to main actor-isolated code}}
|
||||
Task.fakeInit { @MainActor in
|
||||
print(ns)
|
||||
}
|
||||
|
||||
useValue(ns)
|
||||
|
||||
Reference in New Issue
Block a user