> > 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 :) ) Russ
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
