I want to provide some context for a just-merged change to the standard library: [stdlib] ~Escapable raw pointer access: API adoption in URP, UMRP, URBP, UMRBP
#84701.
This PR is the minimal support we need in the standard library to experiment with ~Escapable storage. It simply adds a way to store ~Escapable types to memory via the raw pointer APIs: UnsafeRawPointer and UnsafeMutableRawPointer.
[SE-0446] introduced nonescapable types in Swift making it possible to build lifetime-dependent data types directly on top of unsafe compiler builtins using the experimental Lifetimes feature. This allowed the standard library to provide Span<T>, Borrow<T>, and Inout<T>. Developers have also been building their own ~Escapable types under the experimental Lifetimes feature. And yet it remains impossible to store these types to memory even using experimental features and unsafe constructs.
We're now at a point where new data types are being proposed that either fundamentally rely on Span or that simply want to support generic ~Escapable elements early on in the design. Any type with ~Escapable elements that uses heap-allocated storage under the hood needs basic pointer support for loading and storing its element. Most notably that includes Span itself. Proposals for "Span<~Escapable>" and BorrowingSequence are in the making. I expect that to be followed later by proposals for data types that take ownership of their elements, such as "InlineArray<~Escapable>", "UniqueArray<~Escapable>", etc.
Accessing a ~Escapable value via a raw pointer provides no lifetime safety. It's up to the library author to override the lifetime of any loaded value before it can be returned from an API:
@safe
struct Wrapper<T: ~Escapable & BitwiseCopyable>: ~Escapable {
let p: UnsafeMutableRawPointer
public subscript() -> T {
get {
return unsafe _overrideLifetime(p.load(as: T.self), copying: self)
}
set {
unsafe p.storeBytes(of: newValue, as: T.self)
}
}
}
extension Wrapper: Escapable where T: Escapable {}
Ultimately, we don't expect programmers to work directly with this raw pointer interface. It is initially required for bootstrapping basic container types and view types. We could have also added support for UnsafePointer<~Escapable>. That would add a lot of convenience for library authors, but it also raises a new question: typed pointers could preserve the lifetime of their non-Escapable element. It's worth considering because that would obviate the need for _overrideLifetime when accessing the elements. With raw pointers, there's simply no way around that. Those semantics should be discussed as a separate proposal. We'll defer that discussion for the time being to focus on other more urgent proposals which only require raw pointer support.