I've recorded everyone's feedback so far (mostly on the UnsafeBufferPointer 
issue) and opened a pull request: 
https://github.com/apple/swift-evolution/pull/219. Thanks to everyone who's 
contributed to the draft!

Jordan

> On Mar 17, 2016, at 18:59 , Jordan Rose via swift-evolution 
> <[email protected]> wrote:
> 
> Hey, everyone. If you're like me, you're sick of the fact that 
> 'UnsafePointer<Int>' doesn't tell you whether or not the pointer can be nil. 
> Why do we need to suffer this indignity when reference types—including 
> function pointers!—can distinguish "present" from "absent" with the standard 
> type 'Optional'? Well, good news: here's a proposal to make pointer 
> nullability explicit. 'UnsafePointer<Int>?' can be null (nil), while 
> 'UnsafePointer<Int>' cannot. Read on for details!
> 
> https://github.com/jrose-apple/swift-evolution/blob/optional-pointers/proposals/nnnn-optional-pointers.md
>  
> <https://github.com/jrose-apple/swift-evolution/blob/optional-pointers/proposals/nnnn-optional-pointers.md>
> 
> Bonus good news: I've implemented this locally and updated nearly all the 
> tests already. Assuming this is accepting, the actual changes will go through 
> review as a PR on GitHub, although it's mostly going to be one big mega-patch 
> because the core change has a huge ripple effect.
> 
> Jordan
> 
> ---
> 
> Make pointer nullability explicit using Optional
> 
> Proposal: SE-NNNN 
> <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
> Author(s): Jordan Rose <https://github.com/jrose-apple>
> Status: Awaiting review
> Review manager: TBD
>  
> <https://github.com/jrose-apple/swift-evolution/tree/optional-pointers#introduction>Introduction
> 
> In Objective-C, pointers (whether to objects or to a non-object type) can be 
> marked as nullable or nonnull, depending on whether the pointer value can 
> ever be null. In Swift, however, there is no such way to make this 
> distinction for pointers to non-object types: an UnsafePointer<Int> might be 
> null, or it might never be.
> 
> We already have a way to describe this: Optionals. This proposal makes 
> UnsafePointer<Int> represent a non-nullable pointer, and UnsafePointer<Int>? 
> a nullable pointer. This also allows us to preserve information about pointer 
> nullability available in header files for imported C and Objective-C APIs.
> 
>  
> <https://github.com/jrose-apple/swift-evolution/tree/optional-pointers#motivation>Motivation
> 
> Today, UnsafePointer and friends suffer from a problem inherited from C: 
> every pointer value could potentially be null, and code that works with 
> pointers may or may not expect this. Failing to take the null pointer case 
> into account can lead to assertion failures or crashes. For example, pretty 
> much every operation on UnsafePointer itself requires a valid pointer 
> (reading, writing, and initializing the pointee or performing arithmetic 
> operations).
> 
> Fortunately, when a type has a single invalid value for which no operations 
> are valid, Swift already has a solution: Optionals. Applying this to pointer 
> types makes things very clear: if the type is non-optional, the pointer will 
> never be null, and if it isoptional, the developer must take the "null 
> pointer" case into account. This clarity has already been appreciated in 
> Apple's Objective-C headers, which include nullability annotations for all 
> pointer types (not just object pointers).
> 
> This change also allows developers working with pointers to take advantage of 
> the many syntactic conveniences already built around optionals. For example, 
> the standard library currently has a helper method on UnsafeMutablePointer 
> called _setIfNonNil; with "optional pointers" this can be written simply and 
> clearly:
> 
> ptr?.pointee = newValue
> Finally, this change also reduces the number of types that conform to 
> NilLiteralConvertible, a source of confusion for newcomers who (reasonably) 
> associate nil directly with optionals. Currently the standard library 
> includes the following NilLiteralConvertible types:
> 
> Optional
> ImplicitlyUnwrappedOptional (subject of a separate proposal by Chris Willmore)
> _OptionalNilComparisonType (used for optionalValue == nil)
> UnsafePointer
> UnsafeMutablePointer
> AutoreleasingUnsafeMutablePointer
> OpaquePointer
> plus these Objective-C-specific types:
> 
> Selector
> NSZone (only used to pass nil in Swift)
> All of the italicized types would drop their conformance to 
> NilLiteralConvertible; the "null pointer" would be represented by a nil 
> optional of a particular type.
> 
>  
> <https://github.com/jrose-apple/swift-evolution/tree/optional-pointers#proposed-solution>Proposed
>  solution
> 
> Have the compiler assume that all values with pointer type (the italicized 
> types listed above) are non-null. This allows the representation of 
> Optional.none for a pointer type to be a null pointer value.
> 
> Drop NilLiteralConvertible conformance for all pointer types.
> 
> Teach the Clang importer to treat _Nullable pointers as Optional (and 
> _Null_unspecified pointers as ImplicitlyUnwrappedOptional).
> 
> Deal with the fallout, i.e. adjust the compiler and the standard library to 
> handle this new behavior.
> 
> Test migration and improve the migrator as necessary.
> 
> This proposal does not include the removal of the NilLiteralConvertible 
> protocol altogether; besides still having two distinct optional types, we've 
> seen people wanting to use nil for their own types (e.g. JSON values). 
> (Changing this in the future is not out of the question; it's just out of 
> scope for this proposal.)
> 
>  
> <https://github.com/jrose-apple/swift-evolution/tree/optional-pointers#detailed-design>Detailed
>  design
> 
>  
> <https://github.com/jrose-apple/swift-evolution/tree/optional-pointers#api-changes>API
>  Changes
> 
> Conformance to NilLiteralConvertible is removed from all types except 
> Optional, ImplicitlyUnwrappedOptional, and _OptionalNilComparisonType, along 
> with the implementation of init(nilLiteral:).
> 
> init(bitPattern: Int) and init(bitPattern: UInt) on all pointer types become 
> failable; if the bit pattern represents a null pointer, nil is returned.
> 
> Process.unsafeArgv is a pointer to a null-terminated C array of C strings, so 
> its type changes from UnsafeMutablePointer<UnsafeMutablePointer<Int8>> to 
> UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>, i.e. the inner pointer 
> type becomes optional. It is then an error to access Process.unsafeArgv 
> before entering main. (Previously you would get a null pointer value.)
> 
> NSErrorPointer becomes optional:
> 
> -public typealias NSErrorPointer = AutoreleasingUnsafeMutablePointer<NSError?>
> +public typealias NSErrorPointer = 
> AutoreleasingUnsafeMutablePointer<NSError?>?
> A number of methods on String that came from NSString now have optional 
> parameters:
>    public func completePathIntoString(
> -    outputName: UnsafeMutablePointer<String> = nil,
> +    outputName: UnsafeMutablePointer<String>? = nil,
>      caseSensitive: Bool,
> -    matchesIntoArray: UnsafeMutablePointer<[String]> = nil,
> +    matchesIntoArray: UnsafeMutablePointer<[String]>? = nil,
>      filterTypes: [String]? = nil
>    ) -> Int {
>    public init(
>      contentsOfFile path: String,
> -    usedEncoding: UnsafeMutablePointer<NSStringEncoding> = nil
> +    usedEncoding: UnsafeMutablePointer<NSStringEncoding>? = nil
>    ) throws {
> 
>    public init(
>      contentsOfURL url: NSURL,
> -    usedEncoding enc: UnsafeMutablePointer<NSStringEncoding> = nil
> +    usedEncoding enc: UnsafeMutablePointer<NSStringEncoding>? = nil
>    ) throws {
>    public func linguisticTags(
>      in range: Range<Index>,
>      scheme tagScheme: String,
>      options opts: NSLinguisticTaggerOptions = [],
>      orthography: NSOrthography? = nil,
> -    tokenRanges: UnsafeMutablePointer<[Range<Index>]> = nil
> +    tokenRanges: UnsafeMutablePointer<[Range<Index>]>? = nil
>    ) -> [String] {
> NSZone's no-argument initializer is gone. (It probably should have been 
> removed already as part of the Swift 3 naming cleanup.)
> 
> A small regression: optional pointers can no longer be passed using 
> withVaList because it would require a conditional conformance to the CVarArg 
> protocol. For now, using unsafeBitCast to reinterpret the optional pointer as 
> an Int is the best alternative; Int has the same C variadic calling 
> conventions as a pointer on all supported platforms.
> 
>  
> <https://github.com/jrose-apple/swift-evolution/tree/optional-pointers#conversion-between-pointers>Conversion
>  between pointers
> 
> Currently each pointer type has initializers of this form:
> 
> init<OtherPointee>(_ otherPointer: UnsafePointer<OtherPointee>)
> This simply makes a pointer with a different type but the same address as 
> otherPointer. However, in making pointer nullability explicit, this now only 
> converts non-nil pointers to non-nil pointers. In my experiments, this has 
> led to this idiom becoming very common:
> 
> // Before:
> let untypedPointer = UnsafePointer<Void>(ptr)
> 
> // After:
> let untypedPointer = ptr.map(UnsafePointer<Void>.init)
> 
> // Usually the pointee type is actually inferred:
> foo(ptr.map(UnsafePointer.init))
> I consider this a bit more difficult to understand than the original code, at 
> least at a glance. We should therefore add new initializers of the following 
> form:
> 
> init?<OtherPointee>(_ otherPointer: UnsafePointer<OtherPointee>?) {
>   guard let nonnullPointer = otherPointer else {
>     return nil
>   }
>   self.init(nonnullPointer)
> }
> The body is for explanation purposes only; we'll make sure the actual 
> implementation does not require an extra comparison.
> 
> (This would need to be an overload rather than replacing the previous 
> initializer because the "non-null-ness" should be preserved through the type 
> conversion.)
> 
> The alternative is to leave this initializer out, and require the nil case to 
> be explicitly handled or mapped away.
> 
>  
> <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.
>  
> <https://github.com/jrose-apple/swift-evolution/tree/optional-pointers#impact-on-existing-code>Impact
>  on existing code
> 
> Any code that uses a pointer type (including Selector or NSZone) may be 
> affected by this change. For the most part our existing logic to handle last 
> year's nullability audit should cover this, but the implementer should test 
> migration of several projects to see what issues might arise.
> 
> Anecdotally, in migrating the standard library to use this new logic I've 
> been quite happy with nullability being made explicit. There are many places 
> where a pointer really can't be nil.
>  
> <https://github.com/jrose-apple/swift-evolution/tree/optional-pointers#alternatives-considered>Alternatives
>  considered
> 
> The primary alternative here would be to leave everything as it is today, 
> with UnsafePointer and friends including the null pointer as one of their 
> normal values. This has obviously worked just fine for nearly two years of 
> Swift, but it is leaving information on the table that can help avoid bugs, 
> and is strange in a language that makes fluent use of Optional. As a fairly 
> major source-breaking change, it is also something that we probably should do 
> sooner rather than later in the language's evolution.
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to