Skip to content

Commit 1a9b58d

Browse files
committed
Add support for @actorIndependent on closures.
Closures often end up being implicitly actor-isolated. Allow them to opt out with `@actorIndependent`, as as other declarations can opt out of actor isolation.
1 parent 3fe2534 commit 1a9b58d

File tree

6 files changed

+25
-20
lines changed

6 files changed

+25
-20
lines changed

lib/Sema/TypeCheckAttr.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5614,6 +5614,10 @@ class ClosureAttributeChecker
56145614
// Nothing else to check.
56155615
}
56165616

5617+
void visitActorIndependentAttr(ActorIndependentAttr *attr) {
5618+
// Nothing else to check.
5619+
}
5620+
56175621
void visitCustomAttr(CustomAttr *attr) {
56185622
// Check whether this custom attribute is the global actor attribute.
56195623
auto globalActorAttr = evaluateOrDefault(

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,22 +1566,8 @@ namespace {
15661566

15671567
auto dc = const_cast<DeclContext *>(constDC);
15681568
while (!dc->isModuleScopeContext()) {
1569-
if (auto closure = dyn_cast<AbstractClosureExpr>(dc)) {
1570-
// If this closure has specific isolation, use it.
1571-
auto closureIsolation = getActorIsolationOfContext(dc);
1572-
if (closureIsolation != ActorIsolation::Independent)
1573-
return closureIsolation;
1574-
1575-
// Look through non-escaping closures.
1576-
if (auto type = closure->getType()) {
1577-
if (auto fnType = type->getAs<AnyFunctionType>()) {
1578-
if (fnType->isNoEscape()) {
1579-
dc = closure->getParent();
1580-
continue;
1581-
}
1582-
}
1583-
}
1584-
}
1569+
if (auto closure = dyn_cast<AbstractClosureExpr>(dc))
1570+
return getActorIsolationOfContext(dc);
15851571

15861572
// Functions have actor isolation defined on them.
15871573
if (auto func = dyn_cast<AbstractFunctionDecl>(dc))
@@ -2243,6 +2229,9 @@ namespace {
22432229
AbstractClosureExpr *closure) {
22442230
// If the closure specifies a global actor, use it.
22452231
if (auto explicitClosure = dyn_cast<ClosureExpr>(closure)) {
2232+
if (explicitClosure->getAttrs().hasAttribute<ActorIndependentAttr>())
2233+
return ClosureActorIsolation::forIndependent();
2234+
22462235
if (Type globalActorType = resolveGlobalActorType(explicitClosure))
22472236
return ClosureActorIsolation::forGlobalActor(globalActorType);
22482237
}

test/Concurrency/actor_inout_isolation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ struct MyGlobalActor {
196196
// expected-note@-3{{mutation of this var is only permitted within the actor}}
197197

198198
// expected-error@+2{{actor-isolated var 'number' cannot be passed 'inout' to 'async' function call}}
199-
// expected-error@+1{{var 'number' isolated to global actor 'MyGlobalActor' can not be used 'inout' from this context}}
199+
// expected-error@+1{{var 'number' isolated to global actor 'MyGlobalActor' can not be used 'inout' from a non-isolated context}}
200200
let _ = Task.runDetached { await { (_ foo: inout Int) async in foo += 1 }(&number) }
201201

202202
// attempt to pass global state owned by the global actor to another async function

test/Concurrency/closure_isolation.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ struct SomeGlobalActor {
4545

4646
func someAsyncFunc() async { }
4747

48+
@SomeGlobalActor func getGlobal7() -> Int { 7 }
49+
4850
// CHECK-LABEL: someGlobalActorFunc
4951
@SomeGlobalActor func someGlobalActorFunc() async {
5052
// CHECK: acceptAsyncClosure
@@ -66,5 +68,4 @@ func someAsyncFunc() async { }
6668
// CHECK: closure_expr
6769
// CHECK-NOT:actor-isolated
6870
acceptEscapingAsyncClosure { () async in print("hello") }
69-
7071
}

test/Concurrency/global_actor_from_ordinary_context.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@ func callGlobalActor() {
6161

6262
func fromClosure() {
6363
{ () -> Void in
64-
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
64+
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from a non-isolated context}}
6565
let x = syncGlobActorFn
6666
x()
6767
}()
6868

69-
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this synchronous context}}
69+
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from a non-isolated synchronous context}}
7070
let _ = { syncGlobActorFn() }()
7171
}
7272

test/Concurrency/global_actor_inference.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,14 @@ struct HasWrapperOnUnsafeActor {
426426
synced = 17
427427
}
428428
}
429+
430+
// ----------------------------------------------------------------------
431+
// Actor-independent closures
432+
// ----------------------------------------------------------------------
433+
@SomeGlobalActor func getGlobal7() -> Int { 7 } // expected-note{{calls to global function 'getGlobal7()' from outside of its actor context are implicitly asynchronous}}
434+
func acceptClosure<T>(_: () -> T) { }
435+
436+
@SomeGlobalActor func someGlobalActorFunc() async {
437+
acceptClosure { getGlobal7() } // okay
438+
acceptClosure { @actorIndependent in getGlobal7() } // expected-error{{global function 'getGlobal7()' isolated to global actor 'SomeGlobalActor' can not be referenced from a non-isolated synchronous context}}
439+
}

0 commit comments

Comments
 (0)