Merge pull request #7847 from jckarter/nserror-linux-bridging-3.1

Sema/SIL: NSError has no special powers without ObjC interop.
diff --git a/lib/SIL/DynamicCasts.cpp b/lib/SIL/DynamicCasts.cpp
index e528d87..eedca48 100644
--- a/lib/SIL/DynamicCasts.cpp
+++ b/lib/SIL/DynamicCasts.cpp
@@ -1139,23 +1139,30 @@
   // non-NSError superclass constraint. Casts to archetypes thus must always be
   // indirect.
   if (auto archetype = targetType->getAs<ArchetypeType>()) {
-    auto super = archetype->getSuperclass();
-    if (super.isNull())
-      return false;
-
     // Only ever permit this if the source type is a reference type.
     if (!objectType.isAnyClassReferenceType())
       return false;
+    
+    if (M.getASTContext().LangOpts.EnableObjCInterop) {
+      auto super = archetype->getSuperclass();
+      if (super.isNull())
+        return false;
 
-    // A base class constraint that isn't NSError rules out the archetype being
-    // bound to NSError.
-    if (auto nserror = M.Types.getNSErrorType())
-      return !super->isEqual(nserror);
-    // If NSError wasn't loaded, any base class constraint must not be NSError.
-    return true;
+      // A base class constraint that isn't NSError rules out the archetype being
+      // bound to NSError.
+        if (auto nserror = M.Types.getNSErrorType())
+          return !super->isEqual(nserror);
+      // If NSError wasn't loaded, any base class constraint must not be NSError.
+      return true;
+    } else {
+      // If ObjC bridging isn't enabled, we can do a scalar cast from any
+      // reference type to any class-constrained archetype.
+      return archetype->requiresClass();
+    }
   }
   
