Skip to content

Commit 16d3f78

Browse files
authored
Merge pull request #36142 from DougGregor/implicit-actor-nonactor-self
2 parents c1a9f6f + a864c55 commit 16d3f78

File tree

6 files changed

+90
-70
lines changed

6 files changed

+90
-70
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_call_implicitly_async.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ actor BankAccount {
2121
// expected-note@+1 {{calls to instance method 'balance()' from outside of its actor context are implicitly asynchronous}}
2222
func balance() -> Int { return curBalance }
2323

24-
// expected-note@+1 {{calls to instance method 'deposit' from outside of its actor context are implicitly asynchronous}}
24+
// expected-note@+1 2{{calls to instance method 'deposit' from outside of its actor context are implicitly asynchronous}}
2525
func deposit(_ amount : Int) -> Int {
2626
guard amount >= 0 else { return 0 }
2727

@@ -125,14 +125,12 @@ func anotherAsyncFunc() async {
125125

126126
}
127127

128-
// expected-note@+2 {{add 'async' to function 'regularFunc()' to make it asynchronous}} {{19-19= async}}
129-
// expected-note@+1 {{add '@asyncHandler' to function 'regularFunc()' to create an implicit asynchronous context}} {{1-1=@asyncHandler }}
130128
func regularFunc() {
131129
let a = BankAccount(initialDeposit: 34)
132130

133131
_ = a.deposit //expected-error{{actor-isolated instance method 'deposit' can only be referenced inside the actor}}
134132

135-
_ = a.deposit(1) // expected-error{{'async' in a function that does not support concurrency}}
133+
_ = a.deposit(1) // expected-error{{actor-isolated instance method 'deposit' can only be referenced inside the actor}}
136134
}
137135

138136

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

test/Concurrency/async_let_isolation.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,18 @@ actor MyActor {
55
let immutable: Int = 17
66
var text: [String] = []
77

8-
func synchronous() -> String { text.first ?? "nothing" } // expected-note 2 {{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
8+
func synchronous() -> String { text.first ?? "nothing" }
99
func asynchronous() async -> String { synchronous() }
1010

1111
func testAsyncLetIsolation() async {
1212
async let x = self.synchronous()
13-
// expected-error @-1{{actor-isolated instance method 'synchronous()' cannot be referenced from 'async let' initializer}}
1413

1514
async let y = await self.asynchronous()
1615

1716
async let z = synchronous()
18-
// expected-error @-1{{actor-isolated instance method 'synchronous()' cannot be referenced from 'async let' initializer}}
1917

2018
var localText = text
21-
async let w = localText.removeLast()
22-
// expected-error@-1{{mutation of captured var 'localText' in concurrently-executing code}}
19+
async let w = localText.removeLast() // expected-error{{mutation of captured var 'localText' in concurrently-executing code}}
2320

2421
_ = await x
2522
_ = await y

test/Concurrency/global_actor_from_ordinary_context.swift

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,9 @@ func referenceAsyncGlobalActor() {
5656
}
5757

5858

59-
// expected-note@+3 {{add '@asyncHandler' to function 'callGlobalActor()' to create an implicit asynchronous context}} {{1-1=@asyncHandler }}
60-
// expected-note@+2 {{add 'async' to function 'callGlobalActor()' to make it asynchronous}} {{23-23= async}}
6159
// expected-note@+1 {{add '@SomeGlobalActor' to make global function 'callGlobalActor()' part of global actor 'SomeGlobalActor'}} {{1-1=@SomeGlobalActor }}
6260
func callGlobalActor() {
63-
syncGlobActorFn() // expected-error {{'async' in a function that does not support concurrency}}
61+
syncGlobActorFn() // expected-error {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
6462
}
6563

6664
func fromClosure() {
@@ -70,40 +68,36 @@ func fromClosure() {
7068
x()
7169
}()
7270

73-
// expected-error@+2 {{'async' in a function that does not support concurrency}}
7471
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
7572
let _ = { syncGlobActorFn() }()
7673
}
7774

7875
class Taylor {
79-
init() { // expected-note {{add 'async' to function 'init()' to make it asynchronous}} {{9-9= async}}
80-
syncGlobActorFn() // expected-error {{'async' in a function that does not support concurrency}}
76+
init() {
77+
syncGlobActorFn() // expected-error {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
8178

8279
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
8380
_ = syncGlobActorFn
8481
}
8582

8683
deinit {
87-
syncGlobActorFn() // expected-error {{'async' in a function that does not support concurrency}}
84+
syncGlobActorFn() // expected-error {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
8885

8986
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
9087
_ = syncGlobActorFn
9188
}
9289

93-
// expected-note@+3 2 {{add '@SomeGlobalActor' to make instance method 'method1()' part of global actor 'SomeGlobalActor'}} {{3-3=@SomeGlobalActor }}
94-
// expected-note@+2 {{add '@asyncHandler' to function 'method1()' to create an implicit asynchronous context}} {{3-3=@asyncHandler }}
95-
// expected-note@+1 {{add 'async' to function 'method1()' to make it asynchronous}} {{17-17= async}}
90+
// expected-note@+1 2 {{add '@SomeGlobalActor' to make instance method 'method1()' part of global actor 'SomeGlobalActor'}} {{3-3=@SomeGlobalActor }}
9691
func method1() {
97-
syncGlobActorFn() // expected-error {{'async' in a function that does not support concurrency}}
92+
syncGlobActorFn() // expected-error {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
9893

9994
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
10095
_ = syncGlobActorFn
10196
}
10297

103-
// expected-note@+2 2 {{add '@SomeGlobalActor' to make instance method 'cannotBeHandler()' part of global actor 'SomeGlobalActor'}} {{3-3=@SomeGlobalActor }}
104-
// expected-note@+1 {{add 'async' to function 'cannotBeHandler()' to make it asynchronous}}
98+
// expected-note@+1 2 {{add '@SomeGlobalActor' to make instance method 'cannotBeHandler()' part of global actor 'SomeGlobalActor'}} {{3-3=@SomeGlobalActor }}
10599
func cannotBeHandler() -> Int {
106-
syncGlobActorFn() // expected-error {{'async' in a function that does not support concurrency}}
100+
syncGlobActorFn() // expected-error {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
107101

108102
// expected-error@+1 {{global function 'syncGlobActorFn()' isolated to global actor 'SomeGlobalActor' can not be referenced from this context}}
109103
_ = syncGlobActorFn

0 commit comments

Comments
 (0)