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
This commit is contained in:
Douglas Gregor 2015-07-07 03:58:42 +00:00
parent 10dc9d80cb
commit ab209d83be
26 changed files with 840 additions and 96 deletions

View File

@ -1195,14 +1195,15 @@ public:
QualType getObjCInterfaceType(const ObjCInterfaceDecl *Decl,
ObjCInterfaceDecl *PrevDecl = nullptr) const;
/// Legacy interface: cannot provide type arguments.
/// Legacy interface: cannot provide type arguments or __kindof.
QualType getObjCObjectType(QualType Base,
ObjCProtocolDecl * const *Protocols,
unsigned NumProtocols) const;
QualType getObjCObjectType(QualType Base,
ArrayRef<QualType> typeArgs,
ArrayRef<ObjCProtocolDecl *> protocols) const;
ArrayRef<ObjCProtocolDecl *> protocols,
bool isKindOf) const;
bool ObjCObjectAdoptsQTypeProtocols(QualType QT, ObjCInterfaceDecl *Decl);
/// QIdProtocolsAdoptObjCObjectProtocols - Checks that protocols in

View File

@ -1054,6 +1054,9 @@ public:
const DeclContext *dc,
ObjCSubstitutionContext context) const;
/// Strip Objective-C "__kindof" types from the given type.
QualType stripObjCKindOfType(const ASTContext &ctx) const;
private:
// These methods are implemented in a separate translation unit;
// "static"-ize them to avoid creating temporary QualTypes in the
@ -1353,9 +1356,12 @@ protected:
/// NumProtocols - The number of protocols stored directly on this
/// object type.
unsigned NumProtocols : 7;
unsigned NumProtocols : 6;
/// Whether this is a "kindof" type.
unsigned IsKindOf : 1;
};
static_assert(NumTypeBits + 7 + 7 <= 32, "Does not fit in an unsigned");
static_assert(NumTypeBits + 7 + 6 + 1 <= 32, "Does not fit in an unsigned");
class ReferenceTypeBitfields {
friend class ReferenceType;
@ -1649,7 +1655,27 @@ public:
bool isObjCQualifiedClassType() const; // Class<foo>
bool isObjCObjectOrInterfaceType() const;
bool isObjCIdType() const; // id
/// Whether the type is Objective-C 'id' or a __kindof type of an
/// object type, e.g., __kindof NSView * or __kindof id
/// <NSCopying>.
///
/// \param bound Will be set to the bound on non-id subtype types,
/// which will be (possibly specialized) Objective-C class type, or
/// null for 'id.
bool isObjCIdOrObjectKindOfType(const ASTContext &ctx,
const ObjCObjectType *&bound) const;
bool isObjCClassType() const; // Class
/// Whether the type is Objective-C 'Class' or a __kindof type of an
/// Class type, e.g., __kindof Class <NSCopying>.
///
/// Unlike \c isObjCIdOrObjectKindOfType, there is no relevant bound
/// here because Objective-C's type system cannot express "a class
/// object for a subclass of NSFoo".
bool isObjCClassOrClassKindOfType() const;
bool isBlockCompatibleObjCPointerType(ASTContext &ctx) const;
bool isObjCSelType() const; // Class
bool isObjCBuiltinType() const; // 'id' or 'Class'
@ -3581,6 +3607,7 @@ public:
attr_nonnull,
attr_nullable,
attr_null_unspecified,
attr_objc_kindof,
};
private:
@ -4514,7 +4541,8 @@ class ObjCObjectType : public Type {
protected:
ObjCObjectType(QualType Canonical, QualType Base,
ArrayRef<QualType> typeArgs,
ArrayRef<ObjCProtocolDecl *> protocols);
ArrayRef<ObjCProtocolDecl *> protocols,
bool isKindOf);
enum Nonce_ObjCInterface { Nonce_ObjCInterface };
ObjCObjectType(enum Nonce_ObjCInterface)
@ -4522,6 +4550,7 @@ protected:
BaseType(QualType(this_(), 0)) {
ObjCObjectTypeBits.NumProtocols = 0;
ObjCObjectTypeBits.NumTypeArgs = 0;
ObjCObjectTypeBits.IsKindOf = 0;
}
void computeSuperClassTypeSlow() const;
@ -4603,6 +4632,17 @@ public:
return qual_begin()[I];
}
/// Retrieve all of the protocol qualifiers.
ArrayRef<ObjCProtocolDecl *> getProtocols() const {
return ArrayRef<ObjCProtocolDecl *>(qual_begin(), getNumProtocols());
}
/// Whether this is a "__kindof" type as written.
bool isKindOfTypeAsWritten() const { return ObjCObjectTypeBits.IsKindOf; }
/// Whether this ia a "__kindof" type (semantically).
bool isKindOfType() const;
/// Retrieve the type of the superclass of this object type.
///
/// This operation substitutes any type arguments into the
@ -4617,6 +4657,10 @@ public:
return QualType(CachedSuperClassType.getPointer(), 0);
}
/// Strip off the Objective-C "kindof" type and (with it) any
/// protocol qualifiers.
QualType stripObjCKindOfTypeAndQuals(const ASTContext &ctx) const;
bool isSugared() const { return false; }
QualType desugar() const { return QualType(this, 0); }
@ -4638,15 +4682,17 @@ class ObjCObjectTypeImpl : public ObjCObjectType, public llvm::FoldingSetNode {
ObjCObjectTypeImpl(QualType Canonical, QualType Base,
ArrayRef<QualType> typeArgs,
ArrayRef<ObjCProtocolDecl *> protocols)
: ObjCObjectType(Canonical, Base, typeArgs, protocols) {}
ArrayRef<ObjCProtocolDecl *> protocols,
bool isKindOf)
: ObjCObjectType(Canonical, Base, typeArgs, protocols, isKindOf) {}
public:
void Profile(llvm::FoldingSetNodeID &ID);
static void Profile(llvm::FoldingSetNodeID &ID,
QualType Base,
ArrayRef<QualType> typeArgs,
ArrayRef<ObjCProtocolDecl *> protocols);
ArrayRef<ObjCProtocolDecl *> protocols,
bool isKindOf);
};
inline QualType *ObjCObjectType::getTypeArgStorage() {
@ -4798,6 +4844,12 @@ public:
return getObjectType()->isObjCUnqualifiedClass();
}
/// isObjCIdOrClassType - True if this is equivalent to the 'id' or
/// 'Class' type,
bool isObjCIdOrClassType() const {
return getObjectType()->isObjCUnqualifiedIdOrClass();
}
/// isObjCQualifiedIdType - True if this is equivalent to 'id<P>' for some
/// non-empty set of protocols.
bool isObjCQualifiedIdType() const {
@ -4810,6 +4862,9 @@ public:
return getObjectType()->isObjCQualifiedClass();
}
/// Whether this is a "__kindof" type.
bool isKindOfType() const { return getObjectType()->isKindOfType(); }
/// Whether this type is specialized, meaning that it has type arguments.
bool isSpecialized() const { return getObjectType()->isSpecialized(); }
@ -4873,6 +4928,11 @@ public:
/// null type if there is no superclass.
QualType getSuperClassType() const;
/// Strip off the Objective-C "kindof" type and (with it) any
/// protocol qualifiers.
const ObjCObjectPointerType *stripObjCKindOfTypeAndQuals(
const ASTContext &ctx) const;
void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getPointeeType());
}

