Merge pull request #17515 from mikeash/nested-nserror-to-error-bridging

[Runtime] Extend ObjC bridging casts to convert NSError to Error when nested in a container type.
diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp
index 626e18f..32c7440 100644
--- a/stdlib/public/runtime/Casting.cpp
+++ b/stdlib/public/runtime/Casting.cpp
@@ -2941,6 +2941,12 @@
       return true;
     }
   }
+  // Try to bridge NSError to Error.
+  if (tryDynamicCastNSErrorObjectToValue(sourceValue, destValue, nativeType,
+                                         DynamicCastFlags::Default)) {
+    return true;
+  }
+
   
   return false;
 }
diff --git a/stdlib/public/runtime/ErrorObject.h b/stdlib/public/runtime/ErrorObject.h
index 43358f0..8d4cdfe 100644
--- a/stdlib/public/runtime/ErrorObject.h
+++ b/stdlib/public/runtime/ErrorObject.h
@@ -223,8 +223,17 @@
 SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_SPI
 id _swift_stdlib_bridgeErrorToNSError(SwiftError *errorObject);
 
+/// Attempt to dynamically cast an NSError object to a Swift ErrorType
+/// implementation using the _ObjectiveCBridgeableErrorType protocol or by
+/// putting it directly into an Error existential.
+bool tryDynamicCastNSErrorObjectToValue(HeapObject *object,
+                                        OpaqueValue *dest,
+                                        const Metadata *destType,
+                                        DynamicCastFlags flags);
+
 /// Attempt to dynamically cast an NSError instance to a Swift ErrorType
-/// implementation using the _ObjectiveCBridgeableErrorType protocol.
+/// implementation using the _ObjectiveCBridgeableErrorType protocol or by
+/// putting it directly into an Error existential.
 ///
 /// srcType must be some kind of class metadata.
 bool tryDynamicCastNSErrorToValue(OpaqueValue *dest,
diff --git a/stdlib/public/runtime/ErrorObject.mm b/stdlib/public/runtime/ErrorObject.mm
index d67b4cd..e5b3a39 100644
--- a/stdlib/public/runtime/ErrorObject.mm
+++ b/stdlib/public/runtime/ErrorObject.mm
@@ -462,47 +462,27 @@
 extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error);
 
 bool
