Skip to content

Commit 7077ef5

Browse files
authored
Merge pull request #81737 from xedin/rdar-151720646-6.2
[6.2][TypeChecker] Improve diagnostics for access to actor-isolated values…
2 parents d0d0afb + dd95c60 commit 7077ef5

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
@@ -5582,6 +5582,9 @@ ERROR(actor_isolated_non_self_reference,none,
55825582
"from a nonisolated autoclosure}2",
55835583
(const ValueDecl *, unsigned, unsigned, Type,
55845584
ActorIsolation))
5585+
ERROR(actor_isolated_access_outside_of_actor_context,none,
5586+
"%0 %kind1 cannot be %select{accessed|called}2 from outside of the actor",
5587+
(ActorIsolation, const ValueDecl *, bool))
55855588
ERROR(distributed_actor_isolated_non_self_reference,none,
55865589
"distributed actor-isolated %kind0 can not be accessed from a "
55875590
"nonisolated context",

lib/Sema/TypeCheckEffects.cpp

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

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

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

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)