mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Concurrency] Allow transferring nonisolated(nonsending) to isolation boundary closures
Isolation boundary closures don't assume parent isolation and should be able to get `nonisolated(nonsending)` transferred to them jus like regular nonisolated closures do. Resolves: rdar://163792371
This commit is contained in:
@@ -5013,6 +5013,10 @@ ActorIsolation ActorIsolationChecker::determineClosureIsolation(
|
||||
auto normalIsolation = computeClosureIsolationFromParent(
|
||||
closure, parentIsolation, checkIsolatedCapture);
|
||||
|
||||
bool isIsolationBoundary =
|
||||
isIsolationInferenceBoundaryClosure(closure,
|
||||
/*canInheritActorContext=*/true);
|
||||
|
||||
// The solver has to be conservative and produce a conversion to
|
||||
// `nonisolated(nonsending)` because at solution application time
|
||||
// we don't yet know whether there are any captures which would
|
||||
@@ -5023,7 +5027,7 @@ ActorIsolation ActorIsolationChecker::determineClosureIsolation(
|
||||
// isolated parameters. If our closure is nonisolated and we have a
|
||||
// conversion to nonisolated(nonsending), then we should respect that.
|
||||
if (auto *explicitClosure = dyn_cast<ClosureExpr>(closure);
|
||||
!normalIsolation.isGlobalActor()) {
|
||||
isIsolationBoundary || !normalIsolation.isGlobalActor()) {
|
||||
if (auto *fce =
|
||||
dyn_cast_or_null<FunctionConversionExpr>(Parent.getAsExpr())) {
|
||||
auto expectedIsolation =
|
||||
@@ -5042,8 +5046,7 @@ ActorIsolation ActorIsolationChecker::determineClosureIsolation(
|
||||
// NOTE: Since we already checked for global actor isolated things, we
|
||||
// know that all Sendable closures must be nonisolated. That is why it is
|
||||
// safe to rely on this path to handle Sendable closures.
|
||||
if (isIsolationInferenceBoundaryClosure(closure,
|
||||
/*canInheritActorContext=*/true))
|
||||
if (isIsolationBoundary)
|
||||
return ActorIsolation::forNonisolated(/*unsafe=*/false);
|
||||
|
||||
return normalIsolation;
|
||||
|
||||
@@ -659,3 +659,29 @@ func testSendableToSendableConversionWithNonisilatedNonsending() {
|
||||
nonisolated(nonsending) @escaping (String) async throws -> String
|
||||
) async throws -> Void = test
|
||||
}
|
||||
|
||||
func testNonisolatedNonsendingClosureInGlobalActorContext() {
|
||||
class NonSendable {
|
||||
var state = ""
|
||||
}
|
||||
|
||||
struct S {
|
||||
static func compute(closure: nonisolated(nonsending) @Sendable @escaping (sending NonSendable) async -> Void) async {}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil private [ossa] @$s21attr_execution_silgen52testNonisolatedNonsendingClosureInGlobalActorContextyyF0D0L_yyYaF : $@convention(thin) @async () -> ()
|
||||
// CHECK: [[CLOSURE:%.*]] = function_ref @$s21attr_execution_silgen52testNonisolatedNonsendingClosureInGlobalActorContextyyF0D0L_yyYaFyAaByyF11NonSendableL_CYuYaYbYCcfU_ : $@convention(thin) @Sendable @async (@sil_isolated @sil_implicit_leading_param @guaranteed Builtin.ImplicitActor, @sil_sending @guaranteed NonSendable) -> ()
|
||||
// CHECK: [[THICK_CLOSURE:%.*]] = thin_to_thick_function [[CLOSURE]] to $@Sendable @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Builtin.ImplicitActor, @sil_sending @guaranteed NonSendable) -> ()
|
||||
// CHECK: [[CLOSURE_THUNK:%.*]] = function_ref @$sBA21attr_execution_silgen52testNonisolatedNonsendingClosureInGlobalActorContextyyF11NonSendableL_CIeghHgILgT_BAADIeghHgILxT_TR : $@convention(thin) @Sendable @async (@sil_isolated @sil_implicit_leading_param @guaranteed Builtin.ImplicitActor, @sil_sending @owned NonSendable, @guaranteed @Sendable @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Builtin.ImplicitActor, @sil_sending @guaranteed NonSendable) -> ()) -> ()
|
||||
// CHECK: [[THUNKED_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_THUNK]]([[THICK_CLOSURE]])
|
||||
// CHECK: [[COMPUTE:%.*]] = function_ref @$s21attr_execution_silgen52testNonisolatedNonsendingClosureInGlobalActorContextyyF1SL_V7compute7closureyyAaByyF11NonSendableL_CnYuYaYbYCc_tYaFZ : $@convention(method) @async (@guaranteed @Sendable @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Builtin.ImplicitActor, @sil_sending @owned NonSendable) -> (), @thin S.Type) -> ()
|
||||
// CHECK: apply [[COMPUTE]]([[THUNKED_CLOSURE]], {{.*}}) : $@convention(method) @async (@guaranteed @Sendable @async @callee_guaranteed (@sil_isolated @sil_implicit_leading_param @guaranteed Builtin.ImplicitActor, @sil_sending @owned NonSendable) -> (), @thin S.Type) -> ()
|
||||
// CHECK: } // end sil function '$s21attr_execution_silgen52testNonisolatedNonsendingClosureInGlobalActorContextyyF0D0L_yyYaF'
|
||||
@MainActor
|
||||
func test() async {
|
||||
// CHECK: // closure #1 in test #1 () in testNonisolatedNonsendingClosureInGlobalActorContext()
|
||||
// CHECK: // Isolation: caller_isolation_inheriting
|
||||
await S.compute { _ in
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,11 +143,11 @@ func test_CallerSyncNormal_CalleeAsyncNonIsolated() async {
|
||||
normalAcceptsAsyncClosure { }
|
||||
|
||||
// CHECK-LABEL: closure #2 in test_CallerSyncNormal_CalleeAsyncNonIsolated()
|
||||
// CHECK-NEXT: Isolation: nonisolated
|
||||
// CHECK-NEXT: Isolation: {{nonisolated|caller_isolation_inheriting}}
|
||||
normalAcceptsSendingAsyncClosure { }
|
||||
|
||||
// CHECK-LABEL: // closure #3 in test_CallerSyncNormal_CalleeAsyncNonIsolated()
|
||||
// CHECK-NEXT: // Isolation: nonisolated
|
||||
// CHECK-NEXT: // Isolation: {{nonisolated|caller_isolation_inheriting}}
|
||||
normalAcceptsSendableAsyncClosure { }
|
||||
}
|
||||
|
||||
@@ -177,11 +177,11 @@ func test_CallerSyncNormal_CalleeAsyncMainActorIsolated() async {
|
||||
// expected-ni-ns-note @-4 {{sending global actor 'CustomActor'-isolated value of non-Sendable type '@concurrent () async -> ()' to main actor-isolated global function 'normalGlobalActorAcceptsAsyncClosure' risks causing races in between global actor 'CustomActor'-isolated and main actor-isolated uses}}
|
||||
|
||||
// CHECK-LABEL: // closure #2 in test_CallerSyncNormal_CalleeAsyncMainActorIsolated()
|
||||
// CHECK-NEXT: // Isolation: nonisolated
|
||||
// CHECK-NEXT: Isolation: {{nonisolated|caller_isolation_inheriting}}
|
||||
await normalGlobalActorAcceptsSendingAsyncClosure { }
|
||||
|
||||
// CHECK-LABEL: // closure #3 in test_CallerSyncNormal_CalleeAsyncMainActorIsolated()
|
||||
// CHECK-NEXT: // Isolation: nonisolated
|
||||
// CHECK-NEXT: // Isolation: {{nonisolated|caller_isolation_inheriting}}
|
||||
await normalGlobalActorAcceptsSendableAsyncClosure { }
|
||||
}
|
||||
|
||||
@@ -254,11 +254,11 @@ func test_CallerAsyncNormal_CalleeAsyncNonIsolated() async {
|
||||
// expected-ni-note @-1 {{sending global actor 'CustomActor'-isolated value of non-Sendable type '() async -> ()' to nonisolated global function 'asyncNormalAcceptsAsyncClosure' risks causing races in between global actor 'CustomActor'-isolated and nonisolated uses}}
|
||||
|
||||
// CHECK-LABEL: closure #2 in test_CallerAsyncNormal_CalleeAsyncNonIsolated()
|
||||
// CHECK-NEXT: Isolation: nonisolated
|
||||
// CHECK-NEXT: Isolation: {{nonisolated|caller_isolation_inheriting}}
|
||||
await asyncNormalAcceptsSendingAsyncClosure { }
|
||||
|
||||
// CHECK-LABEL: // closure #3 in test_CallerAsyncNormal_CalleeAsyncNonIsolated()
|
||||
// CHECK-NEXT: // Isolation: nonisolated
|
||||
// CHECK-NEXT: // Isolation: {{nonisolated|caller_isolation_inheriting}}
|
||||
await asyncNormalAcceptsSendableAsyncClosure { }
|
||||
}
|
||||
|
||||
@@ -296,11 +296,11 @@ func test_CallerAsyncNormal_CalleeAsyncMainActorIsolated() async {
|
||||
// expected-ni-ns-note @-4 {{sending global actor 'CustomActor'-isolated value of non-Sendable type '@concurrent () async -> ()' to main actor-isolated global function 'asyncNormalGlobalActorAcceptsAsyncClosure' risks causing races in between global actor 'CustomActor'-isolated and main actor-isolated uses}}
|
||||
|
||||
// CHECK-LABEL: // closure #2 in test_CallerAsyncNormal_CalleeAsyncMainActorIsolated()
|
||||
// CHECK-NEXT: // Isolation: nonisolated
|
||||
// CHECK-NEXT: Isolation: {{nonisolated|caller_isolation_inheriting}}
|
||||
await asyncNormalGlobalActorAcceptsSendingAsyncClosure { }
|
||||
|
||||
// CHECK-LABEL: // closure #3 in test_CallerAsyncNormal_CalleeAsyncMainActorIsolated()
|
||||
// CHECK-NEXT: // Isolation: nonisolated
|
||||
// CHECK-NEXT: // Isolation: {{nonisolated|caller_isolation_inheriting}}
|
||||
await asyncNormalGlobalActorAcceptsSendableAsyncClosure { }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user