Skip to content

[Clang importer] Don't bridge blocks to Swift functions in ObjC generic args #17247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 15, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I meant this comment, which refers to @convention(block).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grrrr. That comment above is a lie. I'll do a separate comment-only PR for that

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since @convention(block) is the default, it seems simple to just change the condition here: canBridgeTypes(importKind) && importKind != ImportTypeKind::ObjCGenericArgument.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the rathole I went down before, because the way in which we pass down the desired "bridgeability" of the imported type doesn't account for blocks, which are bridgeable in some positions but not others. The problem with simply trying to exclude ObjCGenericArgument here is that the mappedType we get here has already been given the Swift function type representation (because we're importing a typedef with "full bridgeability"), and we no longer have the block type around.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, typedefs. In that case, can you update the comment above?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah.

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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this isn't just any generic argument, since most of the time we'd import Foo<NSString *> * as Foo<NSString> and not Foo<String>. "ObjCCollectionElement", maybe?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point!


/// \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