Skip to content

Commit 4f817cb

Browse files
authored
Merge pull request #72607 from hborla/unsafe-isolation-erasure
[Concurrency] Don't allow erasing global actor isolation when the function value crosses an isolation boundary.
2 parents a91bfcc + 6154823 commit 4f817cb

File tree

2 files changed

+20
-2
lines changed

2 files changed

+20
-2
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1917,7 +1917,7 @@ bool swift::isAsyncDecl(ConcreteDeclRef declRef) {
19171917
/// \param ty a function type where \c globalActor was removed from it.
19181918
/// \return true if it is safe to drop the global-actor qualifier.
19191919
static bool safeToDropGlobalActor(
1920-
DeclContext *dc, Type globalActor, Type ty) {
1920+
DeclContext *dc, Type globalActor, Type ty, ApplyExpr *call) {
19211921
auto funcTy = ty->getAs<AnyFunctionType>();
19221922
if (!funcTy)
19231923
return false;
@@ -1942,6 +1942,12 @@ static bool safeToDropGlobalActor(
19421942
if (funcTy->isAsync())
19431943
return true;
19441944

1945+
// If the argument is passed over an isolation boundary, it's not
1946+
// safe to erase actor isolation, because the callee can call the
1947+
// function synchronously from outside the isolation domain.
1948+
if (call && call->getIsolationCrossing())
1949+
return false;
1950+
19451951
// fundamentally cannot be sendable if we want to drop isolation info
19461952
if (funcTy->isSendable())
19471953
return false;
@@ -2364,7 +2370,8 @@ namespace {
23642370
return;
23652371

23662372
auto dc = const_cast<DeclContext*>(getDeclContext());
2367-
if (!safeToDropGlobalActor(dc, fromActor, toType)) {
2373+
if (!safeToDropGlobalActor(dc, fromActor, toType,
2374+
getImmediateApply())) {
23682375
// otherwise, it's not a safe cast.
23692376
dc->getASTContext()
23702377
.Diags

test/Concurrency/actor_isolation.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,8 @@ actor Crystal {
468468
await asyncGlobalActorFunc()
469469
}
470470

471+
func crossIsolationBoundary(_ closure: () -> Void) async {}
472+
471473
@available(SwiftStdlib 5.1, *)
472474
func testGlobalActorClosures() {
473475
let _: Int = acceptAsyncClosure { @SomeGlobalActor in
@@ -480,6 +482,15 @@ func testGlobalActorClosures() {
480482
}
481483

482484
acceptConcurrentClosure { @SomeGlobalActor in 5 } // expected-warning {{converting function value of type '@SomeGlobalActor @Sendable () -> Int' to '@Sendable () -> Int' loses global actor 'SomeGlobalActor'}}
485+
486+
@MainActor func test() async {
487+
let closure = { @MainActor @Sendable in
488+
MainActor.assertIsolated()
489+
}
490+
491+
await crossIsolationBoundary(closure)
492+
// expected-warning@-1 {{converting function value of type '@MainActor @Sendable () -> ()' to '() -> Void' loses global actor 'MainActor'; this is an error in the Swift 6 language mode}}
493+
}
483494
}
484495

485496
@available(SwiftStdlib 5.1, *)

0 commit comments

Comments
 (0)