Skip to content

Commit 91d1d8d

Browse files
committed
Disallow passing global actor state inout
This patch implements banning passing global actor state inout to async functions. A note is also emitted to show where the global state is declared since it won't necessarily be in the actor itself.
1 parent c6d84b3 commit 91d1d8d

File tree

2 files changed

+38
-2
lines changed

2 files changed

+38
-2
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -924,9 +924,17 @@ namespace {
924924
case ActorIsolationRestriction::Unrestricted:
925925
case ActorIsolationRestriction::LocalCapture:
926926
case ActorIsolationRestriction::Unsafe:
927-
case ActorIsolationRestriction::GlobalActor: // TODO: handle global
928-
// actors
929927
break;
928+
case ActorIsolationRestriction::GlobalActor: {
929+
ctx.Diags.diagnose(call->getLoc(),
930+
diag::actor_isolated_inout_state,
931+
valueDecl->getDescriptiveKind(),
932+
valueDecl->getName(),
933+
call->implicitlyAsync());
934+
valueDecl->diagnose(diag::kind_declared_here,
935+
valueDecl->getDescriptiveKind());
936+
return true;
937+
}
930938
case ActorIsolationRestriction::ActorSelf: {
931939
if (isPartialApply) {
932940
// The partially applied InoutArg is a property of actor. This can

test/Concurrency/actor_inout_isolation.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,31 @@ extension TestActor {
131131
await other.modify(&value2)
132132
}
133133
}
134+
135+
// Verify global actor protection
136+
137+
@globalActor
138+
struct MyGlobalActor {
139+
static let shared = TestActor()
140+
}
141+
142+
@MyGlobalActor var number: Int = 0
143+
// expected-note@-1{{var declared here}}
144+
// expected-note@-2{{var declared here}}
145+
// expected-note@-3{{mutable state is only available within the actor instance}}
146+
147+
// expected-error@+2{{actor-isolated var 'number' cannot be passed 'inout' to 'async' function call}}
148+
// expected-error@+1{{var 'number' isolated to global actor 'MyGlobalActor' can not be referenced from this context}}
149+
let _ = Task.runDetached { await { (_ foo: inout Int) async in foo += 1 }(&number) }
150+
151+
// attempt to pass global state owned by the global actor to another async function
152+
// expected-error@+1{{actor-isolated var 'number' cannot be passed 'inout' to 'async' function call}}
153+
@MyGlobalActor func sneaky() async { await modifyAsynchronously(&number) }
154+
155+
// It's okay to pass actor state inout to synchronous functions!
156+
157+
func globalSyncFunction(_ foo: inout Int) { }
158+
@MyGlobalActor func globalActorSyncFunction(_ foo: inout Int) { }
159+
@MyGlobalActor func globalActorAsyncOkay() async { globalActorSyncFunction(&number) }
160+
@MyGlobalActor func globalActorAsyncOkay2() async { globalSyncFunction(&number) }
161+
@MyGlobalActor func globalActorSyncOkay() { globalSyncFunction(&number) }

0 commit comments

Comments
 (0)