Skip to content

Commit

Permalink
[Clang importer] Don't bridge blocks to Swift functions in ObjC gener…
Browse files Browse the repository at this point in the history
…ic args.

While the compiler can bridge C block types to Swift function types,
the Swift runtime cannot. Don't bridge block types to Swift function
types in Objective-C generic arguments, so

  NSArray<some-block-type>

will get imported as

  [@convention(block) (...) -> Whatever]

rather than

  [(...) -> Whatever]

Fixes rdar://problem/40879067 in a fairly narrow way; the Clang
importer's approach to adjusting types based on context needs a
cleanup, but this is the safe, localized fix suitable for 4.2.
  • Loading branch information
DougGregor committed Jun 15, 2018
1 parent e3d69d1 commit d529002
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 8 deletions.
21 changes: 15 additions & 6 deletions lib/ClangImporter/ImportType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -974,8 +974,8 @@ namespace {
// Convert the type arguments.
for (auto typeArg : typeArgs) {
Type importedTypeArg = Impl.importTypeIgnoreIUO(
typeArg, ImportTypeKind::BridgedValue, AllowNSUIntegerAsInt,
Bridging, OTK_None);
typeArg, ImportTypeKind::ObjCGenericArgument,
AllowNSUIntegerAsInt, Bridging, OTK_None);
if (!importedTypeArg) {
importedTypeArgs.clear();
break;
Expand Down Expand Up @@ -1102,7 +1102,7 @@ static bool canBridgeTypes(ImportTypeKind importKind) {
case ImportTypeKind::CFUnretainedOutParameter:
case ImportTypeKind::Property:
case ImportTypeKind::PropertyWithReferenceSemantics:
case ImportTypeKind::BridgedValue:
case ImportTypeKind::ObjCGenericArgument:
case ImportTypeKind::Typedef:
return true;
}
Expand All @@ -1116,7 +1116,7 @@ static bool isCFAudited(ImportTypeKind importKind) {
case ImportTypeKind::Abstract:
case ImportTypeKind::Typedef:
case ImportTypeKind::Value:
case ImportTypeKind::BridgedValue:
case ImportTypeKind::ObjCGenericArgument:
case ImportTypeKind::Variable:
case ImportTypeKind::Result:
case ImportTypeKind::Pointee:
Expand Down Expand Up @@ -1287,10 +1287,19 @@ static ImportedType adjustTypeForConcreteImport(
// we would prefer to instead use the default Swift convention.
if (hint == ImportHint::Block) {
if (canBridgeTypes(importKind)) {
// Determine the representation we need. For Objective-C generic
// arguments, we cannot bridge them to a block, so force a block
// representation even if our imported type thus far is a Swift
// function representation.
auto requiredFunctionTypeRepr = FunctionTypeRepresentation::Swift;
if (importKind == ImportTypeKind::ObjCGenericArgument) {
requiredFunctionTypeRepr = FunctionTypeRepresentation::Block;
}

auto fTy = importedType->castTo<FunctionType>();
FunctionType::ExtInfo einfo = fTy->getExtInfo();
if (einfo.getRepresentation() != FunctionTypeRepresentation::Swift) {
einfo = einfo.withRepresentation(FunctionTypeRepresentation::Swift);
if (einfo.getRepresentation() != requiredFunctionTypeRepr) {
einfo = einfo.withRepresentation(requiredFunctionTypeRepr);
importedType = fTy->withExtInfo(einfo);
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/ClangImporter/ImporterImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ enum class ImportTypeKind {
/// \brief Import the type of a literal value.
Value,

/// \brief Import the type of a literal value that can be bridged.
BridgedValue,
/// \brief Import the type of an Objective-C generic argument.
ObjCGenericArgument,

/// \brief Import the declared type of a variable.
Variable,
Expand Down
4 changes: 4 additions & 0 deletions test/ClangImporter/objc_bridging_generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -414,3 +414,7 @@ func testHashableGenerics(
let _: Int = insufficient.foo // expected-error{{cannot convert value of type 'Set<AnyHashable>' to specified type 'Int'}}
let _: Int = extra.foo // expected-error{{cannot convert value of type 'Set<ElementConcrete>' to specified type 'Int'}}
}

func testGenericsWithTypedefBlocks(hba: HasBlockArray) {
let _: Int = hba.blockArray() // expected-error{{type '[@convention(block) () -> Void]'}}
}
8 changes: 8 additions & 0 deletions test/ClangImporter/objc_ir.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Foundation
import objc_ext
import TestProtocols
import ObjCIRExtras
import objc_generics

// CHECK: @"\01L_selector_data(method:withFloat:)" = private global [18 x i8] c"method:withFloat:\00"
// CHECK: @"\01L_selector_data(method:withDouble:)" = private global [19 x i8] c"method:withDouble:\00"
Expand Down Expand Up @@ -344,6 +345,13 @@ func testConstrGenericCompatibilityAliasMangling(constr_generic_obj: SwiftConstr
// CHECK: call void @llvm.dbg.declare(metadata %TSo26SwiftConstrGenericNameTestCySo8NSNumberCG** {{%.+}}, metadata ![[SWIFT_CONSTR_GENERIC_NAME_ALIAS_VAR:[0-9]+]], metadata !DIExpression())
}

// CHECK-LABEL: S7objc_ir22testBlocksWithGenerics3hbaypSo13HasBlockArrayC_tF
func testBlocksWithGenerics(hba: HasBlockArray) -> Any {
// CHECK: {{call swiftcc.*SSo13HasBlockArrayC05blockC0SayyyXBGyFTcTO}}
let _ = hba.blockPointerType()
return hba.blockArray
}

// CHECK: linkonce_odr hidden {{.*}} @"$SSo1BC3intABSgs5Int32V_tcfcTO"
// CHECK: load i8*, i8** @"\01L_selector(initWithInt:)"
// CHECK: call [[OPAQUE:%.*]]* bitcast (void ()* @objc_msgSend
Expand Down
7 changes: 7 additions & 0 deletions test/Inputs/clang-importer-sdk/usr/include/objc_generics.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,10 @@ typedef id <Fungible> FungibleObject;

@interface Third : Second<Third *>
@end

typedef void (^ _Nonnull BlockPointerType)(void);

@interface HasBlockArray : NSObject
- (NSArray<BlockPointerType> * _Nonnull)blockArray;
- (BlockPointerType)blockPointerType;
@end

0 comments on commit d529002

Please sign in to comment.