Skip to content

Allow subclasses of specific instantiations of ObjC generic classes. #2086

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 1 commit into from
Apr 7, 2016
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
5 changes: 3 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1457,8 +1457,9 @@ NOTE(class_here,none,
"class %0 declared here", (Identifier))
ERROR(inheritance_from_final_class,none,
"inheritance from a final class %0", (Identifier))
ERROR(inheritance_from_objc_generic_class,none,
"inheritance from a generic Objective-C class %0", (Identifier))
ERROR(inheritance_from_unspecialized_objc_generic_class,none,
"inheritance from a generic Objective-C class %0 must bind "
"type parameters of %0 to specific concrete types", (Identifier))
ERROR(inheritance_from_objc_runtime_visible_class,none,
"inheritance from an Objective-C class %0 only visible via the runtime", (Identifier))

Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/GenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ namespace {
}

llvm::Constant *getMetaclassRefOrNull(ClassDecl *theClass) {
if (theClass->isGenericContext()) {
if (theClass->isGenericContext() && !theClass->hasClangNode()) {
return llvm::ConstantPointerNull::get(IGM.ObjCClassPtrTy);
} else {
return IGM.getAddrOfMetaclassObject(theClass, NotForDefinition);
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2254,7 +2254,7 @@ llvm::Constant *IRGenModule::getAddrOfSwiftMetaclassStub(ClassDecl *theClass,
/// on whether the class is published as an ObjC class.
llvm::Constant *IRGenModule::getAddrOfMetaclassObject(ClassDecl *decl,
ForDefinition_t forDefinition) {
assert(!decl->isGenericContext()
assert((!decl->isGenericContext() || decl->hasClangNode())
&& "generic classes do not have a static metaclass object");
if (decl->checkObjCAncestry() != ObjCClassKind::NonObjC ||
decl->hasClangNode()) {
Expand Down
55 changes: 28 additions & 27 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,31 @@ static void emitPolymorphicParametersFromArray(IRGenFunction &IGF,
requirements.bindFromBuffer(IGF, array, getInContext);
}

static bool isTypeErasedGenericClass(NominalTypeDecl *ntd) {
// ObjC classes are type erased.
// TODO: Unless they have magic methods...
if (auto clas = dyn_cast<ClassDecl>(ntd))
return clas->hasClangNode() && clas->isGenericContext();
return false;
}

static bool isTypeErasedGenericClassType(CanType type) {
if (auto nom = type->getAnyNominal())
return isTypeErasedGenericClass(nom);
return false;
}

// Get the type that exists at runtime to represent a compile-time type.
static CanType
getRuntimeReifiedType(IRGenModule &IGM, CanType type) {
return CanType(type.transform([&](Type t) -> Type {
if (isTypeErasedGenericClassType(CanType(t))) {
return t->getAnyNominal()->getDeclaredType()->getCanonicalType();
}
return t;
}));
}

/// Attempts to return a constant heap metadata reference for a
/// class type. This is generally only valid for specific kinds of
/// ObjC reference, like superclasses or category references.
Expand All @@ -257,8 +282,9 @@ llvm::Constant *irgen::tryEmitConstantHeapMetadataRef(IRGenModule &IGM,
return nullptr;

// Otherwise, just respect genericity and Objective-C runtime visibility.
} else if (theDecl->isGenericContext() ||
theDecl->isOnlyObjCRuntimeVisible()) {
} else if ((theDecl->isGenericContext()
&& !isTypeErasedGenericClass(theDecl))
|| theDecl->isOnlyObjCRuntimeVisible()) {
return nullptr;
}

Expand Down Expand Up @@ -1324,31 +1350,6 @@ static llvm::Value *emitTypeMetadataAccessFunctionBody(IRGenFunction &IGF,
using MetadataAccessGenerator =
llvm::function_ref<llvm::Value*(IRGenFunction &IGF, llvm::Constant *cache)>;

static bool isTypeErasedGenericClass(NominalTypeDecl *ntd) {
// ObjC classes are type erased.
// TODO: Unless they have magic methods...
if (auto clas = dyn_cast<ClassDecl>(ntd))
return clas->hasClangNode() && clas->isGenericContext();
return false;
}

static bool isTypeErasedGenericClassType(CanType type) {
if (auto nom = type->getAnyNominal())
return isTypeErasedGenericClass(nom);
return false;
}

// Get the type that exists at runtime to represent a compile-time type.
static CanType
getRuntimeReifiedType(IRGenModule &IGM, CanType type) {
return CanType(type.transform([&](Type t) -> Type {
if (isTypeErasedGenericClassType(CanType(t))) {
return t->getAnyNominal()->getDeclaredType()->getCanonicalType();
}
return t;
}));
}

/// Get or create an accessor function to the given non-dependent type.
static llvm::Function *getTypeMetadataAccessFunction(IRGenModule &IGM,
CanType type,
Expand Down
6 changes: 4 additions & 2 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3836,8 +3836,10 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
return;
}

if (Super->hasClangNode() && Super->getGenericParams()) {
TC.diagnose(CD, diag::inheritance_from_objc_generic_class,
if (Super->hasClangNode() && Super->getGenericParams()
&& superclassTy->hasTypeParameter()) {
TC.diagnose(CD,
diag::inheritance_from_unspecialized_objc_generic_class,
Super->getName());
}

Expand Down
30 changes: 30 additions & 0 deletions test/ClangModules/objc_bridging_generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,33 @@ extension GenericClass {
_ = #selector(GenericClass.doesntUseGenericParam3)
}
}

// expected-error@+1{{inheritance from a generic Objective-C class 'GenericClass' must bind type parameters of 'GenericClass' to specific concrete types}}
class SwiftGenericSubclassA<X: AnyObject>: GenericClass<X> {}
// expected-error@+1{{inheritance from a generic Objective-C class 'GenericClass' must bind type parameters of 'GenericClass' to specific concrete types}}
class SwiftGenericSubclassB<X: AnyObject>: GenericClass<GenericClass<X>> {}
// expected-error@+1{{inheritance from a generic Objective-C class 'GenericClass' must bind type parameters of 'GenericClass' to specific concrete types}}
class SwiftGenericSubclassC<X: NSCopying>: GenericClass<X> {}

class SwiftConcreteSubclassA: GenericClass<AnyObject> {
override init(thing: AnyObject) { }
override func thing() -> AnyObject? { }
override func count() -> Int32 { }
override class func classThing() -> AnyObject? { }
override func arrayOfThings() -> [AnyObject] {}
}
class SwiftConcreteSubclassB: GenericClass<NSString> {
override init(thing: NSString) { }
override func thing() -> NSString? { }
override func count() -> Int32 { }
override class func classThing() -> NSString? { }
override func arrayOfThings() -> [NSString] {}
}
class SwiftConcreteSubclassC<T>: GenericClass<NSString> {
override init(thing: NSString) { }
override func thing() -> NSString? { }
override func count() -> Int32 { }
override class func classThing() -> NSString? { }
override func arrayOfThings() -> [NSString] {}
}

8 changes: 8 additions & 0 deletions test/IRGen/objc_generic_class_metadata.sil
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ import Swift
import Foundation
import objc_generics

// CHECK-LABEL: @"OBJC_METACLASS_$__TtC27objc_generic_class_metadata8Subclass" = hidden global %objc_class
// CHECK: %objc_class* @"OBJC_METACLASS_$_NSObject"
// CHECK: %objc_class* @"OBJC_METACLASS_$_GenericClass"
// CHECK-LABEL: @_TMfC27objc_generic_class_metadata8Subclass = internal global
// CHECK: %objc_class* @"OBJC_METACLASS_$__TtC27objc_generic_class_metadata8Subclass"
// CHECK: %objc_class* @"OBJC_CLASS_$_GenericClass"
class Subclass: GenericClass<NSString> {}

sil @metatype_sink : $@convention(thin) <T> (@thick T.Type) -> ()
sil @objc_class_sink : $@convention(thin) <T: AnyObject> (@objc_metatype T.Type) -> ()

Expand Down
43 changes: 43 additions & 0 deletions test/Interpreter/imported_objc_generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,47 @@ ImportedObjCGenerics.test("InheritanceFromNongeneric") {
expectTrue(Container<NSObject>.self == Container<NSString>.self)
}

public class InheritInSwift: Container<NSString> {
public override init(object: NSString) {
super.init(object: object.lowercase)
}
public override var object: NSString {
get {
return super.object.uppercase
}
set {
super.object = newValue.lowercase
}
}

public var superObject: NSString {
get {
return super.object
}
}
}

ImportedObjCGenerics.test("InheritInSwift") {
let s = InheritInSwift(object: "HEllo")
let sup: Container = s

expectEqual(s.superObject, "hello")
expectEqual(s.object, "HELLO")
expectEqual(sup.object, "HELLO")

s.object = "GOodbye"
expectEqual(s.superObject, "goodbye")
expectEqual(s.object, "GOODBYE")
expectEqual(sup.object, "GOODBYE")

s.processObject { o in
expectEqual(o, "GOODBYE")
}

s.updateObject { "Aloha" }
expectEqual(s.superObject, "aloha")
expectEqual(s.object, "ALOHA")
expectEqual(sup.object, "ALOHA")
}

runAllTests()