Merge pull request #12729 from jrose-apple/bridging-typedefs-is-hard (#12737)

[ClangImporter] Bridging can happen even without full bridgeability

rdar://problem/34913634
diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp
index 6aacdc6..657fe4d 100644
--- a/lib/ClangImporter/ImportDecl.cpp
+++ b/lib/ClangImporter/ImportDecl.cpp
@@ -3457,6 +3457,8 @@
                                                 Impl.CurrentVersion))
           declType = Impl.getClangASTContext().getTypedefType(newtypeDecl);
 
+      // Note that we deliberately don't bridge most globals because we want to
+      // preserve pointer identity.
       Type type = Impl.importType(declType,
                                   (isAudited ? ImportTypeKind::AuditedVariable
                                    : ImportTypeKind::Variable),
diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp
index b7d318d..e8917a4 100644
--- a/lib/ClangImporter/ImportType.cpp
+++ b/lib/ClangImporter/ImportType.cpp
@@ -684,13 +684,18 @@
         auto typedefBridgeability = getTypedefBridgeability(underlyingType);
 
         // Figure out the typedef we should actually use.
-        SwiftTypeConverter innerConverter(Impl, AllowNSUIntegerAsInt, Bridging);
+        auto underlyingBridgeability =
+          (Bridging == Bridgeability::Full
+             ? typedefBridgeability : Bridgeability::None);
+        SwiftTypeConverter innerConverter(Impl, AllowNSUIntegerAsInt,
+                                          underlyingBridgeability);
         auto underlyingResult = innerConverter.Visit(underlyingType);
 
         // If we used different bridgeability than this typedef normally
-        // would, and therefore the underlying type is different from the
-        // mapping of the typedef, use the underlying type.
-        if (Bridging != typedefBridgeability &&
+        // would because we're in a non-bridgeable context, and therefore
+        // the underlying type is different from the mapping of the typedef,
+        // use the underlying type.
+        if (underlyingBridgeability != typedefBridgeability &&
             !underlyingResult.AbstractType->isEqual(mappedType)) {
           return underlyingResult;
         }
@@ -1291,29 +1296,9 @@
 
   // SwiftTypeConverter turns block pointers into @convention(block) types.
   // In a bridgeable context, or in the direct structure of a typedef,
-  // we would prefer to instead use the default Swift convention.  But this
-  // does means that, when we're using a typedef of a block pointer type in
-  // an unbridgable context, we need to go back and do a fully-unbridged
-  // import of the underlying type.
+  // we would prefer to instead use the default Swift convention.
   if (hint == ImportHint::Block) {
-    if (bridging == Bridgeability::None) {
-      if (auto typedefType = clangType->getAs<clang::TypedefType>()) {
-        // In non-bridged contexts, drop the typealias sugar for blocks.
-        // FIXME: This will do the wrong thing if there's any adjustment to do
-        // besides optionality.
-        Type underlyingTy = impl.importType(typedefType->desugar(),
-                                            importKind,
-                                            allowNSUIntegerAsInt,
-                                            bridging,
-                                            OTK_None);
-        if (Type unwrappedTy = underlyingTy->getAnyOptionalObjectType())
-          underlyingTy = unwrappedTy;
-        if (!underlyingTy->isEqual(importedType))
-          importedType = underlyingTy;
-      }
-    }
-
-    if (bridging == Bridgeability::Full) {
+    if (canBridgeTypes(importKind) || importKind == ImportTypeKind::Typedef) {
       auto fTy = importedType->castTo<FunctionType>();
       FunctionType::ExtInfo einfo = fTy->getExtInfo();
       if (einfo.getRepresentation() != FunctionTypeRepresentation::Swift) {
@@ -1356,7 +1341,6 @@
   // If we have a bridged Objective-C type and we are allowed to
   // bridge, do so.
   if (hint == ImportHint::ObjCBridged &&
-      bridging == Bridgeability::Full &&
       canBridgeTypes(importKind) &&
       importKind != ImportTypeKind::PropertyWithReferenceSemantics) {
     // id and Any can be bridged without Foundation. There would be
diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h
index d904d35..d78fe78 100644
--- a/lib/ClangImporter/ImporterImpl.h
+++ b/lib/ClangImporter/ImporterImpl.h
@@ -191,10 +191,13 @@
   Full
 };
 
+/// Controls whether a typedef for \p type should name the fully-bridged Swift
+/// type or the original Clang type.
+///
+/// In either case we end up losing sugar at some uses sites, so this is more
+/// about what the right default is.
 static inline Bridgeability getTypedefBridgeability(clang::QualType type) {
-  return (type->isBlockPointerType() || type->isFunctionType())
-            ? Bridgeability::Full
-            : Bridgeability::None;
+  return type->isBlockPointerType() ? Bridgeability::Full : Bridgeability::None;
 }
 
 /// \brief Describes the kind of the C type that can be mapped to a stdlib
@@ -907,22 +910,45 @@
   ///
   /// \param type The Clang type to import.
   ///
-  /// \param kind The kind of type import we're performing.
+  /// \param kind A classification of the immediate context in which this type
+  ///   will be used. Different contexts result in the type being imported
+  ///   differently; for example, CF types are normally considered Unmanaged,
+  ///   but in parameter position they are known to always be passed at +0.
+  ///   See also the \p topLevelBridgeability parameter.
   ///
   /// \param allowNSUIntegerAsInt If true, NSUInteger will be imported as Int
-  ///        in certain contexts. If false, it will always be imported as UInt.
+  ///   in certain contexts. If false, it will always be imported as UInt.
   ///
-  /// \param bridgeability Whether we can bridge types in this context.
-  ///        This may restrict the ability to bridge types even in contexts
-  ///        that otherwise allow bridging, such as function results and
-  ///        parameters.
+  /// \param topLevelBridgeability A classification of the top-level context in
+  ///   which this type will be used. This and \p kind are used together to
+  ///   determine whether a type can be imported in a more Swifty way than
+  ///   a naive translation of its C type. Full bridgeability requires that SIL
+  ///   can get back to the original Clang type if it needs to, which implies
+  ///   that this type is part of a top-level declaration where we do bridging.
+  ///   Without full bridgeability, we can still do some Swifty importing (e.g.
+  ///   mapping NSString to String) if we're in an immediate context \p kind
+  ///   that allows bridging, but only in cases where Swift's default mapping
+  ///   "back" to C is the correct one. If the original type has something
+  ///   funny going on, we either have to use a less lossy version of the type
+  ///   (ObjCBool rather than Bool) or refuse to import it at all (a block with
+  ///   the \c ns_returns_retained attribute).
+  ///
+  /// \param optional If the imported type was a pointer-like type in C, this
+  ///   optionality is applied to the resulting Swift type.
+  ///
+  /// \param resugarNSErrorPointer If true, Objective-C's `NSError **` is
+  ///   imported as Foundation.NSErrorPointer rather than
+  ///   AutoreleasingUnsafeMutablePointer<...>. This is usually desirable
+  ///   behavior, but isn't necessary when we use Swift's \c throws anyway.
+  ///   Strictly speaking, though, this is a hack used to break cyclic
+  ///   dependencies.
   ///
   /// \returns The imported type, or null if this type could
-  /// not be represented in Swift.
+  ///   not be represented in Swift.
   Type importType(clang::QualType type,
                   ImportTypeKind kind,
                   bool allowNSUIntegerAsInt,
-                  Bridgeability bridgeability,
+                  Bridgeability topLevelBridgeability,
                   OptionalTypeKind optional = OTK_ImplicitlyUnwrappedOptional,
                   bool resugarNSErrorPointer = true);
 
diff --git a/test/ClangImporter/Inputs/custom-modules/ObjCParseExtras.h b/test/ClangImporter/Inputs/custom-modules/ObjCParseExtras.h
index 27849b3..320e4ab 100644
--- a/test/ClangImporter/Inputs/custom-modules/ObjCParseExtras.h
+++ b/test/ClangImporter/Inputs/custom-modules/ObjCParseExtras.h
@@ -209,3 +209,6 @@
 @interface BridgedTypedefs : NSObject
 @property (readonly,nonnull) NSArray<NSStringArray> *arrayOfArrayOfStrings;
 @end
+
+typedef NSString * _Nonnull (*FPTypedef)(NSString * _Nonnull);
+extern FPTypedef _Nonnull getFP(void);
diff --git a/test/ClangImporter/objc_id_as_any.swift b/test/ClangImporter/objc_id_as_any.swift
index 990435e..4d003b0 100644
--- a/test/ClangImporter/objc_id_as_any.swift
+++ b/test/ClangImporter/objc_id_as_any.swift
@@ -25,7 +25,7 @@
 idLover.takesId(x)
 idLover.takesId(y)
 
-install_global_event_handler(idLover) // expected-error {{cannot convert value of type 'NSIdLover' to expected argument type 'event_handler?' (aka 'Optional<@convention(c) (AnyObject) -> ()>')}}
+install_global_event_handler(idLover) // expected-error {{cannot convert value of type 'NSIdLover' to expected argument type 'event_handler?' (aka 'Optional<@convention(c) (Any) -> ()>')}}
 
 // FIXME: this should not type-check!
 // Function conversions are not legal when converting to a thin function type.
diff --git a/test/ClangImporter/objc_parse.swift b/test/ClangImporter/objc_parse.swift
index 2f10fe7..b0d587b 100644
--- a/test/ClangImporter/objc_parse.swift
+++ b/test/ClangImporter/objc_parse.swift
@@ -643,3 +643,9 @@
 func testBridgedTypedef(bt: BridgedTypedefs) {
   let _: Int = bt.arrayOfArrayOfStrings // expected-error{{'[[String]]'}}
 }
+
+func testBridgeFunctionPointerTypedefs(fptrTypedef: FPTypedef) {
+  // See also print_clang_bool_bridging.swift.
+  let _: Int = fptrTypedef // expected-error{{'@convention(c) (String) -> String'}}
+  let _: Int = getFP() // expected-error{{'@convention(c) (String) -> String'}}
+}
diff --git a/test/IDE/print_clang_bool_bridging.swift b/test/IDE/print_clang_bool_bridging.swift
index 50b305e..b1d1c94 100644
--- a/test/IDE/print_clang_bool_bridging.swift
+++ b/test/IDE/print_clang_bool_bridging.swift
@@ -42,13 +42,13 @@
 func testObjCBoolFnToBlockTypedef(_: @escaping ObjCBoolFn) -> ObjCBoolBlock
 func testDarwinBooleanFnToBlockTypedef(_: @escaping DarwinBooleanFn) -> DarwinBooleanBlock
 
-typealias CBoolFnToBlockType = (CBoolFn) -> CBoolBlock
-typealias ObjCBoolFnToBlockType = (ObjCBoolFn) -> ObjCBoolBlock
-typealias DarwinBooleanFnToBlockType = (DarwinBooleanFn) -> DarwinBooleanBlock
+typealias CBoolFnToBlockType = (CBoolFn) -> (Bool) -> Bool
+typealias ObjCBoolFnToBlockType = (ObjCBoolFn) -> (ObjCBool) -> ObjCBool
+typealias DarwinBooleanFnToBlockType = (DarwinBooleanFn) -> (DarwinBoolean) -> DarwinBoolean
 
-var globalObjCBoolFnToBlockFP: @convention(c) (ObjCBoolFn) -> @convention(block) (ObjCBool) -> ObjCBool
-var globalObjCBoolFnToBlockFPP: UnsafeMutablePointer<@convention(c) (ObjCBoolFn) -> @convention(block) (ObjCBool) -> ObjCBool>?
-var globalObjCBoolFnToBlockBP: @convention(block) (ObjCBoolFn) -> @convention(block) (ObjCBool) -> ObjCBool
+var globalObjCBoolFnToBlockFP: @convention(c) (ObjCBoolFn) -> (ObjCBool) -> ObjCBool
+var globalObjCBoolFnToBlockFPP: UnsafeMutablePointer<@convention(c) (ObjCBoolFn) -> (ObjCBool) -> ObjCBool>?
+var globalObjCBoolFnToBlockBP: @convention(block) (ObjCBoolFn) -> (ObjCBool) -> ObjCBool
 
 var globalCBoolFn: CBoolFn
 var globalObjCBoolFn: ObjCBoolFn
diff --git a/test/IDE/print_omit_needless_words.swift b/test/IDE/print_omit_needless_words.swift
index 7b525d6..9cf56bb 100644
--- a/test/IDE/print_omit_needless_words.swift
+++ b/test/IDE/print_omit_needless_words.swift
@@ -143,7 +143,7 @@
 // CHECK-FOUNDATION: func doSomethingElse(with: NSCopying & NSObjectProtocol)
 
 // Note: Function type -> "Function".
-// CHECK-FOUNDATION: func sort(_: @escaping @convention(c) (AnyObject, AnyObject) -> Int)
+// CHECK-FOUNDATION: func sort(_: @escaping @convention(c) (Any, Any) -> Int)
 
 // Note: Plural: NSArray without type arguments -> "Objects".
 // CHECK-FOUNDATION: func remove(_: [Any])
diff --git a/test/Interpreter/SDK/Inputs/BlockGlobals/BlockGlobals.h b/test/Interpreter/SDK/Inputs/BlockGlobals/BlockGlobals.h
new file mode 100644
index 0000000..7470413
--- /dev/null
+++ b/test/Interpreter/SDK/Inputs/BlockGlobals/BlockGlobals.h
@@ -0,0 +1,7 @@
+#import <Foundation/Foundation.h>
+
+extern NSString * _Nonnull (^ _Nonnull mutableBlockGlobal)(NSString * _Nonnull);
+extern NSString * _Nonnull (^ _Nonnull const constBlockGlobal)(NSString * _Nonnull);
+
+extern NSString * _Nonnull (* _Nonnull mutableFPGlobal)(NSString * _Nonnull);
+extern NSString * _Nonnull (* _Nonnull const constFPGlobal)(NSString * _Nonnull);
diff --git a/test/Interpreter/SDK/Inputs/BlockGlobals/BlockGlobals.m b/test/Interpreter/SDK/Inputs/BlockGlobals/BlockGlobals.m
new file mode 100644
index 0000000..c3518c3
--- /dev/null
+++ b/test/Interpreter/SDK/Inputs/BlockGlobals/BlockGlobals.m
@@ -0,0 +1,20 @@
+#import "BlockGlobals.h"
+
+NSString *(^mutableBlockGlobal)(NSString *) = ^ NSString *(NSString *arg) {
+  return [@"default mutable block: " stringByAppendingString: arg];
+};
+NSString *(^ const constBlockGlobal)(NSString *) = ^ NSString *(NSString *arg) {
+  return [@"default const block: " stringByAppendingString: arg];
+};
+
+static NSString *appendToDefault(NSString *arg) {
+  return [@"default mutable FP: " stringByAppendingString: arg];
+}
+
+NSString *(*mutableFPGlobal)(NSString *) = &appendToDefault;
+
+
+static NSString *appendToDefaultConst(NSString *arg) {
+  return [@"default const FP: " stringByAppendingString: arg];
+}
+NSString *(* const constFPGlobal)(NSString *) = &appendToDefaultConst;
diff --git a/test/Interpreter/SDK/block_globals.swift b/test/Interpreter/SDK/block_globals.swift
new file mode 100644
index 0000000..d22559c
--- /dev/null
+++ b/test/Interpreter/SDK/block_globals.swift
@@ -0,0 +1,29 @@
+// RUN: %empty-directory(%t)
+
+// RUN: %target-clang -fobjc-arc %S/Inputs/BlockGlobals/BlockGlobals.m -c -o %t/BlockGlobals.o
+// RUN: %target-build-swift %s -import-objc-header %S/Inputs/BlockGlobals/BlockGlobals.h %t/BlockGlobals.o -o %t/main
+// RUN: %target-run %t/main
+
+// REQUIRES: executable_test
+// REQUIRES: objc_interop
+
+import Foundation
+import StdlibUnittest
+
+var BlockGlobalsTestSuite = TestSuite("BlockGlobals")
+
+BlockGlobalsTestSuite.test("const block") {
+  expectEqual((@convention(block) (String) -> String).self as Any.Type, type(of: constBlockGlobal))
+  expectEqual("default const block: abc", constBlockGlobal("abc"))
+}
+
+BlockGlobalsTestSuite.test("const function pointer") {
+  expectEqual((@convention(c) (String) -> String).self as Any.Type, type(of: constFPGlobal))
+  expectEqual("default const FP: abc", constFPGlobal("abc"))
+}
+
+// FIXME: Add tests for mutable globals, including mutating them, once the
+// compiler supports it, as well as loading from the const globals without
+// immediately calling them.
+
+runAllTests()