Skip to content

Commit b6a3434

Browse files
committed
[Concurrency] Emit a tailored note for a missing await if the call is implicitly
asynchronous due to actor isolation.
1 parent 8e78c8b commit b6a3434

7 files changed

+47
-32
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1722,7 +1722,8 @@ namespace {
17221722
}
17231723

17241724
// Mark as implicitly async.
1725-
apply->setImplicitlyAsync(true);
1725+
if (!fnType->getExtInfo().isAsync())
1726+
apply->setImplicitlyAsync(true);
17261727

17271728
// If we don't need to check for sendability, we're done.
17281729
if (!shouldDiagnoseNonSendableViolations(ctx.LangOpts))

lib/Sema/TypeCheckEffects.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2871,8 +2871,18 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
28712871
}
28722872
continue;
28732873
}
2874-
Ctx.Diags.diagnose(diag.expr.getStartLoc(),
2875-
diag::async_access_without_await, 0);
2874+
2875+
auto *call = dyn_cast<ApplyExpr>(&diag.expr);
2876+
if (call && call->implicitlyAsync()) {
2877+
// Emit a tailored note if the call is implicitly async, meaning the
2878+
// callee is isolated to an actor.
2879+
auto callee = call->getCalledValue();
2880+
Ctx.Diags.diagnose(diag.expr.getStartLoc(), diag::actor_isolated_sync_func,
2881+
callee->getDescriptiveKind(), callee->getName());
2882+
} else {
2883+
Ctx.Diags.diagnose(diag.expr.getStartLoc(),
2884+
diag::async_access_without_await, 0);
2885+
}
28762886

28772887
continue;
28782888
}

test/Concurrency/actor_call_implicitly_async.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -207,26 +207,28 @@ extension BankAccount {
207207
func totalBalance(including other: BankAccount) async -> Int {
208208
//expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{12-12=await }}
209209
return balance()
210-
+ other.balance() // expected-note{{call is 'async'}}
210+
+ other.balance() // expected-note{{calls to instance method 'balance()' from outside of its actor context are implicitly asynchronous}}
211211
}
212212

213213
func breakAccounts(other: BankAccount) async {
214214
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{9-9=await }}
215-
_ = other.deposit( // expected-note{{call is 'async'}}
216-
other.withdraw( // expected-note{{call is 'async'}}
215+
_ = other.deposit( // expected-note{{calls to instance method 'deposit' from outside of its actor context are implicitly asynchronous}}
216+
other.withdraw( // expected-note{{calls to instance method 'withdraw' from outside of its actor context are implicitly asynchronous}}
217217
self.deposit(
218-
other.withdraw( // expected-note{{call is 'async'}}
219-
other.balance())))) // expected-note{{call is 'async'}}
218+
other.withdraw( // expected-note{{calls to instance method 'withdraw' from outside of its actor context are implicitly asynchronous}}
219+
other.balance())))) // expected-note{{calls to instance method 'balance()' from outside of its actor context are implicitly asynchronous}}
220220
}
221221
}
222222

223223
func anotherAsyncFunc() async {
224224
let a = BankAccount(initialDeposit: 34)
225225
let b = BankAccount(initialDeposit: 35)
226226

227-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}} {{7-7=await }} expected-note@+1{{call is 'async'}}
227+
// expected-error@+2{{expression is 'async' but is not marked with 'await'}} {{7-7=await }}
228+
// expected-note@+1{{calls to instance method 'deposit' from outside of its actor context are implicitly asynchronous}}
228229
_ = a.deposit(1)
229-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}} {{7-7=await }} expected-note@+1{{call is 'async'}}
230+
// expected-error@+2{{expression is 'async' but is not marked with 'await'}} {{7-7=await }}
231+
// expected-note@+1{{calls to instance method 'balance()' from outside of its actor context are implicitly asynchronous}}
230232
_ = b.balance()
231233

232234
_ = b.balance // expected-error {{actor-isolated instance method 'balance()' can only be referenced from inside the actor}}