-  if (targetType == M.Types.getNSErrorType()) {
+  if (M.getASTContext().LangOpts.EnableObjCInterop
+      && targetType == M.Types.getNSErrorType()) {
     // If we statically know the source is an NSError subclass, then the cast
     // can go through the scalar path (and it's trivially true so can be
     // killed).
diff --git a/lib/SIL/SILType.cpp b/lib/SIL/SILType.cpp
index a8d48ef..a056232 100644
--- a/lib/SIL/SILType.cpp
+++ b/lib/SIL/SILType.cpp
@@ -461,6 +461,10 @@
 /// Error existentials.
 static bool isBridgedErrorClass(SILModule &M,
                                 Type t) {
+  // There's no bridging if ObjC interop is disabled.
+  if (!M.getASTContext().LangOpts.EnableObjCInterop)
+    return false;
+
   if (!t)
     return false;
 
diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp
index 0cd52a0..cd845dc 100644
--- a/lib/Sema/CSSimplify.cpp
+++ b/lib/Sema/CSSimplify.cpp
@@ -3327,6 +3327,10 @@
                                              Type type2,
                                              TypeMatchOptions flags,
                                              ConstraintLocatorBuilder locator) {
+  // There's no bridging without ObjC interop.
+  if (!TC.Context.LangOpts.EnableObjCInterop)
+    return SolutionKind::Error;
+  
   TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);
 
   /// Form an unresolved result.
diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp
index 31ece4c..e465f2b 100644
--- a/lib/Sema/TypeCheckConstraints.cpp
+++ b/lib/Sema/TypeCheckConstraints.cpp
@@ -3361,16 +3361,18 @@
   // We can conditionally cast from NSError to an Error-conforming
   // type.  This is handled in the runtime, so it doesn't need a special cast
   // kind.
-  if (auto errorTypeProto = Context.getProtocol(KnownProtocolKind::Error)) {
-    if (conformsToProtocol(toType, errorTypeProto, dc,
-                           (ConformanceCheckFlags::InExpression|
-                            ConformanceCheckFlags::Used)))
-      if (auto NSErrorTy = getNSErrorType(dc))
-        if (isSubtypeOf(fromType, NSErrorTy, dc)
-            // Don't mask "always true" warnings if NSError is cast to
-            // Error itself.
-            && !isSubtypeOf(fromType, toType, dc))
-          return CheckedCastKind::ValueCast;
+  if (Context.LangOpts.EnableObjCInterop) {
+    if (auto errorTypeProto = Context.getProtocol(KnownProtocolKind::Error)) {
+      if (conformsToProtocol(toType, errorTypeProto, dc,
+                             (ConformanceCheckFlags::InExpression|
+                              ConformanceCheckFlags::Used)))
+        if (auto NSErrorTy = getNSErrorType(dc))
+          if (isSubtypeOf(fromType, NSErrorTy, dc)
+              // Don't mask "always true" warnings if NSError is cast to
+              // Error itself.
+              && !isSubtypeOf(fromType, toType, dc))
+            return CheckedCastKind::ValueCast;
+    }
   }
 
   // The runtime doesn't support casts to CF types and always lets them succeed.
diff --git a/test/SIL/Parser/undef.sil b/test/SIL/Parser/undef.sil
index 43dbb04..a22c53f 100644
--- a/test/SIL/Parser/undef.sil
+++ b/test/SIL/Parser/undef.sil
@@ -242,14 +242,6 @@
   bridge_object_to_word undef : $Builtin.BridgeObject to $Builtin.Word
   // CHECK: thin_to_thick_function undef : $@convention(thin) () -> () to $() -> ()
   thin_to_thick_function undef : $@convention(thin) () -> () to $() -> ()
-  // CHECK: thick_to_objc_metatype undef : $@thick C.Type to $@objc_metatype C.Type
-  thick_to_objc_metatype undef : $@thick C.Type to $@objc_metatype C.Type
-  // CHECK: objc_to_thick_metatype undef : $@objc_metatype C.Type to $@thick C.Type
-  objc_to_thick_metatype undef : $@objc_metatype C.Type to $@thick C.Type
-  // CHECK: objc_metatype_to_object undef : $@objc_metatype C.Type to $AnyObject
-  objc_metatype_to_object undef : $@objc_metatype C.Type to $AnyObject
-  // CHECK: objc_existential_metatype_to_object undef : $@objc_metatype P.Type to $AnyObject
-  objc_existential_metatype_to_object undef : $@objc_metatype P.Type to $AnyObject
   // CHECK: is_nonnull undef : $C
   is_nonnull undef : $C
 
diff --git a/test/SIL/Parser/undef_objc.sil b/test/SIL/Parser/undef_objc.sil
new file mode 100644
index 0000000..b4119cd
--- /dev/null
+++ b/test/SIL/Parser/undef_objc.sil
@@ -0,0 +1,23 @@
+// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil %s | %target-sil-opt -assume-parsing-unqualified-ownership-sil | %FileCheck %s
+// REQUIRES: objc_interop
+
+sil_stage raw
+
+import Builtin
+import Swift
+
+protocol P { }
+class C { }
+
+sil @general_test : $() -> () {
+bb0:
+  // CHECK: thick_to_objc_metatype undef : $@thick C.Type to $@objc_metatype C.Type
+  thick_to_objc_metatype undef : $@thick C.Type to $@objc_metatype C.Type
+  // CHECK: objc_to_thick_metatype undef : $@objc_metatype C.Type to $@thick C.Type
+  objc_to_thick_metatype undef : $@objc_metatype C.Type to $@thick C.Type
+  // CHECK: objc_metatype_to_object undef : $@objc_metatype C.Type to $AnyObject
+  objc_metatype_to_object undef : $@objc_metatype C.Type to $AnyObject
+  // CHECK: objc_existential_metatype_to_object undef : $@objc_metatype P.Type to $AnyObject
+  objc_existential_metatype_to_object undef : $@objc_metatype P.Type to $AnyObject
+  unreachable
+}
diff --git a/test/SILGen/generic_casts.swift b/test/SILGen/generic_casts.swift
index 959d179..6269058 100644
--- a/test/SILGen/generic_casts.swift
+++ b/test/SILGen/generic_casts.swift
@@ -1,4 +1,4 @@
-// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-silgen %s | %FileCheck %s
+// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-silgen %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-runtime %s
 
 protocol ClassBound : class {}
 protocol NotClassBound {}
@@ -57,16 +57,21 @@
 func class_archetype_to_class_archetype
 <T:ClassBound, U:ClassBound>(_ t:T) -> U {
   return t as! U
-  // CHECK: unconditional_checked_cast_addr {{.*}} T in {{%.*}} : $*T to U in [[DOWNCAST_ADDR:%.*]] : $*U
-  // CHECK: [[DOWNCAST:%.*]] = load [take] [[DOWNCAST_ADDR]]
-  // CHECK: return [[DOWNCAST]] : $U
+  // Error bridging can change the identity of class-constrained archetypes.
+  // CHECK-objc: unconditional_checked_cast_addr {{.*}} T in {{%.*}} : $*T to U in [[DOWNCAST_ADDR:%.*]] : $*U
+  // CHECK-objc: [[DOWNCAST:%.*]] = load [take] [[DOWNCAST_ADDR]]
+  // CHECK-objc: return [[DOWNCAST]] : $U
+
+  // CHECK-native: [[DOWNCAST:%.*]] = unconditional_checked_cast {{.*}} : $T to $U
 }
 
 // CHECK-LABEL: sil hidden @_TF13generic_casts34class_archetype_is_class_archetype{{.*}}
 func class_archetype_is_class_archetype
 <T:ClassBound, U:ClassBound>(_ t:T, u:U.Type) -> Bool {
   return t is U
-  // CHECK: checked_cast_addr_br {{.*}} T in {{%.*}} : $*T to U in {{%.*}} : $*U
+  // Error bridging can change the identity of class-constrained archetypes.
+  // CHECK-objc: checked_cast_addr_br {{.*}} T in {{%.*}} : $*T to U in {{%.*}} : $*U
+  // CHECK-native: checked_cast_br {{.*}} : $T to $U
 }
 
 // CHECK-LABEL: sil hidden @_TF13generic_casts38opaque_archetype_to_addr_only_concrete{{.*}}
@@ -156,16 +161,19 @@
 func class_existential_to_class_archetype
 <T:ClassBound>(_ p:ClassBound) -> T {
   return p as! T
-  // CHECK: unconditional_checked_cast_addr {{.*}} ClassBound in {{%.*}} : $*ClassBound to T in [[DOWNCAST_ADDR:%.*]] : $*T
-  // CHECK: [[DOWNCAST:%.*]] = load [take] [[DOWNCAST_ADDR]]
-  // CHECK: return [[DOWNCAST]] : $T
+  // CHECK-objc: unconditional_checked_cast_addr {{.*}} ClassBound in {{%.*}} : $*ClassBound to T in [[DOWNCAST_ADDR:%.*]] : $*T
+  // CHECK-objc: [[DOWNCAST:%.*]] = load [take] [[DOWNCAST_ADDR]]
+  // CHECK-objc: return [[DOWNCAST]] : $T
+
+  // CHECK-native: [[DOWNCAST:%.*]] = unconditional_checked_cast {{.*}} : $ClassBound to $T
 }
 
 // CHECK-LABEL: sil hidden @_TF13generic_casts36class_existential_is_class_archetype{{.*}}
 func class_existential_is_class_archetype
 <T:ClassBound>(_ p:ClassBound, _: T) -> Bool {
   return p is T
-  // CHECK: checked_cast_addr_br {{.*}} ClassBound in {{%.*}} : $*ClassBound to T in {{%.*}} : $*T
+  // CHECK-objc: checked_cast_addr_br {{.*}} ClassBound in {{%.*}} : $*ClassBound to T in {{%.*}} : $*T
+  // CHECK-native: checked_cast_br {{.*}} : $ClassBound to $T
 }
 
 // CHECK-LABEL: sil hidden @_TF13generic_casts40opaque_existential_to_addr_only_concrete{{.*}}
diff --git a/test/SILOptimizer/cse.sil b/test/SILOptimizer/cse.sil
index b48fbb6..104fe80 100644
--- a/test/SILOptimizer/cse.sil
+++ b/test/SILOptimizer/cse.sil
@@ -720,104 +720,6 @@
   %33 = return %32 : $()
 }
 
-// CHECK-LABEL: sil @cse_value_metatype
-// CHECK: value_metatype $@objc_metatype
-// CHECK: objc_metatype_to_object
-// CHECK-NOT: value_metatype $@objc_metatype
-// CHECK: strong_release
-// CHECK: return
-sil @cse_value_metatype : $@convention(thin) <T where T : AnyObject> (@owned T) -> @owned (AnyObject, AnyObject) {
-bb0(%0 : $T):
-  %2 = value_metatype $@objc_metatype T.Type, %0 : $T
-  %4 = objc_metatype_to_object %2 : $@objc_metatype T.Type to $AnyObject
-  %5 = value_metatype $@objc_metatype T.Type, %0 : $T
-  %7 = objc_metatype_to_object %5 : $@objc_metatype T.Type to $AnyObject
-  strong_release %0 : $T
-  %9 = tuple (%4: $AnyObject, %7: $AnyObject)
-  return %9 : $(AnyObject, AnyObject)
-}
-
-
-@objc(XX) protocol XX {
-}
-
-// CHECK-LABEL: sil @cse_existential_metatype
-// CHECK: existential_metatype $@objc_metatype
-// CHECK: objc_existential_metatype_to_object
-// CHECK-NOT: existential_metatype $@objc_metatype
-// CHECK: strong_release
-// CHECK: return
-sil @cse_existential_metatype : $@convention(thin) (@owned XX) -> @owned (AnyObject, AnyObject) {
-bb0(%0 : $XX):
-  %2 = existential_metatype $@objc_metatype XX.Type, %0 : $XX
-  %4 = objc_existential_metatype_to_object %2 : $@objc_metatype XX.Type to $AnyObject
-  %5 = existential_metatype $@objc_metatype XX.Type, %0 : $XX
-  %6 = objc_existential_metatype_to_object %5 : $@objc_metatype XX.Type to $AnyObject
-  strong_release %0 : $XX
-  %7 = tuple (%4: $AnyObject, %6: $AnyObject)
-  return %7 : $(AnyObject, AnyObject)
-}
-
-// CHECK-LABEL: sil @nocse_existential_metatype_addr
-// CHECK: store
-// CHECK: existential_metatype $@thick Any.Type
-// CHECK: store
-// CHECK: existential_metatype $@thick Any.Type
-// CHECK: return
-sil @nocse_existential_metatype_addr : $@convention(thin) (@owned B, @owned B) -> (@thick Any.Type, @thick Any.Type) {
-bb0(%0 : $B, %1 : $B):
-  %2 = alloc_stack $Any
-  %3 = init_existential_addr %2 : $*Any, $B
-  store %0 to %3 : $*B
-  %5 = existential_metatype $@thick Any.Type, %2 : $*Any
-  store %1 to %3 : $*B
-  %7 = existential_metatype $@thick Any.Type, %2 : $*Any
-  strong_release %1 : $B
-  strong_release %0 : $B
-  %99 = tuple (%5 : $@thick Any.Type, %7 : $@thick Any.Type)
-  dealloc_stack %2 : $*Any
-  return %99 : $(@thick Any.Type, @thick Any.Type)
-}
-
-
-// CHECK-LABEL: sil @cse_objc_metatype_to_object
-// CHECK: value_metatype $@objc_metatype
-// CHECK: objc_metatype_to_object
-// CHECK-NOT: value_metatype $@objc_metatype
-// CHECK-NOT: objc_metatype_to_object
-// CHECK: strong_release
-// CHECK: return
-sil @cse_objc_metatype_to_object : $@convention(thin) <T where T : AnyObject> (@owned T) -> @owned (AnyObject, AnyObject) {
-bb0(%0 : $T):
-  %2 = value_metatype $@objc_metatype T.Type, %0 : $T
-  %4 = objc_metatype_to_object %2 : $@objc_metatype T.Type to $AnyObject
-  %5 = value_metatype $@objc_metatype T.Type, %0 : $T
-  %7 = objc_metatype_to_object %5 : $@objc_metatype T.Type to $AnyObject
-  strong_release %0 : $T
-  %9 = tuple (%4: $AnyObject, %7: $AnyObject)
-  return %9 : $(AnyObject, AnyObject)
-}
-
-
-// CHECK-LABEL: sil @cse_objc_existential_metatype_to_object
-// CHECK: existential_metatype $@objc_metatype
-// CHECK: objc_existential_metatype_to_object
-// CHECK-NOT: existential_metatype $@objc_metatype
-// CHECK-NOT: objc_existential_metatype_to_object
-// CHECK: strong_release
-// CHECK: return
-sil @cse_objc_existential_metatype_to_object : $@convention(thin) (@owned XX) -> @owned (AnyObject, AnyObject) {
-bb0(%0 : $XX):
-  %2 = existential_metatype $@objc_metatype XX.Type, %0 : $XX
-  %4 = objc_existential_metatype_to_object %2 : $@objc_metatype XX.Type to $AnyObject
-  %5 = existential_metatype $@objc_metatype XX.Type, %0 : $XX
-  %6 = objc_existential_metatype_to_object %5 : $@objc_metatype XX.Type to $AnyObject
-  strong_release %0 : $XX
-  %7 = tuple (%4: $AnyObject, %6: $AnyObject)
-  return %7 : $(AnyObject, AnyObject)
-}
-
-
 // CHECK-LABEL: sil @cse_raw_pointer_to_ref
 // CHECK: raw_pointer_to_ref
 // CHECK-NOT: raw_pointer_to_ref
@@ -937,24 +839,6 @@
   return %3: $(Builtin.Int1, Builtin.Int1)
 }
 
-
-@objc
-class XXX {
-}
-
-// CHECK-LABEL: sil @cse_objc_to_thick_metatype
-// CHECK: objc_to_thick_metatype
-// CHECK-NOT: objc_to_thick_metatype
-// CHECK: tuple
-// CHECK: return
-sil @cse_objc_to_thick_metatype : $@convention(thin) (@objc_metatype XXX.Type) -> (@thick XXX.Type, @thick XXX.Type) {
-bb0(%0 : $@objc_metatype XXX.Type):
-  %1 = objc_to_thick_metatype %0 : $@objc_metatype XXX.Type to $@thick XXX.Type
-  %2 = objc_to_thick_metatype %0 : $@objc_metatype XXX.Type to $@thick XXX.Type
-  %3 = tuple (%1: $@thick XXX.Type, %2: $@thick XXX.Type)
-  return %3: $(@thick XXX.Type, @thick XXX.Type)
-}
-
 // CHECK-LABEL: sil @cse_bridge_object_to_ref
 // CHECK: bridge_object_to_ref
 // CHECK-NOT: bridge_object_to_ref
diff --git a/test/SILOptimizer/cse_objc.sil b/test/SILOptimizer/cse_objc.sil
index 9e10fd7..c81d334 100644
--- a/test/SILOptimizer/cse_objc.sil
+++ b/test/SILOptimizer/cse_objc.sil
@@ -154,6 +154,118 @@
   return %13 : $()
 }
 
