[swift-evolution] [Draft] UnsafeRawPointer API

Dave Abrahams dabrahams at apple.com
Mon Jun 27 17:25:55 CDT 2016


on Mon Jun 27 2016, "L. Mihalkovic" <laurent.mihalkovic-AT-gmail.com> wrote:

> Regards
> (From mobile)
>
>> On Jun 27, 2016, at 8:39 AM, Dave Abrahams <dabrahams at apple.com> wrote:
>> 
>> 
>> on Fri Jun 24 2016, Andrew Trick <atrick-AT-apple.com> wrote:
>> 
>>>> On Jun 24, 2016, at 11:22 AM, Andrew Trick via swift-evolution
>>>> <swift-evolution at swift.org> wrote:
>>>> 
>>>>> On Jun 24, 2016, at 11:17 AM, L. Mihalkovic
>>>>> <laurent.mihalkovic at gmail.com
>>>>> <mailto:laurent.mihalkovic at gmail.com>> wrote:
>>>>> 
>>>>> I like the watch-what-you-wish-for warning of unsafeCast.
>>>> 
>>>> I’ll try porting stdlib to the “UnsafeRawPointer.unsafeCast(to:
>>>> T.Type)” syntax and see how bad it is.
>>> 
>>> I don't think there's a clear winner here. Let me enumerate some
>>> options.
>>> 
>>> Option (1) UnsafePointer<T>(cast: UnsafeRawPointer)
>> 
>> The problem with this one is that T can be deduced based on type
>> context.  I think we ought to move away from that for operations like
>> this one.
>> 
>>> Option (2) UnsafePointer<T>(_: UnsafeRawPointer, to: T.self)
>> 
>> I think you mean T.Type, not T.self, because this looks like a declaration.
>> 
>> To evaluate, you have to look at the use-site:
>> 
>>    let p = UnsafePointer(r, to: Int.self)
>> 
>> I don't find “to” to be descriptive enough.  Maybe
>
> toType
>
>> 
>>    let p = UnsafePointer(r, pointee: Int.self)
>
> I find pointee a total aberation :)
>
>> 
>> is better.  But I hate that the language doesn't give us a way to say
>> “don't deduce generic parameters here.”  This is the only syntax that
>> feels right, IMO:
>> 
>>    let p = UnsafePointer<Int>(r)
>> 
>>> Option (3) UnsafeRawPointer.unsafeCast<T>(to: T.Type) ->
>>> UnsafePointer<T>
>> 
>>    r.unsafeCast(to: Int.self)
>> 
>> I don't see adding “unsafe” to the name of the operation as adding
>> anything.  It isn't any more unsafe than other UnsafeRawPointer
>> operations.  
>
> It is unsafe in the sense that there are no guarantees that it is a
> sensible thing to do. 

Just like most of the other operations on UnsafeRawPointer, which is my
point.

> I guess that means it is more 'noguaranteeexplicitorimpliedapplied' in
> the sense that it will like mechanically work, even if it produce an
> aberation as a result
>
>> Also, it reads like we're casting the raw pointer to an
>> Int, rather than to an UnsafePointer<Int>.
>
> Really good one... But then instead of 'to' or 'pointee', something
> along the lines of 'wrappedType', which lookes a little less
> balerina-ish than pointee.....

A pointer does not wrap its pointee.

