Skip to content

Commit 43c3e4a

Browse files
authored
Merge pull request #81719 from xedin/rdar-151720646
[TypeChecker] Improve diagnostics for access to actor-isolated values…
2 parents a478db9 + 9e56288 commit 43c3e4a

22 files changed

+218
-229
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5626,6 +5626,9 @@ ERROR(actor_isolated_non_self_reference,none,
56265626
"from a nonisolated autoclosure}2",
56275627
(const ValueDecl *, unsigned, unsigned, Type,
56285628
ActorIsolation))
5629+
ERROR(actor_isolated_access_outside_of_actor_context,none,
5630+
"%0 %kind1 cannot be %select{accessed|called}2 from outside of the actor",
5631+
(ActorIsolation, const ValueDecl *, bool))
56295632
ERROR(distributed_actor_isolated_non_self_reference,none,
56305633
"distributed actor-isolated %kind0 can not be accessed from a "
56315634
"nonisolated context",

lib/Sema/TypeCheckEffects.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4718,6 +4718,12 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
47184718
return diag.downgradeToWarning;
47194719
});
47204720

4721+
// If there is a single problem, let's attempt to produce a tailed
4722+
// diagnostic about accessing isolated values outside of their actors.
4723+
if (errors.size() == 1 &&
4724+
diagnoseAccessOutsideOfIsolationContext(anchor, errors.front()))
4725+
return;
4726+
47214727
Ctx.Diags.diagnose(anchor->getStartLoc(), diag::async_expr_without_await)
47224728
.warnUntilSwiftVersionIf(downgradeToWarning, 6)
47234729
.fixItInsert(loc, insertText)
@@ -4800,6 +4806,76 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
48004806
}
48014807
}
48024808

4809+
/// Check whether the given error points to an attempt to access
4810+
/// an isolated value or call an isolated function from outside
4811+
/// its actor and diagnose if so.
4812+
/// \returns true if problem was diagnosed, false otherwise.
4813+
bool diagnoseAccessOutsideOfIsolationContext(
4814+
const Expr *anchor, const DiagnosticInfo &errorInfo) const {
4815+
auto diagnoseAccessOutsideOfActor = [&](SourceLoc loc,
4816+
ConcreteDeclRef declRef,
4817+
bool isCall = false) {
4818+
auto declIsolation = getActorIsolation(declRef.getDecl());
4819+
4820+
// If the access is to a unspecified/nonisolated value, let's diagnose
4821+
// it with a generic error/warning about expression being `async`.
4822+
if (declIsolation.isUnspecified() || declIsolation.isNonisolated())
4823+
return false;
4824+
4825+
const auto &[fixItLoc, insertText] =
4826+
getFixItForUncoveredSite(anchor, "await");
4827+
4828+
Ctx.Diags
4829+
.diagnose(loc, diag::actor_isolated_access_outside_of_actor_context,
4830+
declIsolation, declRef.getDecl(), isCall)
4831+
.warnUntilSwiftVersionIf(errorInfo.downgradeToWarning, 6)
4832+
.fixItInsert(fixItLoc, insertText)
4833+
.highlight(anchor->getSourceRange());
4834+
return true;
4835+
};
4836+
4837+
switch (errorInfo.reason.getKind()) {
4838+
case PotentialEffectReason::Kind::AsyncLet:
4839+
case PotentialEffectReason::Kind::PropertyAccess:
4840+
case PotentialEffectReason::Kind::SubscriptAccess:
4841+
if (auto *declRef = dyn_cast<DeclRefExpr>(&errorInfo.expr)) {
4842+
return diagnoseAccessOutsideOfActor(declRef->getLoc(),
4843+
declRef->getDecl());
4844+
}
4845+
4846+
if (auto *memberRef = dyn_cast<MemberRefExpr>(&errorInfo.expr)) {
4847+
return diagnoseAccessOutsideOfActor(memberRef->getLoc(),
4848+
memberRef->getDecl());
4849+
}
4850+
4851+
if (auto *lookupExpr = dyn_cast<LookupExpr>(&errorInfo.expr)) {
4852+
return diagnoseAccessOutsideOfActor(lookupExpr->getLoc(),
4853+
lookupExpr->getMember());
4854+
}
4855+
4856+
break;
4857+
4858+
case PotentialEffectReason::Kind::Apply: {
4859+
auto *call = dyn_cast<ApplyExpr>(&errorInfo.expr);
4860+
if (call && call->getIsolationCrossing()) {
4861+
if (auto callee =
4862+
call->getCalledValue(/*skipFunctionConversions=*/true)) {
4863+
return diagnoseAccessOutsideOfActor(call->getLoc(), callee,
4864+
/*isCall=*/true);
4865+
}
4866+
}
4867+
break;
4868+
}
4869+
4870+
case PotentialEffectReason::Kind::ByClosure:
4871+
case PotentialEffectReason::Kind::ByDefaultClosure:
4872+
case PotentialEffectReason::Kind::ByConformance:
4873+
break;
4874+
}
4875+
4876+
return false;
4877+
}
4878+
48034879
void diagnoseUncoveredUnsafeSite(
48044880
const Expr *anchor, ArrayRef<UnsafeUse> unsafeUses) {
48054881
if (!Ctx.LangOpts.hasFeature(Feature::StrictMemorySafety, /*allowMigration=*/true))

test/Concurrency/actor_call_implicitly_async.swift

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ func someAsyncFunc() async {
168168

169169
_ = await a.deposit(b.withdraw(a.deposit(b.withdraw(b.balance()))))
170170

171-
// expected-error@+1 {{expression is 'async' but is not marked with 'await'}}{{3-3=await }} expected-note@+1 {{call is 'async'}}
171+
// expected-error@+1 {{actor-isolated instance method 'testSelfBalance()' cannot be called from outside of the actor}} {{3-3=await }}
172172
a.testSelfBalance()
173173

174174
await a.testThrowing() // expected-error {{call can throw, but it is not marked with 'try' and the error is not handled}}
@@ -177,16 +177,16 @@ func someAsyncFunc() async {
177177
// effectful properties from outside the actor instance
178178

179179
// expected-warning@+2 {{non-Sendable type 'Box' of property 'effPropA' cannot exit actor-isolated context}}
180-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}} {{7-7=await }} expected-note@+1{{property access is 'async'}}
180+
// expected-error@+1{{actor-isolated property 'effPropA' cannot be accessed from outside of the actor}} {{7-7=await }}
181181
_ = a.effPropA
182182

183183
// expected-warning@+3 {{non-Sendable type 'Box' of property 'effPropT' cannot exit actor-isolated context}}
184184
// expected-error@+2{{property access can throw, but it is not marked with 'try' and the error is not handled}}
185-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}} {{7-7=await }} expected-note@+1{{property access is 'async'}}
185+
// expected-error@+1{{actor-isolated property 'effPropT' cannot be accessed from outside of the actor}} {{7-7=await }}
186186
_ = a.effPropT
187187