+// CHECK-LABEL: sil @cse_value_metatype
+// CHECK: value_metatype $@objc_metatype
+// CHECK: objc_metatype_to_object
+// CHECK-NOT: value_metatype $@objc_metatype
+// CHECK: strong_release
+// CHECK: return
+sil @cse_value_metatype : $@convention(thin) <T where T : AnyObject> (@owned T) -> @owned (AnyObject, AnyObject) {
+bb0(%0 : $T):
+  %2 = value_metatype $@objc_metatype T.Type, %0 : $T
+  %4 = objc_metatype_to_object %2 : $@objc_metatype T.Type to $AnyObject
+  %5 = value_metatype $@objc_metatype T.Type, %0 : $T
+  %7 = objc_metatype_to_object %5 : $@objc_metatype T.Type to $AnyObject
+  strong_release %0 : $T
+  %9 = tuple (%4: $AnyObject, %7: $AnyObject)
+  return %9 : $(AnyObject, AnyObject)
+}
+
+// CHECK-LABEL: sil @cse_existential_metatype
+// CHECK: existential_metatype $@objc_metatype
+// CHECK: objc_existential_metatype_to_object
+// CHECK-NOT: existential_metatype $@objc_metatype
+// CHECK: strong_release
+// CHECK: return
+sil @cse_existential_metatype : $@convention(thin) (@owned XX) -> @owned (AnyObject, AnyObject) {
+bb0(%0 : $XX):
+  %2 = existential_metatype $@objc_metatype XX.Type, %0 : $XX
+  %4 = objc_existential_metatype_to_object %2 : $@objc_metatype XX.Type to $AnyObject
+  %5 = existential_metatype $@objc_metatype XX.Type, %0 : $XX
+  %6 = objc_existential_metatype_to_object %5 : $@objc_metatype XX.Type to $AnyObject
+  strong_release %0 : $XX
+  %7 = tuple (%4: $AnyObject, %6: $AnyObject)
+  return %7 : $(AnyObject, AnyObject)
+}
+
+class B {}
+
+// CHECK-LABEL: sil @nocse_existential_metatype_addr
+// CHECK: store
+// CHECK: existential_metatype $@thick Any.Type
+// CHECK: store
+// CHECK: existential_metatype $@thick Any.Type
+// CHECK: return
+sil @nocse_existential_metatype_addr : $@convention(thin) (@owned B, @owned B) -> (@thick Any.Type, @thick Any.Type) {
+bb0(%0 : $B, %1 : $B):
+  %2 = alloc_stack $Any
+  %3 = init_existential_addr %2 : $*Any, $B
+  store %0 to %3 : $*B
+  %5 = existential_metatype $@thick Any.Type, %2 : $*Any
+  store %1 to %3 : $*B
+  %7 = existential_metatype $@thick Any.Type, %2 : $*Any
+  strong_release %1 : $B
+  strong_release %0 : $B
+  %99 = tuple (%5 : $@thick Any.Type, %7 : $@thick Any.Type)
+  dealloc_stack %2 : $*Any
+  return %99 : $(@thick Any.Type, @thick Any.Type)
+}
+
+
+// CHECK-LABEL: sil @cse_objc_metatype_to_object
+// CHECK: value_metatype $@objc_metatype
+// CHECK: objc_metatype_to_object
+// CHECK-NOT: value_metatype $@objc_metatype
+// CHECK-NOT: objc_metatype_to_object
+// CHECK: strong_release
+// CHECK: return
+sil @cse_objc_metatype_to_object : $@convention(thin) <T where T : AnyObject> (@owned T) -> @owned (AnyObject, AnyObject) {
+bb0(%0 : $T):
+  %2 = value_metatype $@objc_metatype T.Type, %0 : $T
+  %4 = objc_metatype_to_object %2 : $@objc_metatype T.Type to $AnyObject
+  %5 = value_metatype $@objc_metatype T.Type, %0 : $T
+  %7 = objc_metatype_to_object %5 : $@objc_metatype T.Type to $AnyObject
+  strong_release %0 : $T
+  %9 = tuple (%4: $AnyObject, %7: $AnyObject)
+  return %9 : $(AnyObject, AnyObject)
+}
+
+
+// CHECK-LABEL: sil @cse_objc_existential_metatype_to_object
+// CHECK: existential_metatype $@objc_metatype
+// CHECK: objc_existential_metatype_to_object
+// CHECK-NOT: existential_metatype $@objc_metatype
+// CHECK-NOT: objc_existential_metatype_to_object
+// CHECK: strong_release
+// CHECK: return
+sil @cse_objc_existential_metatype_to_object : $@convention(thin) (@owned XX) -> @owned (AnyObject, AnyObject) {
+bb0(%0 : $XX):
+  %2 = existential_metatype $@objc_metatype XX.Type, %0 : $XX
+  %4 = objc_existential_metatype_to_object %2 : $@objc_metatype XX.Type to $AnyObject
+  %5 = existential_metatype $@objc_metatype XX.Type, %0 : $XX
+  %6 = objc_existential_metatype_to_object %5 : $@objc_metatype XX.Type to $AnyObject
+  strong_release %0 : $XX
+  %7 = tuple (%4: $AnyObject, %6: $AnyObject)
+  return %7 : $(AnyObject, AnyObject)
+}
+
+@objc
+class XXX {
+}
+
+// CHECK-LABEL: sil @cse_objc_to_thick_metatype
+// CHECK: objc_to_thick_metatype
+// CHECK-NOT: objc_to_thick_metatype
+// CHECK: tuple
+// CHECK: return
+sil @cse_objc_to_thick_metatype : $@convention(thin) (@objc_metatype XXX.Type) -> (@thick XXX.Type, @thick XXX.Type) {
+bb0(%0 : $@objc_metatype XXX.Type):
+  %1 = objc_to_thick_metatype %0 : $@objc_metatype XXX.Type to $@thick XXX.Type
+  %2 = objc_to_thick_metatype %0 : $@objc_metatype XXX.Type to $@thick XXX.Type
+  %3 = tuple (%1: $@thick XXX.Type, %2: $@thick XXX.Type)
+  return %3: $(@thick XXX.Type, @thick XXX.Type)
+}
+
 sil_vtable Bar {
   #Bar.init!initializer.1: _TFC4test3BarcfMS0_FT_S0_	// test.Bar.init (test.Bar.Type)() -> test.Bar
   #Bar.walk!1: _TFC4test3Bar4walkfS0_FT_T_	// test.Bar.walk (test.Bar)() -> ()
diff --git a/test/SILOptimizer/sil_combine.sil b/test/SILOptimizer/sil_combine.sil
index ccb32a2..aa8d6fb 100644
--- a/test/SILOptimizer/sil_combine.sil
+++ b/test/SILOptimizer/sil_combine.sil
@@ -1969,34 +1969,6 @@
   init()
 }
 
