111 changes: 82 additions & 29 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -3681,7 +3684,7 @@ QualType ASTContext::getObjCObjectType(
}

canonical = getObjCObjectType(getCanonicalType(baseType), canonTypeArgs,
canonProtocols);
canonProtocols, isKindOf);

// Regenerate InsertPos.
ObjCObjectTypes.FindNodeOrInsertPos(ID, InsertPos);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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);
}

Expand All @@ -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
Expand All @@ -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);
}

Expand Down Expand Up @@ -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;
}
}
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/ASTDiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

Expand Down
4 changes: 2 additions & 2 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -1864,7 +1863,8 @@ QualType ASTNodeImporter::VisitObjCObjectType(const ObjCObjectType *T) {
}

return Importer.getToContext().getObjCObjectType(ToBaseType, TypeArgs,
Protocols);
Protocols,
T->isKindOfTypeAsWritten());
}

QualType
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
171 changes: 153 additions & 18 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -971,18 +1064,28 @@ QualType QualType::substObjCTypeArgs(
splitType.Quals);

case ObjCSubstitutionContext::Result:
case ObjCSubstitutionContext::Property:
// Substitute 'id' or 'Class', as appropriate.

// If the underlying type is based on 'Class', substitute 'Class'.
if (typeParam->getUnderlyingType()->isObjCClassType() ||
typeParam->getUnderlyingType()->isObjCQualifiedClassType()) {
return ctx.getQualifiedType(ctx.getObjCClassType(),
case ObjCSubstitutionContext::Property: {
// Substitute the __kindof form of the underlying type.
const auto *objPtr = typeParam->getUnderlyingType()
->castAs<ObjCObjectPointerType>();

// __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);
}
}
}
}
Expand Down Expand Up @@ -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;
Expand All @@ -1101,7 +1206,8 @@ QualType QualType::substObjCTypeArgs(
objcObjectType->qual_begin(),
objcObjectType->getNumProtocols());
return ctx.getObjCObjectType(objcObjectType->getBaseType(),
newTypeArgs, protocols);
newTypeArgs, protocols,
objcObjectType->isKindOfTypeAsWritten());
}
}

Expand All @@ -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.
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -2895,19 +3027,22 @@ 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)
ID.AddPointer(typeArg.getAsOpaquePtr());
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 {
Expand Down
15 changes: 13 additions & 2 deletions clang/lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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);
}

Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Basic/IdentifierTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
};

Expand Down Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Lex/PPMacroExpansion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Parse/ParseTentative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 17 additions & 10 deletions clang/lib/Sema/SemaExprObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,8 @@ ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR,
Context.getObjCObjectType(Context.ObjCBuiltinIdTy, { },
llvm::makeArrayRef(
(ObjCProtocolDecl**) PQ,
1));
1),
false);
QIDNSCopying = Context.getObjCObjectPointerType(QIDNSCopying);
}
}
Expand Down Expand Up @@ -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) {
Expand Down
18 changes: 1 addition & 17 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
84 changes: 78 additions & 6 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand All @@ -944,7 +947,8 @@ static QualType applyObjCProtocolQualifiers(

return ctx.getObjCObjectType(objT->getBaseType(),
objT->getTypeArgsAsWritten(),
protocols);
protocols,
objT->isKindOfTypeAsWritten());
}

if (type->isObjCObjectType()) {
Expand All @@ -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);
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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!");
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
15 changes: 15 additions & 0 deletions clang/test/CodeGenObjCXX/mangle.mm
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,18 @@ void overload1(void (f)(A<P>*)) {}
// 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) {}
33 changes: 33 additions & 0 deletions clang/test/PCH/objc_kindof.m
Original file line number Diff line number Diff line change
@@ -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
304 changes: 304 additions & 0 deletions clang/test/SemaObjC/kindof.m
Original file line number Diff line number Diff line change
@@ -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}}
}
14 changes: 13 additions & 1 deletion clang/test/SemaObjC/parameterized_classes_subst.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ @protocol NSCopying
@interface NSString : NSObject <NSCopying>
@end

@interface NSMutableString : NSString
@end

@interface NSNumber : NSObject <NSCopying>
@end

Expand Down Expand Up @@ -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 *'}}
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion clang/test/SemaObjCXX/parameterized_classes_subst.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down