View File

@ -976,6 +976,11 @@ def TypeNullUnspecified : TypeAttr {
let Documentation = [TypeNullUnspecifiedDocs];
}
def ObjCKindOf : TypeAttr {
let Spellings = [Keyword<"__kindof">];
let Documentation = [Undocumented];
}
def AssumeAligned : InheritableAttr {
let Spellings = [GCC<"assume_aligned">];
let Subjects = SubjectList<[ObjCMethod, Function]>;

View File

@ -978,6 +978,12 @@ def warning_multiple_selectors: Warning<
"several methods with selector %0 of mismatched types are found "
"for the @selector expression">,
InGroup<SelectorTypeMismatch>, DefaultIgnore;
def err_objc_kindof_nonobject : Error<
"'__kindof' specifier cannot be applied to non-object type %0">;
def err_objc_kindof_wrong_position : Error<
"'__kindof' type specifier must precede the declarator">;
// C++ declarations
def err_static_assert_expression_is_not_constant : Error<
"static_assert expression is not an integral constant expression">;

View File

@ -526,6 +526,9 @@ KEYWORD(__bridge_transfer , KEYARC)
KEYWORD(__bridge_retained , KEYARC)
KEYWORD(__bridge_retain , KEYARC)
// Objective-C keywords.
KEYWORD(__kindof , KEYOBJC2)
// Alternate spelling for various tokens. There are GCC extensions in all
// languages, but should not be disabled in strict conformance mode.
ALIAS("__alignof__" , __alignof , KEYALL)

View File

@ -7240,6 +7240,10 @@ public:
SourceLocation ProtocolRAngleLoc,
bool FailOnError = false);
/// Check the application of the Objective-C '__kindof' qualifier to
/// the given type.
bool checkObjCKindOfType(QualType &type, SourceLocation loc);
/// Ensure attributes are consistent with type.
/// \param [in, out] Attributes The attributes to check; they will
/// be modified to be consistent with \p PropertyTy.

View File

@ -3615,21 +3615,24 @@ QualType ASTContext::getObjCObjectType(QualType BaseType,
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 @@ QualType ASTContext::getObjCObjectType(
}
canonical = getObjCObjectType(getCanonicalType(baseType), canonTypeArgs,
canonProtocols);
canonProtocols, isKindOf);
// Regenerate InsertPos.
ObjCObjectTypes.FindNodeOrInsertPos(ID, InsertPos);
@ -3692,7 +3695,8 @@ QualType ASTContext::getObjCObjectType(
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 @@ bool ASTContext::canAssignObjCInterfaces(const ObjCObjectPointerType *LHSOPT,
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 @@ bool ASTContext::canAssignObjCInterfacesInBlockPointer(
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 @@ void getIntersectionOfProtocols(ASTContext &Context,
// 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 @@ QualType ASTContext::areCommonBaseCompatible(
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 @@ QualType ASTContext::areCommonBaseCompatible(
// 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 @@ QualType ASTContext::areCommonBaseCompatible(
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 @@ QualType ASTContext::areCommonBaseCompatible(
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 @@ bool ASTContext::canAssignObjCInterfaces(const ObjCObjectType *LHS,
// 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;
}
}

View File

@ -139,7 +139,8 @@ break; \
QualType BaseType = Desugar(Context, Ty->getBaseType(), ShouldAKA);
QT = Context.getObjCObjectType(BaseType, Ty->getTypeArgsAsWritten(),
llvm::makeArrayRef(Ty->qual_begin(),
Ty->getNumProtocols()));
Ty->getNumProtocols()),
Ty->isKindOfTypeAsWritten());
}
}

View File

@ -1853,7 +1853,6 @@ QualType ASTNodeImporter::VisitObjCObjectType(const ObjCObjectType *T) {
TypeArgs.push_back(ImportedTypeArg);
}
SmallVector<ObjCProtocolDecl *, 4> Protocols;
for (auto *P : T->quals()) {
ObjCProtocolDecl *Protocol
@ -1864,7 +1863,8 @@ QualType ASTNodeImporter::VisitObjCObjectType(const ObjCObjectType *T) {
}
return Importer.getToContext().getObjCObjectType(ToBaseType, TypeArgs,
Protocols);
Protocols,
T->isKindOfTypeAsWritten());
}
QualType

View File

@ -2379,6 +2379,10 @@ void CXXNameMangler::mangleType(const ObjCInterfaceType *T) {
}
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 @@ void CXXNameMangler::mangleType(const ObjCObjectType *T) {
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) {

View File

@ -466,15 +466,61 @@ const RecordType *Type::getAsUnionType() const {
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 @@ ArrayRef<QualType> ObjCObjectType::getTypeArgs() const {
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 @@ QualType simpleTransform(ASTContext &ctx, QualType type, F &&f) {
return Ctx.getObjCObjectType(baseType, typeArgs,
llvm::makeArrayRef(T->qual_begin(),
T->getNumProtocols()));
T->getNumProtocols()),
T->isKindOfTypeAsWritten());
}
TRIVIAL_TYPE_CLASS(ObjCInterface)
@ -971,18 +1064,28 @@ QualType QualType::substObjCTypeArgs(
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 @@ QualType QualType::substObjCTypeArgs(
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 @@ QualType QualType::substObjCTypeArgs(
objcObjectType->qual_begin(),
objcObjectType->getNumProtocols());
return ctx.getObjCObjectType(objcObjectType->getBaseType(),
newTypeArgs, protocols);
newTypeArgs, protocols,
objcObjectType->isKindOfTypeAsWritten());
}
}
@ -1121,6 +1227,30 @@ QualType QualType::substObjCMemberType(QualType objectType,
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 @@ bool AttributedType::isCallingConv() const {
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 @@ QualifierCollector::apply(const ASTContext &Context, const Type *T) const {
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 @@ void ObjCObjectTypeImpl::Profile(llvm::FoldingSetNodeID &ID,
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 {

View File

@ -1129,6 +1129,9 @@ void TypePrinter::printAttributedBefore(const AttributedType *T,
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 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
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::printObjCInterfaceAfter(const ObjCInterfaceType *T,
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::printObjCObjectBefore(const ObjCObjectType *T,
}
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);
}

View File

@ -109,7 +109,8 @@ namespace {
WCHARSUPPORT = 0x04000,
HALFSUPPORT = 0x08000,
KEYCONCEPTS = 0x10000,
KEYALL = (0x1ffff & ~KEYNOMS18 &
KEYOBJC2 = 0x20000,
KEYALL = (0x3ffff & ~KEYNOMS18 &
~KEYNOOPENCL) // KEYNOMS18 and KEYNOOPENCL are used to exclude.
};
@ -144,6 +145,7 @@ static KeywordStatus getKeywordStatus(const LangOptions &LangOpts,
// in non-arc mode.
if (LangOpts.ObjC2 && (Flags & KEYARC)) return KS_Enabled;
if (LangOpts.ConceptsTS && (Flags & KEYCONCEPTS)) return KS_Enabled;
if (LangOpts.ObjC2 && (Flags & KEYOBJC2)) return KS_Enabled;
if (LangOpts.CPlusPlus && (Flags & KEYCXX11)) return KS_Future;
return KS_Disabled;
}

View File

@ -1088,6 +1088,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
.Case("objc_default_synthesize_properties", LangOpts.ObjC2)
.Case("objc_fixed_enum", LangOpts.ObjC2)
.Case("objc_instancetype", LangOpts.ObjC2)
.Case("objc_kindof", LangOpts.ObjC2)
.Case("objc_modules", LangOpts.ObjC2 && LangOpts.Modules)
.Case("objc_nonfragile_abi", LangOpts.ObjCRuntime.isNonFragile())
.Case("objc_property_explicit_atomic",

View File

@ -2601,6 +2601,7 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS,
/// [C11] alignment-specifier declaration-specifiers[opt]
/// [GNU] attributes declaration-specifiers[opt]
/// [Clang] '__module_private__' declaration-specifiers[opt]
/// [ObjC1] '__kindof' declaration-specifiers[opt]
///
/// storage-class-specifier: [C99 6.7.1]
/// 'typedef'
@ -3083,6 +3084,13 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
ParseNullabilityTypeSpecifiers(DS.getAttributes());
continue;
// Objective-C 'kindof' types.
case tok::kw___kindof:
DS.getAttributes().addNew(Tok.getIdentifierInfo(), Loc, nullptr, Loc,
nullptr, 0, AttributeList::AS_Keyword);
(void)ConsumeToken();
continue;
// storage-class-specifier
case tok::kw_typedef:
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_typedef, Loc,
@ -4345,6 +4353,8 @@ bool Parser::isTypeSpecifierQualifier() {
case tok::kw__Nullable:
case tok::kw__Null_unspecified:
case tok::kw___kindof:
case tok::kw___private:
case tok::kw___local:
case tok::kw___global:
@ -4525,6 +4535,8 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
case tok::kw__Nullable:
case tok::kw__Null_unspecified:
case tok::kw___kindof:
case tok::kw___private:
case tok::kw___local:
case tok::kw___global:
@ -4762,6 +4774,13 @@ void Parser::ParseTypeQualifierListOpt(DeclSpec &DS, unsigned AttrReqs,
ParseNullabilityTypeSpecifiers(DS.getAttributes());
continue;
// Objective-C 'kindof' types.
case tok::kw___kindof:
DS.getAttributes().addNew(Tok.getIdentifierInfo(), Loc, nullptr, Loc,
nullptr, 0, AttributeList::AS_Keyword);
(void)ConsumeToken();
continue;
case tok::kw___attribute:
if (AttrReqs & AR_GNUAttributesParsedAndRejected)
// When GNU attributes are expressly forbidden, diagnose their usage.

View File

@ -1281,6 +1281,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
case tok::kw__Nonnull:
case tok::kw__Nullable:
case tok::kw__Null_unspecified:
case tok::kw___kindof:
return TPResult::True;
// Borland

View File

@ -962,7 +962,8 @@ ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR,
Context.getObjCObjectType(Context.ObjCBuiltinIdTy, { },
llvm::makeArrayRef(
(ObjCProtocolDecl**) PQ,
1));
1),
false);
QIDNSCopying = Context.getObjCObjectPointerType(QIDNSCopying);
}
}
@ -2620,35 +2621,41 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
// of the more detailed type-checking on the receiver.
if (!Method) {
// Handle messages to id.
bool receiverIsId = ReceiverType->isObjCIdType();
if (receiverIsId || ReceiverType->isBlockPointerType() ||
// Handle messages to id and __kindof types (where we use the
// global method pool).
// FIXME: The type bound is currently ignored by lookup in the
// global pool.
const ObjCObjectType *typeBound = nullptr;
bool receiverIsIdLike = ReceiverType->isObjCIdOrObjectKindOfType(Context,
typeBound);
if (receiverIsIdLike || ReceiverType->isBlockPointerType() ||
(Receiver && Context.isObjCNSObjectType(Receiver->getType()))) {
Method = LookupInstanceMethodInGlobalPool(Sel,
SourceRange(LBracLoc, RBracLoc),
receiverIsId);
receiverIsIdLike);
if (!Method)
Method = LookupFactoryMethodInGlobalPool(Sel,
SourceRange(LBracLoc,RBracLoc),
receiverIsId);
receiverIsIdLike);
if (Method) {
if (ObjCMethodDecl *BestMethod =
SelectBestMethod(Sel, ArgsIn, Method->isInstanceMethod()))
Method = BestMethod;
if (!AreMultipleMethodsInGlobalPool(Sel, Method,
SourceRange(LBracLoc, RBracLoc),
receiverIsId)) {
receiverIsIdLike)) {
DiagnoseUseOfDecl(Method, SelLoc);
}
}
} else if (ReceiverType->isObjCClassType() ||
} else if (ReceiverType->isObjCClassOrClassKindOfType() ||
ReceiverType->isObjCQualifiedClassType()) {
// Handle messages to Class.
// We allow sending a message to a qualified Class ("Class<foo>"), which
// is ok as long as one of the protocols implements the selector (if not,
// warn).
if (const ObjCObjectPointerType *QClassTy
= ReceiverType->getAsObjCQualifiedClassType()) {
if (!ReceiverType->isObjCClassOrClassKindOfType()) {
const ObjCObjectPointerType *QClassTy
= ReceiverType->getAsObjCQualifiedClassType();
// Search protocols for class methods.
Method = LookupMethodInQualifiedType(Sel, QClassTy, false);
if (!Method) {

View File

@ -2152,23 +2152,7 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType,
FromObjCPtr->getPointeeType()))
return false;
// Check for compatible
// Objective C++: We're able to convert between "id" or "Class" and a
// pointer to any interface (in both directions).
if (ToObjCPtr->isObjCBuiltinType() && FromObjCPtr->isObjCBuiltinType()) {
ConvertedType = AdoptQualifiers(Context, ToType, FromQualifiers);
return true;
}
// Conversions with Objective-C's id<...>.
if ((FromObjCPtr->isObjCQualifiedIdType() ||
ToObjCPtr->isObjCQualifiedIdType()) &&
Context.ObjCQualifiedIdTypesAreCompatible(ToType, FromType,
/*compare=*/false)) {
ConvertedType = AdoptQualifiers(Context, ToType, FromQualifiers);
return true;
}
// Objective C++: We're able to convert from a pointer to an
// interface to a pointer to a different interface.
// Conversion between Objective-C pointers.
if (Context.canAssignObjCInterfaces(ToObjCPtr, FromObjCPtr)) {
const ObjCInterfaceType* LHS = ToObjCPtr->getInterfaceType();
const ObjCInterfaceType* RHS = FromObjCPtr->getInterfaceType();

View File

@ -643,6 +643,9 @@ static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state,
NULLABILITY_TYPE_ATTRS_CASELIST:
// Nullability specifiers cannot go after the declarator-id.
// Objective-C __kindof does not get distributed.
case AttributeList::AT_ObjCKindOf:
continue;
default:
@ -928,7 +931,7 @@ static QualType applyObjCTypeArgs(Sema &S, SourceLocation loc, QualType type,
}
// Success. Form the specialized type.
return S.Context.getObjCObjectType(type, finalTypeArgs, { });
return S.Context.getObjCObjectType(type, finalTypeArgs, { }, false);
}
/// Apply Objective-C protocol qualifiers to the given type.
@ -944,7 +947,8 @@ static QualType applyObjCProtocolQualifiers(
return ctx.getObjCObjectType(objT->getBaseType(),
objT->getTypeArgsAsWritten(),
protocols);
protocols,
objT->isKindOfTypeAsWritten());
}
if (type->isObjCObjectType()) {
@ -953,18 +957,22 @@ static QualType applyObjCProtocolQualifiers(
// FIXME: Check for protocols to which the class type is already
// known to conform.
return ctx.getObjCObjectType(type, { }, protocols);
return ctx.getObjCObjectType(type, { }, protocols, false);
}
// id<protocol-list>
if (type->isObjCIdType()) {
type = ctx.getObjCObjectType(ctx.ObjCBuiltinIdTy, { }, protocols);
const ObjCObjectPointerType *objPtr = type->castAs<ObjCObjectPointerType>();
type = ctx.getObjCObjectType(ctx.ObjCBuiltinIdTy, { }, protocols,
objPtr->isKindOfType());
return ctx.getObjCObjectPointerType(type);
}
// Class<protocol-list>
if (type->isObjCClassType()) {
type = ctx.getObjCObjectType(ctx.ObjCBuiltinClassTy, { }, protocols);
const ObjCObjectPointerType *objPtr = type->castAs<ObjCObjectPointerType>();
type = ctx.getObjCObjectType(ctx.ObjCBuiltinClassTy, { }, protocols,
objPtr->isKindOfType());
return ctx.getObjCObjectPointerType(type);
}
@ -1021,7 +1029,8 @@ TypeResult Sema::actOnObjCProtocolQualifierType(
Context.ObjCBuiltinIdTy, { },
llvm::makeArrayRef(
(ObjCProtocolDecl * const *)protocols.data(),
protocols.size()));
protocols.size()),
false);
Result = Context.getObjCObjectPointerType(Result);
TypeSourceInfo *ResultTInfo = Context.CreateTypeSourceInfo(Result);
@ -4430,6 +4439,8 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) {
return AttributeList::AT_TypeNullable;
case AttributedType::attr_null_unspecified:
return AttributeList::AT_TypeNullUnspecified;
case AttributedType::attr_objc_kindof:
return AttributeList::AT_ObjCKindOf;
}
llvm_unreachable("unexpected attribute kind!");
}
@ -5514,6 +5525,44 @@ bool Sema::checkNullabilityTypeSpecifier(QualType &type,
return false;
}
bool Sema::checkObjCKindOfType(QualType &type, SourceLocation loc) {
// Find out if it's an Objective-C object or object pointer type;
const ObjCObjectPointerType *ptrType = type->getAs<ObjCObjectPointerType>();
const ObjCObjectType *objType = ptrType ? ptrType->getObjectType()
: type->getAs<ObjCObjectType>();
// If not, we can't apply __kindof.
if (!objType) {
// FIXME: Handle dependent types that aren't yet object types.
Diag(loc, diag::err_objc_kindof_nonobject)
<< type;
return true;
}
// Rebuild the "equivalent" type, which pushes __kindof down into
// the object type.
QualType equivType = Context.getObjCObjectType(objType->getBaseType(),
objType->getTypeArgsAsWritten(),
objType->getProtocols(),
/*isKindOf=*/true);
// If we started with an object pointer type, rebuild it.
if (ptrType) {
equivType = Context.getObjCObjectPointerType(equivType);
if (auto nullability = type->getNullability(Context)) {
auto attrKind = AttributedType::getNullabilityAttrKind(*nullability);
equivType = Context.getAttributedType(attrKind, equivType, equivType);
}
}
// Build the attributed type to record where __kindof occurred.
type = Context.getAttributedType(AttributedType::attr_objc_kindof,
type,
equivType);
return false;
}
/// Map a nullability attribute kind to a nullability kind.
static NullabilityKind mapNullabilityAttrKind(AttributeList::Kind kind) {
switch (kind) {
@ -6124,6 +6173,7 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
attr.setUsedAsTypeAttr();
break;
NULLABILITY_TYPE_ATTRS_CASELIST:
// Either add nullability here or try to distribute it. We
// don't want to distribute the nullability specifier past any
@ -6142,6 +6192,28 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
}
break;
case AttributeList::AT_ObjCKindOf:
// '__kindof' must be part of the decl-specifiers.
switch (TAL) {
case TAL_DeclSpec:
break;
case TAL_DeclChunk:
case TAL_DeclName:
state.getSema().Diag(attr.getLoc(),
diag::err_objc_kindof_wrong_position)
<< FixItHint::CreateRemoval(attr.getLoc())
<< FixItHint::CreateInsertion(
state.getDeclarator().getDeclSpec().getLocStart(), "__kindof ");
break;
}
// Apply it regardless.
if (state.getSema().checkObjCKindOfType(type, attr.getLoc()))
attr.setInvalid();
attr.setUsedAsTypeAttr();
break;
case AttributeList::AT_NSReturnsRetained:
if (!state.getSema().getLangOpts().ObjCAutoRefCount)
break;

View File

@ -5271,7 +5271,8 @@ QualType ASTReader::readTypeRecord(unsigned Index) {
SmallVector<ObjCProtocolDecl*, 4> Protos;
for (unsigned I = 0; I != NumProtos; ++I)
Protos.push_back(ReadDeclAs<ObjCProtocolDecl>(*Loc.F, Record, Idx));
return Context.getObjCObjectType(Base, TypeArgs, Protos);
bool IsKindOf = Record[Idx++];
return Context.getObjCObjectType(Base, TypeArgs, Protos, IsKindOf);
}
case TYPE_OBJC_OBJECT_POINTER: {

View File

@ -427,6 +427,7 @@ void ASTTypeWriter::VisitObjCObjectType(const ObjCObjectType *T) {
Record.push_back(T->getNumProtocols());
for (const auto *I : T->quals())
Writer.AddDeclRef(I, Record);
Record.push_back(T->isKindOfTypeAsWritten());
Code = TYPE_OBJC_OBJECT;
}

View File

@ -98,3 +98,18 @@ template<> void X<A*>::f() {}
// CHECK-LABEL: define void @_ZN1XIP1AE1fEv
template<> void X<A<P>*>::f() {}
// CHECK-LABEL: define void @_ZN1XIPU11objcproto1P1AE1fEv
// CHECK-LABEL: define void @_Z12kindof_test2PU8__kindof5Test2
void kindof_test2(__kindof Test2 *t2) { }
@interface Parameterized<T, U> : A
@end
// CHECK-LABEL: define void @_Z19parameterized_test1P13ParameterizedIP1AP4TestE
void parameterized_test1(Parameterized<A *, Test *> *p) {}
// CHECK-LABEL: define void @_Z19parameterized_test2PU8__kindof13ParameterizedIP1AP4TestE
void parameterized_test2(__kindof Parameterized<A *, Test *> *p) {}
// CHECK-LABEL: define void @_Z19parameterized_test3P13Parameterized
void parameterized_test3(Parameterized *p) {}

View File

@ -0,0 +1,33 @@
// RUN: %clang_cc1 -emit-pch %s -o %t
// RUN: %clang_cc1 -include-pch %t -verify %s
#ifndef HEADER_INCLUDED
#define HEADER_INCLUDED
@protocol NSObject
@end
@protocol NSCopying
@end
__attribute__((objc_root_class))
@interface NSObject <NSObject>
@end
@interface NSString : NSObject <NSCopying>
@end
@interface NSMutableString : NSString
@end
@interface NSNumber : NSObject <NSCopying>
@end
extern __kindof NSObject <NSCopying> *kindof_NSObject_NSCopying;
#else
void testPrettyPrint(int *ip) {
ip = kindof_NSObject_NSCopying; // expected-warning{{from '__kindof NSObject<NSCopying> *'}}
}
#endif

View File

@ -0,0 +1,304 @@
// RUN: %clang_cc1 -fblocks -fsyntax-only %s -verify
// Tests Objective-C 'kindof' types.
#if !__has_feature(objc_kindof)
#error does not support __kindof
#endif
@protocol NSObject
@end
@protocol NSCopying
- (id)copy;
+ (Class)classCopy;
@end
@protocol NSRandomProto
- (void)randomMethod;
+ (void)randomClassMethod;
@end
__attribute__((objc_root_class))
@interface NSObject <NSObject>
- (NSObject *)retain;
@end
@interface NSString : NSObject <NSCopying>
- (NSString *)stringByAppendingString:(NSString *)string;
+ (instancetype)string;
@end
@interface NSMutableString : NSString
- (void)appendString:(NSString *)string;
@end
@interface NSNumber : NSObject <NSCopying>
- (NSNumber *)numberByAddingNumber:(NSNumber *)number;
@end
// ---------------------------------------------------------------------------
// Parsing and semantic analysis for __kindof
// ---------------------------------------------------------------------------
// Test proper application of __kindof.
typedef __kindof NSObject *typedef1;
typedef NSObject __kindof *typedef2;
typedef __kindof NSObject<NSCopying> typedef3;
typedef NSObject<NSCopying> __kindof *typedef4;
typedef __kindof id<NSCopying> typedef5;
typedef __kindof Class<NSCopying> typedef6;
// Test redundancy of __kindof.
typedef __kindof id __kindof redundant_typedef1;
typedef __kindof NSObject __kindof *redundant_typedef2;
// Test application of __kindof to typedefs.
typedef NSObject *NSObject_ptr_typedef;
typedef NSObject NSObject_typedef;
typedef __kindof NSObject_ptr_typedef typedef_typedef1;
typedef __kindof NSObject_typedef typedef_typedef2;
// Test application of __kindof to non-object types.
typedef __kindof int nonobject_typedef1; // expected-error{{'__kindof' specifier cannot be applied to non-object type 'int'}}
typedef NSObject **NSObject_ptr_ptr;
typedef __kindof NSObject_ptr_ptr nonobject_typedef2; // expected-error{{'__kindof' specifier cannot be applied to non-object type 'NSObject_ptr_ptr' (aka 'NSObject **')}}
// Test application of __kindof outside of the decl-specifiers.
typedef NSObject * __kindof bad_specifier_location1; // expected-error{{'__kindof' type specifier must precede the declarator}}
typedef NSObject bad_specifier_location2 __kindof; // expected-error{{expected ';' after top level declarator}}
// expected-warning@-1{{declaration does not declare anything}}
// ---------------------------------------------------------------------------
// Pretty printing of __kindof
// ---------------------------------------------------------------------------
void test_pretty_print(int *ip) {
__kindof NSObject *kindof_NSObject;
ip = kindof_NSObject; // expected-warning{{from '__kindof NSObject *'}}
__kindof NSObject_ptr_typedef kindof_NSObject_ptr;
ip = kindof_NSObject_ptr; // expected-warning{{from '__kindof NSObject_ptr_typedef'}}
__kindof id <NSCopying> *kindof_NSCopying;
ip = kindof_NSCopying; // expected-warning{{from '__kindof id<NSCopying> *'}}
__kindof NSObject_ptr_typedef *kindof_NSObject_ptr_typedef;
ip = kindof_NSObject_ptr_typedef; // expected-warning{{from '__kindof NSObject_ptr_typedef *'}}
}
// ---------------------------------------------------------------------------
// Basic implicit conversions (dropping __kindof, upcasts, etc.)
// ---------------------------------------------------------------------------
void test_add_remove_kindof_conversions(void) {
__kindof NSObject *kindof_NSObject_obj;
NSObject *NSObject_obj;
// Conversion back and forth
kindof_NSObject_obj = NSObject_obj;
NSObject_obj = kindof_NSObject_obj;
// Qualified-id conversion back and forth.
__kindof id <NSCopying> kindof_id_NSCopying_obj;
id <NSCopying> id_NSCopying_obj;
kindof_id_NSCopying_obj = id_NSCopying_obj;
id_NSCopying_obj = kindof_id_NSCopying_obj;
}
void test_upcast_conversions(void) {
__kindof NSObject *kindof_NSObject_obj;
NSObject *NSObject_obj;
// Upcasts
__kindof NSString *kindof_NSString_obj;
NSString *NSString_obj;
kindof_NSObject_obj = kindof_NSString_obj;
kindof_NSObject_obj = NSString_obj;
NSObject_obj = kindof_NSString_obj;
NSObject_obj = NSString_obj;
// "Upcasts" with qualified-id.
__kindof id <NSCopying> kindof_id_NSCopying_obj;
id <NSCopying> id_NSCopying_obj;
kindof_id_NSCopying_obj = kindof_NSString_obj;
kindof_id_NSCopying_obj = NSString_obj;
id_NSCopying_obj = kindof_NSString_obj;
id_NSCopying_obj = NSString_obj;
}
void test_ptr_object_conversions(void) {
__kindof NSObject **ptr_kindof_NSObject_obj;
NSObject **ptr_NSObject_obj;
// Conversions back and forth.
ptr_kindof_NSObject_obj = ptr_NSObject_obj;
ptr_NSObject_obj = ptr_kindof_NSObject_obj;
// Conversions back and forth with qualified-id.
__kindof id <NSCopying> *ptr_kindof_id_NSCopying_obj;
id <NSCopying> *ptr_id_NSCopying_obj;
ptr_kindof_id_NSCopying_obj = ptr_id_NSCopying_obj;
ptr_id_NSCopying_obj = ptr_kindof_id_NSCopying_obj;
// Upcasts.
__kindof NSString **ptr_kindof_NSString_obj;
NSString **ptr_NSString_obj;
ptr_kindof_NSObject_obj = ptr_kindof_NSString_obj;
ptr_kindof_NSObject_obj = ptr_NSString_obj;
ptr_NSObject_obj = ptr_kindof_NSString_obj;
ptr_NSObject_obj = ptr_NSString_obj;
}
// ---------------------------------------------------------------------------
// Implicit downcasting
// ---------------------------------------------------------------------------
void test_downcast_conversions(void) {
__kindof NSObject *kindof_NSObject_obj;
NSObject *NSObject_obj;
__kindof NSString *kindof_NSString_obj;
NSString *NSString_obj;
// Implicit downcasting.
kindof_NSString_obj = kindof_NSObject_obj;
kindof_NSString_obj = NSObject_obj; // expected-warning{{assigning to '__kindof NSString *' from 'NSObject *'}}
NSString_obj = kindof_NSObject_obj;
NSString_obj = NSObject_obj; // expected-warning{{assigning to 'NSString *' from 'NSObject *'}}
// Implicit downcasting with qualified id.
__kindof id <NSCopying> kindof_NSCopying_obj;
id <NSCopying> NSCopying_obj;
kindof_NSString_obj = kindof_NSCopying_obj;
kindof_NSString_obj = NSCopying_obj; // expected-warning{{from incompatible type 'id<NSCopying>'}}
NSString_obj = kindof_NSCopying_obj;
NSString_obj = NSCopying_obj; // expected-warning{{from incompatible type 'id<NSCopying>'}}
kindof_NSObject_obj = kindof_NSCopying_obj;
kindof_NSObject_obj = NSCopying_obj; // expected-warning{{from incompatible type 'id<NSCopying>'}}
NSObject_obj = kindof_NSCopying_obj;
NSObject_obj = NSCopying_obj; // expected-warning{{from incompatible type 'id<NSCopying>'}}
}
void test_crosscast_conversions(void) {
__kindof NSString *kindof_NSString_obj;
NSString *NSString_obj;
__kindof NSNumber *kindof_NSNumber_obj;
NSNumber *NSNumber_obj;
NSString_obj = kindof_NSNumber_obj; // expected-warning{{from '__kindof NSNumber *'}}
}
// ---------------------------------------------------------------------------
// Blocks
// ---------------------------------------------------------------------------
void test_block_conversions(void) {
// Adding/removing __kindof from return type.
__kindof NSString *(^kindof_NSString_void_block)(void);
NSString *(^NSString_void_block)(void);
kindof_NSString_void_block = NSString_void_block;
NSString_void_block = kindof_NSString_void_block;
// Covariant return type.
__kindof NSMutableString *(^kindof_NSMutableString_void_block)(void);
NSMutableString *(^NSMutableString_void_block)(void);
kindof_NSString_void_block = NSMutableString_void_block;
NSString_void_block = kindof_NSMutableString_void_block;
kindof_NSString_void_block = NSMutableString_void_block;
NSString_void_block = kindof_NSMutableString_void_block;
// "Covariant" return type via downcasting rule.
kindof_NSMutableString_void_block = NSString_void_block; // expected-error{{from 'NSString *(^)(void)'}}
NSMutableString_void_block = kindof_NSString_void_block;
kindof_NSMutableString_void_block = NSString_void_block; // expected-error{{from 'NSString *(^)(void)'}}
NSMutableString_void_block = kindof_NSString_void_block;
// Cross-casted return type.
__kindof NSNumber *(^kindof_NSNumber_void_block)(void);
NSNumber *(^NSNumber_void_block)(void);
kindof_NSString_void_block = NSNumber_void_block; // expected-error{{from 'NSNumber *(^)(void)'}}
NSString_void_block = kindof_NSNumber_void_block; // expected-error{{'__kindof NSNumber *(^)(void)'}}
kindof_NSString_void_block = NSNumber_void_block; // expected-error{{from 'NSNumber *(^)(void)'}}
NSString_void_block = kindof_NSNumber_void_block; // expected-error{{'__kindof NSNumber *(^)(void)'}}
// Adding/removing __kindof from argument type.
void (^void_kindof_NSString_block)(__kindof NSString *);
void (^void_NSString_block)(NSString *);
void_kindof_NSString_block = void_NSString_block;
void_NSString_block = void_kindof_NSString_block;
// Contravariant argument type.
void (^void_kindof_NSMutableString_block)(__kindof NSMutableString *);
void (^void_NSMutableString_block)(NSMutableString *);
void_kindof_NSMutableString_block = void_kindof_NSString_block;
void_kindof_NSMutableString_block = void_NSString_block;
void_NSMutableString_block = void_kindof_NSString_block;
void_NSMutableString_block = void_NSString_block;
// "Contravariant" argument type via downcasting rule.
void_kindof_NSString_block = void_kindof_NSMutableString_block;
void_kindof_NSString_block = void_NSMutableString_block;
void_NSString_block = void_kindof_NSMutableString_block; // expected-error{{from 'void (^)(__kindof NSMutableString *)'}}
void_NSString_block = void_NSMutableString_block; // expected-error{{from 'void (^)(NSMutableString *)'}}
}
// ---------------------------------------------------------------------------
// Messaging __kindof types.
// ---------------------------------------------------------------------------
void message_kindof_object(__kindof NSString *kindof_NSString) {
[kindof_NSString retain]; // in superclass
[kindof_NSString stringByAppendingString:0]; // in class
[kindof_NSString appendString:0]; // in subclass
[kindof_NSString numberByAddingNumber: 0]; // FIXME: in unrelated class
[kindof_NSString randomMethod]; // in protocol
}
void message_kindof_qualified_id(__kindof id <NSCopying> kindof_NSCopying) {
[kindof_NSCopying copy]; // in protocol
[kindof_NSCopying stringByAppendingString:0]; // in some class
[kindof_NSCopying randomMethod]; // in unrelated protocol
}
void message_kindof_qualified_class(
__kindof Class <NSCopying> kindof_NSCopying) {
[kindof_NSCopying classCopy]; // in protocol
[kindof_NSCopying string]; // in some class
[kindof_NSCopying randomClassMethod]; // in unrelated protocol
}
// ---------------------------------------------------------------------------
// __kindof within specialized types
// ---------------------------------------------------------------------------
@interface NSArray<T> : NSObject
@end
void implicit_convert_array(NSArray<__kindof NSString *> *kindofStringsArray,
NSArray<NSString *> *stringsArray,
NSArray<__kindof NSMutableString *>
*kindofMutStringsArray,
NSArray<NSMutableString *> *mutStringsArray) {
// Adding/removing __kindof is okay.
kindofStringsArray = stringsArray;
stringsArray = kindofStringsArray;
// Other covariant and contravariant conversions still not permitted.
kindofStringsArray = mutStringsArray; // expected-warning{{incompatible pointer types}}
stringsArray = kindofMutStringsArray; // expected-warning{{incompatible pointer types}}
mutStringsArray = kindofStringsArray; // expected-warning{{incompatible pointer types}}
// Adding/removing nested __kindof is okay.
NSArray<NSArray<__kindof NSString *> *> *kindofStringsArrayArray;
NSArray<NSArray<NSString *> *> *stringsArrayArray;
kindofStringsArrayArray = stringsArrayArray;
stringsArrayArray = kindofStringsArrayArray;
}
// ---------------------------------------------------------------------------
// __kindof + nullability
// ---------------------------------------------------------------------------
void testNullability() {
// The base type being a pointer type tickles the bug.
extern __kindof id <NSCopying> __nonnull getSomeCopyable();
NSString *string = getSomeCopyable(); // no-warning
void processCopyable(__typeof(getSomeCopyable()) string);
processCopyable(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
}

View File

@ -18,6 +18,9 @@ __attribute__((objc_root_class))
@interface NSString : NSObject <NSCopying>
@end
@interface NSMutableString : NSString
@end
@interface NSNumber : NSObject <NSCopying>
@end
@ -144,6 +147,7 @@ void test_message_send_result(
NSMutableSet *mutSet,
MutableSetOfArrays *mutArraySet,
NSArray<NSString *> *stringArray,
NSArray<__kindof NSString *> *kindofStringArray,
void (^block)(void)) {
int *ip;
ip = [stringSet firstObject]; // expected-warning{{from 'NSString *'}}
@ -171,6 +175,12 @@ void test_message_send_result(
[[NSMutableArray alloc] initWithArray: stringArray]; // okay
[[NSMutableArray<NSString *> alloc] initWithArray: stringArray]; // okay
[[NSMutableArray<NSNumber *> alloc] initWithArray: stringArray]; // expected-warning{{sending 'NSArray<NSString *> *' to parameter of type 'NSArray<NSNumber *> *'}}
ip = [[[NSViewController alloc] init] view]; // expected-warning{{from '__kindof NSView *'}}
[[[[NSViewController alloc] init] view] toggle];
NSMutableString *mutStr = kindofStringArray[0];
NSNumber *number = kindofStringArray[0]; // expected-warning{{of type '__kindof NSString *'}}
}
void test_message_send_param(
@ -215,7 +225,9 @@ void test_property_read(
ip = mutSet.allObjects; // expected-warning{{from 'NSArray *'}}
ip = mutArraySet.allObjects; // expected-warning{{from 'NSArray *'}}
ip = mutDict.someRandomKey; // expected-warning{{from 'id'}}
ip = mutDict.someRandomKey; // expected-warning{{from '__kindof id<NSCopying>'}}
ip = [[NSViewController alloc] init].view; // expected-warning{{from '__kindof NSView *'}}
}
void test_property_write(

View File

@ -172,7 +172,7 @@ void test_property_read(
ip = mutSet.allObjects; // expected-error{{from incompatible type 'NSArray *'}}
ip = mutArraySet.allObjects; // expected-error{{from incompatible type 'NSArray *'}}
ip = mutDict.someRandomKey; // expected-error{{from incompatible type 'id'}}
ip = mutDict.someRandomKey; // expected-error{{from incompatible type '__kindof id<NSCopying>'}}
}
void test_property_write(