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
+35-85Lines changed: 35 additions & 85 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,7 +14,7 @@
14
14
We propose adding a new type constraint `~Escapable` for types that can be locally copied but cannot be assigned or transferred outside of the immediate context.
15
15
This complements the `~Copyable` types added with SE-0390 by introducing another set of compile-time-enforced lifetime controls that can be used for safe, highly-performant APIs.
16
16
17
-
In addition, these types will support lifetime-dependency constraints (being tracked in a separate proposal), that allow them to safely hold pointers referring to data stored in other types.
17
+
In addition, these types will support lifetime-dependency constraints (being tracked in a future proposal), that allow them to safely hold pointers referring to data stored in other types.
18
18
19
19
This feature is a key requirement for the proposed `Span` type.
20
20
@@ -165,7 +165,7 @@ func f() {
165
165
166
166
#### Constraints on nonescapable parameters
167
167
168
-
A value of nonescapable type received as an parameter is subject to the same constraints as any other local variable.
168
+
A value of nonescapable type received as a parameter is subject to the same constraints as any other local variable.
169
169
In particular, a nonescapable `consuming` parameter (and all direct copies thereof) must actually be destroyed during the execution of the function.
170
170
This is in contrast to an _escapable_`consuming` parameter which can be disposed of by being returned or stored to an instance property or global variable.
171
171
@@ -206,8 +206,8 @@ func f() -> NotEscapable { // 🛑 Cannot return a nonescapable type
206
206
}
207
207
```
208
208
209
-
A separate proposal describes “lifetime dependency annotations” that can relax this requirement by tying the lifetime of the returned value to the lifetime of another binding. The other binding can be a parameter of a function returning a vaule of a nonescapable type, or con be `self` for a method or computed property returning a value of a nonescapable type.
210
-
In particular, struct and enum initializers (which build a new value and return it to the caller) cannot be written without some mechanism similar to that outlined in our companion proposal.
209
+
A future proposal will describe “lifetime dependency annotations” that can relax this requirement by tying the lifetime of the returned value to the lifetime of another binding.
210
+
In particular, struct and enum initializers (which build a new value and return it to the caller) cannot be written without some such mechanism.
211
211
212
212
#### Globals and static variables cannot be nonescapable
213
213
@@ -219,7 +219,7 @@ This implies that they cannot be stored in global or static variables.
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`.
257
-
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
Now recall from SE-427 that suppressible protocols must be explicitly suppressed on type parameters in extensions.
263
-
This means that
264
-
```swift
265
-
extensionWrapper: Copyable where T:~Escapable {}
266
-
```
267
-
is exactly the same as
268
-
```swift
269
-
extensionWrapper: Copyable where T:Copyable & ~Escapable {}
270
-
```
271
-
which implies that `Wrapper` becomes `Copyable` when `T` is `Copyable`.
272
-
Finally, remember that `~Escapable` means that `Escapable` is not required, so
273
-
this condition on `Copyable` applies regardless of whether `T` is `Escapable` or not.
274
-
275
-
Similarly,
276
-
```swift
277
-
extensionWrapper: Escapable where T:~Copyable {}
278
-
```
279
-
is exactly the same as
280
-
```swift
281
-
extensionWrapper: Escapable where T:Escapable & ~Copyable {}
282
-
```
283
-
which means that `Wrapper` is `Escapable` whenever `T` is `Escapable` regardless of whether `T` is `Copyable` or not.
284
-
285
256
## Source compatibility
286
257
287
258
The compiler will treat any type without explicit `~Escapable` as escapable.
@@ -316,24 +287,36 @@ Briefly, this type would provide an efficient universal “view” of array-like
316
287
Since values of this type do not own any data but only refer to data stored elsewhere, their lifetime must be limited to not exceed that of the owning storage.
317
288
We expect to publish a sample implementation and proposal for that type very soon.
318
289
319
-
#### Lifetime dependency annotations
290
+
#### Initializers and Lifetime Dependencies
320
291
321
-
Nonescapable types have a set of inherent restrictions on how they can be passed as arguments, stored in variables, or returned from functions.
322
-
A companion proposal builds on this by supporting more detailed annotations that link the lifetimes of different objects.
323
-
This would allow, for example, a container to vend an iterator value that held a direct unmanaged pointer to the container's contents.
324
-
The lifetime dependency would ensure that such an iterator could not outlive the container to whose contents it referred.
292
+
All values come into existence within the body of some initializer and are returned to the caller of that initializer.
293
+
Since nonescapable types cannot be returned,
294
+
it follows that nonescapable types cannot have initializers without some additional language affordance.
325
295
296
+
A subsequent proposal will provide such an affordance.
297
+
This will allow values to be returned subject to the requirement that they not outlive some other specific value.
298
+
For example, a nonescapable iterator might be initialized so as to not outlive the container that created it:
326
299
```swift
327
-
// Example: Nonescaping iterator
328
-
structNEIterator {
329
-
// `dependsOn(container)` indicates that the constructed value
These lifetime dependencies will be enforced entirely at compile time without any runtime overhead.
318
+
Invalid uses such as the one above will produce compiler errors.
319
+
337
320
#### Expanding standard library types
338
321
339
322
We expect that many standard library types will need to be updated to support possibly-nonescapable types, including `Optional`, `Array`, `Set`, `Dictionary`, and the `Unsafe*Pointer` family of types.
@@ -349,7 +332,8 @@ For example, this can greatly improve the safety of locking APIs that expect to
349
332
350
333
#### Nonescapable classes
351
334
352
-
We’ve explicitly excluded class types from being nonescapable. In the future, we could allow class types to be declared nonescapable as a way to avoid most reference-counting operations on class objects.
335
+
We’ve explicitly excluded class types from being nonescapable.
336
+
In the future, we could allow class types to be declared nonescapable as a way to avoid most reference-counting operations on class objects.
353
337
354
338
#### Concurrency
355
339
@@ -407,40 +391,6 @@ The iterator example in the beginning of this document provides another motivati
407
391
Iterators are routinely copied in order to record a particular point in a collection.
408
392
Thus we concluded that non-copyable was not the correct lifetime restriction for types of this sort, and it was worthwhile to introduce a new lifetime concept to the language.
409
393
410
-
#### Returns and initializers
411
-
412
-
This proposal does not by itself provide any way to initialize a nonescapable value, requiring the additional proposed lifetime dependency annotations to support that mechanism.
413
-
Since those annotations require that the lifetime of the returned value be bound to that of one of the arguments, this implies that our current proposal does not permit nonescapable types to have trivial initializers:
414
-
415
-
```swift
416
-
struct NE:~Escapable {
417
-
init() {} // 🛑 Initializer return must depend on an argument
418
-
}
419
-
```
420
-
421
-
We considered introducing an annotation that would specifically allow this and related uses:
422
-
423
-
```swift
424
-
struct NE:~Escapable {
425
-
@_unsafeNonescapableResult
426
-
init() {} // OK because of annotation
427
-
}
428
-
```
429
-
430
-
We omitted this annotation from our proposal because there is more than one possible interpretation of such a marker. And we did not see a compelling reason for preferring one particular interpretation because we have yet to find a use case that actually requires this.
431
-
432
-
In particular, the use cases we’ve so far considered have all been resolvable by adding an argument specifically for the purpose of anchoring a lifetime dependency:
433
-
434
-
```swift
435
-
struct NE:~Escapable {
436
-
// Proposed lifetime dependency notation;
437
-
// see separate proposal for details.
438
-
init(from: SomeType) -> dependsOn(from) Self {}
439
-
}
440
-
```
441
-
442
-
We expect that future experience with nonescapable types will clarify whether additional lifetime modifiers of this sort are justified.
443
-
444
394
## Acknowledgements
445
395
446
396
Many people discussed this proposal and gave important feedback, including: Kavon Farvardin, Meghana Gupta, John McCall, Slava Pestov, Joe Groff, Guillaume Lessard, and Franz Busch.
0 commit comments