188188
// expected-error@+2{{property access can throw, but it is not marked with 'try' and the error is not handled}}
189-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}} {{7-7=await }} expected-note@+1{{property access is 'async'}}
189+
// expected-error@+1{{actor-isolated property 'effPropAT' cannot be accessed from outside of the actor}} {{7-7=await }}
190190
_ = a.effPropAT
191191

192192
// (mostly) corrected ones
@@ -204,9 +204,9 @@ func someAsyncFunc() async {
204204

205205
extension BankAccount {
206206
func totalBalance(including other: BankAccount) async -> Int {
207-
//expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{12-12=await }}
208207
return balance()
209-
+ other.balance() // expected-note{{calls to instance method 'balance()' from outside of its actor context are implicitly asynchronous}}
208+
+ other.balance()
209+
// expected-error@-1 {{actor-isolated instance method 'balance()' cannot be called from outside of the actor}}{{207:12-12=await }}
210210
}
211211

212212
func breakAccounts(other: BankAccount) async {
@@ -223,19 +223,17 @@ func anotherAsyncFunc() async {
223223
let a = BankAccount(initialDeposit: 34)
224224
let b = BankAccount(initialDeposit: 35)
225225

226-
// expected-error@+2{{expression is 'async' but is not marked with 'await'}} {{7-7=await }}
227-
// expected-note@+1{{calls to instance method 'deposit' from outside of its actor context are implicitly asynchronous}}
226+
// expected-error@+1{{actor-isolated instance method 'deposit' cannot be called from outside of the actor}} {{7-7=await }}
228227
_ = a.deposit(1)
229-
// expected-error@+2{{expression is 'async' but is not marked with 'await'}} {{7-7=await }}
230-
// expected-note@+1{{calls to instance method 'balance()' from outside of its actor context are implicitly asynchronous}}
228+
// expected-error@+1{{actor-isolated instance method 'balance()' cannot be called from outside of the actor}} {{7-7=await }}
231229
_ = b.balance()
232230

233231
_ = b.balance // expected-error {{actor-isolated instance method 'balance()' can not be partially applied}}
234232

235233
// expected-error@+2{{actor-isolated property 'owner' can not be mutated from a nonisolated context}}
236234
// expected-note@+1{{consider declaring an isolated method on 'BankAccount' to perform the mutation}}
237235
a.owner = "cat"
238-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}} {{7-7=await }} expected-note@+1{{property access is 'async'}}
236+
// expected-error@+1{{actor-isolated property 'owner' cannot be accessed from outside of the actor}} {{7-7=await }}
239237
_ = b.owner
240238
_ = await b.owner == "cat"
241239

@@ -334,7 +332,7 @@ func walkChain(chain : Chain) async {
334332

335333
@OrangeActor func quinoa() async {
336334

337-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{3-3=await }} expected-note@+1 {{call is 'async'}}
335+
// expected-error@+1{{global actor 'BananaActor'-isolated global function 'rice()' cannot be called from outside of the actor}}{{3-3=await }}
338336
rice()
339337
}
340338

@@ -458,21 +456,21 @@ func tryEffPropsFromSync() {
458456
}
459457

460458
@OrangeActor func tryEffPropertiesFromGlobalActor() async throws {
461-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }} expected-note@+1 {{property access is 'async'}}
459+
// expected-error@+1{{global actor 'BananaActor'-isolated var 'effPropA' cannot be accessed from outside of the actor}}{{7-7=await }}
462460
_ = effPropA
463461

464462
// expected-note@+5{{did you mean to handle error as optional value?}}
465463
// expected-note@+4{{did you mean to use 'try'?}}
466464
// expected-note@+3{{did you mean to disable error propagation?}}
467465
// expected-error@+2{{property access can throw but is not marked with 'try'}}
468-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }} expected-note@+1 {{property access is 'async'}}
466+
// expected-error@+1{{global actor 'BananaActor'-isolated var 'effPropT' cannot be accessed from outside of the actor}}{{7-7=await }}
469467
_ = effPropT
470468

