Skip to content

[ClangImporter] Import 'Class<SomeProto>' as 'SomeProto.Type'. #3185

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
Show file tree
Hide file tree
Changes from all commits
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: 11 additions & 10 deletions lib/ClangImporter/ImportType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -901,27 +901,28 @@ namespace {
return { importedType, ImportHint::ObjCPointer };
}

// If this is id<P>, turn this into a protocol type.
// FIXME: What about Class<P>?
if (type->isObjCQualifiedIdType()) {
// If this is id<P> or Class<P>, turn this into a protocol type.
if (type->isObjCQualifiedIdType() || type->isObjCQualifiedClassType()) {
SmallVector<Type, 4> protocols;
for (auto cp = type->qual_begin(), cpEnd = type->qual_end();
cp != cpEnd; ++cp) {
auto proto = cast_or_null<ProtocolDecl>(Impl.importDecl(*cp,
false));
auto proto = cast_or_null<ProtocolDecl>(Impl.importDecl(*cp, false));
if (!proto)
return Type();

protocols.push_back(proto->getDeclaredType());
}

return { ProtocolCompositionType::get(Impl.SwiftContext, protocols),
ImportHint::ObjCPointer };
Type result = ProtocolCompositionType::get(Impl.SwiftContext,
protocols);
if (type->isObjCQualifiedClassType())
result = ExistentialMetatypeType::get(result);

return { result, ImportHint::ObjCPointer };
}

// Beyond here, we're using AnyObject.
auto proto = Impl.SwiftContext.getProtocol(
KnownProtocolKind::AnyObject);
auto proto = Impl.SwiftContext.getProtocol(KnownProtocolKind::AnyObject);
if (!proto)
return Type();

Expand All @@ -931,7 +932,7 @@ namespace {
}

// Class maps to AnyObject.Type.
assert(type->isObjCClassType() || type->isObjCQualifiedClassType());
assert(type->isObjCClassType());
return { ExistentialMetatypeType::get(proto->getDeclaredType()),
ImportHint::ObjCPointer };
}
Expand Down
7 changes: 7 additions & 0 deletions test/ClangModules/Inputs/custom-modules/Protocols.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,10 @@
@property int bar;
@end

@protocol AnotherProto
@end

Class <FooProto> _Nonnull processFooType(Class <FooProto> _Nonnull);
Class <FooProto, AnotherProto> _Nonnull processComboType(Class <FooProto, AnotherProto> _Nonnull);
Class <AnotherProto, FooProto> _Nonnull processComboType2(Class <AnotherProto, FooProto> _Nonnull);

43 changes: 43 additions & 0 deletions test/ClangModules/objc_ir.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,49 @@ func getset(p p: FooProto) {
p.bar = prop
}

// CHECK-LABEL: define hidden %swift.type* @_TF7objc_ir16protocolMetatypeFT1pPSo8FooProto__PMPS0__(%objc_object*) {{.*}} {
func protocolMetatype(p: FooProto) -> FooProto.Type {
// CHECK: = call %swift.type* @swift_getObjectType(%objc_object* %0)
// CHECK-NOT: {{retain|release}}
// CHECK: [[RAW_RESULT:%.+]] = call i8* @processFooType(i8* {{%.+}})
// CHECK: [[CASTED_RESULT:%.+]] = bitcast i8* [[RAW_RESULT]] to %objc_class*
// CHECK: [[SWIFT_RESULT:%.+]] = call %swift.type* @swift_getObjCClassMetadata(%objc_class* [[CASTED_RESULT]])
// CHECK: call void @swift_unknownRelease(%objc_object* %0)
// CHECK: ret %swift.type* [[SWIFT_RESULT]]
let type = processFooType(p.dynamicType)
return type
} // CHECK: }

class Impl: FooProto, AnotherProto {
@objc var bar: Int32 = 0
}

// CHECK-LABEL: define hidden %swift.type* @_TF7objc_ir27protocolCompositionMetatypeFT1pCS_4Impl_PMPSo12AnotherProtoSo8FooProto_(%C7objc_ir4Impl*) {{.*}} {
func protocolCompositionMetatype(p: Impl) -> protocol<FooProto, AnotherProto>.Type {
// CHECK: = getelementptr inbounds %C7objc_ir4Impl, %C7objc_ir4Impl* %0, i32 0, i32 0, i32 0
// CHECK-NOT: {{retain|release}}
// CHECK: [[RAW_RESULT:%.+]] = call i8* @processComboType(i8* {{%.+}})
// CHECK: [[CASTED_RESULT:%.+]] = bitcast i8* [[RAW_RESULT]] to %objc_class*
// CHECK: [[SWIFT_RESULT:%.+]] = call %swift.type* @swift_getObjCClassMetadata(%objc_class* [[CASTED_RESULT]])
// CHECK: call void bitcast (void (%swift.refcounted*)* @rt_swift_release to void (%C7objc_ir4Impl*)*)(%C7objc_ir4Impl* %0)
// CHECK: ret %swift.type* [[SWIFT_RESULT]]
let type = processComboType(p.dynamicType)
return type
} // CHECK: }

