Skip to content

Commit 8eae527

Browse files
committed
Tighten up actor isolation checking for closures and local functions.
Make sure that we check the isolation of the context in which a reference to `self` is made, rather than the context in which `self` is declared, when checking whether we are within actor-isolated code. This ensures that we report errors as actor-isolation errors rather than falling back to the "may execute concurrently with" checking.
1 parent 4e96cef commit 8eae527

File tree

4 files changed

+78
-38
lines changed

4 files changed

+78
-38
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4261,6 +4261,18 @@ ERROR(actor_isolated_concurrent_access,none,
42614261
"actor-isolated %0 %1 is unsafe to reference in code "
42624262
"that may execute concurrently",
42634263
(DescriptiveDeclKind, DeclName))
4264+
ERROR(actor_isolated_from_concurrent_closure,none,
4265+
"actor-isolated %0 %1 cannot be referenced from a concurrent closure",
4266+
(DescriptiveDeclKind, DeclName))
4267+
ERROR(actor_isolated_from_concurrent_function,none,
4268+
"actor-isolated %0 %1 cannot be referenced from a concurrent function",
4269+
(DescriptiveDeclKind, DeclName))
4270+
ERROR(actor_isolated_from_async_let,none,
4271+
"actor-isolated %0 %1 cannot be referenced from 'async let' initializer",
4272+
(DescriptiveDeclKind, DeclName))
4273+
ERROR(actor_isolated_from_escaping_closure,none,
4274+
"actor-isolated %0 %1 cannot be referenced from an '@escaping' closure",
4275+
(DescriptiveDeclKind, DeclName))
42644276
ERROR(local_function_executed_concurrently,none,
42654277
"concurrently-executed %0 %1 must be marked as '@concurrent'",
42664278
(DescriptiveDeclKind, DeclName))

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -869,9 +869,8 @@ namespace {
869869
}
870870

