Skip to content

[SR-7865] swift-corelibs-foundation does not compile on macOS after 'as' bridging was merged #1579

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 110 additions & 3 deletions Foundation/Bridging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,24 @@

import CoreFoundation

#if canImport(ObjectiveC)
import ObjectiveC
#endif

public protocol _StructBridgeable {
func _bridgeToAny() -> Any
}

fileprivate protocol Unwrappable {
func unwrap() -> Any?
}

extension Optional: Unwrappable {
func unwrap() -> Any? {
return self
}
}

/// - Note: This does not exist currently on Darwin but it is the inverse correlation to the bridge types such that a
/// reference type can be converted via a callout to a conversion method.
public protocol _StructTypeBridgeable : _StructBridgeable {
Expand Down Expand Up @@ -54,18 +68,92 @@ internal protocol _NSBridgeable {
}


#if !canImport(ObjectiveC)
// The _NSSwiftValue protocol is in the stdlib, and only available on platforms without ObjC.
extension _SwiftValue: _NSSwiftValue {}
#endif

/// - Note: This is an internal boxing value for containing abstract structures
internal final class _SwiftValue : NSObject, NSCopying, _NSSwiftValue {
internal final class _SwiftValue : NSObject, NSCopying {
public private(set) var value: Any

static func fetch(_ object: AnyObject?) -> Any? {
if let obj = object {
return fetch(nonOptional: obj)
let value = fetch(nonOptional: obj)
if let wrapper = value as? Unwrappable, wrapper.unwrap() == nil {
return nil
} else {
return value
}
}
return nil
}

#if canImport(ObjectiveC)
private static var _objCNSNullClassStorage: Any.Type?
private static var objCNSNullClass: Any.Type? {
if let type = _objCNSNullClassStorage {
return type
}

let name = "NSNull"
let maybeType = name.withCString { cString in
return objc_getClass(cString)
}

if let type = maybeType as? Any.Type {
_objCNSNullClassStorage = type
return type
} else {
return nil
}
}

private static var _swiftStdlibSwiftValueClassStorage: Any.Type?
private static var swiftStdlibSwiftValueClass: Any.Type? {
if let type = _swiftStdlibSwiftValueClassStorage {
return type
}

let name = "_SwiftValue"
let maybeType = name.withCString { cString in
return objc_getClass(cString)
}

if let type = maybeType as? Any.Type {
_swiftStdlibSwiftValueClassStorage = type
return type
} else {
return nil
}
}

#endif

static func fetch(nonOptional object: AnyObject) -> Any {
#if canImport(ObjectiveC)
// You can pass the result of a `as AnyObject` expression to this method. This can have one of three results on Darwin:
// - It's a SwiftFoundation type. Bridging will take care of it below.
// - It's nil. The compiler is hardcoded to return [NSNull null] for nils.
// - It's some other Swift type. The compiler will box it in a native _SwiftValue.
// Case 1 is handled below.
// Case 2 is handled here:
if type(of: object as Any) == objCNSNullClass {
return Optional<Any>.none as Any
}
// Case 3 is handled here:
if type(of: object as Any) == swiftStdlibSwiftValueClass {
return object
// Since this returns Any, the object is casted almost immediately — e.g.:
// _SwiftValue.fetch(x) as SomeStruct
// which will immediately unbox the native box. For callers, it will be exactly
// as if we returned the unboxed value directly.
}

// On Linux, case 2 is handled by the stdlib bridging machinery, and case 3 can't happen —
// the compiler will produce SwiftFoundation._SwiftValue boxes rather than ObjC ones.
#endif

if object === kCFBooleanTrue {
return true
} else if object === kCFBooleanFalse {
Expand All @@ -79,6 +167,13 @@ internal final class _SwiftValue : NSObject, NSCopying, _NSSwiftValue {
}
}

static func store(optional value: Any?) -> NSObject? {
if let val = value {
return store(val)
}
return nil
}

static func store(_ value: Any?) -> NSObject? {
if let val = value {
return store(val)
Expand All @@ -89,8 +184,20 @@ internal final class _SwiftValue : NSObject, NSCopying, _NSSwiftValue {
static func store(_ value: Any) -> NSObject {
if let val = value as? NSObject {
return val
} else if let opt = value as? Unwrappable, opt.unwrap() == nil {
return NSNull()
} else {
return (value as AnyObject) as! NSObject
#if canImport(ObjectiveC)
// On Darwin, this can be a native (ObjC) _SwiftValue.
let boxed = (value as AnyObject)
if !(boxed is NSObject) {
return _SwiftValue(value) // Do not emit native boxes — wrap them in Swift Foundation boxes instead.
} else {
return boxed as! NSObject
}
#else
return (value as AnyObject) as! NSObject
#endif
}
}

Expand Down
2 changes: 1 addition & 1 deletion Foundation/NSArray.swift
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ open class NSArray : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCo
} else {
let val1 = object(at: idx)
let val2 = otherArray[idx]
if !((val1 as AnyObject) as! NSObject).isEqual(val2 as AnyObject) {
if !_SwiftValue.store(val1).isEqual(_SwiftValue.store(val2)) {
return false
}
}
Expand Down
7 changes: 4 additions & 3 deletions Foundation/NSDictionary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,10 @@ open class NSDictionary : NSObject, NSCopying, NSMutableCopying, NSSecureCoding,
return false
}
} else {
let otherBridgeable = otherDictionary[key as! AnyHashable]
let bridgeable = object(forKey: key)!
if !((otherBridgeable as AnyObject) as! NSObject).isEqual(bridgeable as AnyObject) {
let otherBridgeable = otherDictionary[key as! AnyHashable]
let bridgeable = object(forKey: key)!
let equal = _SwiftValue.store(optional: otherBridgeable)?.isEqual(_SwiftValue.store(bridgeable))
if equal != true {
return false
}
}
Expand Down
5 changes: 5 additions & 0 deletions Foundation/NSError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,8 @@ enum UnknownNSError: Error {
case missingError
}

#if !canImport(ObjectiveC)

public // COMPILER_INTRINSIC
func _convertNSErrorToError(_ error: NSError?) -> Error {
return error ?? UnknownNSError.missingError
Expand Down Expand Up @@ -1410,3 +1412,6 @@ func _convertErrorToNSError(_ error: Error) -> NSError {
return NSError(domain: domain, code: code, userInfo: userInfo)
}
}

#endif

2 changes: 1 addition & 1 deletion Foundation/NSKeyedArchiver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ open class NSKeyedArchiver : NSCoder {
object = _replacementObject(objv)

// bridge value types
object = object as AnyObject
object = _SwiftValue.store(object)

objectRef = _referenceObject(object, conditional: conditional)
guard let unwrappedObjectRef = objectRef else {
Expand Down
2 changes: 1 addition & 1 deletion Foundation/NSKeyedUnarchiver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ open class NSKeyedUnarchiver : NSCoder {
_cacheObject(object!, forReference: objectRef as! _NSKeyedArchiverUID)
}
} else {
object = dereferencedObject as AnyObject
object = _SwiftValue.store(dereferencedObject)
}

return _replacementObject(object)
Expand Down
2 changes: 1 addition & 1 deletion Foundation/NSSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ open class NSSet : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCodi
super.init()
let buffer = UnsafeBufferPointer(start: objects, count: cnt)
for obj in buffer {
_storage.insert(obj as! NSObject)
_storage.insert(_SwiftValue.store(obj))
}
}

Expand Down