mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[rbi] Teach RBI how to infer nonisolated(unsafe) of closure captures.
The previous commit in this PR exposed that we were not handling this correctly. Specifically, we incorrectly started to error in SwiftFoundation. rdar://162629359
This commit is contained in:
@@ -990,6 +990,21 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
|
||||
if (fArg->isSending())
|
||||
return SILIsolationInfo::getDisconnected(false /*nonisolated(unsafe)*/);
|
||||
|
||||
// Check if we have a closure captured parameter that is nonisolated(unsafe)
|
||||
// at its original declaration sign. In such a case, we want to propagate that
|
||||
// bit.
|
||||
bool isClosureCapturedNonisolatedUnsafe = [&]() -> bool {
|
||||
if (!fArg->isClosureCapture())
|
||||
return false;
|
||||
auto *decl = fArg->getDecl();
|
||||
if (!decl)
|
||||
return false;
|
||||
auto *attr = decl->getAttrs().getAttribute<NonisolatedAttr>();
|
||||
if (!attr)
|
||||
return false;
|
||||
return attr->isUnsafe();
|
||||
}();
|
||||
|
||||
// If we have a closure capture that is not an indirect result or indirect
|
||||
// result error, we want to treat it as sending so that we properly handle
|
||||
// async lets.
|
||||
@@ -1001,7 +1016,8 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
|
||||
fArg->isClosureCapture()) {
|
||||
if (auto declRef = arg->getFunction()->getDeclRef();
|
||||
declRef && declRef.isAsyncLetClosure) {
|
||||
return SILIsolationInfo::getDisconnected(false /*nonisolated(unsafe)*/);
|
||||
return SILIsolationInfo::getDisconnected(
|
||||
isClosureCapturedNonisolatedUnsafe);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1014,12 +1030,20 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
|
||||
if (auto funcIsolation = fArg->getFunction()->getActorIsolation();
|
||||
funcIsolation && funcIsolation->isCallerIsolationInheriting()) {
|
||||
return SILIsolationInfo::getTaskIsolated(fArg)
|
||||
.withNonisolatedNonsendingTaskIsolated(true);
|
||||
.withNonisolatedNonsendingTaskIsolated(true)
|
||||
.withUnsafeNonIsolated(isClosureCapturedNonisolatedUnsafe);
|
||||
}
|
||||
|
||||
auto astType = isolatedArg->getType().getASTType();
|
||||
if (astType->lookThroughAllOptionalTypes()->getAnyActor()) {
|
||||
return SILIsolationInfo::getActorInstanceIsolated(fArg, isolatedArg);
|
||||
if (auto isolation =
|
||||
SILIsolationInfo::getActorInstanceIsolated(fArg, isolatedArg)) {
|
||||
return isolation.withUnsafeNonIsolated(
|
||||
isClosureCapturedNonisolatedUnsafe);
|
||||
}
|
||||
// If we had an isolated parameter that was an actor type, but we did not
|
||||
// find any isolation for the actor instance... return {}.
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1061,12 +1085,14 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
|
||||
}
|
||||
|
||||
// Otherwise, if we do not have an isolated argument and are not in an
|
||||
// allocator, then we might be isolated via global isolation.
|
||||
// allocator, then we might be isolated via global isolation or in a global
|
||||
// actor isolated initializer.
|
||||
if (auto functionIsolation = fArg->getFunction()->getActorIsolation()) {
|
||||
if (functionIsolation->isActorIsolated()) {
|
||||
if (functionIsolation->isGlobalActor()) {
|
||||
return SILIsolationInfo::getGlobalActorIsolated(
|
||||
fArg, functionIsolation->getGlobalActor());
|
||||
fArg, functionIsolation->getGlobalActor())
|
||||
.withUnsafeNonIsolated(isClosureCapturedNonisolatedUnsafe);
|
||||
}
|
||||
|
||||
return SILIsolationInfo::getActorInstanceIsolated(
|
||||
@@ -1075,7 +1101,8 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
return SILIsolationInfo::getTaskIsolated(fArg);
|
||||
return SILIsolationInfo::getTaskIsolated(fArg).withUnsafeNonIsolated(
|
||||
isClosureCapturedNonisolatedUnsafe);
|
||||
}
|
||||
|
||||
/// Infer isolation region from the set of protocol conformances.
|
||||
|
||||
@@ -823,9 +823,9 @@ actor ActorContainingSendableStruct {
|
||||
}
|
||||
|
||||
|
||||
////////////////////
|
||||
// MARK: Closures //
|
||||
////////////////////
|
||||
////////////////////////////
|
||||
// MARK: Sending Closures //
|
||||
////////////////////////////
|
||||
|
||||
func closureTests() async {
|
||||
func sendingClosure(_ x: sending () -> ()) {
|
||||
@@ -1043,3 +1043,64 @@ func closureTests() async {
|
||||
sendingClosure(y) // expected-note {{access can happen concurrently}}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
// MARK: Closure Capture Propagation //
|
||||
///////////////////////////////////////
|
||||
//
|
||||
// DISCUSSION: For tests that involve closure captures with nonisolated(unsafe)
|
||||
// and suppressing errors in the closure itself. Tests where the suppressed
|
||||
// error is outside the closure itself are in other places in the file.
|
||||
|
||||
enum NonisolatedUnsafeCapture {
|
||||
func testSimple() {
|
||||
nonisolated(unsafe) let x = NonSendableKlass()
|
||||
let _ = {
|
||||
await transferToMainDirect(x)
|
||||
print(x)
|
||||
}
|
||||
}
|
||||
|
||||
func testAsyncLet() async {
|
||||
nonisolated(unsafe) let x = NonSendableKlass()
|
||||
async let y: () = {
|
||||
await transferToMainDirect(x)
|
||||
await transferToMainDirect(x)
|
||||
}()
|
||||
_ = await y
|
||||
}
|
||||
|
||||
func testIsolatedParamViaActor() async {
|
||||
actor A {
|
||||
let y = NonSendableKlass()
|
||||
func test() {
|
||||
nonisolated(unsafe) let x = y
|
||||
_ = {
|
||||
await transferToMainDirect(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testExplicitIsolatedParam() async {
|
||||
nonisolated(unsafe) let x = NonSendableKlass()
|
||||
_ = { (y: isolated CustomActorInstance) in
|
||||
await transferToMainDirect(x)
|
||||
}
|
||||
}
|
||||
|
||||
func testIsolatedParamNonisolatedNonSending() async {
|
||||
nonisolated(unsafe) let x = NonSendableKlass()
|
||||
let _: nonisolated(nonsending) () async -> () = {
|
||||
await transferToMainDirect(x)
|
||||
await transferToMainDirect(x)
|
||||
}
|
||||
}
|
||||
|
||||
func testGlobalActorIsolated() async {
|
||||
nonisolated(unsafe) let x = NonSendableKlass()
|
||||
_ = { @MainActor in
|
||||
await transferToCustom(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user