forked from OSchip/llvm-project
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:
parent
10dc9d80cb
commit
ab209d83be
clang
include/clang
lib
AST
Basic
Lex
Parse
Sema
Serialization
test
CodeGenObjCXX
PCH
SemaObjC
SemaObjCXX
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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]>;
|
||||
|
|
|
@ -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">;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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
|
|
@ -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}}
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue