SR-0140: Bridge Optionals to nonnull ObjC objects by bridging their payload, or using a sentinel.

id-as-Any lets you pass Optional to an ObjC API that takes `nonnull id`, and also lets you bridge containers of `Optional` to `NSArray` etc. When this occurs, we can unwrap the value and bridge it so that inhabited optionals still pass into ObjC in the expected way, but we need something to represent `none` other than the `nil` pointer. Cocoa provides `NSNull` as the canonical "null for containers" object, which is the least bad of many possible answers. If we happen to have the rare nested optional `T??`, there is no precedented analog for these in Cocoa, so just generate a unique sentinel object to preserve the `nil`-ness depth so we at least don't lose information round-tripping across the ObjC-Swift bridge.

Making Optional conform to _ObjectiveCBridgeable is more or less enough to make this all work, though there are a few additional edge case things that need to be fixed up. We don't want to accept `AnyObject??` as an @objc-compatible type, so special-case Optional in `getForeignRepresentable`.

Implements SR-0140 (rdar://problem/27905315).
This commit is contained in:
Joe Groff
2016-09-14 15:27:08 -07:00
parent c9bad5ec4c
commit cfa9cd9a08
10 changed files with 365 additions and 16 deletions

View File

@@ -2513,6 +2513,18 @@ bool swift::swift_dynamicCast(OpaqueValue *dest,
// unwrapping the target. This handles an optional source wrapped within an
// existential that Optional conforms to (Any).
if (auto srcExistentialType = dyn_cast<ExistentialTypeMetadata>(srcType)) {
#if SWIFT_OBJC_INTEROP
// If coming from AnyObject, we may want to bridge.
if (srcExistentialType->Flags.getSpecialProtocol()
== SpecialProtocol::AnyObject) {
if (auto targetBridgeWitness = findBridgeWitness(targetType)) {
return _dynamicCastClassToValueViaObjCBridgeable(dest, src, srcType,
targetType,
targetBridgeWitness,
flags);
}
}
#endif
return _dynamicCastFromExistential(dest, src, srcExistentialType,
targetType, flags);
}