> On Mar 18, 2016, at 14:54, Russ Bishop <[email protected]> wrote: > > >> On Mar 18, 2016, at 9:49 AM, Jordan Rose <[email protected] >> <mailto:[email protected]>> wrote: >> >> >>> On Mar 17, 2016, at 21:08 , Russ Bishop <[email protected] >>> <mailto:[email protected]>> wrote: >>> >>> I’m very much +1 on this idea. >>> >>>> On Mar 17, 2016, at 6:59 PM, Jordan Rose via swift-evolution >>>> <[email protected] <mailto:[email protected]>> wrote: >>>> >>>> <https://github.com/jrose-apple/swift-evolution/tree/optional-pointers#open-issue-unsafebufferpointer>Open >>>> Issue: UnsafeBufferPointer >>>> >>>> The type UnsafeBufferPointer represents a bounded typed memory region with >>>> no ownership or lifetime semantics; it is logically a bare typed pointer >>>> (its baseAddress) and a length (count). For a buffer with 0 elements, >>>> however, there's no need to provide the address of allocated memory, since >>>> it can't be read from. Previously this case would be represented as a nil >>>> base address and a count of 0. >>>> >>>> With optional pointers, this now imposes a cost on clients that want to >>>> access the base address: they need to consider the nil case explicitly, >>>> where previously they wouldn't have had to. There are several >>>> possibilities here, each with their own possible implementations: >>>> >>>> Like UnsafePointer, UnsafeBufferPointer should always have a valid base >>>> address, even when the count is 0. An UnsafeBufferPointer with a >>>> potentially-nil base address should be optional. >>>> >>>> UnsafeBufferPointer's initializer accepts an optional pointer and becomes >>>> failable, returning nil if the input pointer is nil. >>>> >>>> UnsafeBufferPointer's initializer accepts an optional pointer and >>>> synthesizes a non-null aligned pointer value if given nil as a base >>>> address. >>>> >>>> UnsafeBufferPointer's initializer only accepts non-optional pointers. >>>> Clients such as withUnsafeBufferPointermust synthesize a non-null aligned >>>> pointer value if they do not have a valid pointer to provide. >>>> >>>> UnsafeBufferPointer's initializer only accepts non-optional pointers. >>>> Clients using withUnsafeBufferPointermust handle a nil buffer. >>>> >>>> UnsafeBufferPointer should allow nil base addresses, i.e. the baseAddress >>>> property will be optional. Clients will need to handle this case >>>> explicitly. >>>> >>>> UnsafeBufferPointer's initializer accepts an optional pointer, but no >>>> other changes are made. >>>> >>>> UnsafeBufferPointer's initializer accepts an optional pointer. >>>> Additionally, any buffers initialized with a count of 0 will be >>>> canonicalized to having a base address of nil. >>>> >>>> I'm currently leaning towards option (2i). Clients that expect a pointer >>>> and length probably shouldn't require the pointer to be non-null, but if >>>> they do then perhaps there's a reason for it. It's also the least work. >>>> >>>> Chris (Lattner) is leaning towards option (1ii), which treats >>>> UnsafeBufferPointer similar to UnsafePointer while not penalizing the >>>> common case of withUnsafeBufferPointer. >>> What’s the use of an UnsafeBufferPointer with zero count? Semantically that >>> is making a claim that it can’t back up (“I have allocated memory at >>> location X” which isn’t compatible with the idea of “zero count/size"). >>> >>> Without knowing more context I’d strongly favor (1i). If an array is empty >>> the natural expectation for withUnsafeBufferPointer is you get >>> UnsafeBufferPointer<Element>? = nil, which follows the behavior of the rest >>> of the language and things like guard let make it trivial to handle >>> properly. If someone really has a problem with it they can add >>> ifUnsafeBufferPointer() that returns a non-optional pointer and skips >>> executing the closure if the Array is empty (which is the behavior of your >>> standard for loop). >> >> The important use case here is that "array.withUnsafeBufferPointer" should >> always do something (i.e. it usually can't just skip the closure), and it >> turns out it's easiest if the zero-element case is treated the same as >> everything else. When converting over the standard library I found that very >> few of them wanted to do something different in the zero-element case, and >> then it would be bad to force Array to allocate memory just to not use it. >> That is, there aren't actually any clients interested in knowing whether the >> base address is valid, and all of the ones that do have to think about it >> (because they use it directly) aren't getting any value out of it. >> >> Jordan > > Does optional chaining (ptr?.count ?? 0) or the guard check (guard let ptr = > ptr else { return }) impose a performance (or cognitive) burden here? I’m OK > with (2i), it just seems less Swift-ish than (1i). > > I don’t use UnsafeBufferPointer a lot so I’ll happily live with whatever the > choice is.
Well, yes, optional chaining is a branch, which is a minor performance cost that can't always be optimized away. The problem cases I've seen are either using a for-loop over the buffer (something that's come up on the list before) or passing the base address to a C function (which could probably be handled with ??). The main thing is just that UnsafeBufferPointer doesn't act like a pointer; it acts like a buffer, and a zero-element buffer turns out to be a perfectly useful degenerate case (and something that will happen anyway for an array with capacity reserved but no elements). In the cases where you use it as a Collection, nothing cares whether the base pointer is null. Jordan
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
