Skip to content

Commit 5ecb9eb

Browse files
committed
[Actors] Permit non-sendable, escaping closures to be actor-isolated.
Remove the heuristic that escaping closures cannot be actor-isolated. This is in line with the long term plan for actor isolation, but opens up some holes in the short term because Sendable closures are not enforced throughout the Swift ecosystem. One significant effect of this change is that we will now, statically, fail to detect many cases where actor isolation can be broken.
1 parent ad3568e commit 5ecb9eb

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
@@ -22,7 +22,7 @@ actor MySuperActor {
2222
var superState: Int = 25 // expected-note {{mutation of this property is only permitted within the actor}}
2323

2424

25-
func superMethod() { // expected-note 2 {{calls to instance method 'superMethod()' from outside of its actor context are implicitly asynchronous}}
25+
func superMethod() { // expected-note {{calls to instance method 'superMethod()' from outside of its actor context are implicitly asynchronous}}
2626
self.superState += 5
2727
}
2828

@@ -40,12 +40,12 @@ class Point {
4040

4141
actor MyActor: MySuperActor {
4242
let immutable: Int = 17
43-
// expected-note@+2 3{{property declared here}}
44-
// expected-note@+1 8{{mutation of this property is only permitted within the actor}}
43+
// expected-note@+2 2{{property declared here}}
44+
// expected-note@+1 6{{mutation of this property is only permitted within the actor}}
4545
var mutable: Int = 71
4646

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

5151
let point : Point = Point()
@@ -60,7 +60,7 @@ actor MyActor: MySuperActor {
6060
class func synchronousClass() { }
6161
static func synchronousStatic() { }
6262

63-
func synchronous() -> String { text.first ?? "nothing" } // expected-note 20{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
63+
func synchronous() -> String { text.first ?? "nothing" } // expected-note 19{{calls to instance method 'synchronous()' from outside of its actor context are implicitly asynchronous}}
6464
func asynchronous() async -> String {
6565
super.superState += 4
6666
return synchronous()
@@ -241,15 +241,15 @@ extension MyActor {
241241
_ = otherLocalVar
242242
}
243243

244-
// Escaping closures might run concurrently.
244+
// Escaping closures are still actor-isolated
245245
acceptEscapingClosure {
246-
_ = self.text[0] // expected-error{{actor-isolated property 'text' cannot be referenced from an '@escaping' closure}}
247-
_ = self.mutable // expected-error{{actor-isolated property 'mutable' cannot be referenced from an '@escaping' closure}}
248-
self.mutable = 0 // expected-error{{actor-isolated property 'mutable' cannot be mutated from an '@escaping' closure}}
249-
acceptInout(&self.mutable) // expected-error{{actor-isolated property 'mutable' cannot be used 'inout' from an '@escaping' closure}}
246+
_ = self.text[0]
247+
_ = self.mutable
248+
self.mutable = 0
249+
acceptInout(&self.mutable)
250250
_ = self.immutable
251-
_ = self.synchronous() // expected-error{{actor-isolated instance method 'synchronous()' cannot be referenced from an '@escaping' closure}}
252-
_ = localVar // okay, don't complain about escaping
251+
_ = self.synchronous()
252+
_ = localVar
253253
_ = localConstant
254254
}
255255

@@ -281,7 +281,7 @@ extension MyActor {
281281

282282
// Partial application
283283
_ = synchronous // expected-error{{actor-isolated instance method 'synchronous()' can not be partially applied}}
284-
_ = super.superMethod // expected-error{{actor-isolated instance method 'superMethod()' can not be referenced from a non-isolated context}}
284+
_ = super.superMethod
285285
acceptClosure(synchronous)
286286
acceptClosure(self.synchronous)
287287
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+
// CHEC: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)