-// CHECK-LABEL: sil @remove_release_objc_metatype_to_object
-// CHECK-NOT: strong_release {{%[0-9]+}} : $AnyObject
-sil @remove_release_objc_metatype_to_object : $@convention(thin) () -> () {
-bb0:
-  %0 = metatype $@thick XXImpl.Type                // user: %1
-  %1 = thick_to_objc_metatype %0 : $@thick XXImpl.Type to $@objc_metatype XXImpl.Type // user: %2
-  %2 = objc_metatype_to_object %1 : $@objc_metatype XXImpl.Type to $AnyObject // users: %3, %4
-  debug_value %2 : $AnyObject  // id: %3
-  strong_release %2 : $AnyObject                  // id: %4
-  %5 = tuple ()                                   // user: %6
-  return %5 : $()                                 // id: %6
-}
-
-// CHECK-LABEL: sil @remove_release_objc_existential_metatype_to_object
-// CHECK-NOT: strong_release {{%[0-9]+}} : $AnyObject
-sil @remove_release_objc_existential_metatype_to_object: $@convention(thin) (@owned XX) -> () {
-bb0(%0 : $XX):
-  debug_value %0 : $XX                   // id: %1
-  %2 = existential_metatype $@thick XX.Type, %0 : $XX // user: %3
-  %3 = thick_to_objc_metatype %2 : $@thick XX.Type to $@objc_metatype XX.Type // user: %4
-  %4 = objc_existential_metatype_to_object %3 : $@objc_metatype XX.Type to $AnyObject // users: %5, %6
-  debug_value %4 : $AnyObject, let, name "obj1" // id: %5
-  strong_release %4 : $AnyObject                  // id: %6
-  strong_release %0 : $XX                         // id: %7
-  %8 = tuple ()                                   // user: %9
-  return %8 : $()                                 // id: %9
-}
-
 // CHECK-LABEL: sil @unowned_round_trips : $@convention(thin) (B, @sil_unowned B, AnyObject, @sil_unmanaged AnyObject) -> (B, @sil_unowned B, AnyObject, @sil_unmanaged AnyObject) {
 // CHECK: bb0(
 // CHECK-NEXT: tuple
