[swift-evolution] [RFC] UnsafeBytePointer API for In-Memory Layout
xenadu at gmail.com
Fri May 20 00:35:40 CDT 2016
> UnsafeBytePointer API for In-Memory Layout
> UnsafePointer and UnsafeMutable refer to a typed region of memory, and the compiler must be able to assume that UnsafePointer element (Pointee) type is consistent with other access to the same memory. See proposed Type Safe Memory Access documentation <https://github.com/atrick/swift/blob/type-safe-mem-docs/docs/TypeSafeMemory.rst>. Consequently, conversion between UnsafePointer element types exposes an easy way to abuse the type system.
I don’t necessarily disagree with the proposal but I think we should clearly answer the following question:
Why doesn’t UnsafePointer<T>(_: UnsafePointer<U>) read as UnsafePointer<T>(_: UnsafePointer<Void>). That is to say you can only “type pun” through a Void pointer. A convenience method could be offered, something like UnsafePointer.reinterpretBytes<U>(_ ptr: UnsafePointer<U>, as: U.Type) -> U so all valid cases of type punning can be explicit.
> As motivation for such an API, consider that an UnsafePointer<Void> or OpaquePointer may be currently be obtained from an external API. However, the developer may know the memory layout and may want to read or write elements whose types are compatible with that layout. This a reasonable use case, but unless the developer can guarantee that all accesses to the same memory location have the same type, then they cannot use UnsafePointer to access the memory without risking undefined behavior.
IMHO if we had a @packed attribute a lot of this nonsense could be made explicit by defining a Swift struct that had the appropriate memory layout. This is how a lot of “PInvoke” stuff was done in the C# world. It also gives you an “out” if you need a very specific layout in memory for some other reason.
> Just as with unsafeBitCast, although the destination of the cast can usually be inferred, we want the developer to explicitly state the intended destination type, both because type inferrence can be surprising, and because it's important to the reader for code comprehension.
I’d definitely prefer a labelled initializer, especially one with an uncommon name. IMHO It should immediately stand out in code reviews.
> Note: For API clarity we could consider a typealias for VoidPointer. A separate VoidPointer type would not be very useful--there's no danger that UnsafeBytePointer will be casually dereferenced, and no danger in allowing pointer arithmetic since the only reasonable interpretation is that of a byte-addressable memory.
Agreed; even today messing with UnsafeMutablePointer<Void> requires you to understand that the size corresponds to bytes which is not intuitive.
> Loading from and storing to memory via an Unsafe[Mutable]BytePointer is safe independent of the type of value being loaded or stored and independent of the memory's allocated type as long as layout guarantees are met (per the ABI). This allows legal type punning within Swift and allows Swift code to access a common region of memory that may be shared across an external interface that does not provide type safety guarantees. Accessing type punned memory directly through a designated Unsafe[Mutable]BytePointer type provides sound basis for compiler implementation of strict aliasing. This is in contrast with the approach of simply providing a special unsafe pointer cast operation for bypassing type safety, which cannot be reliably implemented.
I’m not sure how to word it but I feel like some of this might help if it were included at the very beginning so people understand why this is a problem. I also think the stdlib docs should have a lot more to say about the rules, undefined behavior, and the consequences thereof. That will be all that a lot of developers ever bother to learn on the subject (a shame but out of scope for a swift evolution proposal :) )
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the swift-evolution