Merge pull request #13207 from rjmccall/bridging-peephole-blocks-and-optionality-4.1

[4.1] Weaken an assertion: conversions can go through Any
diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp
index 5cb229c..7de2c5d 100644
--- a/lib/SILGen/SILGenBridging.cpp
+++ b/lib/SILGen/SILGenBridging.cpp
@@ -665,6 +665,14 @@
   // The destination type should be AnyObject in this case.
   assert(bridgedType->isEqual(SGF.getASTContext().getAnyObjectType()));
 
+  // Blocks bridge to id with a cast.
+  if (auto nativeFnType = dyn_cast<AnyFunctionType>(nativeType)) {
+    if (nativeFnType->getRepresentation() ==
+          FunctionTypeRepresentation::Block) {
+      return SGF.B.createBlockToAnyObject(loc, v, loweredBridgedTy);
+    }
+  }
+
   // If the input argument is known to be an existential, save the runtime
   // some work by opening it.
   if (nativeType->isExistentialType()) {
diff --git a/lib/SILGen/SILGenBuilder.cpp b/lib/SILGen/SILGenBuilder.cpp
index 4411825..83e7ea1 100644
--- a/lib/SILGen/SILGenBuilder.cpp
+++ b/lib/SILGen/SILGenBuilder.cpp
@@ -714,6 +714,18 @@
   return ManagedValue::forUnmanaged(v);
 }
 
+ManagedValue SILGenBuilder::createBlockToAnyObject(SILLocation loc,
+                                                   ManagedValue v,
+                                                   SILType destType) {
+  assert(destType.isAnyObject());
+  assert(v.getType().is<SILFunctionType>());
+  assert(v.getType().castTo<SILFunctionType>()->getRepresentation() ==
+           SILFunctionTypeRepresentation::Block);
+
+  // For now, we don't have a better instruction than this.
+  return createUncheckedRefCast(loc, v, destType);
+}
+
 //===----------------------------------------------------------------------===//
 //                            Switch Enum Builder
 //===----------------------------------------------------------------------===//
diff --git a/lib/SILGen/SILGenBuilder.h b/lib/SILGen/SILGenBuilder.h
index 29ddeac..dc1697e 100644
--- a/lib/SILGen/SILGenBuilder.h
+++ b/lib/SILGen/SILGenBuilder.h
@@ -291,6 +291,10 @@
   ManagedValue createOpenExistentialBoxValue(SILLocation loc,
                                           ManagedValue original, SILType type);
 
+  /// Convert a @convention(block) value to AnyObject.
+  ManagedValue createBlockToAnyObject(SILLocation loc, ManagedValue block,
+                                      SILType type);
+
   using SILBuilder::createOptionalSome;
   ManagedValue createOptionalSome(SILLocation Loc, ManagedValue Arg);
   ManagedValue createManagedOptionalNone(SILLocation Loc, SILType Type);
diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp
index 7d8193d..d749ca9 100644
--- a/lib/SILGen/SILGenConvert.cpp
+++ b/lib/SILGen/SILGenConvert.cpp
@@ -1269,7 +1269,14 @@
   }
 
   assert(to->isAny());
-  return !from->isAnyClassReferenceType();
+
+  // Types that we can easily transform into AnyObject:
+  //   - classes and class-bounded archetypes
+  //   - class existentials, even if not pure-@objc
+  //   - @convention(objc) metatypes
+  //   - @convention(block) functions
+  return !from->isAnyClassReferenceType() &&
+         !from->isBridgeableObjectType();
 }
 
 /// Check whether this conversion is Any??? to AnyObject???.  If the result
@@ -1283,7 +1290,7 @@
   }
 
   if (from->isAny()) {
-    assert(to->isAnyObject());
+    assert(to->lookThroughAllAnyOptionalTypes()->isAnyObject());
     return true;
   }
   return false;
diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp
index f00e3d0..0768297 100644
--- a/lib/SILGen/SILGenPoly.cpp
+++ b/lib/SILGen/SILGenPoly.cpp
@@ -517,6 +517,14 @@
                                    SGF.getLoweredLoadableType(outputSubstType));
   }
 
+  // - block to AnyObject conversion
+  if (outputSubstType->isAnyObject()) {
+    if (auto inputFnType = dyn_cast<AnyFunctionType>(inputSubstType)) {
+      if (inputFnType->getRepresentation() == FunctionTypeRepresentation::Block)
+        return SGF.B.createBlockToAnyObject(Loc, v, loweredResultTy);
+    }
+  }
+
   //  - existentials
   if (outputSubstType->isAnyExistentialType()) {
     // We have to re-abstract payload if its a metatype or a function
diff --git a/test/Inputs/clang-importer-sdk/usr/include/Foundation.h b/test/Inputs/clang-importer-sdk/usr/include/Foundation.h
index e6a416e..a69ca34 100644
--- a/test/Inputs/clang-importer-sdk/usr/include/Foundation.h
+++ b/test/Inputs/clang-importer-sdk/usr/include/Foundation.h
@@ -1145,3 +1145,4 @@
 @end
 
 __nullable id returnNullableId(void);
+void takeNullableId(__nullable id);
diff --git a/test/SILGen/objc_bridging_peephole.swift b/test/SILGen/objc_bridging_peephole.swift
index e9483c0..f3df4cb 100644
--- a/test/SILGen/objc_bridging_peephole.swift
+++ b/test/SILGen/objc_bridging_peephole.swift
@@ -645,7 +645,7 @@
 
 // rdar://35402853
 //   Make sure that we don't peephole AnyObject? -> Any? -> AnyObject naively.
-// CHECK: sil hidden @_T022objc_bridging_peephole017testOptionalToNonE6BridgeyyF
+// CHECK-LABEL: sil hidden @_T022objc_bridging_peephole017testOptionalToNonE6BridgeyyF
 func testOptionalToNonOptionalBridge() {
   // CHECK: apply {{.*}}() : $@convention(c) () -> @autoreleased Optional<AnyObject>
   // CHECK: function_ref @_T0s018_bridgeAnyObjectToB0ypyXlSgF :
@@ -653,3 +653,16 @@
   // CHECK: apply [[T0]]<Any>
   useAnyObject(returnNullableId() as AnyObject)
 } // CHECK: end sil function '_T022objc_bridging_peephole017testOptionalToNonE6BridgeyyF'
+
+// CHECK-LABEL: sil hidden @_T022objc_bridging_peephole34testBlockToOptionalAnyObjectBridgeyyyXB5block_tF
+func testBlockToOptionalAnyObjectBridge(block: @escaping @convention(block) () -> ()) {
+  // CHECK:      [[FN:%.*]] = function_ref @takeNullableId : $@convention(c) (Optional<AnyObject>) -> ()
+  // CHECK-NEXT: [[T0:%.*]] = begin_borrow {{%.*}} : $@convention(block) () -> ()
+  // CHECK-NEXT: [[T1:%.*]] = copy_value [[T0]]
+  // CHECK-NEXT: [[REF:%.*]] = unchecked_ref_cast [[T1]] : $@convention(block) () -> () to $AnyObject
+  // CHECK-NEXT: [[OPTREF:%.*]] = enum $Optional<AnyObject>, #Optional.some!enumelt.1, [[REF]] : $AnyObject
+  // CHECK-NEXT: end_borrow [[T0]]
+  // CHECK-NEXT: apply [[FN]]([[OPTREF]])
+  // CHECK-NEXT: destroy_value [[OPTREF]]
+  takeNullableId(block as Any)
+}