Skip to content

Commit c91c99d

Browse files
authored
Merge pull request swiftlang#1579 from millenomi/bridging-darwin-fixes
[SR-7865] swift-corelibs-foundation does not compile on macOS after 'as' bridging was merged
2 parents e159524 + 8d4b6a5 commit c91c99d

7 files changed

+123
-10
lines changed

Foundation/Bridging.swift

+110-3
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,24 @@
1212

1313
import CoreFoundation
1414

15+
#if canImport(ObjectiveC)
16+
import ObjectiveC
17+
#endif
18+
1519
public protocol _StructBridgeable {
1620
func _bridgeToAny() -> Any
1721
}
1822

23+
fileprivate protocol Unwrappable {
24+
func unwrap() -> Any?
25+
}
26+
27+
extension Optional: Unwrappable {
28+
func unwrap() -> Any? {
29+
return self
30+
}
31+
}
32+
1933
/// - Note: This does not exist currently on Darwin but it is the inverse correlation to the bridge types such that a
2034
/// reference type can be converted via a callout to a conversion method.
2135
public protocol _StructTypeBridgeable : _StructBridgeable {
@@ -54,18 +68,92 @@ internal protocol _NSBridgeable {
5468
}
5569

5670

71+
#if !canImport(ObjectiveC)
72+
// The _NSSwiftValue protocol is in the stdlib, and only available on platforms without ObjC.
73+
extension _SwiftValue: _NSSwiftValue {}
74+
#endif
75+
5776
/// - Note: This is an internal boxing value for containing abstract structures
58-
internal final class _SwiftValue : NSObject, NSCopying, _NSSwiftValue {
77+
internal final class _SwiftValue : NSObject, NSCopying {
5978
public private(set) var value: Any
6079

6180
static func fetch(_ object: AnyObject?) -> Any? {
6281
if let obj = object {
63-
return fetch(nonOptional: obj)
82+
let value = fetch(nonOptional: obj)
83+
if let wrapper = value as? Unwrappable, wrapper.unwrap() == nil {
84+
return nil
85+
} else {
86+
return value
87+
}
6488
}
6589
return nil
6690
}
6791

92+
#if canImport(ObjectiveC)
93+
private static var _objCNSNullClassStorage: Any.Type?
94+
private static var objCNSNullClass: Any.Type? {
95+
if let type = _objCNSNullClassStorage {
96+
return type
97+
}
98+
99+
let name = "NSNull"
100+
let maybeType = name.withCString { cString in
101+
return objc_getClass(cString)
102+
}
103+
104+
if let type = maybeType as? Any.Type {
105+
_objCNSNullClassStorage = type
106+
return type
107+
} else {
108+
return nil
109+
}
110+
}
111+
112+
private static var _swiftStdlibSwiftValueClassStorage: Any.Type?
113+
private static var swiftStdlibSwiftValueClass: Any.Type? {
114+
if let type = _swiftStdlibSwiftValueClassStorage {
115+
return type
116+
}
117+
118+
let name = "_SwiftValue"
119+
let maybeType = name.withCString { cString in
120+
return objc_getClass(cString)
121+
}
122+
123+
if let type = maybeType as? Any.Type {
124+
_swiftStdlibSwiftValueClassStorage = type
125+
return type
126+
} else {
127+
return nil
128+
}
129+
}
130+
131+
#endif
132+
68133
static func fetch(nonOptional object: AnyObject) -> Any {
134+
#if canImport(ObjectiveC)
135+
// You can pass the result of a `as AnyObject` expression to this method. This can have one of three results on Darwin:
136+
// - It's a SwiftFoundation type. Bridging will take care of it below.
137+
// - It's nil. The compiler is hardcoded to return [NSNull null] for nils.
138+
// - It's some other Swift type. The compiler will box it in a native _SwiftValue.
139+
// Case 1 is handled below.
140+
// Case 2 is handled here:
141+
if type(of: object as Any) == objCNSNullClass {
142+
return Optional<Any>.none as Any
143+
}
144+
// Case 3 is handled here:
145+
if type(of: object as Any) == swiftStdlibSwiftValueClass {
146+
return object
147+
// Since this returns Any, the object is casted almost immediately — e.g.:
148+
// _SwiftValue.fetch(x) as SomeStruct
149+
// which will immediately unbox the native box. For callers, it will be exactly
150+
// as if we returned the unboxed value directly.
151+
}
152+
153+
// On Linux, case 2 is handled by the stdlib bridging machinery, and case 3 can't happen —
154+
// the compiler will produce SwiftFoundation._SwiftValue boxes rather than ObjC ones.
155+
#endif
156+
69157
if object === kCFBooleanTrue {
70158
return true
71159
} else if object === kCFBooleanFalse {
@@ -79,6 +167,13 @@ internal final class _SwiftValue : NSObject, NSCopying, _NSSwiftValue {
79167
}
80168
}
81169

170+
static func store(optional value: Any?) -> NSObject? {
171+
if let val = value {
172+
return store(val)
173+
}
174+
return nil
175+
}
176+
82177
static func store(_ value: Any?) -> NSObject? {
83178
if let val = value {
84179
return store(val)
@@ -89,8 +184,20 @@ internal final class _SwiftValue : NSObject, NSCopying, _NSSwiftValue {
89184
static func store(_ value: Any) -> NSObject {
90185
if let val = value as? NSObject {
91186
return val
187+
} else if let opt = value as? Unwrappable, opt.unwrap() == nil {
188+
return NSNull()
92189
} else {
93-
return (value as AnyObject) as! NSObject
190+
#if canImport(ObjectiveC)
191+
// On Darwin, this can be a native (ObjC) _SwiftValue.
192+
let boxed = (value as AnyObject)
193+
if !(boxed is NSObject) {
194+
return _SwiftValue(value) // Do not emit native boxes — wrap them in Swift Foundation boxes instead.
195+
} else {
196+
return boxed as! NSObject
197+
}
198+
#else
199+
return (value as AnyObject) as! NSObject
200+
#endif
94201
}
95202
}
96203

Foundation/NSArray.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ open class NSArray : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCo
337337
} else {
338338
let val1 = object(at: idx)
339339
let val2 = otherArray[idx]
340-
if !((val1 as AnyObject) as! NSObject).isEqual(val2 as AnyObject) {
340+
if !_SwiftValue.store(val1).isEqual(_SwiftValue.store(val2)) {
341341
return false
342342
}
343343
}

Foundation/NSDictionary.swift

+4-3
Original file line numberDiff line numberDiff line change
@@ -396,9 +396,10 @@ open class NSDictionary : NSObject, NSCopying, NSMutableCopying, NSSecureCoding,
396396
return false
397397
}
398398
} else {
399-
let otherBridgeable = otherDictionary[key as! AnyHashable]
400-
let bridgeable = object(forKey: key)!
401-
if !((otherBridgeable as AnyObject) as! NSObject).isEqual(bridgeable as AnyObject) {
399+
let otherBridgeable = otherDictionary[key as! AnyHashable]
400+
let bridgeable = object(forKey: key)!
401+
let equal = _SwiftValue.store(optional: otherBridgeable)?.isEqual(_SwiftValue.store(bridgeable))
402+
if equal != true {
402403
return false
403404
}
404405
}

Foundation/NSError.swift

+5
Original file line numberDiff line numberDiff line change
@@ -1383,6 +1383,8 @@ enum UnknownNSError: Error {
13831383
case missingError
13841384
}
13851385

1386+
#if !canImport(ObjectiveC)
1387+
13861388
public // COMPILER_INTRINSIC
13871389
func _convertNSErrorToError(_ error: NSError?) -> Error {
13881390
return error ?? UnknownNSError.missingError
@@ -1410,3 +1412,6 @@ func _convertErrorToNSError(_ error: Error) -> NSError {
14101412
return NSError(domain: domain, code: code, userInfo: userInfo)
14111413
}
14121414
}
1415+
1416+
#endif
1417+

Foundation/NSKeyedArchiver.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ open class NSKeyedArchiver : NSCoder {
597597
object = _replacementObject(objv)
598598

599599
// bridge value types
600-
object = object as AnyObject
600+
object = _SwiftValue.store(object)
601601

602602
objectRef = _referenceObject(object, conditional: conditional)
603603
guard let unwrappedObjectRef = objectRef else {

Foundation/NSKeyedUnarchiver.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ open class NSKeyedUnarchiver : NSCoder {
483483
_cacheObject(object!, forReference: objectRef as! _NSKeyedArchiverUID)
484484
}
485485
} else {
486-
object = dereferencedObject as AnyObject
486+
object = _SwiftValue.store(dereferencedObject)
487487
}
488488

489489
return _replacementObject(object)

Foundation/NSSet.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ open class NSSet : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCodi
4646
super.init()
4747
let buffer = UnsafeBufferPointer(start: objects, count: cnt)
4848
for obj in buffer {
49-
_storage.insert(obj as! NSObject)
49+
_storage.insert(_SwiftValue.store(obj))
5050
}
5151
}
5252

0 commit comments

Comments
 (0)