Skip to content

Commit d3d2d75

Browse files
authored
Merge pull request #36698 from DougGregor/actor-escaping-isolation
[Actors] Permit non-sendable, escaping closures to be actor-isolated.
2 parents 1ab4511 + a201885 commit d3d2d75

File tree

4 files changed

+34
-40
lines changed

4 files changed

+34
-40
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4394,9 +4394,6 @@ ERROR(actor_isolated_from_concurrent_function,none,
43944394
ERROR(actor_isolated_from_async_let,none,
43954395
"actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from 'async let' initializer",
43964396
(DescriptiveDeclKind, DeclName, unsigned))
4397-
ERROR(actor_isolated_from_escaping_closure,none,
4398-
"actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from an '@escaping' closure",
4399-
(DescriptiveDeclKind, DeclName, unsigned))
44004397
ERROR(actor_isolated_keypath_component,none,
44014398
"cannot form key path to actor-isolated %0 %1",
44024399
(DescriptiveDeclKind, DeclName))

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -760,16 +760,6 @@ static bool isAsyncCall(const ApplyExpr *call) {
760760
/// features.
761761
static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc);
762762

763-
/// Determine whether this closure is escaping.
764-
static bool isEscapingClosure(const AbstractClosureExpr *closure) {
765-
if (auto type = closure->getType()) {
766-
if (auto fnType = type->getAs<AnyFunctionType>())
767-
return !fnType->isNoEscape();
768-
}
769-
770-
return true;
771-
}
772-
773763
/// Determine whether this closure is escaping.
774764
static bool isSendableClosure(const AbstractClosureExpr *closure) {
775765
if (auto type = closure->getType()) {
@@ -2018,10 +2008,6 @@ namespace {
20182008
return diag::actor_isolated_from_concurrent_closure;
20192009
}
20202010

2021-
if (isEscapingClosure(closure)) {
2022-
return diag::actor_isolated_from_escaping_closure;
2023-
}
2024-
20252011
return findActorIndependentReason(dc->getParent());
20262012
}
20272013

@@ -2215,8 +2201,8 @@ namespace {
22152201
}
22162202
}
22172203

2218-
// Escaping and concurrent closures are always actor-independent.
2219-
if (isEscapingClosure(closure) || isSendableClosure(closure))
2204+
// Sendable closures are always actor-independent.
2205+
if (isSendableClosure(closure))
22202206
return ClosureActorIsolation::forIndependent();
22212207

22222208
// A non-escaping closure gets its isolation from its context.

test/Concurrency/actor_isolation.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ actor MySuperActor {
3131
var superState: Int = 25 // expected-note {{mutation of this property is only permitted within the actor}}
3232

3333

34-
func superMethod() { // expected-note 2 {{calls to instance method 'superMethod()' from outside of its actor context are implicitly asynchronous}}
34+
func superMethod() { // expected-note {{calls to instance method 'superMethod()' from outside of its actor context are implicitly asynchronous}}
3535
self.superState += 5
3636
}
3737

@@ -50,12 +50,12 @@ class Point {
5050
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
5151
actor MyActor: MySuperActor {
5252
let immutable: Int = 17
53-
// expected-note@+2 3{{property declared here}}
54-
// expected-note@+1 8{{mutation of this property is only permitted within the actor}}
53+
// expected-note@+2 2{{property declared here}}
54+
// expected-note@+1 6{{mutation of this property is only permitted within the actor}}
5555
var mutable: Int = 71
5656

5757
// expected-note@+2 3 {{mutation of this property is only permitted within the actor}}
58-
// expected-note@+1 5{{property declared here}}
58+
// expected-note@+1 4{{property declared here}}
5959
var text: [String] = []
6060

6161
let point : Point = Point()
@@ -70,7 +70,7 @@ actor MyActor: MySuperActor {
7070
class func synchronousClass() { }
7171
static func synchronousStatic() { }
7272

73-
func synchronous() -> String { text.first ?? "nothing" } // expected-note 20{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
73+
func synchronous() -> String { text.first ?? "nothing" } // expected-note 19{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
7474
func asynchronous() async -> String {
7575
super.superState += 4
7676
return synchronous()
@@ -254,15 +254,15 @@ extension MyActor {
254254
_ = otherLocalVar
255255
}
256256

257-
// Escaping closures might run concurrently.
257+
// Escaping closures are still actor-isolated
258258
acceptEscapingClosure {
259-
_ = self.text[0] // expected-error{{actor-isolated property 'text' cannot be referenced from an '@escaping' closure}}
260-
_ = self.mutable // expected-error{{actor-isolated property 'mutable' cannot be referenced from an '@escaping' closure}}
261-
self.mutable = 0 // expected-error{{actor-isolated property 'mutable' cannot be mutated from an '@escaping' closure}}
262-
acceptInout(&self.mutable) // expected-error{{actor-isolated property 'mutable' cannot be used 'inout' from an '@escaping' closure}}
259+
_ = self.text[0]
260+
_ = self.mutable
261+
self.mutable = 0
262+
acceptInout(&self.mutable)
263263
_ = self.immutable
264-
_ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' cannot be referenced from an '@escaping' closure}}
265-
_ = localVar // okay, don't complain about escaping
264+
_ = self.synchronous()
265+
_ = localVar
266266
_ = localConstant
267267
}
268268

@@ -294,7 +294,7 @@ extension MyActor {
294294

295295
// Partial application
296296
_ = synchronous // expected-error{{actor-isolated instance method 'synchronous()' can not be partially applied}}
297-
_ = super.superMethod // expected-error{{actor-isolated instance method 'superMethod()' can not be referenced from a non-isolated context}}
297+
_ = super.superMethod
298298
acceptClosure(synchronous)
299299
acceptClosure(self.synchronous)
300300
acceptClosure(otherActor.synchronous) // expected-error{{actor-isolated instance method 'synchronous()' can only be referenced on 'self'}}

test/Concurrency/closure_isolation.swift

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,29 @@
22
// REQUIRES: concurrency
33

44
func acceptClosure<T>(_: () -> T) { }
5-
func acceptEscapingClosure<T>(_: @escaping () -> T) { }
5+
func acceptSendableClosure<T>(_: @Sendable () -> T) { }
66

77
func acceptAsyncClosure<T>(_: () async -> T) { }
88
func acceptEscapingAsyncClosure<T>(_: @escaping () async -> T) { }
99

1010
actor MyActor {
1111
func method() async -> String { "" }
12+
func syncMethod() -> String { "" }
1213
}
1314

1415
extension MyActor {
1516
// CHECK-LABEL: testClosureIsolation
1617
func testClosureIsolation() async {
18+
// CHECK: acceptClosure
19+
// CHECK: closure_expr
20+
// CHECK: actor-isolated
21+
acceptClosure { self.syncMethod() }
22+
23+
// CHECK: acceptSendableClosure
24+
// CHECK: closure_expr
25+
// CHECK-NOT: actor-isolated
26+
acceptSendableClosure { print(self) }
27+
1728
// CHECK: acceptAsyncClosure
1829
// CHECK: closure_expr
1930
// CHECK-SAME: actor-isolated=closure_isolation.(file).MyActor extension.testClosureIsolation().self
@@ -22,17 +33,17 @@ extension MyActor {
2233
// CHECK: acceptAsyncClosure
2334
// CHECK: closure_expr
2435
// CHECK-NOT: actor-isolated
25-
acceptAsyncClosure { () async in print("hello") }
36+
acceptAsyncClosure { () async in print() }
2637

2738
// CHECK: acceptEscapingAsyncClosure
2839
// CHECK: closure_expr
29-
// CHECK-NOT: actor-isolated
30-
acceptEscapingAsyncClosure { await self.method() }
40+
// CHECK: actor-isolated
41+
acceptEscapingAsyncClosure { self.syncMethod() }
3142

3243
// CHECK: acceptEscapingAsyncClosure
3344
// CHECK: closure_expr
34-
// CHECK-NOT:actor-isolated
35-
acceptEscapingAsyncClosure { () async in print("hello") }
45+
// CHECK: actor-isolated
46+
acceptEscapingAsyncClosure { () async in print(self) }
3647
}
3748
}
3849

@@ -61,11 +72,11 @@ func someAsyncFunc() async { }
6172

6273
// CHECK: acceptEscapingAsyncClosure
6374
// CHECK: closure_expr
64-
// CHECK-NOT: actor-isolated
75+
// CHECK: actor-isolated
6576
acceptEscapingAsyncClosure { await someAsyncFunc() }
6677

6778
// CHECK: acceptEscapingAsyncClosure
6879
// CHECK: closure_expr
69-
// CHECK-NOT:actor-isolated
80+
// CHECK: actor-isolated
7081
acceptEscapingAsyncClosure { () async in print("hello") }
7182
}

0 commit comments

Comments
 (0)