Make pointer nullability explicit using Optional.

Implements SE-0055: https://github.com/apple/swift-evolution/blob/master/proposals/0055-optional-unsafe-pointers.md

- Add NULL as an extra inhabitant of Builtin.RawPointer (currently
  hardcoded to 0 rather than being target-dependent).
- Import non-object pointers as Optional/IUO when nullable/null_unspecified
  (like everything else).
- Change the type checker's *-to-pointer conversions to handle a layer of
  optional.
- Use 'AutoreleasingUnsafeMutablePointer<NSError?>?' as the type of error
  parameters exported to Objective-C.
- Drop NilLiteralConvertible conformance for all pointer types.
- Update the standard library and then all the tests.

I've decided to leave this commit only updating existing tests; any new
tests will come in the following commits. (That may mean some additional
implementation work to follow.)

The other major piece that's missing here is migration. I'm hoping we get
a lot of that with Swift 1.1's work for optional object references, but
I still need to investigate.
This commit is contained in:
Jordan Rose
2016-03-30 14:47:04 -07:00
parent 8e292daec1
commit bc83940301
150 changed files with 2374 additions and 2169 deletions

View File

@@ -64,6 +64,28 @@ func _countFormatSpecifiers(_ a: String) -> Int {
return count
}
// We only need this for UnsafeMutablePointer, but there's not currently a way
// to write that constraint.
extension Optional {
/// Invokes `body` with `nil` if `self` is `nil`; otherwise, passes the
/// address of `object` to `body`.
///
/// This is intended for use with Foundation APIs that return an Objective-C
/// type via out-parameter where it is important to be able to *ignore* that
/// parameter by passing `nil`. (For some APIs, this may allow the
/// implementation to avoid some work.)
///
/// In most cases it would be simpler to just write this code inline, but if
/// `body` is complicated than that results in unnecessarily repeated code.
internal func _withNilOrAddress<NSType : AnyObject, ResultType>(
of object: inout NSType?,
body: @noescape AutoreleasingUnsafeMutablePointer<NSType?>? -> ResultType
) -> ResultType {
return self == nil ? body(nil) : body(&object)
}
}
extension String {
//===--- Bridging Helpers -----------------------------------------------===//
@@ -102,14 +124,12 @@ extension String {
/// non-`nil`, convert the buffer to an `Index` and write it into the
/// memory referred to by `index`
func _withOptionalOutParameter<Result>(
_ index: UnsafeMutablePointer<Index>,
@noescape body: (UnsafeMutablePointer<Int>) -> Result
_ index: UnsafeMutablePointer<Index>?,
@noescape body: (UnsafeMutablePointer<Int>?) -> Result
) -> Result {
var utf16Index: Int = 0
let result = index._withBridgeValue(&utf16Index) {
body($0)
}
index._setIfNonNil { self._index(utf16Index) }
let result = (index != nil ? body(&utf16Index) : body(nil))
index?.pointee = self._index(utf16Index)
return result
}
@@ -117,14 +137,12 @@ extension String {
/// from non-`nil`, convert the buffer to a `Range<Index>` and write
/// it into the memory referred to by `range`
func _withOptionalOutParameter<Result>(
_ range: UnsafeMutablePointer<Range<Index>>,
@noescape body: (UnsafeMutablePointer<NSRange>) -> Result
_ range: UnsafeMutablePointer<Range<Index>>?,
@noescape body: (UnsafeMutablePointer<NSRange>?) -> Result
) -> Result {
var nsRange = NSRange(location: 0, length: 0)
let result = range._withBridgeValue(&nsRange) {
body($0)
}
range._setIfNonNil { self._range(nsRange) }
let result = (range != nil ? body(&nsRange) : body(nil))
range?.pointee = self._range(nsRange)
return result
}
@@ -363,16 +381,16 @@ extension String {
/// Returns the actual number of matching paths.
@warn_unused_result
public func completePath(
into outputName: UnsafeMutablePointer<String> = nil,
into outputName: UnsafeMutablePointer<String>? = nil,
caseSensitive: Bool,
matchesInto matchesIntoArray: UnsafeMutablePointer<[String]> = nil,
matchesInto matchesIntoArray: UnsafeMutablePointer<[String]>? = nil,
filterTypes: [String]? = nil
) -> Int {
var nsMatches: NSArray?
var nsOutputName: NSString?
let result = outputName._withBridgeObject(&nsOutputName) {
outputName in matchesIntoArray._withBridgeObject(&nsMatches) {
let result = outputName._withNilOrAddress(of: &nsOutputName) {
outputName in matchesIntoArray._withNilOrAddress(of: &nsMatches) {
matchesIntoArray in
self._ns.completePath(
into: outputName, caseSensitive: caseSensitive,
@@ -384,11 +402,11 @@ extension String {
if let matches = nsMatches {
// Since this function is effectively a bridge thunk, use the
// bridge thunk semantics for the NSArray conversion
matchesIntoArray._setIfNonNil { return matches as! [String] }
matchesIntoArray?.pointee = matches as! [String]
}
if let n = nsOutputName {
outputName._setIfNonNil { n as String }
outputName?.pointee = n as String
}
return result
}
@@ -839,7 +857,7 @@ extension String {
/// interpret the file.
public init(
contentsOfFile path: String,
usedEncoding: UnsafeMutablePointer<NSStringEncoding> = nil
usedEncoding: UnsafeMutablePointer<NSStringEncoding>? = nil
) throws {
let ns = try NSString(contentsOfFile: path, usedEncoding: usedEncoding)
self = ns as String
@@ -871,7 +889,7 @@ extension String {
/// data. Errors are written into the inout `error` argument.
public init(
contentsOf url: NSURL,
usedEncoding enc: UnsafeMutablePointer<NSStringEncoding> = nil
usedEncoding enc: UnsafeMutablePointer<NSStringEncoding>? = nil
) throws {
let ns = try NSString(contentsOf: url, usedEncoding: enc)
self = ns as String
@@ -1021,20 +1039,18 @@ extension String {
scheme tagScheme: String,
options opts: NSLinguisticTaggerOptions = [],
orthography: NSOrthography? = nil,
tokenRanges: UnsafeMutablePointer<[Range<Index>]> = nil // FIXME:Can this be nil?
tokenRanges: UnsafeMutablePointer<[Range<Index>]>? = nil // FIXME:Can this be nil?
) -> [String] {
var nsTokenRanges: NSArray? = nil
let result = tokenRanges._withBridgeObject(&nsTokenRanges) {
let result = tokenRanges._withNilOrAddress(of: &nsTokenRanges) {
self._ns.linguisticTags(
in: _toNSRange(range), scheme: tagScheme, options: opts,
orthography: orthography != nil ? orthography! : nil, tokenRanges: $0) as NSArray
orthography: orthography, tokenRanges: $0) as NSArray
}
if nsTokenRanges != nil {
tokenRanges._setIfNonNil {
(nsTokenRanges! as [AnyObject]).map {
self._range($0.rangeValue)
}
tokenRanges?.pointee = (nsTokenRanges! as [AnyObject]).map {
self._range($0.rangeValue)
}
}
@@ -1712,9 +1728,9 @@ extension String {
@available(*, unavailable, renamed: "completePath(into:outputName:caseSensitive:matchesInto:filterTypes:)")
public func completePathInto(
_ outputName: UnsafeMutablePointer<String> = nil,
_ outputName: UnsafeMutablePointer<String>? = nil,
caseSensitive: Bool,
matchesInto matchesIntoArray: UnsafeMutablePointer<[String]> = nil,
matchesInto matchesIntoArray: UnsafeMutablePointer<[String]>? = nil,
filterTypes: [String]? = nil
) -> Int {
fatalError("unavailable function can't be called")
@@ -1818,7 +1834,7 @@ extension String {
scheme tagScheme: String,
options opts: NSLinguisticTaggerOptions = [],
orthography: NSOrthography? = nil,
tokenRanges: UnsafeMutablePointer<[Range<Index>]> = nil
tokenRanges: UnsafeMutablePointer<[Range<Index>]>? = nil
) -> [String] {
fatalError("unavailable function can't be called")
}