-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Non-Escapable Types #2304
base: main
Are you sure you want to change the base?
Non-Escapable Types #2304
Conversation
Briefly, non-escapable types are types that cannot "escape" the local execution context. These types can also participate in extended lifetime dependency constraints. This enables compile-time lifetime enforcement for values that contain pointers into other values. This makes it possible to implement containers that require no runtime lifetime management of their iterators or slice objects. In particular, this is a foundational technology for BufferView.
| // Example: Local variable with non-escapable type | ||
| func borrowingFunc(_: borrowing NotEscapable) { ... } | ||
| func consumingFunc(_: consuming NotEscapable) { ... } | ||
| func inoutFunc(_: inout NotEscapable) { ... } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be good to add a case for async functions and async let here, it's barely mentioned in the proposal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll see what I can do about including async examples.
Until then, note that the comments about functions apply to all functions, regardless of whether they are async or not, throwing or not.
Similarly, async let guarantees that the called function will return before the current function completes, so it's safe to pass a ~Escapable value to a function called with async let.
| These "lifetime dependency" constraints can also be verified at compile time to ensure that the source of the iterator is not modified and that the iterator specifically does not outlive its source. | ||
|
|
||
| **Note**: We are using iterators here to illustrate the issues we are considering. | ||
| We are not at this time proposing any changes to Swift's current `Iterator` protocol. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| We are not at this time proposing any changes to Swift's current `Iterator` protocol. | |
| We are not at this time proposing any changes to Swift's current `IteratorProtocol` construct. |
| struct Wrapper<T: ~Copyable & ~Escapable> { ... } | ||
| extension Wrapper: Copyable where T: ~Escapable {} | ||
| extension Wrapper: Escapable where T: ~Copyable {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The review notes of SE-0427 imply that all suppressed quasi-protocols need to be explicitly spelled out:
| struct Wrapper<T: ~Copyable & ~Escapable> { ... } | |
| extension Wrapper: Copyable where T: ~Escapable {} | |
| extension Wrapper: Escapable where T: ~Copyable {} | |
| struct Wrapper<T: ~Copyable & ~Escapable>: ~Copyable, ~Escapable { ... } | |
| extension Wrapper: Copyable where T: Copyable & ~Escapable {} | |
| extension Wrapper: Escapable where T: ~Copyable & Escapable {} |
| ``` | ||
|
|
||
| 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`. | ||
| 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not think it would be desirable to have such inference rules. I would prefer to understand type declarations at a glance; I would not enjoy being forced to solve clever little cross-referencing puzzles to understand the nature of a type.
It is extremely noteworthy for a type not to be copyable or escapable. These suppressions deserve to be explicitly noted directly on the type's primary declaration, not inferred from context hidden elsewhere in the source file.
| Now recall from SE-427 that suppressible protocols must be explicitly suppressed on type parameters in extensions. | ||
| This means that | ||
| ```swift | ||
| extension Wrapper: Copyable where T: ~Escapable {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per SE-0427, this should be an error; the conditional conformance needs to explicitly declare whether it wants T to be copiable. (I.e., the only legal way to spell the conditional conformance is to use the long form below.)
|
|
||
| Similarly, | ||
| ```swift | ||
| extension Wrapper: Escapable where T: ~Copyable {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here; SE-0427 implies that this should be an error.
|
|
||
| ## Future directions | ||
|
|
||
| #### `StorageView` type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| #### `StorageView` type | |
| #### `Span` family of types |
|
|
||
| #### `StorageView` type | ||
|
|
||
| This proposal is being driven in large part by the needs of the `StorageView` type that has been discussed elsewhere. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| This proposal is being driven in large part by the needs of the `StorageView` type that has been discussed elsewhere. | |
| This proposal is being driven in large part by the needs of the `Span` type that has been discussed elsewhere. |
|
|
||
| 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. | ||
|
|
||
| This feature is a key requirement for the proposed `StorageView` type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| This feature is a key requirement for the proposed `StorageView` type. | |
| This feature is a key requirement for the proposed `Span` family of types. |
This is the first of two proposals covering this new feature:
It propose an
Escapabletype marker to indicate that a value of this type can escape the local scope -- by being stored in a global or returned, for example.This new marker would be implicitly satisfied by all current Swift types to avoid breaking existing code and to reflect the expectation that most types can in fact be freely copied and assigned without limit.
We then add a new
~Escapabletype requirement that can be used to selectively remove this capability.We explain the motivation for such types and the basic capabilities and restrictions on their use.
The companion proposal "Lifetime Dependency Constraints for ~Escapable Values" proposes a set of annotations that selectively modify the constraints of
~Escapabletypes so they can be used to provide accurate compile-time tracking of values that are inherently subsidiary to some other value (for example, iterators over collections or slices that must not outlive the container to which they refer).These are part of the BufferView roadmap and are prerequisites for that type.