On Mar 17, 2016, at 8:21 PM, Félix Cloutier via swift-evolution <swift-evolution@swift.org> wrote: > >> Fortunately, when a type has a single invalid value for which no operations >> are valid, Swift already has a solution: Optionals. > > To me, this makes it sound like dereferencing an unsafe pointer is unsafe > only if the pointer is nil. (Nil does have the merit that it's generally one > of the only addresses known to be invalid, though.) > > One thing that I would worry about, though, is the case where dereferencing > address 0 is legal. My understanding is that Swift is aimed to be a systems > language, and this case is relatively frequent on embedded devices. >
This is definitely an important case to consider, but I don't think this is a problem for the proposal. C compilers for these sorts of embedded systems have two choices: they can either reserve 1 byte for the null value (wasting a byte of physmem) or they can use a null value with a representation that is not equal to zero. In either case, the null value (whatever it is) maps to the swift none case. Everything works out properly. -Chris > Félix > >> Le 17 mars 2016 à 21:59:49, Jordan Rose via swift-evolution >> <swift-evolution@swift.org> a écrit : >> >> 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 >> >> 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 >> Author(s): Jordan Rose >> Status: Awaiting review >> Review manager: TBD >> 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. >> >> 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. >> >> 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.) >> >> Detailed design >> >> 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. >> >> 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. >> >> 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. >> 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. >> 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 >> swift-evolution@swift.org >> https://lists.swift.org/mailman/listinfo/swift-evolution > > _______________________________________________ > swift-evolution mailing list > swift-evolution@swift.org > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution