Implement the Objective-C __kindof type qualifier.
The __kindof type qualifier can be applied to Objective-C object
(pointer) types to indicate id-like behavior, which includes implicit
"downcasting" of __kindof types to subclasses and id-like message-send
behavior. __kindof types provide better type bounds for substitutions
into unspecified generic types, which preserves more type information.
llvm-svn: 241548
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a92f005..58d703a 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3615,21 +3615,24 @@
ObjCProtocolDecl * const *Protocols,
unsigned NumProtocols) const {
return getObjCObjectType(BaseType, { },
- llvm::makeArrayRef(Protocols, NumProtocols));
+ llvm::makeArrayRef(Protocols, NumProtocols),
+ /*isKindOf=*/false);
}
QualType ASTContext::getObjCObjectType(
QualType baseType,
ArrayRef<QualType> typeArgs,
- ArrayRef<ObjCProtocolDecl *> protocols) const {
+ ArrayRef<ObjCProtocolDecl *> protocols,
+ bool isKindOf) const {
// If the base type is an interface and there aren't any protocols or
// type arguments to add, then the interface type will do just fine.
- if (typeArgs.empty() && protocols.empty() && isa<ObjCInterfaceType>(baseType))
+ if (typeArgs.empty() && protocols.empty() && !isKindOf &&
+ isa<ObjCInterfaceType>(baseType))
return baseType;
// Look in the folding set for an existing type.
llvm::FoldingSetNodeID ID;
- ObjCObjectTypeImpl::Profile(ID, baseType, typeArgs, protocols);
+ ObjCObjectTypeImpl::Profile(ID, baseType, typeArgs, protocols, isKindOf);
void *InsertPos = nullptr;
if (ObjCObjectType *QT = ObjCObjectTypes.FindNodeOrInsertPos(ID, InsertPos))
return QualType(QT, 0);
@@ -3681,7 +3684,7 @@
}
canonical = getObjCObjectType(getCanonicalType(baseType), canonTypeArgs,
- canonProtocols);
+ canonProtocols, isKindOf);
// Regenerate InsertPos.
ObjCObjectTypes.FindNodeOrInsertPos(ID, InsertPos);
@@ -3692,7 +3695,8 @@
size += protocols.size() * sizeof(ObjCProtocolDecl *);
void *mem = Allocate(size, TypeAlignment);
ObjCObjectTypeImpl *T =
- new (mem) ObjCObjectTypeImpl(canonical, baseType, typeArgs, protocols);
+ new (mem) ObjCObjectTypeImpl(canonical, baseType, typeArgs, protocols,
+ isKindOf);
Types.push_back(T);
ObjCObjectTypes.InsertNode(T, InsertPos);
@@ -6775,18 +6779,36 @@
RHS->isObjCUnqualifiedIdOrClass())
return true;
- if (LHS->isObjCQualifiedId() || RHS->isObjCQualifiedId())
- return ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0),
- QualType(RHSOPT,0),
- false);
+ // Function object that propagates a successful result or handles
+ // __kindof types.
+ auto finish = [&](bool succeeded) -> bool {
+ if (succeeded)
+ return true;
+
+ if (!RHS->isKindOfType())
+ return false;
+
+ // Strip off __kindof and protocol qualifiers, then check whether
+ // we can assign the other way.
+ return canAssignObjCInterfaces(RHSOPT->stripObjCKindOfTypeAndQuals(*this),
+ LHSOPT->stripObjCKindOfTypeAndQuals(*this));
+ };
+
+ if (LHS->isObjCQualifiedId() || RHS->isObjCQualifiedId()) {
+ return finish(ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0),
+ QualType(RHSOPT,0),
+ false));
+ }
- if (LHS->isObjCQualifiedClass() && RHS->isObjCQualifiedClass())
- return ObjCQualifiedClassTypesAreCompatible(QualType(LHSOPT,0),
- QualType(RHSOPT,0));
+ if (LHS->isObjCQualifiedClass() && RHS->isObjCQualifiedClass()) {
+ return finish(ObjCQualifiedClassTypesAreCompatible(QualType(LHSOPT,0),
+ QualType(RHSOPT,0)));
+ }
// If we have 2 user-defined types, fall into that path.
- if (LHS->getInterface() && RHS->getInterface())
- return canAssignObjCInterfaces(LHS, RHS);
+ if (LHS->getInterface() && RHS->getInterface()) {
+ return finish(canAssignObjCInterfaces(LHS, RHS));
+ }
return false;
}
@@ -6800,26 +6822,46 @@
const ObjCObjectPointerType *LHSOPT,
const ObjCObjectPointerType *RHSOPT,
bool BlockReturnType) {
+
+ // Function object that propagates a successful result or handles
+ // __kindof types.
+ auto finish = [&](bool succeeded) -> bool {
+ if (succeeded)
+ return true;
+
+ const ObjCObjectPointerType *Expected = BlockReturnType ? RHSOPT : LHSOPT;
+ if (!Expected->isKindOfType())
+ return false;
+
+ // Strip off __kindof and protocol qualifiers, then check whether
+ // we can assign the other way.
+ return canAssignObjCInterfacesInBlockPointer(
+ RHSOPT->stripObjCKindOfTypeAndQuals(*this),
+ LHSOPT->stripObjCKindOfTypeAndQuals(*this),
+ BlockReturnType);
+ };
+
if (RHSOPT->isObjCBuiltinType() || LHSOPT->isObjCIdType())
return true;
if (LHSOPT->isObjCBuiltinType()) {
- return RHSOPT->isObjCBuiltinType() || RHSOPT->isObjCQualifiedIdType();
+ return finish(RHSOPT->isObjCBuiltinType() ||
+ RHSOPT->isObjCQualifiedIdType());
}
if (LHSOPT->isObjCQualifiedIdType() || RHSOPT->isObjCQualifiedIdType())
- return ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0),
- QualType(RHSOPT,0),
- false);
+ return finish(ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0),
+ QualType(RHSOPT,0),
+ false));
const ObjCInterfaceType* LHS = LHSOPT->getInterfaceType();
const ObjCInterfaceType* RHS = RHSOPT->getInterfaceType();
if (LHS && RHS) { // We have 2 user-defined types.
if (LHS != RHS) {
if (LHS->getDecl()->isSuperClassOf(RHS->getDecl()))
- return BlockReturnType;
+ return finish(BlockReturnType);
if (RHS->getDecl()->isSuperClassOf(LHS->getDecl()))
- return !BlockReturnType;
+ return finish(!BlockReturnType);
}
else
return true;
@@ -6903,13 +6945,19 @@
// Check that the given Objective-C type argument lists are equivalent.
static bool sameObjCTypeArgs(const ASTContext &ctx, ArrayRef<QualType> lhsArgs,
- ArrayRef<QualType> rhsArgs) {
+ ArrayRef<QualType> rhsArgs,
+ bool stripKindOf) {
if (lhsArgs.size() != rhsArgs.size())
return false;
for (unsigned i = 0, n = lhsArgs.size(); i != n; ++i) {
- if (!ctx.hasSameType(lhsArgs[i], rhsArgs[i]))
- return false;
+ if (!ctx.hasSameType(lhsArgs[i], rhsArgs[i])) {
+ if (!stripKindOf ||
+ !ctx.hasSameType(lhsArgs[i].stripObjCKindOfType(ctx),
+ rhsArgs[i].stripObjCKindOfType(ctx))) {
+ return false;
+ }
+ }
}
return true;
@@ -6941,7 +6989,8 @@
bool anyChanges = false;
if (LHS->isSpecialized() && RHS->isSpecialized()) {
// Both have type arguments, compare them.
- if (!sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHS->getTypeArgs()))
+ if (!sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHS->getTypeArgs(),
+ /*stripKindOf=*/true))
return QualType();
} else if (LHS->isSpecialized() != RHS->isSpecialized()) {
// If only one has type arguments, the result will not have type
@@ -6960,7 +7009,8 @@
// If anything in the LHS will have changed, build a new result type.
if (anyChanges) {
QualType Result = getObjCInterfaceType(LHS->getInterface());
- Result = getObjCObjectType(Result, LHSTypeArgs, Protocols);
+ Result = getObjCObjectType(Result, LHSTypeArgs, Protocols,
+ LHS->isKindOfType());
return getObjCObjectPointerType(Result);
}
@@ -6987,7 +7037,8 @@
bool anyChanges = false;
if (LHS->isSpecialized() && RHS->isSpecialized()) {
// Both have type arguments, compare them.
- if (!sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHS->getTypeArgs()))
+ if (!sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHS->getTypeArgs(),
+ /*stripKindOf=*/true))
return QualType();
} else if (LHS->isSpecialized() != RHS->isSpecialized()) {
// If only one has type arguments, the result will not have type
@@ -7005,7 +7056,8 @@
if (anyChanges) {
QualType Result = getObjCInterfaceType(RHS->getInterface());
- Result = getObjCObjectType(Result, RHSTypeArgs, Protocols);
+ Result = getObjCObjectType(Result, RHSTypeArgs, Protocols,
+ RHS->isKindOfType());
return getObjCObjectPointerType(Result);
}
@@ -7075,7 +7127,8 @@
// If the RHS is specializd, compare type arguments.
if (RHSSuper->isSpecialized() &&
- !sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHSSuper->getTypeArgs())) {
+ !sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHSSuper->getTypeArgs(),
+ /*stripKindOf=*/true)) {
return false;
}
}
diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp
index d6de454..68a58a5 100644
--- a/clang/lib/AST/ASTDiagnostic.cpp
+++ b/clang/lib/AST/ASTDiagnostic.cpp
@@ -139,7 +139,8 @@
QualType BaseType = Desugar(Context, Ty->getBaseType(), ShouldAKA);
QT = Context.getObjCObjectType(BaseType, Ty->getTypeArgsAsWritten(),
llvm::makeArrayRef(Ty->qual_begin(),
- Ty->getNumProtocols()));
+ Ty->getNumProtocols()),
+ Ty->isKindOfTypeAsWritten());
}
}
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index f7bfcaa..d264e96 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1853,7 +1853,6 @@
TypeArgs.push_back(ImportedTypeArg);
}
-
SmallVector<ObjCProtocolDecl *, 4> Protocols;
for (auto *P : T->quals()) {
ObjCProtocolDecl *Protocol
@@ -1864,7 +1863,8 @@
}
return Importer.getToContext().getObjCObjectType(ToBaseType, TypeArgs,
- Protocols);
+ Protocols,
+ T->isKindOfTypeAsWritten());
}
QualType
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 0134c09..dac803e 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -2379,6 +2379,10 @@
}
void CXXNameMangler::mangleType(const ObjCObjectType *T) {
+ // Treat __kindof as a vendor extended type qualifier.
+ if (T->isKindOfType())
+ Out << "U8__kindof";
+
if (!T->qual_empty()) {
// Mangle protocol qualifiers.
SmallString<64> QualStr;
@@ -2391,7 +2395,16 @@
QualOS.flush();
Out << 'U' << QualStr.size() << QualStr;
}
+
mangleType(T->getBaseType());
+
+ if (T->isSpecialized()) {
+ // Mangle type arguments as I <type>+ E
+ Out << 'I';
+ for (auto typeArg : T->getTypeArgs())
+ mangleType(typeArg);
+ Out << 'E';
+ }
}
void CXXNameMangler::mangleType(const BlockPointerType *T) {
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index c3d5feb..73cf60d 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -466,15 +466,61 @@
return nullptr;
}
+bool Type::isObjCIdOrObjectKindOfType(const ASTContext &ctx,
+ const ObjCObjectType *&bound) const {
+ bound = nullptr;
+
+ const ObjCObjectPointerType *OPT = getAs<ObjCObjectPointerType>();
+ if (!OPT)
+ return false;
+
+ // Easy case: id.
+ if (OPT->isObjCIdType())
+ return true;
+
+ // If it's not a __kindof type, reject it now.
+ if (!OPT->isKindOfType())
+ return false;
+
+ // If it's Class or qualified Class, it's not an object type.
+ if (OPT->isObjCClassType() || OPT->isObjCQualifiedClassType())
+ return false;
+
+ // Figure out the type bound for the __kindof type.
+ bound = OPT->getObjectType()->stripObjCKindOfTypeAndQuals(ctx)
+ ->getAs<ObjCObjectType>();
+ return true;
+}
+
+bool Type::isObjCClassOrClassKindOfType() const {
+ const ObjCObjectPointerType *OPT = getAs<ObjCObjectPointerType>();
+ if (!OPT)
+ return false;
+
+ // Easy case: Class.
+ if (OPT->isObjCClassType())
+ return true;
+
+ // If it's not a __kindof type, reject it now.
+ if (!OPT->isKindOfType())
+ return false;
+
+ // If it's Class or qualified Class, it's a class __kindof type.
+ return OPT->isObjCClassType() || OPT->isObjCQualifiedClassType();
+}
+
ObjCObjectType::ObjCObjectType(QualType Canonical, QualType Base,
ArrayRef<QualType> typeArgs,
- ArrayRef<ObjCProtocolDecl *> protocols)
+ ArrayRef<ObjCProtocolDecl *> protocols,
+ bool isKindOf)
: Type(ObjCObject, Canonical, Base->isDependentType(),
Base->isInstantiationDependentType(),
Base->isVariablyModifiedType(),
Base->containsUnexpandedParameterPack()),
BaseType(Base)
{
+ ObjCObjectTypeBits.IsKindOf = isKindOf;
+
ObjCObjectTypeBits.NumTypeArgs = typeArgs.size();
assert(getTypeArgsAsWritten().size() == typeArgs.size() &&
"bitfield overflow in type argument count");
@@ -535,6 +581,52 @@
return { };
}
+bool ObjCObjectType::isKindOfType() const {
+ if (isKindOfTypeAsWritten())
+ return true;
+
+ // Look at the base type, which might have type arguments.
+ if (auto objcObject = getBaseType()->getAs<ObjCObjectType>()) {
+ // Terminate when we reach an interface type.
+ if (isa<ObjCInterfaceType>(objcObject))
+ return false;
+
+ return objcObject->isKindOfType();
+ }
+
+ // Not a "__kindof" type.
+ return false;
+}
+
+QualType ObjCObjectType::stripObjCKindOfTypeAndQuals(
+ const ASTContext &ctx) const {
+ if (!isKindOfType() && qual_empty())
+ return QualType(this, 0);
+
+ // Recursively strip __kindof.
+ SplitQualType splitBaseType = getBaseType().split();
+ QualType baseType(splitBaseType.Ty, 0);
+ if (const ObjCObjectType *baseObj
+ = splitBaseType.Ty->getAs<ObjCObjectType>()) {
+ baseType = baseObj->stripObjCKindOfTypeAndQuals(ctx);
+ }
+
+ return ctx.getObjCObjectType(ctx.getQualifiedType(baseType,
+ splitBaseType.Quals),
+ getTypeArgsAsWritten(),
+ /*protocols=*/{ },
+ /*isKindOf=*/false);
+}
+
+const ObjCObjectPointerType *ObjCObjectPointerType::stripObjCKindOfTypeAndQuals(
+ const ASTContext &ctx) const {
+ if (!isKindOfType() && qual_empty())
+ return this;
+
+ QualType obj = getObjectType()->stripObjCKindOfTypeAndQuals(ctx);
+ return ctx.getObjCObjectPointerType(obj)->castAs<ObjCObjectPointerType>();
+}
+
namespace {
/// Perform a simple type transformation that does not change the
@@ -888,7 +980,8 @@
return Ctx.getObjCObjectType(baseType, typeArgs,
llvm::makeArrayRef(T->qual_begin(),
- T->getNumProtocols()));
+ T->getNumProtocols()),
+ T->isKindOfTypeAsWritten());
}
TRIVIAL_TYPE_CLASS(ObjCInterface)
@@ -971,18 +1064,28 @@
splitType.Quals);
case ObjCSubstitutionContext::Result:
- case ObjCSubstitutionContext::Property:
- // Substitute 'id' or 'Class', as appropriate.
+ case ObjCSubstitutionContext::Property: {
+ // Substitute the __kindof form of the underlying type.
+ const auto *objPtr = typeParam->getUnderlyingType()
+ ->castAs<ObjCObjectPointerType>();
- // If the underlying type is based on 'Class', substitute 'Class'.
- if (typeParam->getUnderlyingType()->isObjCClassType() ||
- typeParam->getUnderlyingType()->isObjCQualifiedClassType()) {
- return ctx.getQualifiedType(ctx.getObjCClassType(),
+ // __kindof types, id, and Class don't need an additional
+ // __kindof.
+ if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType())
+ return ctx.getQualifiedType(typeParam->getUnderlyingType(),
splitType.Quals);
- }
- // Otherwise, substitute 'id'.
- return ctx.getQualifiedType(ctx.getObjCIdType(), splitType.Quals);
+ // Add __kindof.
+ const auto *obj = objPtr->getObjectType();
+ QualType resultTy = ctx.getObjCObjectType(obj->getBaseType(),
+ obj->getTypeArgsAsWritten(),
+ obj->getProtocols(),
+ /*isKindOf=*/true);
+
+ // Rebuild object pointer type.
+ resultTy = ctx.getObjCObjectPointerType(resultTy);
+ return ctx.getQualifiedType(resultTy, splitType.Quals);
+ }
}
}
}
@@ -1086,8 +1189,10 @@
objcObjectType->getNumProtocols());
if (typeArgs.empty() &&
context != ObjCSubstitutionContext::Superclass) {
- return ctx.getObjCObjectType(objcObjectType->getBaseType(), { },
- protocols);
+ return ctx.getObjCObjectType(
+ objcObjectType->getBaseType(), { },
+ protocols,
+ objcObjectType->isKindOfTypeAsWritten());
}
anyChanged = true;
@@ -1101,7 +1206,8 @@
objcObjectType->qual_begin(),
objcObjectType->getNumProtocols());
return ctx.getObjCObjectType(objcObjectType->getBaseType(),
- newTypeArgs, protocols);
+ newTypeArgs, protocols,
+ objcObjectType->isKindOfTypeAsWritten());
}
}
@@ -1121,6 +1227,30 @@
return *this;
}
+QualType QualType::stripObjCKindOfType(const ASTContext &constCtx) const {
+ // FIXME: Because ASTContext::getAttributedType() is non-const.
+ auto &ctx = const_cast<ASTContext &>(constCtx);
+ return simpleTransform(ctx, *this,
+ [&](QualType type) -> QualType {
+ SplitQualType splitType = type.split();
+ if (auto *objType = splitType.Ty->getAs<ObjCObjectType>()) {
+ if (!objType->isKindOfType())
+ return type;
+
+ QualType baseType
+ = objType->getBaseType().stripObjCKindOfType(ctx);
+ return ctx.getQualifiedType(
+ ctx.getObjCObjectType(baseType,
+ objType->getTypeArgsAsWritten(),
+ objType->getProtocols(),
+ /*isKindOf=*/false),
+ splitType.Quals);
+ }
+
+ return type;
+ });
+}
+
Optional<ArrayRef<QualType>> Type::getObjCSubstitutions(
const DeclContext *dc) const {
// Look through method scopes.
@@ -2745,7 +2875,9 @@
case attr_nonnull:
case attr_nullable:
case attr_null_unspecified:
+ case attr_objc_kindof:
return false;
+
case attr_pcs:
case attr_pcs_vfp:
case attr_cdecl:
@@ -2895,7 +3027,8 @@
void ObjCObjectTypeImpl::Profile(llvm::FoldingSetNodeID &ID,
QualType BaseType,
ArrayRef<QualType> typeArgs,
- ArrayRef<ObjCProtocolDecl *> protocols) {
+ ArrayRef<ObjCProtocolDecl *> protocols,
+ bool isKindOf) {
ID.AddPointer(BaseType.getAsOpaquePtr());
ID.AddInteger(typeArgs.size());
for (auto typeArg : typeArgs)
@@ -2903,11 +3036,13 @@
ID.AddInteger(protocols.size());
for (auto proto : protocols)
ID.AddPointer(proto);
+ ID.AddBoolean(isKindOf);
}
void ObjCObjectTypeImpl::Profile(llvm::FoldingSetNodeID &ID) {
- Profile(ID, getBaseType(), getTypeArgs(),
- llvm::makeArrayRef(qual_begin(), getNumProtocols()));
+ Profile(ID, getBaseType(), getTypeArgsAsWritten(),
+ llvm::makeArrayRef(qual_begin(), getNumProtocols()),
+ isKindOfTypeAsWritten());
}
namespace {
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index ab63bab..0bb50c6 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1129,6 +1129,9 @@
T->getAttrKind() == AttributedType::attr_objc_ownership)
return printBefore(T->getEquivalentType(), OS);
+ if (T->getAttrKind() == AttributedType::attr_objc_kindof)
+ OS << "__kindof ";
+
printBefore(T->getModifiedType(), OS);
if (T->isMSTypeSpec()) {
@@ -1165,6 +1168,9 @@
T->getAttrKind() == AttributedType::attr_objc_ownership)
return printAfter(T->getEquivalentType(), OS);
+ if (T->getAttrKind() == AttributedType::attr_objc_kindof)
+ return;
+
// TODO: not all attributes are GCC-style attributes.
if (T->isMSTypeSpec())
return;
@@ -1310,9 +1316,13 @@
void TypePrinter::printObjCObjectBefore(const ObjCObjectType *T,
raw_ostream &OS) {
- if (T->qual_empty() && T->isUnspecializedAsWritten())
+ if (T->qual_empty() && T->isUnspecializedAsWritten() &&
+ !T->isKindOfTypeAsWritten())
return printBefore(T->getBaseType(), OS);
+ if (T->isKindOfTypeAsWritten())
+ OS << "__kindof ";
+
print(T->getBaseType(), OS, StringRef());
if (T->isSpecializedAsWritten()) {
@@ -1346,7 +1356,8 @@
}
void TypePrinter::printObjCObjectAfter(const ObjCObjectType *T,
raw_ostream &OS) {
- if (T->qual_empty() && T->isUnspecializedAsWritten())
+ if (T->qual_empty() && T->isUnspecializedAsWritten() &&
+ !T->isKindOfTypeAsWritten())
return printAfter(T->getBaseType(), OS);
}