Skip to content

Commit ef0d6b2

Browse files
committed
closure isolation
1 parent 421b2f1 commit ef0d6b2

File tree

1 file changed

+114
-0
lines changed

1 file changed

+114
-0
lines changed

proposals/nnnn-closure-isolation.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Closure isolation control
2+
3+
* Proposal: [SE-NNNN](nnnn-closure-isolation.md)
4+
* Authors: [Sophia Poirier](https://github.com/sophiapoirier), [John McCall](https://github.com/rjmccall)
5+
* Review Manager: TBD
6+
* Implementation: On `main` gated behind `-enable-experimental-feature TODO`
7+
* Previous Proposals: [SE-0313](0313-actor-isolation-control.md), [SE-0316](0316-global-actors.md)
8+
* Review: ([pitch](https://forums.swift.org/TODO))
9+
10+
## Introduction
11+
12+
This proposal provides the ability to explicitly specify actor-isolation or non-isolation of a closure, as well as providing a parameter attribute to guarantee that a closure parameter inherits the isolation of the context.
13+
14+
## Motivation
15+
16+
The formal isolation of a closure can be explicitly specified as global actor isolation:
17+
18+
```swift
19+
Task { @MainActor in
20+
print("global actor isolation")
21+
}
22+
```
23+
24+
Without a global actor isolation annotation, actor-isolation or non-isolation of a closure is inferred but cannot be explicitly specified. This proposal allows closures to be fully explicit about all three types of formal isolation:
25+
* `nonisolated`
26+
* global actor
27+
* specific actor value
28+
29+
Explicit annotation has the benefit of disabling inference rules and the potential that they lead to a formal isolation that is not preferred. For example, there are circumstances where it is beneficial to guarantee that a closure is `nonisolated` therefore knowing that its execution will hop off the current actor. Explicit annotation also offers the ability to identify a mismatch of intention, such as a case where the developer expected `nonisolated` but inference landed on actor-isolated, and the closure is used in an isolated context. With explicit annotation, the developer would receive a diagnostic about a `nonisolated` closure being used in an actor-isolated context which helpfully identifies this mismatch of intention.
30+
31+
## Proposed solution
32+
33+
Enable explicit specification of non-isolation by allowing `nonisolated` to be a specifier on a closure:
34+
35+
```swift
36+
Task { nonisolated in
37+
print("nonisolated")
38+
}
39+
```
40+
41+
Enable explicit specification of actor-isolation via an isolated parameter in a closure's capture list by using the `isolated` specifier:
42+
43+
```swift
44+
actor A {
45+
func isolate() {
46+
Task { [isolated self] in
47+
print("isolated to 'self'")
48+
}
49+
}
50+
}
51+
```
52+
53+
Providing a formal replacement of the experimental parameter attribute `@_inheritActorContext` is needed to resolve another area of ambiguity with closure isolation. Its replacement `@inheritsIsolation` changes the behavior so that it unconditionally and implicitly captures the isolation context (as opposed to currently in actor-isolated contexts it being conditional on whether you capture an isolated parameter or isolated capture or actor-isolated function, but guaranteed if the context is isolated to a global actor or `nonisolated`).
54+
55+
```swift
56+
class Old {
57+
public init(@_inheritActorContext operation: () async)
58+
}
59+
60+
class New {
61+
public init(@inheritsIsolation operation: () async)
62+
}
63+
64+
class C {
65+
var value = 0
66+
67+
@MainActor
68+
func staticIsolation() {
69+
Old {
70+
value = 1 // closure is MainActor-isolated and therefore okay to access self
71+
}
72+
New {
73+
value = 2 // closure is MainActor-isolated and therefore okay to access self
74+
}
75+
}
76+
77+
func dynamicIsolation(_ actor: isolated any Actor) {
78+
Old {
79+
// not isolated to actor without explicit capture
80+
}
81+
New {
82+
// isolated to actor through guaranteed implicit capture
83+
}
84+
}
85+
}
86+
```
87+
88+
## Detailed design
89+
90+
An isolated parameter in a capture list must be of actor type, or conform to or imply an actor, potentially optional, and there can only be one isolated parameter captured, following the same rules described in [SE-0313](0313-actor-isolation-control.md#actor-isolated-parameters) for actor-isolated parameters.
91+
92+
Opting out of `@inheritsIsolation` can be achieved by explicitly annotating the closure argument as `nonisolated`.
93+
94+
`@_inheritActorContext` is currently used by the `Task` initializer in the standard library which should be updated to use `@inheritsIsolation` instead.
95+
96+
## Source compatibility
97+
98+
The language changes are additive and therefore have no implications on source compatibility. The change to `Task.init` in the standard library does have the potential to isolate some closures that previously were inferred to be `nonisolated`.
99+
100+
## ABI compatibility
101+
102+
The language change does not add or affect ABI since formal isolation is already part of a closure's type regardless of whether it is explicitly specified. The `Task.init` cahnge does not impact ABI since the function is annotated with `@_alwaysEmitIntoClient` and therefore has no ABI.
103+
104+
## Implications on adoption
105+
106+
none
107+
108+
## Alternatives considered
109+
110+
TODO
111+
112+
## Future directions
113+
114+
TODO

0 commit comments

Comments
 (0)