Substitute type arguments into uses of Objective-C interface members.
When messaging a method that was defined in an Objective-C class (or
category or extension thereof) that has type parameters, substitute
the type arguments for those type parameters. Similarly, substitute
into property accesses, instance variables, and other references.
This includes general infrastructure for substituting the type
arguments associated with an ObjCObject(Pointer)Type into a type
referenced within a particular context, handling all of the
substitutions required to deal with (e.g.) inheritance involving
parameterized classes. In cases where no type arguments are available
(e.g., because we're messaging via some unspecialized type, id, etc.),
we substitute in the type bounds for the type parameters instead.
Example:
@interface NSSet<T : id<NSCopying>> : NSObject <NSCopying>
- (T)firstObject;
@end
void f(NSSet<NSString *> *stringSet, NSSet *anySet) {
[stringSet firstObject]; // produces NSString*
[anySet firstObject]; // produces id<NSCopying> (the bound)
}
When substituting for the type parameters given an unspecialized
context (i.e., no specific type arguments were given), substituting
the type bounds unconditionally produces type signatures that are too
strong compared to the pre-generics signatures. Instead, use the
following rule:
- In covariant positions, such as method return types, replace type
parameters with “id” or “Class” (the latter only when the type
parameter bound is “Class” or qualified class, e.g,
“Class<NSCopying>”)
- In other positions (e.g., parameter types), replace type
parameters with their type bounds.
- When a specialized Objective-C object or object pointer type
contains a type parameter in its type arguments (e.g.,
NSArray<T>*, but not NSArray<NSString *> *), replace the entire
object/object pointer type with its unspecialized version (e.g.,
NSArray *).
llvm-svn: 241543
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index c7a66d7..d04121a 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -6893,9 +6893,13 @@
const ObjCInterfaceDecl* RDecl = RHS->getInterface();
if (!LDecl || !RDecl || (declaresSameEntity(LDecl, RDecl)))
return QualType();
-
- do {
- LHS = cast<ObjCInterfaceType>(getObjCInterfaceType(LDecl));
+
+ while (!declaresSameEntity(LHS->getInterface(), RDecl)) {
+ // Strip protocols from the left-hand side.
+ if (LHS->getNumProtocols() > 0)
+ LHS = getObjCObjectType(LHS->getBaseType(), LHS->getTypeArgsAsWritten(),
+ { })->castAs<ObjCObjectType>();
+
if (canAssignObjCInterfaces(LHS, RHS)) {
SmallVector<ObjCProtocolDecl *, 8> Protocols;
getIntersectionOfProtocols(*this, Lptr, Rptr, Protocols);
@@ -6906,7 +6910,13 @@
Result = getObjCObjectPointerType(Result);
return Result;
}
- } while ((LDecl = LDecl->getSuperClass()));
+
+ QualType LHSSuperType = LHS->getSuperClassType();
+ if (LHSSuperType.isNull())
+ break;
+
+ LHS = LHSSuperType->castAs<ObjCObjectType>();
+ }
return QualType();
}
@@ -6918,21 +6928,15 @@
// Verify that the base decls are compatible: the RHS must be a subclass of
// the LHS.
- if (!LHS->getInterface()->isSuperClassOf(RHS->getInterface()))
+ ObjCInterfaceDecl *LHSInterface = LHS->getInterface();
+ bool IsSuperClass = LHSInterface->isSuperClassOf(RHS->getInterface());
+ if (!IsSuperClass)
return false;
- // RHS must have a superset of the protocols in the LHS. If the LHS is not
- // protocol qualified at all, then we are good.
- if (LHS->getNumProtocols() == 0)
- return true;
-
- // Okay, we know the LHS has protocol qualifiers. But RHS may or may not.
- // More detailed analysis is required.
- // OK, if LHS is same or a superclass of RHS *and*
- // this LHS, or as RHS's super class is assignment compatible with LHS.
- bool IsSuperClass =
- LHS->getInterface()->isSuperClassOf(RHS->getInterface());
- if (IsSuperClass) {
+ // If the LHS has protocol qualifiers, determine whether all of them are
+ // satisfied by the RHS (i.e., the RHS has a superset of the protocols in the
+ // LHS).
+ if (LHS->getNumProtocols() > 0) {
// OK if conversion of LHS to SuperClass results in narrowing of types
// ; i.e., SuperClass may implement at least one of the protocols
// in LHS's protocol list. Example, SuperObj<P1> = lhs<P1,P2> is ok.
@@ -6957,9 +6961,28 @@
if (!SuperImplementsProtocol)
return false;
}
- return true;
}
- return false;
+
+ // If the LHS is specialized, we may need to check type arguments.
+ if (LHS->isSpecialized()) {
+ // Follow the superclass chain until we've matched the LHS class in the
+ // hierarchy. This substitutes type arguments through.
+ const ObjCObjectType *RHSSuper = RHS;
+ while (!declaresSameEntity(RHSSuper->getInterface(), LHSInterface))
+ RHSSuper = RHSSuper->getSuperClassType()->castAs<ObjCObjectType>();
+
+ // If the RHS is specializd, compare type arguments.
+ if (RHSSuper->isSpecialized()) {
+ ArrayRef<QualType> LHSTypeArgs = LHS->getTypeArgs();
+ ArrayRef<QualType> RHSTypeArgs = RHSSuper->getTypeArgs();
+ for (unsigned i = 0, n = LHSTypeArgs.size(); i != n; ++i) {
+ if (!hasSameType(LHSTypeArgs[i], RHSTypeArgs[i]))
+ return false;
+ }
+ }
+ }
+
+ return true;
}
bool ASTContext::areComparableObjCPointerTypes(QualType LHS, QualType RHS) {