diff --git a/test/SILOptimizer/sil_combine_objc.sil b/test/SILOptimizer/sil_combine_objc.sil
index e631e6d..ed1e877 100644
--- a/test/SILOptimizer/sil_combine_objc.sil
+++ b/test/SILOptimizer/sil_combine_objc.sil
@@ -151,3 +151,39 @@
   return %4 : $Int
 }
 
+@objc(XX) protocol XX {
+}
+
+class XXImpl : XX {
+  @objc deinit
+  init()
+}
+
+// CHECK-LABEL: sil @remove_release_objc_metatype_to_object
+// CHECK-NOT: strong_release {{%[0-9]+}} : $AnyObject
+sil @remove_release_objc_metatype_to_object : $@convention(thin) () -> () {
+bb0:
+  %0 = metatype $@thick XXImpl.Type                // user: %1
+  %1 = thick_to_objc_metatype %0 : $@thick XXImpl.Type to $@objc_metatype XXImpl.Type // user: %2
+  %2 = objc_metatype_to_object %1 : $@objc_metatype XXImpl.Type to $AnyObject // users: %3, %4
+  debug_value %2 : $AnyObject  // id: %3
+  strong_release %2 : $AnyObject                  // id: %4
+  %5 = tuple ()                                   // user: %6
+  return %5 : $()                                 // id: %6
+}
+
+// CHECK-LABEL: sil @remove_release_objc_existential_metatype_to_object
+// CHECK-NOT: strong_release {{%[0-9]+}} : $AnyObject
+sil @remove_release_objc_existential_metatype_to_object: $@convention(thin) (@owned XX) -> () {
+bb0(%0 : $XX):
+  debug_value %0 : $XX                   // id: %1
+  %2 = existential_metatype $@thick XX.Type, %0 : $XX // user: %3
+  %3 = thick_to_objc_metatype %2 : $@thick XX.Type to $@objc_metatype XX.Type // user: %4
+  %4 = objc_existential_metatype_to_object %3 : $@objc_metatype XX.Type to $AnyObject // users: %5, %6
+  debug_value %4 : $AnyObject, let, name "obj1" // id: %5
+  strong_release %4 : $AnyObject                  // id: %6
+  strong_release %0 : $XX                         // id: %7
+  %8 = tuple ()                                   // user: %9
+  return %8 : $()                                 // id: %9
+}
+
diff --git a/test/stmt/Inputs/Foundation-with-NSError.swift b/test/stmt/Inputs/Foundation-with-NSError.swift
new file mode 100644
index 0000000..0005b24
--- /dev/null
+++ b/test/stmt/Inputs/Foundation-with-NSError.swift
@@ -0,0 +1 @@
+public class NSError: Error {}
diff --git a/test/stmt/errors_nonobjc.swift b/test/stmt/errors_nonobjc.swift
new file mode 100644
index 0000000..2c3369d
--- /dev/null
+++ b/test/stmt/errors_nonobjc.swift
@@ -0,0 +1,19 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: %target-swift-frontend -emit-module -module-name Foundation -o %t/Foundation.swiftmodule %S/Inputs/Foundation-with-NSError.swift
+// RUN: %target-swift-frontend -I %t -typecheck -verify %s
+// UNSUPPORTED: objc_interop
+
+import Foundation
+
+// Catching `as NSError` ought *not* to be exhaustive when ObjC interop is
+// disabled. It's just another error type.
+
+func bar() throws {}
+
+func foo() {
+  do {
+    try bar() // expected-error{{enclosing catch is not exhaustive}}
+  } catch _ as NSError {
+  }
+}
diff --git a/test/stmt/errors_objc.swift b/test/stmt/errors_objc.swift
new file mode 100644
index 0000000..42caa10
--- /dev/null
+++ b/test/stmt/errors_objc.swift
@@ -0,0 +1,14 @@
+// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s
+// REQUIRES: objc_interop
+
+import Foundation
+
+// Catching `as NSError` ought to be exhaustive when ObjC interop is enabled.
+func bar() throws {}
+
+func foo() {
+  do {
+    try bar()
+  } catch _ as NSError {
+  }
+}
diff --git a/tools/sil-opt/SILOpt.cpp b/tools/sil-opt/SILOpt.cpp
index 1a588dc..915e29d 100644
--- a/tools/sil-opt/SILOpt.cpp
+++ b/tools/sil-opt/SILOpt.cpp
@@ -234,6 +234,8 @@
   Invocation.getLangOptions().DisableAvailabilityChecking = true;
   Invocation.getLangOptions().EnableAccessControl = false;
   Invocation.getLangOptions().EnableObjCAttrRequiresFoundation = false;
+  Invocation.getLangOptions().EnableObjCInterop =
+    llvm::Triple(Target).isOSDarwin();
 
   Invocation.getLangOptions().ASTVerifierProcessCount =
       ASTVerifierProcessCount;