test/Concurrency/actor_isolation.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ extension MyActor {
202202

203203
_ = otherActor.synchronous()
204204
// expected-error@-1{{expression is 'async' but is not marked with 'await'}}{{9-9=await }}
205-
// expected-note@-2{{call is 'async'}}
205+
// expected-note@-2{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
206206
_ = await otherActor.asynchronous()
207207
_ = otherActor.text[0]
208208
// expected-error@-1{{expression is 'async' but is not marked with 'await'}}{{9-9=await }}
@@ -222,7 +222,7 @@ extension MyActor {
222222

223223
// Global actors
224224
syncGlobalActorFunc() // expected-error{{expression is 'async' but is not marked with 'await'}}{{5-5=await }}
225-
// expected-note@-1{{call is 'async'}}
225+
// expected-note@-1{{calls to global function 'syncGlobalActorFunc()' from outside of its actor context are implicitly asynchronous}}
226226

227227
await asyncGlobalActorFunc()
228228

@@ -399,7 +399,7 @@ actor Crystal {
399399
@SomeGlobalActor func goo1() async {
400400
let _ = goo2
401401
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{3-3=await }}
402-
goo2() // expected-note{{call is 'async'}}
402+
goo2() // expected-note{{calls to global function 'goo2()' from outside of its actor context are implicitly asynchronous}}
403403
}
404404
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
405405
@asyncHandler @SomeOtherGlobalActor func goo2() { await goo1() }
@@ -409,7 +409,7 @@ func testGlobalActorClosures() {
409409
let _: Int = acceptAsyncClosure { @SomeGlobalActor in
410410
syncGlobalActorFunc()
411411
syncOtherGlobalActorFunc() // expected-error{{expression is 'async' but is not marked with 'await'}}{{5-5=await }}
412-
// expected-note@-1{{call is 'async'}}
412+
// expected-note@-1{{calls to global function 'syncOtherGlobalActorFunc()' from outside of its actor context are implicitly asynchronous}}
413413

414414
await syncOtherGlobalActorFunc()
415415
return 17
@@ -434,7 +434,7 @@ extension MyActor {
434434
// expected-note@-1{{property access is 'async'}}
435435
_ = await mutable
436436
_ = synchronous() // expected-error{{expression is 'async' but is not marked with 'await'}}{{9-9=await }}
437-
// expected-note@-1{{call is 'async'}}
437+
// expected-note@-1{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
438438
_ = await synchronous()
439439
_ = text[0] // expected-error{{expression is 'async' but is not marked with 'await'}}
440440
// expected-note@-1{{property access is 'async'}}
@@ -445,7 +445,7 @@ extension MyActor {
445445
// we are outside of the actor instance.
446446
_ = self.immutable
447447
_ = self.synchronous() // expected-error{{expression is 'async' but is not marked with 'await'}}{{9-9=await }}
448-
// expected-note@-1{{call is 'async'}}
448+
// expected-note@-1{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
449449
_ = await self.synchronous()
450450

451451
_ = await self.asynchronous()
@@ -461,7 +461,7 @@ extension MyActor {
461461
// expected-note@-1{{property access is 'async'}}
462462
_ = await super.superState
463463
super.superMethod() // expected-error{{expression is 'async' but is not marked with 'await'}}{{5-5=await }}
464-
// expected-note@-1{{call is 'async'}}
464+
// expected-note@-1{{calls to instance method 'superMethod()' from outside of its actor context are implicitly asynchronous}}
465465

466466
await super.superMethod()
467467
await super.superAsyncMethod()
@@ -473,7 +473,7 @@ extension MyActor {
473473
// call asychronous methods
474474
_ = otherActor.immutable // okay
475475
_ = otherActor.synchronous() // expected-error{{expression is 'async' but is not marked with 'await'}}{{9-9=await }}
476-
// expected-note@-1{{call is 'async'}}
476+
// expected-note@-1{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
477477
_ = otherActor.synchronous // expected-error{{actor-isolated instance method 'synchronous()' can only be referenced on 'self'}}
478478
_ = await otherActor.asynchronous()
479479
_ = otherActor.text[0] // expected-error{{expression is 'async' but is not marked with 'await'}}{{9-9=await }}
@@ -534,7 +534,7 @@ func testGlobalRestrictions(actor: MyActor) async {
534534

535535
// any kind of method can be called from outside the actor, so long as it's marked with 'await'
536536
_ = actor.synchronous() // expected-error{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
537-
// expected-note@-1{{call is 'async}}
537+
// expected-note@-1{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
538538
_ = actor.asynchronous() // expected-error{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
539539
// expected-note@-1{{call is 'async'}}
540540

@@ -751,7 +751,7 @@ class SomeClassWithInits {
751751
await self.isolated() // expected-warning{{cannot use parameter 'self' with a non-sendable type 'SomeClassWithInits' from concurrently-executed code}}
752752
self.isolated() // expected-warning{{cannot use parameter 'self' with a non-sendable type 'SomeClassWithInits' from concurrently-executed code}}
753753
// expected-error@-1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
754-
// expected-note@-2{{call is 'async'}}
754+
// expected-note@-2{{calls to instance method 'isolated()' from outside of its actor context are implicitly asynchronous}}
755755

756756
print(await self.mutableState) // expected-warning{{cannot use parameter 'self' with a non-sendable type 'SomeClassWithInits' from concurrently-executed code}}
757757
}
@@ -791,10 +791,10 @@ func testCrossActorProtocol<T: P>(t: T) async {
791791
await t.g()
792792
t.f()
793793
// expected-error@-1{{expression is 'async' but is not marked with 'await'}}{{3-3=await }}
794-
// expected-note@-2{{call is 'async'}}
794+
// expected-note@-2{{calls to instance method 'f()' from outside of its actor context are implicitly asynchronous}}
795795
t.g()
796796
// expected-error@-1{{expression is 'async' but is not marked with 'await'}}{{3-3=await }}
797-
// expected-note@-2{{call is 'async'}}
797+
// expected-note@-2{{calls to instance method 'g()' from outside of its actor context are implicitly asynchronous}}
798798
}
799799

800800
// ----------------------------------------------------------------------
@@ -808,7 +808,7 @@ extension MyActor {
808808
func testSendableAndInheriting() {
809809
acceptAsyncSendableClosure {
810810
synchronous() // expected-error{{expression is 'async' but is not marked with 'await'}}
811-
// expected-note@-1 {{call is 'async'}}
811+
// expected-note@-1{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
812812
}
813813

814814
acceptAsyncSendableClosure {

test/Concurrency/global_actor_from_ordinary_context.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class Taylor {
109109
func fromAsync() async {
110110
let x = syncGlobActorFn
111111
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{3-3=await }}
112-
x() // expected-note{{call is 'async'}}
112+
x() // expected-note{{calls to let 'x' from outside of its actor context are implicitly asynchronous}}
113113

114114

115115
let y = asyncGlobalActFn
@@ -120,7 +120,7 @@ func fromAsync() async {
120120

121121
let fn = a.method
122122
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{3-3=await }}
123-
fn() //expected-note{{call is 'async}}
123+
fn() //expected-note{{calls to let 'fn' from outside of its actor context are implicitly asynchronous}}
124124
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
125125
_ = a.const_memb // expected-note{{property access is 'async'}}
126126
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}

test/Concurrency/global_actor_function_types.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,14 @@ func testTypesConcurrencyContext() async {
130130
let _: () -> Int = f2 // expected-error{{converting function value of type '@SomeGlobalActor () -> Int' to '() -> Int' loses global actor 'SomeGlobalActor'}}
131131

132132
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
133-
_ = f1() //expected-note{{call is 'async}}
133+
_ = f1() //expected-note{{calls to let 'f1' from outside of its actor context are implicitly asynchronous}}
134134
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
135-
_ = f2() //expected-note{{call is 'async'}}
135+
_ = f2() //expected-note{{calls to let 'f2' from outside of its actor context are implicitly asynchronous}}
136136

137-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
138-
_ = f1() + f2() // expected-note 2 {{call is 'async'}}
137+
// expected-error@+3{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
138+
//expected-note@+2 {{calls to let 'f1' from outside of its actor context are implicitly asynchronous}}
139+
// expected-note@+1 {{calls to let 'f2' from outside of its actor context are implicitly asynchronous}}
140+
_ = f1() + f2()
139141

140142
_ = await f1()
141143
_ = await f2()

test/Concurrency/global_actor_inference.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ class SubclassWithGlobalActors : SuperclassWithGlobalActors {
238238
onGenericGlobalActorString() // okay
239239

240240
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{5-5=await }}
241-
onGenericGlobalActorInt() // expected-note{{call is 'async'}}
241+
onGenericGlobalActorInt() // expected-note{{calls to instance method 'onGenericGlobalActorInt()' from outside of its actor context are implicitly asynchronous}}
242242
}
243243
}
244244

@@ -253,7 +253,7 @@ class SubclassWithGlobalActors : SuperclassWithGlobalActors {
253253

254254
func bar() async {
255255
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{3-3=await }}
256-
foo() // expected-note{{call is 'async'}}
256+
foo() // expected-note{{calls to global function 'foo()' from outside of its actor context are implicitly asynchronous}}
257257
}
258258

259259
// expected-note@+1 {{add '@SomeGlobalActor' to make global function 'barSync()' part of global actor 'SomeGlobalActor'}} {{1-1=@SomeGlobalActor }}
@@ -526,7 +526,7 @@ func acceptAsyncSendableClosureInheriting<T>(@_inheritActorContext _: @Sendable
526526
@MainActor func testCallFromMainActor() {
527527
acceptAsyncSendableClosure {
528528
onlyOnMainActor() // expected-error{{expression is 'async' but is not marked with 'await'}}
529-
// expected-note@-1 {{call is 'async'}}
529+
// expected-note@-1 {{calls to global function 'onlyOnMainActor()' from outside of its actor context are implicitly asynchronous}}
530530
}
531531

532532
acceptAsyncSendableClosure {

0 commit comments

Comments
 (0)