Skip to content

Commit 66d5971

Browse files
committed
Merge pull request #2086 from jckarter/generic-objc-subclass
Allow subclasses of specific instantiations of ObjC generic classes.
2 parents 28401eb + 9c5564f commit 66d5971

File tree

8 files changed

+118
-33
lines changed

8 files changed

+118
-33
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,8 +1457,9 @@ NOTE(class_here,none,
14571457
"class %0 declared here", (Identifier))
14581458
ERROR(inheritance_from_final_class,none,
14591459
"inheritance from a final class %0", (Identifier))
1460-
ERROR(inheritance_from_objc_generic_class,none,
1461-
"inheritance from a generic Objective-C class %0", (Identifier))
1460+
ERROR(inheritance_from_unspecialized_objc_generic_class,none,
1461+
"inheritance from a generic Objective-C class %0 must bind "
1462+
"type parameters of %0 to specific concrete types", (Identifier))
14621463
ERROR(inheritance_from_objc_runtime_visible_class,none,
14631464
"inheritance from an Objective-C class %0 only visible via the runtime", (Identifier))
14641465

lib/IRGen/GenClass.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ namespace {
857857
}
858858

859859
llvm::Constant *getMetaclassRefOrNull(ClassDecl *theClass) {
860-
if (theClass->isGenericContext()) {
860+
if (theClass->isGenericContext() && !theClass->hasClangNode()) {
861861
return llvm::ConstantPointerNull::get(IGM.ObjCClassPtrTy);
862862
} else {
863863
return IGM.getAddrOfMetaclassObject(theClass, NotForDefinition);

lib/IRGen/GenDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2285,7 +2285,7 @@ llvm::Constant *IRGenModule::getAddrOfSwiftMetaclassStub(ClassDecl *theClass,
22852285
/// on whether the class is published as an ObjC class.
22862286
llvm::Constant *IRGenModule::getAddrOfMetaclassObject(ClassDecl *decl,
22872287
ForDefinition_t forDefinition) {
2288-
assert(!decl->isGenericContext()
2288+
assert((!decl->isGenericContext() || decl->hasClangNode())
22892289
&& "generic classes do not have a static metaclass object");
22902290
if (decl->checkObjCAncestry() != ObjCClassKind::NonObjC ||
22912291
decl->hasClangNode()) {

lib/IRGen/GenMeta.cpp

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,31 @@ static void emitPolymorphicParametersFromArray(IRGenFunction &IGF,
241241
requirements.bindFromBuffer(IGF, array, getInContext);
242242
}
243243

244+
static bool isTypeErasedGenericClass(NominalTypeDecl *ntd) {
245+
// ObjC classes are type erased.
246+
// TODO: Unless they have magic methods...
247+
if (auto clas = dyn_cast<ClassDecl>(ntd))
248+
return clas->hasClangNode() && clas->isGenericContext();
249+
return false;
250+
}
251+
252+
static bool isTypeErasedGenericClassType(CanType type) {
253+
if (auto nom = type->getAnyNominal())
254+
return isTypeErasedGenericClass(nom);
255+
return false;
256+
}
257+
258+
// Get the type that exists at runtime to represent a compile-time type.
259+
static CanType
260+
getRuntimeReifiedType(IRGenModule &IGM, CanType type) {
261+
return CanType(type.transform([&](Type t) -> Type {
262+
if (isTypeErasedGenericClassType(CanType(t))) {
263+
return t->getAnyNominal()->getDeclaredType()->getCanonicalType();
264+
}
265+
return t;
266+
}));
267+
}
268+
244269
/// Attempts to return a constant heap metadata reference for a
245270
/// class type. This is generally only valid for specific kinds of
246271
/// ObjC reference, like superclasses or category references.
@@ -257,8 +282,9 @@ llvm::Constant *irgen::tryEmitConstantHeapMetadataRef(IRGenModule &IGM,
257282
return nullptr;
258283

259284
// Otherwise, just respect genericity and Objective-C runtime visibility.
260-
} else if (theDecl->isGenericContext() ||
261-
theDecl->isOnlyObjCRuntimeVisible()) {
285+
} else if ((theDecl->isGenericContext()
286+
&& !isTypeErasedGenericClass(theDecl))
287+
|| theDecl->isOnlyObjCRuntimeVisible()) {
262288
return nullptr;
263289
}
264290

@@ -1325,31 +1351,6 @@ static llvm::Value *emitTypeMetadataAccessFunctionBody(IRGenFunction &IGF,
13251351
using MetadataAccessGenerator =
13261352
llvm::function_ref<llvm::Value*(IRGenFunction &IGF, llvm::Constant *cache)>;
13271353

1328-
static bool isTypeErasedGenericClass(NominalTypeDecl *ntd) {
1329-
// ObjC classes are type erased.
1330-
// TODO: Unless they have magic methods...
1331-
if (auto clas = dyn_cast<ClassDecl>(ntd))
1332-
return clas->hasClangNode() && clas->isGenericContext();
1333-
return false;
1334-
}
1335-
1336-
static bool isTypeErasedGenericClassType(CanType type) {
1337-
if (auto nom = type->getAnyNominal())
1338-
return isTypeErasedGenericClass(nom);
1339-
return false;
1340-
}
1341-
1342-
// Get the type that exists at runtime to represent a compile-time type.
1343-
static CanType
1344-
getRuntimeReifiedType(IRGenModule &IGM, CanType type) {
1345-
return CanType(type.transform([&](Type t) -> Type {
1346-
if (isTypeErasedGenericClassType(CanType(t))) {
1347-
return t->getAnyNominal()->getDeclaredType()->getCanonicalType();
1348-
}
1349-
return t;
1350-
}));
1351-
}
1352-
13531354
/// Get or create an accessor function to the given non-dependent type.
13541355
static llvm::Function *getTypeMetadataAccessFunction(IRGenModule &IGM,
13551356
CanType type,

lib/Sema/TypeCheckDecl.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3836,8 +3836,10 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
38363836
return;
38373837
}
38383838

3839-
if (Super->hasClangNode() && Super->getGenericParams()) {
3840-
TC.diagnose(CD, diag::inheritance_from_objc_generic_class,
3839+
if (Super->hasClangNode() && Super->getGenericParams()
3840+
&& superclassTy->hasTypeParameter()) {
3841+
TC.diagnose(CD,
3842+
diag::inheritance_from_unspecialized_objc_generic_class,
38413843
Super->getName());
38423844
}
38433845

test/ClangModules/objc_bridging_generics.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,33 @@ extension GenericClass {
129129
_ = #selector(GenericClass.doesntUseGenericParam3)
130130
}
131131
}
132+
133+
// expected-error@+1{{inheritance from a generic Objective-C class 'GenericClass' must bind type parameters of 'GenericClass' to specific concrete types}}
134+
class SwiftGenericSubclassA<X: AnyObject>: GenericClass<X> {}
135+
// expected-error@+1{{inheritance from a generic Objective-C class 'GenericClass' must bind type parameters of 'GenericClass' to specific concrete types}}
136+
class SwiftGenericSubclassB<X: AnyObject>: GenericClass<GenericClass<X>> {}
137+
// expected-error@+1{{inheritance from a generic Objective-C class 'GenericClass' must bind type parameters of 'GenericClass' to specific concrete types}}
138+
class SwiftGenericSubclassC<X: NSCopying>: GenericClass<X> {}
139+
140+
class SwiftConcreteSubclassA: GenericClass<AnyObject> {
141+
override init(thing: AnyObject) { }
142+
override func thing() -> AnyObject? { }
143+
override func count() -> Int32 { }
144+
override class func classThing() -> AnyObject? { }
145+
override func arrayOfThings() -> [AnyObject] {}
146+
}
147+
class SwiftConcreteSubclassB: GenericClass<NSString> {
148+
override init(thing: NSString) { }
149+
override func thing() -> NSString? { }
150+
override func count() -> Int32 { }
151+
override class func classThing() -> NSString? { }
152+
override func arrayOfThings() -> [NSString] {}
153+
}
154+
class SwiftConcreteSubclassC<T>: GenericClass<NSString> {
155+
override init(thing: NSString) { }
156+
override func thing() -> NSString? { }
157+
override func count() -> Int32 { }
158+
override class func classThing() -> NSString? { }
159+
override func arrayOfThings() -> [NSString] {}
160+
}
161+

test/IRGen/objc_generic_class_metadata.sil

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ import Swift
88
import Foundation
99
import objc_generics
1010

11+
// CHECK-LABEL: @"OBJC_METACLASS_$__TtC27objc_generic_class_metadata8Subclass" = hidden global %objc_class
12+
// CHECK: %objc_class* @"OBJC_METACLASS_$_NSObject"
13+
// CHECK: %objc_class* @"OBJC_METACLASS_$_GenericClass"
14+
// CHECK-LABEL: @_TMfC27objc_generic_class_metadata8Subclass = internal global
15+
// CHECK: %objc_class* @"OBJC_METACLASS_$__TtC27objc_generic_class_metadata8Subclass"
16+
// CHECK: %objc_class* @"OBJC_CLASS_$_GenericClass"
17+
class Subclass: GenericClass<NSString> {}
18+
1119
sil @metatype_sink : $@convention(thin) <T> (@thick T.Type) -> ()
1220
sil @objc_class_sink : $@convention(thin) <T: AnyObject> (@objc_metatype T.Type) -> ()
1321

test/Interpreter/imported_objc_generics.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,47 @@ ImportedObjCGenerics.test("InheritanceFromNongeneric") {
146146
expectTrue(Container<NSObject>.self == Container<NSString>.self)
147147
}
148148

149+
public class InheritInSwift: Container<NSString> {
150+
public override init(object: NSString) {
151+
super.init(object: object.lowercase)
152+
}
153+
public override var object: NSString {
154+
get {
155+
return super.object.uppercase
156+
}
157+
set {
158+
super.object = newValue.lowercase
159+
}
160+
}
161+
162+
public var superObject: NSString {
163+
get {
164+
return super.object
165+
}
166+
}
167+
}
168+
169+
ImportedObjCGenerics.test("InheritInSwift") {
170+
let s = InheritInSwift(object: "HEllo")
171+
let sup: Container = s
172+
173+
expectEqual(s.superObject, "hello")
174+
expectEqual(s.object, "HELLO")
175+
expectEqual(sup.object, "HELLO")
176+
177+
s.object = "GOodbye"
178+
expectEqual(s.superObject, "goodbye")
179+
expectEqual(s.object, "GOODBYE")
180+
expectEqual(sup.object, "GOODBYE")
181+
182+
s.processObject { o in
183+
expectEqual(o, "GOODBYE")
184+
}
185+
186+
s.updateObject { "Aloha" }
187+
expectEqual(s.superObject, "aloha")
188+
expectEqual(s.object, "ALOHA")
189+
expectEqual(sup.object, "ALOHA")
190+
}
191+
149192
runAllTests()

0 commit comments

Comments
 (0)