871871
private:
872-
/// If the expression is a reference to `self`, return the context of
873-
/// the 'self' parameter.
874-
static DeclContext *getSelfReferenceContext(Expr *expr) {
872+
/// If the expression is a reference to `self`, the `self` declaration.
873+
static VarDecl *getReferencedSelf(Expr *expr) {
875874
// Look through identity expressions and implicit conversions.
876875
Expr *prior;
877876
do {
@@ -885,24 +884,13 @@ namespace {
885884

886885
// 'super' references always act on self.
887886
if (auto super = dyn_cast<SuperRefExpr>(expr))
888-
return super->getSelf()->getDeclContext();
887+
return super->getSelf();
889888

890889
// Declaration references to 'self'.
891890
if (auto declRef = dyn_cast<DeclRefExpr>(expr)) {
892891
if (auto var = dyn_cast<VarDecl>(declRef->getDecl())) {
893-
if (var->isSelfParameter())
894-
return var->getDeclContext();
895-
896-
// If this is a 'self' capture in a capture list, recurse through
897-
// the capture list entry's initializer to find the original 'self'.
898-
if (var->isSelfParamCapture()) {
899-
for (auto capture : var->getParentCaptureList()->getCaptureList()) {
900-
if (capture.Var == var) {
901-
expr = capture.Init->getInit(0);
902-
return getSelfReferenceContext(expr);
903-
}
904-
}
905-
}
892+
if (var->isSelfParameter() || var->isSelfParamCapture())
893+
return var;
906894
}
907895
}
908896

@@ -1268,6 +1256,44 @@ namespace {
12681256
llvm_unreachable("unhandled actor isolation kind!");
12691257
}
12701258

1259+
/// Determine the reason for the given declaration context to be
1260+
/// actor-independent.
1261+
static Diag<DescriptiveDeclKind, DeclName>
1262+
findActorIndependentReason(DeclContext *dc) {
1263+
if (auto autoclosure = dyn_cast<AutoClosureExpr>(dc)) {
1264+
switch (autoclosure->getThunkKind()) {
1265+
case AutoClosureExpr::Kind::AsyncLet:
1266+
return diag::actor_isolated_from_async_let;
1267+
1268+
case AutoClosureExpr::Kind::DoubleCurryThunk:
1269+
case AutoClosureExpr::Kind::SingleCurryThunk:
1270+
return findActorIndependentReason(dc->getParent());
1271+
1272+
case AutoClosureExpr::Kind::None:
1273+
break;
1274+
}
1275+
}
1276+
1277+
if (auto closure = dyn_cast<AbstractClosureExpr>(dc)) {
1278+
if (isConcurrentClosure(closure)) {
1279+
return diag::actor_isolated_from_concurrent_closure;
1280+
}
1281+
1282+
if (isEscapingClosure(closure)) {
1283+
return diag::actor_isolated_from_escaping_closure;
1284+
}
1285+
1286+
return findActorIndependentReason(dc->getParent());
1287+
}
1288+
1289+
if (auto func = dyn_cast<AbstractFunctionDecl>(dc)) {
1290+
if (func->isConcurrent())
1291+
return diag::actor_isolated_from_concurrent_function;
1292+
}
1293+
1294+
return diag::actor_isolated_self_independent_context;
1295+
}
1296+
12711297
/// Check a reference with the given base expression to the given member.
12721298
/// Returns true iff the member reference refers to actor-isolated state
12731299
/// in an invalid or unsafe way such that a diagnostic was emitted.
@@ -1286,8 +1312,8 @@ namespace {
12861312

12871313
case ActorIsolationRestriction::ActorSelf: {
12881314
// Must reference actor-isolated state on 'self'.
1289-
auto *selfDC = getSelfReferenceContext(base);
1290-
if (!selfDC) {
1315+
auto *selfVar = getReferencedSelf(base);
1316+
if (!selfVar) {
12911317
// actor-isolated non-self calls are implicitly async and thus OK.
12921318
if (maybeImplicitAsync && isa<AbstractFunctionDecl>(member)) {
12931319
markNearestCallAsImplicitlyAsync();
@@ -1303,8 +1329,9 @@ namespace {
13031329
return true;
13041330
}
13051331

1306-
// Check whether the context of 'self' is actor-isolated.
1307-
switch (auto contextIsolation = getActorIsolationOfContext(selfDC)) {
1332+
// Check whether the current context is differently-isolated.
1333+
auto curDC = const_cast<DeclContext *>(getDeclContext());
1334+
switch (auto contextIsolation = getActorIsolationOfContext(curDC)) {
13081335
case ActorIsolation::ActorInstance:
13091336
// An escaping partial application of something that is part of
13101337
// the actor's isolated state is never permitted.
@@ -1323,15 +1350,15 @@ namespace {
13231350
// Okay
13241351
break;
13251352

1326-
case ActorIsolation::Independent:
1353+
case ActorIsolation::Independent: {
13271354
// The 'self' is for an actor-independent member, which means
13281355
// we cannot refer to actor-isolated state.
1329-
ctx.Diags.diagnose(
1330-
memberLoc, diag::actor_isolated_self_independent_context,
1331-
member->getDescriptiveKind(),
1332-
member->getName());
1356+
auto diag = findActorIndependentReason(curDC);
1357+
ctx.Diags.diagnose(memberLoc, diag, member->getDescriptiveKind(),
1358+
member->getName());
13331359
noteIsolatedActorMember(member);
13341360
return true;
1361+
}
13351362

13361363
case ActorIsolation::GlobalActor:
13371364
// The 'self' is for a member that's part of a global actor, which
@@ -1347,7 +1374,8 @@ namespace {
13471374

13481375
// Check whether we are in a context that will not execute concurrently
13491376
// with the context of 'self'.
1350-
if (mayExecuteConcurrentlyWith(getDeclContext(), selfDC)) {
1377+
if (mayExecuteConcurrentlyWith(
1378+
getDeclContext(), selfVar->getDeclContext())) {
13511379
ctx.Diags.diagnose(
13521380
memberLoc, diag::actor_isolated_concurrent_access,
13531381
member->getDescriptiveKind(), member->getName());

test/Concurrency/actor_isolation.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,16 +140,16 @@ extension MyActor {
140140

141141
// Concurrent closures might run... concurrently.
142142
acceptConcurrentClosure {
143-
_ = self.text[0] // expected-error{{actor-isolated property 'text' is unsafe to reference in code that may execute concurrently}}
144-
_ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' is unsafe to reference in code that may execute concurrently}}
143+
_ = self.text[0] // expected-error{{actor-isolated property 'text' cannot be referenced from a concurrent closure}}
144+
_ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' cannot be referenced from a concurrent closure}}
145145
_ = localVar // expected-warning{{local var 'localVar' is unsafe to reference in code that may execute concurrently}}
146146
_ = localConstant
147147
}
148148

149149
// Escaping closures might run concurrently.
150150
acceptEscapingClosure {
151-
_ = self.text[0] // expected-error{{actor-isolated property 'text' is unsafe to reference in code that may execute concurrently}}
152-
_ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' is unsafe to reference in code that may execute concurrently}}
151+
_ = self.text[0] // expected-error{{actor-isolated property 'text' cannot be referenced from an '@escaping' closure}}
152+
_ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' cannot be referenced from an '@escaping' closure}}
153153
_ = localVar // expected-warning{{local var 'localVar' is unsafe to reference in code that may execute concurrently}}
154154
_ = localConstant
155155
}
@@ -164,8 +164,8 @@ extension MyActor {
164164

165165
@concurrent func localFn2() {
166166
acceptClosure {
167-
_ = text[0] // expected-error{{actor-isolated property 'text' is unsafe to reference in code that may execute concurrently}}
168-
_ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' is unsafe to reference in code that may execute concurrently}}
167+
_ = text[0] // expected-error{{actor-isolated property 'text' cannot be referenced from a concurrent function}}
168+
_ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' cannot be referenced from a concurrent function}}
169169
_ = localVar // expected-warning{{local var 'localVar' is unsafe to reference in code that may execute concurrently}}
170170
_ = localConstant
171171
}
@@ -180,7 +180,7 @@ extension MyActor {
180180

181181
// Partial application
182182
_ = synchronous // expected-error{{actor-isolated instance method 'synchronous()' can not be partially applied}}
183-
_ = super.superMethod // expected-error{{actor-isolated instance method 'superMethod()' is unsafe to reference in code that may execute concurrently}}
183+
_ = super.superMethod // expected-error{{actor-isolated instance method 'superMethod()' can not be referenced from an '@actorIndependent' context}}
184184
acceptClosure(synchronous)
185185
acceptClosure(self.synchronous)
186186
acceptClosure(otherActor.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can only be referenced on 'self'}}
@@ -384,15 +384,15 @@ func checkLocalFunctions() async {
384384

385385
actor class LazyActor {
386386
var v: Int = 0
387-
// expected-note@-1 5 {{mutable state is only available within the actor instance}}
387+
// expected-note@-1 6 {{mutable state is only available within the actor instance}}
388388

389389
let l: Int = 0
390390

391391
lazy var l11: Int = { v }()
392392
lazy var l12: Int = v
393393
lazy var l13: Int = { self.v }()
394394
lazy var l14: Int = self.v
395-
lazy var l15: Int = { [unowned self] in self.v }()
395+
lazy var l15: Int = { [unowned self] in self.v }() // expected-error{{actor-isolated property 'v' can not be referenced from an '@actorIndependent' context}}
396396

397397
lazy var l21: Int = { l }()
398398
lazy var l22: Int = l

test/Concurrency/async_let_isolation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ actor class MyActor {
1010

1111
func testAsyncLetIsolation() async {
1212
async let x = self.synchronous()
13-
// expected-error @-1{{actor-isolated instance method 'synchronous()' is unsafe to reference in code that may execute concurrently}}
13+
// expected-error @-1{{actor-isolated instance method 'synchronous()' cannot be referenced from 'async let' initializer}}
1414

1515
async let y = await self.asynchronous()
1616

1717
async let z = synchronous()
18-
// expected-error @-1{{actor-isolated instance method 'synchronous()' is unsafe to reference in code that may execute concurrently}}
18+
// expected-error @-1{{actor-isolated instance method 'synchronous()' cannot be referenced from 'async let' initializer}}
1919

2020
var localText = text // expected-note{{var declared here}}
2121
async let w = localText.removeLast()

0 commit comments

Comments
 (0)