Skip to content

Commit 48a4a71

Browse files
authored
Merge pull request #12729 from jrose-apple/bridging-typedefs-is-hard
[ClangImporter] Bridging can happen even without full bridgeability
2 parents 694cbb6 + e0beba5 commit 48a4a71

File tree

11 files changed

+124
-47
lines changed

11 files changed

+124
-47
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3476,6 +3476,8 @@ namespace {
34763476
Impl.CurrentVersion))
34773477
declType = Impl.getClangASTContext().getTypedefType(newtypeDecl);
34783478

3479+
// Note that we deliberately don't bridge most globals because we want to
3480+
// preserve pointer identity.
34793481
Type type = Impl.importType(declType,
34803482
(isAudited ? ImportTypeKind::AuditedVariable
34813483
: ImportTypeKind::Variable),

lib/ClangImporter/ImportType.cpp

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -684,13 +684,18 @@ namespace {
684684
auto typedefBridgeability = getTypedefBridgeability(underlyingType);
685685

686686
// Figure out the typedef we should actually use.
687-
SwiftTypeConverter innerConverter(Impl, AllowNSUIntegerAsInt, Bridging);
687+
auto underlyingBridgeability =
688+
(Bridging == Bridgeability::Full
689+
? typedefBridgeability : Bridgeability::None);
690+
SwiftTypeConverter innerConverter(Impl, AllowNSUIntegerAsInt,
691+
underlyingBridgeability);
688692
auto underlyingResult = innerConverter.Visit(underlyingType);
689693

690694
// If we used different bridgeability than this typedef normally
691-
// would, and therefore the underlying type is different from the
692-
// mapping of the typedef, use the underlying type.
693-
if (Bridging != typedefBridgeability &&
695+
// would because we're in a non-bridgeable context, and therefore
696+
// the underlying type is different from the mapping of the typedef,
697+
// use the underlying type.
698+
if (underlyingBridgeability != typedefBridgeability &&
694699
!underlyingResult.AbstractType->isEqual(mappedType)) {
695700
return underlyingResult;
696701
}
@@ -1291,29 +1296,9 @@ static Type adjustTypeForConcreteImport(ClangImporter::Implementation &impl,
12911296

12921297
// SwiftTypeConverter turns block pointers into @convention(block) types.
12931298
// In a bridgeable context, or in the direct structure of a typedef,
1294-
// we would prefer to instead use the default Swift convention. But this
1295-
// does means that, when we're using a typedef of a block pointer type in
1296-
// an unbridgable context, we need to go back and do a fully-unbridged
1297-
// import of the underlying type.
1299+
// we would prefer to instead use the default Swift convention.
12981300
if (hint == ImportHint::Block) {
1299-
if (bridging == Bridgeability::None) {
1300-
if (auto typedefType = clangType->getAs<clang::TypedefType>()) {
1301-
// In non-bridged contexts, drop the typealias sugar for blocks.
1302-
// FIXME: This will do the wrong thing if there's any adjustment to do
1303-
// besides optionality.
1304-
Type underlyingTy = impl.importType(typedefType->desugar(),
1305-
importKind,
1306-
allowNSUIntegerAsInt,
1307-
bridging,
1308-
OTK_None);
1309-
if (Type unwrappedTy = underlyingTy->getAnyOptionalObjectType())
1310-
underlyingTy = unwrappedTy;
1311-
if (!underlyingTy->isEqual(importedType))
1312-
importedType = underlyingTy;
1313-
}
1314-
}
1315-
1316-
if (bridging == Bridgeability::Full) {
1301+
if (canBridgeTypes(importKind) || importKind == ImportTypeKind::Typedef) {
13171302
auto fTy = importedType->castTo<FunctionType>();
13181303
FunctionType::ExtInfo einfo = fTy->getExtInfo();
13191304
if (einfo.getRepresentation() != FunctionTypeRepresentation::Swift) {
@@ -1356,7 +1341,6 @@ static Type adjustTypeForConcreteImport(ClangImporter::Implementation &impl,
13561341
// If we have a bridged Objective-C type and we are allowed to
13571342
// bridge, do so.
13581343
if (hint == ImportHint::ObjCBridged &&
1359-
bridging == Bridgeability::Full &&
13601344
canBridgeTypes(importKind) &&
13611345
importKind != ImportTypeKind::PropertyWithReferenceSemantics) {
13621346
// id and Any can be bridged without Foundation. There would be

lib/ClangImporter/ImporterImpl.h

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,13 @@ enum class Bridgeability {
191191
Full
192192
};
193193

194+
/// Controls whether a typedef for \p type should name the fully-bridged Swift
195+
/// type or the original Clang type.
196+
///
197+
/// In either case we end up losing sugar at some uses sites, so this is more
198+
/// about what the right default is.
194199
static inline Bridgeability getTypedefBridgeability(clang::QualType type) {
195-
return (type->isBlockPointerType() || type->isFunctionType())
196-
? Bridgeability::Full
197-
: Bridgeability::None;
200+
return type->isBlockPointerType() ? Bridgeability::Full : Bridgeability::None;
198201
}
199202

200203
/// \brief Describes the kind of the C type that can be mapped to a stdlib
@@ -907,22 +910,45 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
907910
///
908911
/// \param type The Clang type to import.
909912
///
910-
/// \param kind The kind of type import we're performing.
913+
/// \param kind A classification of the immediate context in which this type
914+
/// will be used. Different contexts result in the type being imported
915+
/// differently; for example, CF types are normally considered Unmanaged,
916+
/// but in parameter position they are known to always be passed at +0.
917+
/// See also the \p topLevelBridgeability parameter.
911918
///
912919
/// \param allowNSUIntegerAsInt If true, NSUInteger will be imported as Int
913-
/// in certain contexts. If false, it will always be imported as UInt.
914-
///
915-
/// \param bridgeability Whether we can bridge types in this context.
916-
/// This may restrict the ability to bridge types even in contexts
917-
/// that otherwise allow bridging, such as function results and
918-
/// parameters.
920+
/// in certain contexts. If false, it will always be imported as UInt.
921+
///
922+
/// \param topLevelBridgeability A classification of the top-level context in
923+
/// which this type will be used. This and \p kind are used together to
924+
/// determine whether a type can be imported in a more Swifty way than
925+
/// a naive translation of its C type. Full bridgeability requires that SIL
926+
/// can get back to the original Clang type if it needs to, which implies
927+
/// that this type is part of a top-level declaration where we do bridging.
928+
/// Without full bridgeability, we can still do some Swifty importing (e.g.
929+
/// mapping NSString to String) if we're in an immediate context \p kind
930+
/// that allows bridging, but only in cases where Swift's default mapping
931+
/// "back" to C is the correct one. If the original type has something
932+
/// funny going on, we either have to use a less lossy version of the type
933+
/// (ObjCBool rather than Bool) or refuse to import it at all (a block with
934+
/// the \c ns_returns_retained attribute).
935+
///
936+
/// \param optional If the imported type was a pointer-like type in C, this
937+
/// optionality is applied to the resulting Swift type.
938+
///
939+
/// \param resugarNSErrorPointer If true, Objective-C's `NSError **` is
940+
/// imported as Foundation.NSErrorPointer rather than
941+
/// AutoreleasingUnsafeMutablePointer<...>. This is usually desirable
942+
/// behavior, but isn't necessary when we use Swift's \c throws anyway.
943+
/// Strictly speaking, though, this is a hack used to break cyclic
944+
/// dependencies.
919945
///
920946
/// \returns The imported type, or null if this type could
921-
/// not be represented in Swift.
947+
/// not be represented in Swift.
922948
Type importType(clang::QualType type,
923949
ImportTypeKind kind,
924950
bool allowNSUIntegerAsInt,
925-
Bridgeability bridgeability,
951+
Bridgeability topLevelBridgeability,
926952
OptionalTypeKind optional = OTK_ImplicitlyUnwrappedOptional,
927953
bool resugarNSErrorPointer = true);
928954

test/ClangImporter/Inputs/custom-modules/ObjCParseExtras.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,6 @@ typedef NSArray<NSString *> *NSStringArray;
209209
@interface BridgedTypedefs : NSObject
210210
@property (readonly,nonnull) NSArray<NSStringArray> *arrayOfArrayOfStrings;
211211
@end
212+
213+
typedef NSString * _Nonnull (*FPTypedef)(NSString * _Nonnull);
214+
extern FPTypedef _Nonnull getFP(void);

test/ClangImporter/objc_id_as_any.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ idLover.takesArray(ofId: &y) // expected-error{{argument type 'Any' does not con
2525
idLover.takesId(x)
2626
idLover.takesId(y)
2727

28-
install_global_event_handler(idLover) // expected-error {{cannot convert value of type 'NSIdLover' to expected argument type 'event_handler?' (aka 'Optional<@convention(c) (AnyObject) -> ()>')}}
28+
install_global_event_handler(idLover) // expected-error {{cannot convert value of type 'NSIdLover' to expected argument type 'event_handler?' (aka 'Optional<@convention(c) (Any) -> ()>')}}
2929

3030
// FIXME: this should not type-check!
3131
// Function conversions are not legal when converting to a thin function type.

test/ClangImporter/objc_parse.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,3 +643,9 @@ func testTypeAndValue() {
643643
func testBridgedTypedef(bt: BridgedTypedefs) {
644644
let _: Int = bt.arrayOfArrayOfStrings // expected-error{{'[[String]]'}}
645645
}
646+
647+
func testBridgeFunctionPointerTypedefs(fptrTypedef: FPTypedef) {
648+
// See also print_clang_bool_bridging.swift.
649+
let _: Int = fptrTypedef // expected-error{{'@convention(c) (String) -> String'}}
650+
let _: Int = getFP() // expected-error{{'@convention(c) (String) -> String'}}
651+
}

test/IDE/print_clang_bool_bridging.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ func testCBoolFnToBlockTypedef(_: @escaping CBoolFn) -> CBoolBlock
4242
func testObjCBoolFnToBlockTypedef(_: @escaping ObjCBoolFn) -> ObjCBoolBlock
4343
func testDarwinBooleanFnToBlockTypedef(_: @escaping DarwinBooleanFn) -> DarwinBooleanBlock
4444

45-
typealias CBoolFnToBlockType = (CBoolFn) -> CBoolBlock
46-
typealias ObjCBoolFnToBlockType = (ObjCBoolFn) -> ObjCBoolBlock
47-
typealias DarwinBooleanFnToBlockType = (DarwinBooleanFn) -> DarwinBooleanBlock
45+
typealias CBoolFnToBlockType = (CBoolFn) -> (Bool) -> Bool
46+
typealias ObjCBoolFnToBlockType = (ObjCBoolFn) -> (ObjCBool) -> ObjCBool
47+
typealias DarwinBooleanFnToBlockType = (DarwinBooleanFn) -> (DarwinBoolean) -> DarwinBoolean
4848

49-
var globalObjCBoolFnToBlockFP: @convention(c) (ObjCBoolFn) -> @convention(block) (ObjCBool) -> ObjCBool
50-
var globalObjCBoolFnToBlockFPP: UnsafeMutablePointer<@convention(c) (ObjCBoolFn) -> @convention(block) (ObjCBool) -> ObjCBool>?
51-
var globalObjCBoolFnToBlockBP: @convention(block) (ObjCBoolFn) -> @convention(block) (ObjCBool) -> ObjCBool
49+
var globalObjCBoolFnToBlockFP: @convention(c) (ObjCBoolFn) -> (ObjCBool) -> ObjCBool
50+
var globalObjCBoolFnToBlockFPP: UnsafeMutablePointer<@convention(c) (ObjCBoolFn) -> (ObjCBool) -> ObjCBool>?
51+
var globalObjCBoolFnToBlockBP: @convention(block) (ObjCBoolFn) -> (ObjCBool) -> ObjCBool
5252

5353
var globalCBoolFn: CBoolFn
5454
var globalObjCBoolFn: ObjCBoolFn

test/IDE/print_omit_needless_words.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@
143143
// CHECK-FOUNDATION: func doSomethingElse(with: NSCopying & NSObjectProtocol)
144144

145145
// Note: Function type -> "Function".
146-
// CHECK-FOUNDATION: func sort(_: @escaping @convention(c) (AnyObject, AnyObject) -> Int)
146+
// CHECK-FOUNDATION: func sort(_: @escaping @convention(c) (Any, Any) -> Int)
147147

148148
// Note: Plural: NSArray without type arguments -> "Objects".
149149
// CHECK-FOUNDATION: func remove(_: [Any])
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#import <Foundation/Foundation.h>
2+
3+
extern NSString * _Nonnull (^ _Nonnull mutableBlockGlobal)(NSString * _Nonnull);
4+
extern NSString * _Nonnull (^ _Nonnull const constBlockGlobal)(NSString * _Nonnull);
5+
6+
extern NSString * _Nonnull (* _Nonnull mutableFPGlobal)(NSString * _Nonnull);
7+
extern NSString * _Nonnull (* _Nonnull const constFPGlobal)(NSString * _Nonnull);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#import "BlockGlobals.h"
2+
3+
NSString *(^mutableBlockGlobal)(NSString *) = ^ NSString *(NSString *arg) {
4+
return [@"default mutable block: " stringByAppendingString: arg];
5+
};
6+
NSString *(^ const constBlockGlobal)(NSString *) = ^ NSString *(NSString *arg) {
7+
return [@"default const block: " stringByAppendingString: arg];
8+
};
9+
10+
static NSString *appendToDefault(NSString *arg) {
11+
return [@"default mutable FP: " stringByAppendingString: arg];
12+
}
13+
14+
NSString *(*mutableFPGlobal)(NSString *) = &appendToDefault;
15+
16+
17+
static NSString *appendToDefaultConst(NSString *arg) {
18+
return [@"default const FP: " stringByAppendingString: arg];
19+
}
20+
NSString *(* const constFPGlobal)(NSString *) = &appendToDefaultConst;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-clang -fobjc-arc %S/Inputs/BlockGlobals/BlockGlobals.m -c -o %t/BlockGlobals.o
4+
// RUN: %target-build-swift %s -import-objc-header %S/Inputs/BlockGlobals/BlockGlobals.h %t/BlockGlobals.o -o %t/main
5+
// RUN: %target-run %t/main
6+
7+
// REQUIRES: executable_test
8+
// REQUIRES: objc_interop
9+
10+
import Foundation
11+
import StdlibUnittest
12+
13+
var BlockGlobalsTestSuite = TestSuite("BlockGlobals")
14+
15+
BlockGlobalsTestSuite.test("const block") {
16+
expectEqual((@convention(block) (String) -> String).self as Any.Type, type(of: constBlockGlobal))
17+
expectEqual("default const block: abc", constBlockGlobal("abc"))
18+
}
19+
20+
BlockGlobalsTestSuite.test("const function pointer") {
21+
expectEqual((@convention(c) (String) -> String).self as Any.Type, type(of: constFPGlobal))
22+
expectEqual("default const FP: abc", constFPGlobal("abc"))
23+
}
24+
25+
// FIXME: Add tests for mutable globals, including mutating them, once the
26+
// compiler supports it, as well as loading from the const globals without
27+
// immediately calling them.
28+
29+
runAllTests()

0 commit comments

Comments
 (0)