>>  Also, how do you get an
>> UnsafeMutablePointer?
>> 
>>> Option (4) unsafeCast(rawPointer: UnsafeRawPointer, to: T.self) ->
>>> UnsafePointer<T>
>> 
>> This one won't read correctly for the same reasons as #3.
>> 
>>    r.cast(to: UnsafePointer<Int>.self)
>> 
>> works better for me than any of the alternatives given our inability to
>> get the One True Syntax.
>> 
>>> ---
>>> Option (3) is the most explicit and searchable, and forces
>>> UnsafeRawPointer to be spelled out in the conversion (unless you
>>> already have a raw pointer).
>> 
>> Huh?  I'm confused here.  What you wrote looks like it's intended to be
>> a regular method, in which case of course invoking it would require a raw
>> pointer and wouldn't force you to write UnsafeRawPointer out anywhere.
>> 
>> The only way it could force you to write UnsafeRawPointer would be if it
>> was a static method, but in that case it has too few arguments.
>> 
>>> I like this because conceptually, you need to cast to a raw pointer
>>> before casting to a new pointee type, and casting a raw pointer to a
>>> typed pointer carries important semantics beyond simply converting to
>>> a typed pointer. The main problem with Option (3) is that optional raw
>>> pointers can't be converted naturally (without using `map`).
>> 
>>  r ?? someExpressionUsing(r!)
>> 
>> best I can do.
>> 
>>> Another thing I'm a little nervous about is confusing a cast of the
>>> pointer value with a cast of the pointee type:
>>> 
>>>  `unsafeBitCast(rawPtr, to: Int.self)`
>>> 
>>> is very different from
>>> 
>>>  `rawPtr.unsafeCast(to: Int.self)`
>>> 
>>> Does this need to be clarified?
>> 
>> Yes!
>> 
>>> If so, we can go back to the `toPointee` label that I proposed
>>> earlier.
>>> 
>>> With that in mind, Option(4) is starting to look pretty good.
>>> 
>>> Examples:
>>> 
>>> ---
>>> Case 1: casting a raw pointer as an argument
>> 
>> Use sites! (yay)...
>> 
>>> func foo(_: UnsafePointer<A>)
>>> 
>>> let rawPtr = UnsafeRawPointer(...)
>>> 
>>> (1) foo(UnsafePointer(cast: rawPtr))
>>> 
>>> (2) foo(UnsafePointer(rawPtr, to: A.self))
>>> 
>>> (3) foo(rawPtr.unsafeCast(to: A.self))
>>> 
>>> (4) foo(unsafeCast(rawPointer: rawPtr, to: A.self))
>> 
>> 
>> foo(rawPtr.cast(to: UnsafePointer<A>.self))
>> 
>>> ---
>>> Case 2: "recasting" a typed pointer argument
>>> 
>>> Note that typed pointer arguments are implicitly cast to raw pointer
>>> arguments, so the conversion from PtrB to raw is implicit.
>>> 
>>> func foo(_: UnsafePointer<A>)
>>> 
>>> let ptrB = UnsafePointer<B>(...)
>>> 
>>> (1) foo(UnsafePointer(cast: ptrB))
>>> 
>>> (2) foo(UnsafePointer(ptrB, to: A.self))
>>> 
>>> (3) foo(UnsafeRawPointer(ptrB).unsafeCast(to: A.self))
>>> 
>>> (4) foo(unsafeCast(rawPointer: ptrB, to: A.self))
>> 
>> foo(UnsafeRawPointer(ptrB).cast(to: UnsafePointer<A>.self))
>> 
>> I don't believe in making these “double-hops” concise.
>> 
>>> ---
>>> Case 3: Optional argument (only Option 3 is affected)
>>> 
>>> func nullableFoo(_: UnsafePointer<Int>?)
>>> 
>>> let ptrB: UnsafePointer<UInt>? = ...
>>> 
>>> (1) nullableFoo(UnsafePointer(cast: ptrB))
>>> 
>>> (2) nullableFoo(UnsafePointer(ptrB, to: A.self))
>>> 
>>> (3) nullableFoo(UnsafeRawPointer(ptrB).map { $0.unsafeCast(to: A.self) })
>>> 
>>> (4) nullableFoo(unsafeCast(rawPointer: ptrB, to: A.self))
>> 
>> nullableFoo(UnsafeRawPointer(ptrB)?.cast(to: UnsafePointer<A>.self))
>> 
>> You do the above with a failable init on UnsafeRawPointer that takes an
>> optional UnsafePointer.
>> 
>>> ---
>>> Case 4: Return values
>>> 
>>> func foo() -> UnsafePointer<A>
>>> 
>>> func caller() -> UnsafePointer<B> { ...
>>> 
>>> (1) return UnsafePointer(cast: foo())
>>> 
>>> (2) return UnsafePointer(foo(), to: B.self)
>>> 
>>> (3) let rawPtr = UnsafeRawPointer(foo())
>>>    return rawPtr.unsafeCast(to: B.self)
>>> 
>>> (4) return unsafeCast(rawPointer: foo(), to: B.self)
>> 
>> return UnsafeRawPointer(foo()).cast(to: UnsafePointer<B>.self)
>> 
>> IMO-ly y'rs,
>> 
>> -- 
>> -Dave

-- 
Dave


More information about the swift-evolution mailing list