[swift-evolution] Pitch: Improved Swift pointers
atrick at apple.com
Tue Jul 18 13:18:55 CDT 2017
> On Jul 17, 2017, at 10:06 PM, Taylor Swift via swift-evolution <swift-evolution at swift.org> wrote:
> I’ve drafted a new version of the unsafe pointer proposal based on feedback I’ve gotten from this thread. You can read it here <https://gist.github.com/kelvin13/1b8ae906be23dff22f7a7c4767f0c907>.
> Swift’s pointer types are an important interface for low-level memory manipulation, but the current API design is not very safe, consistent, or convenient. Many memory methods demand a capacity: or count: argument, forcing the user to manually track the size of the memory block, even though most of the time this is either unnecessary, or redundant as buffer pointers track this information natively. In some places, this design turns UnsafePointers into outright DangerousPointers, leading users to believe that they have allocated or freed memory when in fact, they have not.
> The current API suffers from inconsistent naming, poor usage of default argument values, missing methods, and excessive verbosity, and encourages excessively unsafe programming practices. This proposal seeks to iron out these inconsistencies, and offer a more convenient, more sensible, and less bug-prone API for Swift pointers.
> The previous draft <https://gist.github.com/kelvin13/a9c033193a28b1d4960a89b25fbffb06> of this proposal was relatively source-breaking, calling for a separation of functionality between singular pointer types and vector (buffer) pointer types. This proposal instead separates functionality between internally-tracked length pointer types and externally-tracked length pointer types. This results in an equally elegant API with about one-third less surface area.
> <https://gist.github.com/kelvin13/1b8ae906be23dff22f7a7c4767f0c907 <https://gist.github.com/kelvin13/1b8ae906be23dff22f7a7c4767f0c907>>
> remove the capacity parameter from deallocate(capacity:) and deallocate(bytes:alignedTo:)
That's probably for the best.
> add unsized memory methods to UnsafeMutableBufferPointer
> add an assign(to:count:) method to UnsafeMutablePointer and an assign(to:) method to UnsafeMutableBufferPointer
> add a default value of 1 to all size parameters on UnsafeMutablePointer and applicable
> size parameters on UnsafeMutableRawPointer
I'm not opposed to it.
> rename copyBytes(from:count:) to copy(from:bytes:)
LGTM in the interest of consistency. I should not have caved on this the first time around.
> bytes refers to, well, a byte quantity that is not assumed to be initialized.
> capacity refers to a strided quantity that is not assumed to be initialized.
> count refers to a strided quantity that is assumed to be initialized.
That's how I see it.
> rename count in UnsafeMutableRawBufferPointer.allocate(count:) to bytes and add an
> alignedTo parameter to make it UnsafeMutableRawBufferPointer.allocate(bytes:alignedTo:)
Memory allocation is an issue unto itself. I generally prefer your
proposed API. However...
1. Larger-than-pointer alignments aren't currently respected.
2. Users virtually never want to specify the alignment explicitly. They
just want platform alignment. Unfortunately, there's no reasonable
"maximal" alignment to use as a default. I think pointer-alignment
is an excellent default guarantee.
3. The current allocation builtins seem to presume that
allocation/deallocation can be made more efficient if the user code
specifies alignment at deallocation. I don't think
UnsafeRawBufferPointer should expose that to the user, so I agree
with your proposal. In fact, I think aligned `free` should be
handled within the Swift runtime.
Resolving these issues requires changes to the Swift runtime API and
implementation. This might be a good time to revisit that design, but
it might slow down the rest of the proposal.
> fix the ordering of the arguments in initializeMemory<Element>(as:at:count:to:)
I think this ordering was an attempt to avoid confusion with binding
memory where `to` refers to a type. However, it should be consistent
with `UnsafePointer.initialize`, so we need to pick one of those to
> add the sized memorystate functions withMemoryRebound<Element, Result>(to:count:_:) to
> UnsafeMutableBufferPointer, and initializeMemory<Element>(as:at:to:count:),
> initializeMemory<Element>(as:from:count:) moveInitializeMemory<Element>(as:from:count:),
> and bindMemory<Element>(to:count:) to UnsafeMutableRawBufferPointer
> add mutable overloads to non-vacating memorystate method arguments
I'm not sure removing the need for implicit casts is a goal. I did
that with the pointer `init` methods, but now I think that should be
cleaned up to reduce API surface. I think smaller API surface wins in
these cases. Is there a usability issue you're solving?
> add a init(mutating:) initializer to UnsafeMutableBufferPointer
> remove initialize<C>(from:) from UnsafeMutablePointer
> adding an initializer UnsafeMutableBufferPointer<Element>.init(allocatingCount:) instead > of a type method to UnsafeMutableBufferPointer
For the record, I strongly prefer a type method for allocation for the reason you mention, it has important side effects beyond simply initializingn the pointer.
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the swift-evolution