You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: proposals/NNNN-non-escapable.md
+67-29Lines changed: 67 additions & 29 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -41,8 +41,8 @@ In addition, the use of reference counting to ensure correctness at runtime make
41
41
## Proposed solution
42
42
43
43
Currently, the notion of "escapability" appears in the Swift language as a feature of closures.
44
-
Closures that are declared as `@nonescapable` can use a very efficient stack-based representation;
45
-
closures that are `@escapable` store their state on the heap.
44
+
Nonescapable closures can use a very efficient stack-based representation;
45
+
closures that are `@escapable` store their captures on the heap.
46
46
47
47
By allowing Swift developers to mark various types as nonescapable, we provide a mechanism for them to opt into a specific set of usage limitations that:
48
48
@@ -64,7 +64,7 @@ We are not at this time proposing any changes to Swift's current `Iterator` prot
64
64
65
65
#### New Escapable Concept
66
66
67
-
We add a new suppressible type constraint `Escapable` to the standard library and implicitly apply it to all current Swift types (with the sole exception of `@nonescapable` closures).
67
+
We add a new suppressible protocol `Escapable` to the standard library and implicitly apply it to all current Swift types (with the sole exception of nonescapable closures).
68
68
`Escapable` types can be assigned to global variables, passed into arbitrary functions, or returned from the current function or closure.
69
69
This matches the existing semantics of all Swift types prior to this proposal.
A nonescapable type is not allowed to escape the local context:
89
+
A nonescapable value is not allowed to escape the local context:
90
90
```swift
91
91
// Example: Basic limits on ~Escapable types
92
92
funcf() -> NotEscapable {
@@ -98,10 +98,10 @@ func f() -> NotEscapable {
98
98
}
99
99
```
100
100
101
-
**Note**: The inability to return a nonescapable type has implications for how initializers must be written.
102
-
The section "Returned nonescapable values require lifetime dependency" has more details.
101
+
**Note**:
102
+
The section "Returned nonescapable values require lifetime dependency" explains the implications for how you must write initializers.
103
103
104
-
Without a `~Escapable` marker, the default for any type is to be escapable. Since `~Escapable`indicates the lack of a capability, you cannot put this in an extension.
104
+
Without `~Escapable`, the default for any type is to be escapable. Since `~Escapable`suppresses a capability, you cannot put this in an extension.
globalVar = value // 🛑 Cannot assign possibly-nonescapable type to a global var
@@ -128,21 +126,6 @@ f(NotEscapable()) // Ok to call with nonescapable argument
128
126
f(7) // Ok to call with escapable argument
129
127
```
130
128
131
-
This also permits the definition of types whose escapability varies depending on their generic arguments.
132
-
As with other conditional behaviors, this is expressed by using an extension to conditionally add a new capability to the type:
133
-
134
-
```swift
135
-
// Example: Conditionally Escapable generic type
136
-
// By default, Box is itself nonescapable
137
-
structBox<T: ~Escapable>: ~Escapable {
138
-
var t: T
139
-
}
140
-
141
-
// Box gains the ability to escape whenever its
142
-
// generic argument is Escapable
143
-
extensionBox: Escapable when T: Escapable { }
144
-
```
145
-
146
129
[SE-0427 Noncopyable Generics](https://github.com/apple/swift-evolution/blob/main/proposals/0427-noncopyable-generics.md) provides more detail on
147
130
how suppressible protocols such as `Escapable` are handled in the generic type system.
148
131
@@ -238,9 +221,64 @@ All of the requirements on use of nonescapable values as function arguments and
238
221
239
222
The closures used in `Task.init`, `Task.detached`, or `TaskGroup.addTask` are escaping closures and therefore cannot capture nonescapable values.
240
223
224
+
#### Conditionally `Escapable` types
225
+
226
+
You can define types whose escapability varies depending on their generic arguments.
227
+
As with other conditional behaviors, this is expressed by using an extension to conditionally add a new capability to the type:
228
+
229
+
```swift
230
+
// Example: Conditionally Escapable generic type
231
+
// By default, Box is itself nonescapable
232
+
structBox<T: ~Escapable>: ~Escapable {
233
+
var t: T
234
+
}
235
+
236
+
// Box gains the ability to escape whenever its
237
+
// generic argument is Escapable
238
+
extensionBox: Escapable when T: Escapable { }
239
+
```
240
+
241
+
This can be used in conjunction with other suppressible protocols.
242
+
For example, many general library types will need to be copyable and/or escapable following their contents.
243
+
Here's a compact way to declare such a type:
244
+
```swift
245
+
structWrapper<T: ~Copyable &~Escapable> { ... }
246
+
extensionWrapper: Copyable where T:~Escapable {}
247
+
extensionWrapper: Escapable where T:~Copyable {}
248
+
```
249
+
250
+
The above declarations all in a single source file will result in a type `Wrapper` that is `Escapable` exactly when `T` is `Escapable` and `Copyable` exactly when `T` is `Copyable`.
251
+
To see why, first note that the explicit `extension Wrapper: Escapable` in the same source file implies that the original `struct Wrapper` must be `~Escapable` and similarly for `Copyable`, exactly as if the first line had been
0 commit comments