on Mon Jun 27 2016, Andrew Trick <atrick-AT-apple.com> wrote: >> On Jun 27, 2016, at 7:15 PM, Dave Abrahams <[email protected]> wrote: >> >> >> >> Sent from my moss-covered three-handled family gradunza > >> >> On Jun 27, 2016, at 4:27 PM, Andrew Trick <[email protected] >> <mailto:[email protected]>> wrote: >> >>> >>>> On Jun 27, 2016, at 3:35 PM, Dave Abrahams <[email protected] >>>> <mailto:[email protected]>> wrote: >>>> >>>>> Casting from a raw pointer to a typed pointer is only more dangerous >>>>> than other raw pointer operations because it is the first step in this >>>>> sequence of operations, which is undefined: >>>>> >>>>> ptrA = rawPtr.cast(to: UnsafePointer<A>.self) >>>>> ptrA.initialize(with: A()) >>>>> ptrA.deinitialize() >>>>> >>>>> ptrB = rawPtr.cast(to: UnsafePointer<B>.self) >>>>> ptrB.initialize(with: B()) >>>> >>>> But it's trivial to get undefined behavior without any of that. Just: >>>> >>>> _ = rawPtr.load(UnsafePointer<NonTrivialType>.self) >>> >>> That's another way to obtain a typed pointer, but by itself it is well >>> defined. >> >> Sorry, I meant to dereference that typed pointer as part of the expression. >> Now boom. > > Loading an UnsafePointer<T> from the contents of the raw pointer’s > memory is fine if that’s what the memory contains. Dereferencing the > loaded typed pointer is fine if that memory has been previously > initialized with value of NonTrivialType.
Yes of course. The point was that rawPtr points to uninitialized memory. > Also, loading NonTrivialType directly from a raw pointer is fine if > the memory contains an initialized NonTrivialType. This is fine: > > let v = rawPtr.load(NonTrivialType.self) > > It returns an initialized `v` (a proper copy of the value in memory). > >>> This is an important point, so I want to make sure I’m getting it across. >>> >>> The following code is well-defined: >>> ``` >>> ptrA = rawPtr.initialize(with: A()) >>> ptrA.deinitialize() >>> ptrB = rawPtr.initialize(with: B()) >>> ``` >>> The following code is undefined: >>> ``` >>> ptrA = rawPtr.cast(to: UnsafePointer<A>.self) >>> ptrA.initialize(with: A()) >>> ptrA.deinitialize() >>> ptrB = rawPtr.cast(to: UnsafePointer<B>.self) >>> ptrB.initialize(with: B()) >>> ``` >>> It is hard to spot the difference between the two styles without >>> drawing attention to the unsafe cast. >>> >> How is that substantially different from my example? > > There are different sources of memory unsafety. One rule is that > (roughly) when memory is dereferenced as a type it should contain an > initialized value of that type (or related type). As long as users > follow this very intuitive rule, and don’t force-cast into a typed > pointer, then the raw pointer API is very safe! I still maintain that using cast to get an UnsafePointer that you'll dereference is no less safe than using load to get an UnsafePointer that you'll dereference. > Another source of memory unsafety is strict aliasing. This is not intuitive. > > I am showing an example in which the memory location is never accessed > using a type that is inconsistent with its initialized value. There is > nothing wrong with the order and type of the initialization, access, > deinitialization operations, so the user has followed the first, > intuitive rule. The only thing wrong with my example is that the > initialization uses a typed pointer. You can’t have two > initializations of the same memory both using a typed pointer but with > unrelated types. > > All of this presumes that `A` and `B` are layout compatible (same alignment > and size). > > It’s true that the user could run into the same problem if they obtain > a typed pointer via unsafeBitCast or > rawPtr.load(UnsafePointer<T>). But we don’t want to provide an API > explicitly for converting raw pointers into typed pointers without > indicating that it opens up a new safety hole… > > The *safe* way to get a typed pointer is this: > let ptrA = rawPtr.initialize(A.self, with: A()) > > If you’re getting a typed pointer any other way then you need to be > aware of the strict aliasing rules, which I suspect most users will > not want to think about. > > So, UnsafeRawPointer.cast(to: UnsafePointer<T>.Type) is *not* as safe > as any other operation because it opens up a very subtle issue > involving strict aliasing. IMO load is even worse, because there might not even be a valid pointer of any kind in the address you're loading. I just don't see a fundamental distinction here. -- Dave _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