471469
// expected-note@+5{{did you mean to handle error as optional value?}}
472470
// expected-note@+4{{did you mean to use 'try'?}}
473471
// expected-note@+3{{did you mean to disable error propagation?}}
474472
// expected-error@+2{{property access can throw but is not marked with 'try'}}
475-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }} expected-note@+1 {{property access is 'async'}}
473+
// expected-error@+1{{global actor 'BananaActor'-isolated var 'effPropAT' cannot be accessed from outside of the actor}}{{7-7=await }}
476474
_ = effPropAT
477475

478476
_ = await effPropA
@@ -494,7 +492,7 @@ actor SubscriptA {
494492

495493
func f() async {
496494

497-
// expected-error@+1{{expression is 'async' but is not marked with 'await'}} {{9-9=await }} expected-note@+1{{subscript access is 'async'}}
495+
// expected-error@+1{{actor-isolated subscript 'subscript(_:)' cannot be accessed from outside of the actor}} {{9-9=await }}
498496
_ = self[0]
499497
}
500498
}
@@ -543,7 +541,7 @@ actor SubscriptAT {
543541
}
544542

545543
func tryTheActorSubscripts(a : SubscriptA, t : SubscriptT, at : SubscriptAT) async throws {
546-
// expected-error@+1 {{expression is 'async' but is not marked with 'await'}}{{7-7=await }} expected-note@+1 {{subscript access is 'async'}}
544+
// expected-error@+1 {{actor-isolated subscript 'subscript(_:)' cannot be accessed from outside of the actor}}{{7-7=await }}
547545
_ = a[0]
548546

549547
_ = await a[0]
@@ -552,7 +550,7 @@ func tryTheActorSubscripts(a : SubscriptA, t : SubscriptT, at : SubscriptAT) asy
552550
// expected-note@+4{{did you mean to use 'try'?}}
553551
// expected-note@+3{{did you mean to disable error propagation?}}
554552
// expected-error@+2{{subscript access can throw but is not marked with 'try'}}
555-
// expected-error@+1 {{expression is 'async' but is not marked with 'await'}}{{7-7=await }} expected-note@+1 {{subscript access is 'async'}}
553+
// expected-error@+1 {{actor-isolated subscript 'subscript(_:)' cannot be accessed from outside of the actor}}{{7-7=await }}
556554
_ = t[0]
557555

558556
_ = try await t[0]
@@ -563,7 +561,7 @@ func tryTheActorSubscripts(a : SubscriptA, t : SubscriptT, at : SubscriptAT) asy
563561
// expected-note@+4{{did you mean to use 'try'?}}
564562
// expected-note@+3{{did you mean to disable error propagation?}}
565563
// expected-error@+2{{subscript access can throw but is not marked with 'try'}}
566-
// expected-error@+1 {{expression is 'async' but is not marked with 'await'}}{{7-7=await }} expected-note@+1 {{subscript access is 'async'}}
564+
// expected-error@+1 {{actor-isolated subscript 'subscript(_:)' cannot be accessed from outside of the actor}}{{7-7=await }}
567565
_ = at[0]
568566

569567
_ = try await at[0]
@@ -583,8 +581,7 @@ final class IsolatedOperator: @preconcurrency Equatable {
583581

584582
nonisolated func callEqual() async -> Bool {
585583
let foo = await IsolatedOperator()
586-
// expected-error@+2{{expression is 'async' but is not marked with 'await'}}
587-
// expected-note@+1{{calls to operator function '==' from outside of its actor context are implicitly asynchronous}}
584+
// expected-error@+1{{main actor-isolated operator function '==' cannot be called from outside of the actor}} {{12-12=await }}
588585
return foo == self
589586
}
590587
}

0 commit comments

Comments
 (0)