-swift::tryDynamicCastNSErrorToValue(OpaqueValue *dest,
-                                    OpaqueValue *src,
-                                    const Metadata *srcType,
-                                    const Metadata *destType,
-                                    DynamicCastFlags flags) {
+swift::tryDynamicCastNSErrorObjectToValue(HeapObject *object,
+                                          OpaqueValue *dest,
+                                          const Metadata *destType,
+                                          DynamicCastFlags flags) {
   Class NSErrorClass = getNSErrorClass();
-  auto CFErrorTypeID = SWIFT_LAZY_CONSTANT(CFErrorGetTypeID());
 
-  NSError *srcInstance;
-
-  // Is the input type an NSError?
-  switch (srcType->getKind()) {
-  case MetadataKind::Class:
-  case MetadataKind::ObjCClassWrapper:
-    // Native class or ObjC class should be an NSError subclass.
-    if (![srcType->getObjCClassObject() isSubclassOfClass: NSErrorClass])
-      return false;
-    
-    srcInstance = *reinterpret_cast<NSError * const*>(src);
-    
-    // A _SwiftNativeNSError box can always be unwrapped to cast the value back
-    // out as an Error existential.
-    if (!reinterpret_cast<SwiftError*>(srcInstance)->isPureNSError()) {
-      auto theErrorProtocol = &PROTOCOL_DESCR_SYM(s5Error);
-      auto theErrorTy =
-        swift_getExistentialTypeMetadata(ProtocolClassConstraint::Any,
-                                         nullptr, 1, &theErrorProtocol);
-      return swift_dynamicCast(dest, src, theErrorTy, destType, flags);
-    }
-    
-    break;
-  case MetadataKind::ForeignClass: {
-    // Foreign class should be CFError.
-    CFTypeRef srcInstance = *reinterpret_cast<CFTypeRef *>(src);
-    if (CFGetTypeID(srcInstance) != CFErrorTypeID)
-      return false;
-    break;
-  }
-  // Not a class.
-  default:
+  // The object must be an NSError subclass.
+  if (![reinterpret_cast<id>(object) isKindOfClass: NSErrorClass])
     return false;
+
+  NSError *srcInstance = reinterpret_cast<NSError *>(object);
+
+  // A _SwiftNativeNSError box can always be unwrapped to cast the value back
+  // out as an Error existential.
+  if (!reinterpret_cast<SwiftError*>(srcInstance)->isPureNSError()) {
+    auto theErrorProtocol = &PROTOCOL_DESCR_SYM(s5Error);
+    auto theErrorTy =
+      swift_getExistentialTypeMetadata(ProtocolClassConstraint::Any,
+                                       nullptr, 1, &theErrorProtocol);
+    return swift_dynamicCast(dest, reinterpret_cast<OpaqueValue *>(&object),
+                             theErrorTy, destType, flags);
   }
 
   // public func Foundation._bridgeNSErrorToError<
@@ -521,19 +501,47 @@
   auto witness = swift_conformsToProtocol(destType,
                                           TheObjectiveCBridgeableError);
 
-  if (!witness)
-    return false;
+  if (witness) {
+    // If so, attempt the bridge.
+    if (bridgeNSErrorToError(srcInstance, dest, destType, witness)) {
+      if (flags & DynamicCastFlags::TakeOnSuccess)
+        objc_release(srcInstance);
+      return true;
+    }
+  }
 
-  // If so, attempt the bridge.
-  SWIFT_CC_PLUSONE_GUARD(objc_retain(srcInstance));
-  if (bridgeNSErrorToError(srcInstance, dest, destType, witness)) {
-    if (flags & DynamicCastFlags::TakeOnSuccess)
-      objc_release(srcInstance);
+  // If the destination is just an Error then we can bridge directly.
+  auto *destTypeExistential = dyn_cast<ExistentialTypeMetadata>(destType);
+  if (destTypeExistential &&
+      destTypeExistential->getRepresentation() == ExistentialTypeRepresentation::Error) {
+    auto destBoxAddr = reinterpret_cast<NSError**>(dest);
+    *destBoxAddr = objc_retain(srcInstance);
     return true;
   }
+
   return false;
 }
 
+bool
+swift::tryDynamicCastNSErrorToValue(OpaqueValue *dest,
+                                    OpaqueValue *src,
+                                    const Metadata *srcType,
+                                    const Metadata *destType,
+                                    DynamicCastFlags flags) {
+  // NSError instances must be class instances, anything else automatically fails.
+  switch (srcType->getKind()) {
+  case MetadataKind::Class:
+  case MetadataKind::ObjCClassWrapper:
+  case MetadataKind::ForeignClass:
+    return tryDynamicCastNSErrorObjectToValue(*reinterpret_cast<HeapObject **>(src),
+                                              dest, destType, flags);
+
+  // Not a class.
+  default:
+    return false;
+  }
+}
+
 SwiftError *
 swift::swift_errorRetain(SwiftError *error) {
   // For now, SwiftError is always objc-refcounted.
diff --git a/test/stdlib/ErrorBridged.swift b/test/stdlib/ErrorBridged.swift
index dcce4ae..05121d1 100644
--- a/test/stdlib/ErrorBridged.swift
+++ b/test/stdlib/ErrorBridged.swift
@@ -276,6 +276,16 @@
   expectEqual(NoisyErrorDeathCount, NoisyErrorLifeCount)
 }
 
+ErrorBridgingTests.test("NSError-to-error bridging in bridged container") {
+  autoreleasepool {
+    let error = NSError(domain: "domain", code: 42, userInfo: nil)
+    let nsdictionary = ["error": error] as NSDictionary
+    let dictionary = nsdictionary as? Dictionary<String, Error>
+    expectNotNil(dictionary)
+    expectEqual(error, dictionary?["error"] as NSError?)
+  }
+}
+
 ErrorBridgingTests.test("enum-to-NSError round trip") {
   autoreleasepool {
     // Emulate throwing an error from Objective-C.