Skip to content

Commit 6ce4bea

Browse files
authored
Merge pull request #60688 from theblixguy/defer-isolation-closures
[Concurrency] Handle actor isolation for defers in closures
2 parents 553dc20 + dd668d3 commit 6ce4bea

File tree

3 files changed

+104
-21
lines changed

3 files changed

+104
-21
lines changed

lib/AST/Decl.cpp

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9212,14 +9212,20 @@ ActorIsolation swift::getActorIsolation(ValueDecl *value) {
92129212
}
92139213

92149214
ActorIsolation swift::getActorIsolationOfContext(DeclContext *dc) {
9215-
if (auto *vd = dyn_cast_or_null<ValueDecl>(dc->getAsDecl()))
9215+
auto dcToUse = dc;
9216+
// Defer bodies share actor isolation of their enclosing context.
9217+
if (auto FD = dyn_cast<FuncDecl>(dcToUse)) {
9218+
if (FD->isDeferBody()) {
9219+
dcToUse = FD->getDeclContext();
9220+
}
9221+
}
9222+
if (auto *vd = dyn_cast_or_null<ValueDecl>(dcToUse->getAsDecl()))
92169223
return getActorIsolation(vd);
92179224

9218-
if (auto *var = dc->getNonLocalVarDecl())
9219-
return getActorIsolation(var);
9220-
9225+
if (auto *var = dcToUse->getNonLocalVarDecl())
9226+
return getActorIsolation(var);
92219227

9222-
if (auto *closure = dyn_cast<AbstractClosureExpr>(dc)) {
9228+
if (auto *closure = dyn_cast<AbstractClosureExpr>(dcToUse)) {
92239229
switch (auto isolation = closure->getActorIsolation()) {
92249230
case ClosureActorIsolation::Independent:
92259231
return ActorIsolation::forIndependent()
@@ -9243,14 +9249,14 @@ ActorIsolation swift::getActorIsolationOfContext(DeclContext *dc) {
92439249
}
92449250
}
92459251

9246-
if (auto *tld = dyn_cast<TopLevelCodeDecl>(dc)) {
9247-
if (dc->isAsyncContext() ||
9248-
dc->getASTContext().LangOpts.StrictConcurrencyLevel
9249-
>= StrictConcurrency::Complete) {
9250-
if (Type mainActor = dc->getASTContext().getMainActorType())
9252+
if (auto *tld = dyn_cast<TopLevelCodeDecl>(dcToUse)) {
9253+
if (dcToUse->isAsyncContext() ||
9254+
dcToUse->getASTContext().LangOpts.StrictConcurrencyLevel >=
9255+
StrictConcurrency::Complete) {
9256+
if (Type mainActor = dcToUse->getASTContext().getMainActorType())
92519257
return ActorIsolation::forGlobalActor(
92529258
mainActor,
9253-
/*unsafe=*/!dc->getASTContext().isSwiftVersionAtLeast(6));
9259+
/*unsafe=*/!dcToUse->getASTContext().isSwiftVersionAtLeast(6));
92549260
}
92559261
}
92569262

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,6 +1522,7 @@ static FuncDecl *findAnnotatableFunction(DeclContext *dc) {
15221522
}
15231523

15241524
/// Note when the enclosing context could be put on a global actor.
1525+
// FIXME: This should handle closures too.
15251526
static void noteGlobalActorOnContext(DeclContext *dc, Type globalActor) {
15261527
// If we are in a synchronous function on the global actor,
15271528
// suggest annotating with the global actor itself.
@@ -3832,14 +3833,6 @@ ActorIsolation ActorIsolationRequest::evaluate(
38323833
// If this is a local function, inherit the actor isolation from its
38333834
// context if it global or was captured.
38343835
if (auto func = dyn_cast<FuncDecl>(value)) {
3835-
// If this is a defer body, inherit unconditionally; we don't
3836-
// care if the enclosing function captures the isolated parameter.
3837-
if (func->isDeferBody()) {
3838-
auto enclosingIsolation =
3839-
getActorIsolationOfContext(func->getDeclContext());
3840-
return inferredIsolation(enclosingIsolation);
3841-
}
3842-
38433836
if (func->isLocalCapture() && !func->isSendable()) {
38443837
switch (auto enclosingIsolation =
38453838
getActorIsolationOfContext(func->getDeclContext())) {

test/Concurrency/actor_defer.swift

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
func doSomething() {}
77

8-
// expected-note @+1 4 {{calls to global function 'requiresMainActor()' from outside of its actor context are implicitly asynchronous}}
8+
// expected-note @+1 6 {{calls to global function 'requiresMainActor()' from outside of its actor context are implicitly asynchronous}}
99
@MainActor func requiresMainActor() {}
1010

1111
@MainActor func testNonDefer_positive() {
@@ -66,7 +66,7 @@ func testGlobalActorAsync_negative() async {
6666

6767
@available(SwiftStdlib 5.1, *)
6868
actor Actor {
69-
// expected-note @+1 3 {{mutation of this property is only permitted within the actor}}
69+
// expected-note @+1 6 {{mutation of this property is only permitted within the actor}}
7070
var actorProperty = 0
7171

7272
func testActor_positive() {
@@ -76,6 +76,13 @@ actor Actor {
7676
doSomething()
7777
}
7878

79+
func testActor_task_positive() {
80+
Task {
81+
defer { actorProperty += 1 }
82+
doSomething()
83+
}
84+
}
85+
7986
#if NEGATIVES
8087
nonisolated func testActor_negative() {
8188
defer {
@@ -84,13 +91,30 @@ actor Actor {
8491
}
8592
doSomething()
8693
}
94+
95+
nonisolated func testActor_task_negative() {
96+
Task {
97+
// expected-error @+1 {{actor-isolated property 'actorProperty' can not be mutated from a non-isolated context}}
98+
defer { actorProperty += 1 }
99+
doSomething()
100+
}
101+
}
102+
87103
@MainActor func testActor_negative_globalActor() {
88104
defer {
89105
// expected-error @+1 {{actor-isolated property 'actorProperty' can not be mutated from the main actor}}
90106
actorProperty += 1
91107
}
92108
doSomething()
93109
}
110+
111+
func testActor_task_negative_globalActor() {
112+
Task { @MainActor in
113+
// expected-error @+1 {{actor-isolated property 'actorProperty' can not be mutated from the main actor}}
114+
defer { actorProperty += 1 }
115+
doSomething()
116+
}
117+
}
94118
#endif
95119

96120
@MainActor func testGlobalActor_positive() {
@@ -99,6 +123,13 @@ actor Actor {
99123
}
100124
doSomething()
101125
}
126+
127+
func testGlobalActor_task_positive() {
128+
Task { @MainActor in
129+
defer { requiresMainActor() }
130+
doSomething()
131+
}
132+
}
102133

103134
#if NEGATIVES
104135
func testGlobalActor_negative() {
@@ -108,6 +139,14 @@ actor Actor {
108139
}
109140
doSomething()
110141
}
142+
143+
func testGlobalActor_task_negative() {
144+
Task {
145+
// expected-error @+1 {{call to main actor-isolated global function 'requiresMainActor()' in a synchronous nonisolated context}}
146+
defer { requiresMainActor() }
147+
doSomething()
148+
}
149+
}
111150
#endif
112151
}
113152

@@ -130,3 +169,48 @@ func testIsolatedActor_negative(actor: Actor) {
130169
doSomething()
131170
}
132171
#endif
172+
173+
@available(SwiftStdlib 5.1, *)
174+
func testGlobalActor_inTask_positive() {
175+
Task { @MainActor in
176+
defer { requiresMainActor() }
177+
doSomething()
178+
}
179+
}
180+
181+
#if NEGATIVES
182+
@available(SwiftStdlib 5.1, *)
183+
func testGlobalActor_inTask_negative() {
184+
Task {
185+
// expected-error @+1 {{call to main actor-isolated global function 'requiresMainActor()' in a synchronous nonisolated context}}
186+
defer { requiresMainActor() }
187+
doSomething()
188+
}
189+
}
190+
#endif
191+
192+
@available(SwiftStdlib 5.1, *)
193+
func takeClosureWithIsolatedParam(body: (isolated Actor) -> Void) {}
194+
195+
@available(SwiftStdlib 5.1, *)
196+
func takeClosureWithNotIsolatedParam(body: (Actor) -> Void) {}
197+
198+
@available(SwiftStdlib 5.1, *)
199+
func testIsolatedActor_closure_positive() {
200+
takeClosureWithIsolatedParam { actor in
201+
actor.actorProperty += 1
202+
defer { actor.actorProperty += 1 }
203+
doSomething()
204+
}
205+
}
206+
207+
#if NEGATIVES
208+
@available(SwiftStdlib 5.1, *)
209+
func testIsolatedActor_closure_negative() {
210+
takeClosureWithNotIsolatedParam { actor in
211+
// expected-error @+1 {{actor-isolated property 'actorProperty' can not be mutated from a non-isolated context}}
212+
defer { actor.actorProperty += 1 }
213+
doSomething()
214+
}
215+
}
216+
#endif

0 commit comments

Comments
 (0)