Skip to content

Commit 6dd87a2

Browse files
committed
[Concurrency] Ensure we do implicit-async from @actorIndependent & global-actors contexts
We weren't allowing implicit "async" promotions for cross-actor references from functions defined within an actor that were @actorIndependent or part of a global actor. Ensure we do promotion there. ... and because these lead to misleading diagnostics in a lot of places, don't do implicit "async" promotion if we're not in an async context already. We still need to improve the diagnostics here.
1 parent b802e5b commit 6dd87a2

File tree

2 files changed

+60
-19
lines changed

2 files changed

+60
-19
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,21 @@ namespace {
13941394
return ActorIsolation::forIndependent(ActorIndependentKind::Safe);
13951395
}
13961396

1397+
bool isInAsynchronousContext() const {
1398+
auto dc = getDeclContext();
1399+
if (auto func = dyn_cast<AbstractFunctionDecl>(dc))
1400+
return func->isAsyncContext();
1401+
1402+
if (auto closure = dyn_cast<AbstractClosureExpr>(dc)) {
1403+
if (auto type = closure->getType()) {
1404+
if (auto fnType = type->getAs<AnyFunctionType>())
1405+
return fnType->isAsync();
1406+
}
1407+
}
1408+
1409+
return false;
1410+
}
1411+
13971412
/// Check a reference to an entity within a global actor.
13981413
bool checkGlobalActorReference(
13991414
ConcreteDeclRef valueRef, SourceLoc loc, Type globalActor,
@@ -1403,6 +1418,9 @@ namespace {
14031418
/// Returns true if this global actor reference is the callee of an Apply.
14041419
/// NOTE: This check mutates the identified ApplyExpr if it returns true!
14051420
auto inspectForImplicitlyAsync = [&] () -> bool {
1421+
// If our current context isn't an asynchronous one, don't
1422+
if (!isInAsynchronousContext())
1423+
return false;
14061424

14071425
// Is this global actor reference outside of an ApplyExpr?
14081426
if (applyStack.size() == 0)
@@ -1727,10 +1745,13 @@ namespace {
17271745
}
17281746

17291747
case ActorIsolationRestriction::ActorSelf: {
1730-
// Must reference actor-isolated state on 'self'.
1731-
auto *selfVar = getReferencedSelf(base);
1732-
if (!selfVar) {
1733-
// actor-isolated non-self calls are implicitly async and thus OK.
1748+
// Local function to check for implicit async promotion.
1749+
auto checkImplicitlyAsync = [&]() -> Optional<bool> {
1750+
if (!isInAsynchronousContext())
1751+
return None;
1752+
1753+
// actor-isolated non-isolated-self calls are implicitly async
1754+
// and thus OK.
17341755
if (maybeImplicitAsync && isa<AbstractFunctionDecl>(member)) {
17351756
markNearestCallAsImplicitlyAsync();
17361757

@@ -1740,6 +1761,16 @@ namespace {
17401761
ConcurrentReferenceKind::SynchronousAsAsyncCall);
17411762
}
17421763

1764+
return None;
1765+
};
1766+
1767+
// Must reference actor-isolated state on 'self'.
1768+
auto *selfVar = getReferencedSelf(base);
1769+
if (!selfVar) {
1770+
// Check for implicit async.
1771+
if (auto result = checkImplicitlyAsync())
1772+
return *result;
1773+
17431774
ctx.Diags.diagnose(
17441775
memberLoc, diag::actor_isolated_non_self_reference,
17451776
member->getDescriptiveKind(),
@@ -1772,6 +1803,10 @@ namespace {
17721803
return false;
17731804

17741805
case ActorIsolation::Independent: {
1806+
// Check for implicit async.
1807+
if (auto result = checkImplicitlyAsync())
1808+
return *result;
1809+
17751810
// The 'self' is for an actor-independent member, which means
17761811
// we cannot refer to actor-isolated state.
17771812
auto diag = findActorIndependentReason(curDC);
@@ -1782,6 +1817,10 @@ namespace {
17821817
}
17831818

17841819
case ActorIsolation::GlobalActor:
1820+
// Check for implicit async.
1821+
if (auto result = checkImplicitlyAsync())
1822+
return *result;
1823+
17851824
// The 'self' is for a member that's part of a global actor, which
17861825
// means we cannot refer to actor-isolated state.
17871826
ctx.Diags.diagnose(

test/Concurrency/actor_isolation.swift

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func acceptEscapingAsyncClosure<T>(_: @escaping () async -> T) { }
2020
actor MySuperActor {
2121
var superState: Int = 25 // expected-note {{mutable state is only available within the actor instance}}
2222

23-
func superMethod() { } // expected-note 3 {{calls to instance method 'superMethod()' from outside of its actor context are implicitly asynchronous}}
23+
func superMethod() { } // expected-note 2 {{calls to instance method 'superMethod()' from outside of its actor context are implicitly asynchronous}}
2424
func superAsyncMethod() async { }
2525

2626
subscript (index: Int) -> String { // expected-note 3{{subscript declared here}}
@@ -35,7 +35,7 @@ actor MyActor: MySuperActor {
3535
class func synchronousClass() { }
3636
static func synchronousStatic() { }
3737

38-
func synchronous() -> String { text.first ?? "nothing" } // expected-note 21{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
38+
func synchronous() -> String { text.first ?? "nothing" } // expected-note 20{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
3939
func asynchronous() async -> String { synchronous() }
4040
}
4141

@@ -45,7 +45,6 @@ extension MyActor {
4545
set { }
4646
}
4747

48-
// expected-note@+1 {{add 'async' to function 'actorIndependentFunc(otherActor:)' to make it asynchronous}} {{67-67= async}}
4948
@actorIndependent func actorIndependentFunc(otherActor: MyActor) -> Int {
5049
_ = immutable
5150
_ = text[0] // expected-error{{actor-isolated property 'text' can not be referenced from an '@actorIndependent' context}}
@@ -65,8 +64,11 @@ extension MyActor {
6564
_ = otherActor.actorIndependentVar
6665
otherActor.actorIndependentVar = 17
6766

67+
// async promotion
68+
_ = synchronous() // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from an '@actorIndependent' context}}
69+
6870
// Global actors
69-
syncGlobalActorFunc() /// expected-error{{'async' in a function that does not support concurrency}}
71+
syncGlobalActorFunc() /// expected-error{{global function 'syncGlobalActorFunc()' isolated to global actor 'SomeGlobalActor' can not be referenced from an '@actorIndependent' context}}
7072
_ = syncGlobalActorFunc // expected-error{{global function 'syncGlobalActorFunc()' isolated to global actor 'SomeGlobalActor' can not be referenced from an '@actorIndependent' context}}
7173

7274
// Global data is okay if it is immutable.
@@ -232,7 +234,7 @@ struct GenericGlobalActor<T> {
232234
static var shared: SomeActor { SomeActor() }
233235
}
234236

235-
@SomeGlobalActor func syncGlobalActorFunc() { syncGlobalActorFunc() } // expected-note{{calls to global function 'syncGlobalActorFunc()' from outside of its actor context are implicitly asynchronous}}
237+
@SomeGlobalActor func syncGlobalActorFunc() { syncGlobalActorFunc() } // expected-note 2{{calls to global function 'syncGlobalActorFunc()' from outside of its actor context are implicitly asynchronous}}
236238
@SomeGlobalActor func asyncGlobalActorFunc() async { await asyncGlobalActorFunc() }
237239

238240
@SomeOtherGlobalActor func syncOtherGlobalActorFunc() { }
@@ -260,20 +262,24 @@ extension MyActor {
260262
await asyncOtherGlobalActorFunc()
261263

262264
_ = immutable
263-
_ = synchronous() // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from context of global actor 'SomeGlobalActor'}}
265+
_ = synchronous() // expected-error{{call is 'async' but is not marked with 'await'}}
266+
_ = await synchronous()
264267
_ = text[0] // expected-error{{actor-isolated property 'text' can not be referenced from context of global actor 'SomeGlobalActor'}}
265268

266269
// Accesses on 'self' are only okay for immutable and asynchronous, because
267270
// we are outside of the actor instance.
268271
_ = self.immutable
269-
_ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' can not be referenced from context of global actor 'SomeGlobalActor'}}
272+
_ = self.synchronous() // expected-error{{call is 'async' but is not marked with 'await'}}
273+
_ = await self.synchronous()
274+
270275
_ = await self.asynchronous()
271276
_ = self.text[0] // expected-error{{actor-isolated property 'text' can not be referenced from context of global actor 'SomeGlobalActor'}}
272277
_ = self[0] // expected-error{{actor-isolated subscript 'subscript(_:)' can not be referenced from context of global actor 'SomeGlobalActor'}}
273278

274279
// Accesses on 'super' are not okay; we're outside of the actor.
275280
_ = super.superState // expected-error{{actor-isolated property 'superState' can not be referenced from context of global actor 'SomeGlobalActor'}}
276-
super.superMethod() // expected-error{{actor-isolated instance method 'superMethod()' can not be referenced from context of global actor 'SomeGlobalActor'}}
281+
super.superMethod() // expected-error{{call is 'async' but is not marked with 'await'}}
282+
await super.superMethod()
277283
await super.superAsyncMethod()
278284
_ = super[0] // expected-error{{actor-isolated subscript 'subscript(_:)' can not be referenced from context of global actor 'SomeGlobalActor'}}
279285

@@ -288,16 +294,14 @@ extension MyActor {
288294
}
289295

290296
struct GenericStruct<T> {
291-
@GenericGlobalActor<T> func f() { } // expected-note{{calls to instance method 'f()' from outside of its actor context are implicitly asynchronous}}
297+
@GenericGlobalActor<T> func f() { } // expected-note 2{{calls to instance method 'f()' from outside of its actor context are implicitly asynchronous}}
292298

293299
@GenericGlobalActor<T> func g() {
294300
f() // okay
295301
}
296302

297-
// expected-note@+2 {{add '@asyncHandler' to function 'h()' to create an implicit asynchronous context}} {{3-3=@asyncHandler }}
298-
// expected-note@+1 {{add 'async' to function 'h()' to make it asynchronous}} {{39-39= async}}
299303
@GenericGlobalActor<String> func h() {
300-
f() // expected-error{{'async' in a function that does not support concurrency}}
304+
f() // expected-error{{instance method 'f()' isolated to global actor 'GenericGlobalActor<T>' can not be referenced from different global actor 'GenericGlobalActor<String>'}}
301305
_ = f // expected-error{{instance method 'f()' isolated to global actor 'GenericGlobalActor<T>' can not be referenced from different global actor 'GenericGlobalActor<String>'}}
302306
}
303307
}
@@ -472,9 +476,7 @@ class SomeClassInActor {
472476

473477
extension SomeClassInActor.ID {
474478
func f(_ object: SomeClassInActor) { // expected-note{{add '@MainActor' to make instance method 'f' part of global actor 'MainActor'}}
475-
// expected-note@-1{{add 'async' to function 'f' to make it asynchronous}}
476-
// expected-note@-2{{add '@asyncHandler' to function 'f' to create an implicit asynchronous context}}
477-
object.inActor() // expected-error{{'async' in a function that does not support concurrency}}
479+
object.inActor() // expected-error{{instance method 'inActor()' isolated to global actor 'MainActor' can not be referenced from this context}}
478480
}
479481
}
480482

0 commit comments

Comments
 (0)