Skip to content

Commit

Permalink
Implement the Objective-C __kindof type qualifier.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
DougGregor committed Jul 7, 2015
1 parent 10dc9d8 commit ab209d8
Show file tree
Hide file tree
Showing 26 changed files with 841 additions and 97 deletions.
5 changes: 3 additions & 2 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -1195,14 +1195,15 @@ class ASTContext : public RefCountedBase<ASTContext> {
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
Expand Down
72 changes: 66 additions & 6 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,9 @@ class QualType {
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
Expand Down Expand Up @@ -1353,9 +1356,12 @@ class Type : public ExtQualsTypeCommonBase {

/// 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;
Expand Down Expand Up @@ -1649,7 +1655,27 @@ class Type : public ExtQualsTypeCommonBase {
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'
Expand Down Expand Up @@ -3581,6 +3607,7 @@ class AttributedType : public Type, public llvm::FoldingSetNode {
attr_nonnull,
attr_nullable,
attr_null_unspecified,
attr_objc_kindof,
};

private:
Expand Down Expand Up @@ -4514,14 +4541,16 @@ 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)
: Type(ObjCInterface, QualType(), false, false, false, false),
BaseType(QualType(this_(), 0)) {
ObjCObjectTypeBits.NumProtocols = 0;
ObjCObjectTypeBits.NumTypeArgs = 0;
ObjCObjectTypeBits.IsKindOf = 0;
}

void computeSuperClassTypeSlow() const;
Expand Down Expand Up @@ -4603,6 +4632,17 @@ class ObjCObjectType : public Type {
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
Expand All @@ -4617,6 +4657,10 @@ class ObjCObjectType : public Type {
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); }

Expand All @@ -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() {
Expand Down Expand Up @@ -4798,6 +4844,12 @@ class ObjCObjectPointerType : public Type, public llvm::FoldingSetNode {
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 {
Expand All @@ -4810,6 +4862,9 @@ class ObjCObjectPointerType : public Type, public llvm::FoldingSetNode {
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(); }

Expand Down Expand Up @@ -4873,6 +4928,11 @@ class ObjCObjectPointerType : public Type, public llvm::FoldingSetNode {
/// 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());
}
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -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]>;
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -7240,6 +7240,10 @@ class Sema {
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.
Expand Down

0 comments on commit ab209d8

Please sign in to comment.