// CHECK-LABEL: define hidden %swift.type* @_TF7objc_ir28protocolCompositionMetatype2FT1pCS_4Impl_PMPSo12AnotherProtoSo8FooProto_(%C7objc_ir4Impl*) {{.*}} {
func protocolCompositionMetatype2(p: Impl) -> protocol<FooProto, AnotherProto>.Type {
// CHECK: = getelementptr inbounds %C7objc_ir4Impl, %C7objc_ir4Impl* %0, i32 0, i32 0, i32 0
// CHECK-NOT: {{retain|release}}
// CHECK: [[RAW_RESULT:%.+]] = call i8* @processComboType2(i8* {{%.+}})
// CHECK: [[CASTED_RESULT:%.+]] = bitcast i8* [[RAW_RESULT]] to %objc_class*
// CHECK: [[SWIFT_RESULT:%.+]] = call %swift.type* @swift_getObjCClassMetadata(%objc_class* [[CASTED_RESULT]])
// CHECK: call void bitcast (void (%swift.refcounted*)* @rt_swift_release to void (%C7objc_ir4Impl*)*)(%C7objc_ir4Impl* %0)
// CHECK: ret %swift.type* [[SWIFT_RESULT]]
let type = processComboType2(p.dynamicType)
return type
} // CHECK: }

// CHECK-LABEL: define hidden void @_TF7objc_ir17pointerPropertiesFCSo14PointerWrapperT_(%CSo14PointerWrapper*) {{.*}} {
func pointerProperties(_ obj: PointerWrapper) {
// CHECK: load i8*, i8** @"\01L_selector(setVoidPtr:)"
Expand Down
2 changes: 2 additions & 0 deletions test/PrintAsObjC/classes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ typealias AliasForNSRect = NSRect
// CHECK-NEXT: - (NSArray * _Nonnull)emptyArray;
// CHECK-NEXT: - (NSArray * _Nullable)maybeArray;
// CHECK-NEXT: - (NSRuncingMode)someEnum;
// CHECK-NEXT: - (Class <NSCoding> _Nullable)protocolClass;
// CHECK-NEXT: - (struct _NSZone * _Nullable)zone;
// CHECK-NEXT: - (CFTypeRef _Nullable)cf:(CFTreeRef _Nonnull)x str:(CFStringRef _Nonnull)str str2:(CFMutableStringRef _Nonnull)str2 obj:(CFAliasForTypeRef _Nonnull)obj;
// CHECK-NEXT: - (void)appKitInImplementation;
Expand All @@ -248,6 +249,7 @@ typealias AliasForNSRect = NSRect
func maybeArray() -> NSArray? { return nil }

func someEnum() -> RuncingMode { return .mince }
func protocolClass() -> NSCoding.Type? { return nil }

func zone() -> NSZone? { return nil }

Expand Down
10 changes: 6 additions & 4 deletions test/SILGen/objc_ownership_conventions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,18 @@ func test11(_ g: Gizmo) -> AnyClass {
// CHECK: bb0([[G:%[0-9]+]] : $Gizmo):
// CHECK: strong_retain [[G]]
// CHECK: [[NS_G:%[0-9]+]] = upcast [[G:%[0-9]+]] : $Gizmo to $NSObject
// CHECK: [[GETTER:%[0-9]+]] = class_method [volatile] [[NS_G]] : $NSObject, #NSObject.qualifiedClassProp!getter.1.foreign : NSObject -> () -> AnyObject.Type! , $@convention(objc_method) (NSObject) -> ImplicitlyUnwrappedOptional<@objc_metatype AnyObject.Type>
// CHECK-NEXT: [[OPT_OBJC:%.*]] = apply [[GETTER]]([[NS_G]]) : $@convention(objc_method) (NSObject) -> ImplicitlyUnwrappedOptional<@objc_metatype AnyObject.Type>
// CHECK: [[GETTER:%[0-9]+]] = class_method [volatile] [[NS_G]] : $NSObject, #NSObject.qualifiedClassProp!getter.1.foreign : NSObject -> () -> NSAnsing.Type! , $@convention(objc_method) (NSObject) -> ImplicitlyUnwrappedOptional<@objc_metatype NSAnsing.Type>
// CHECK-NEXT: [[OPT_OBJC:%.*]] = apply [[GETTER]]([[NS_G]]) : $@convention(objc_method) (NSObject) -> ImplicitlyUnwrappedOptional<@objc_metatype NSAnsing.Type>
// CHECK: select_enum [[OPT_OBJC]]
// CHECK: [[OBJC:%.*]] = unchecked_enum_data [[OPT_OBJC]]
// CHECK-NEXT: [[THICK:%.*]] = objc_to_thick_metatype [[OBJC]]
// CHECK: [[T0:%.*]] = enum $ImplicitlyUnwrappedOptional<AnyObject.Type>, #ImplicitlyUnwrappedOptional.some!enumelt.1, [[THICK]]
// CHECK: [[T0:%.*]] = enum $ImplicitlyUnwrappedOptional<NSAnsing.Type>, #ImplicitlyUnwrappedOptional.some!enumelt.1, [[THICK]]
// CHECK: [[RES:%.*]] = unchecked_enum_data
// CHECK: [[OPENED:%.*]] = open_existential_metatype [[RES]]
// CHECK: [[RES_ANY:%.*]] = init_existential_metatype [[OPENED]]
// CHECK: strong_release [[G]] : $Gizmo
// CHECK: strong_release [[G]] : $Gizmo
// CHECK-NEXT: return [[RES]] : $@thick AnyObject.Type
// CHECK-NEXT: return [[RES_ANY]] : $@thick AnyObject.Type
return g.qualifiedClassProp
}

Expand Down