From 5df68d34cd4785fd723857ea8004092b96203cf9 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 7 Jul 2015 03:57:15 +0000 Subject: [PATCH] Parsing, semantic analysis, and AST for Objective-C type parameters. Produce type parameter declarations for Objective-C type parameters, and attach lists of type parameters to Objective-C classes, categories, forward declarations, and extensions as appropriate. Perform semantic analysis of type bounds for type parameters, both in isolation and across classes/categories/extensions to ensure consistency. Also handle (de-)serialization of Objective-C type parameter lists, along with sundry other things one must do to add a new declaration to Clang. Note that Objective-C type parameters are typedef name declarations, like typedefs and C++11 type aliases, in support of type erasure. Part of rdar://problem/6294649. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@241541 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DataRecursiveASTVisitor.h | 23 +- include/clang/AST/DeclObjC.h | 157 ++++++++- include/clang/AST/RecursiveASTVisitor.h | 21 +- include/clang/Basic/DeclNodes.td | 1 + include/clang/Basic/DiagnosticParseKinds.td | 13 + include/clang/Basic/DiagnosticSemaKinds.td | 36 ++ include/clang/Parse/Parser.h | 12 +- include/clang/Sema/Sema.h | 19 +- include/clang/Sema/Template.h | 1 + include/clang/Serialization/ASTBitCodes.h | 4 +- lib/AST/ASTContext.cpp | 1 + lib/AST/ASTDumper.cpp | 25 ++ lib/AST/ASTImporter.cpp | 56 +++- lib/AST/DeclBase.cpp | 1 + lib/AST/DeclObjC.cpp | 117 ++++++- lib/AST/DeclPrinter.cpp | 45 ++- lib/CodeGen/CGDecl.cpp | 1 + lib/Parse/ParseObjc.cpp | 268 ++++++++++++++- lib/Parse/ParseTemplate.cpp | 80 +++-- lib/Sema/SemaCodeComplete.cpp | 4 +- lib/Sema/SemaDeclObjC.cpp | 308 +++++++++++++++++- lib/Sema/SemaExprObjC.cpp | 15 +- lib/Serialization/ASTCommon.cpp | 1 + lib/Serialization/ASTReaderDecl.cpp | 37 ++- lib/Serialization/ASTWriterDecl.cpp | 26 ++ .../comment-objc-parameterized-classes.m | 19 ++ test/PCH/objc_parameterized_classes.m | 30 ++ .../objc-error-qualified-implementation.m | 2 +- test/SemaObjC/parameterized_classes.m | 192 +++++++++++ tools/libclang/CIndex.cpp | 2 + 30 files changed, 1425 insertions(+), 92 deletions(-) create mode 100644 test/Index/comment-objc-parameterized-classes.m create mode 100644 test/PCH/objc_parameterized_classes.m create mode 100644 test/SemaObjC/parameterized_classes.m diff --git a/include/clang/AST/DataRecursiveASTVisitor.h b/include/clang/AST/DataRecursiveASTVisitor.h index 923d98fb16..789bb8fb41 100644 --- a/include/clang/AST/DataRecursiveASTVisitor.h +++ b/include/clang/AST/DataRecursiveASTVisitor.h @@ -1307,7 +1307,12 @@ DEF_TRAVERSE_DECL(ObjCCompatibleAliasDecl, {// FIXME: implement }) DEF_TRAVERSE_DECL(ObjCCategoryDecl, {// FIXME: implement - }) + if (ObjCTypeParamList *typeParamList = D->getTypeParamList()) { + for (auto typeParam : *typeParamList) + TRY_TO(TraverseObjCTypeParamDecl(typeParam)); + } + return true; +}) DEF_TRAVERSE_DECL(ObjCCategoryImplDecl, {// FIXME: implement }) @@ -1316,7 +1321,12 @@ DEF_TRAVERSE_DECL(ObjCImplementationDecl, {// FIXME: implement }) DEF_TRAVERSE_DECL(ObjCInterfaceDecl, {// FIXME: implement - }) + if (ObjCTypeParamList *typeParamList = D->getTypeParamListAsWritten()) { + for (auto typeParam : *typeParamList) + TRY_TO(TraverseObjCTypeParamDecl(typeParam)); + } + return true; +}) DEF_TRAVERSE_DECL(ObjCProtocolDecl, {// FIXME: implement }) @@ -1335,6 +1345,15 @@ DEF_TRAVERSE_DECL(ObjCMethodDecl, { return true; }) +DEF_TRAVERSE_DECL(ObjCTypeParamDecl, { + if (D->hasExplicitBound()) { + TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc())); + // We shouldn't traverse D->getTypeForDecl(); it's a result of + // declaring the type alias, not something that was written in the + // source. + } +}) + DEF_TRAVERSE_DECL(ObjCPropertyDecl, { if (D->getTypeSourceInfo()) TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc())); diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index c3fb577702..78990c9f39 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -501,6 +501,124 @@ public: friend class ASTDeclWriter; }; +/// Represents the declaration of an Objective-C type parameter. +/// +/// \code +/// @interface NSDictionary, Value> +/// @end +/// \endcode +/// +/// In the example above, both \c Key and \c Value are represented by +/// \c ObjCTypeParamDecl. \c Key has an explicit bound of \c id, +/// while \c Value gets an implicit bound of \c id. +/// +/// Objective-C type parameters are typedef-names in the grammar, +class ObjCTypeParamDecl : public TypedefNameDecl { + void anchor() override; + + // The location of the ':', which will be valid when the bound was + // explicitly specified. + SourceLocation ColonLoc; + + ObjCTypeParamDecl(ASTContext &ctx, DeclContext *dc, + SourceLocation nameLoc, IdentifierInfo *name, + SourceLocation colonLoc, TypeSourceInfo *boundInfo) + : TypedefNameDecl(ObjCTypeParam, ctx, dc, nameLoc, nameLoc, name, + boundInfo), + ColonLoc(colonLoc) { } + +public: + static ObjCTypeParamDecl *Create(ASTContext &ctx, DeclContext *dc, + SourceLocation nameLoc, + IdentifierInfo *name, + SourceLocation colonLoc, + TypeSourceInfo *boundInfo); + static ObjCTypeParamDecl *CreateDeserialized(ASTContext &ctx, unsigned ID); + + SourceRange getSourceRange() const override LLVM_READONLY; + + /// Whether this type parameter has an explicitly-written type bound, e.g., + /// "T : NSView". + bool hasExplicitBound() const { return ColonLoc.isValid(); } + + /// Retrieve the location of the ':' separating the type parameter name + /// from the explicitly-specified bound. + SourceLocation getColonLoc() const { return ColonLoc; } + + // Implement isa/cast/dyncast/etc. + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == ObjCTypeParam; } + + friend class ASTDeclReader; + friend class ASTDeclWriter; +}; + +/// Stores a list of Objective-C type parameters for a parameterized class +/// or a category/extension thereof. +/// +/// \code +/// @interface NSArray // stores the +/// @end +/// \endcode +class ObjCTypeParamList { + union { + /// Location of the left and right angle brackets. + SourceRange Brackets; + + // Used only for alignment. + ObjCTypeParamDecl *AlignmentHack; + }; + + /// The number of parameters in the list, which are tail-allocated. + unsigned NumParams; + + ObjCTypeParamList(SourceLocation lAngleLoc, + ArrayRef typeParams, + SourceLocation rAngleLoc); + +public: + /// Create a new Objective-C type parameter list. + static ObjCTypeParamList *create(ASTContext &ctx, + SourceLocation lAngleLoc, + ArrayRef typeParams, + SourceLocation rAngleLoc); + + /// Iterate through the type parameters in the list. + typedef ObjCTypeParamDecl **iterator; + + iterator begin() { return reinterpret_cast(this + 1); } + + iterator end() { return begin() + size(); } + + /// Determine the number of type parameters in this list. + unsigned size() const { return NumParams; } + + // Iterate through the type parameters in the list. + typedef ObjCTypeParamDecl * const *const_iterator; + + const_iterator begin() const { + return reinterpret_cast(this + 1); + } + + const_iterator end() const { + return begin() + size(); + } + + ObjCTypeParamDecl *front() const { + assert(size() > 0 && "empty Objective-C type parameter list"); + return *begin(); + } + + ObjCTypeParamDecl *back() const { + assert(size() > 0 && "empty Objective-C type parameter list"); + return *(end() - 1); + } + + SourceLocation getLAngleLoc() const { return Brackets.getBegin(); } + SourceLocation getRAngleLoc() const { return Brackets.getEnd(); } + SourceRange getSourceRange() const { return Brackets; } +}; + /// ObjCContainerDecl - Represents a container for method declarations. /// Current sub-classes are ObjCInterfaceDecl, ObjCCategoryDecl, /// ObjCProtocolDecl, and ObjCImplDecl. @@ -736,11 +854,15 @@ class ObjCInterfaceDecl : public ObjCContainerDecl }; ObjCInterfaceDecl(const ASTContext &C, DeclContext *DC, SourceLocation AtLoc, - IdentifierInfo *Id, SourceLocation CLoc, - ObjCInterfaceDecl *PrevDecl, bool IsInternal); + IdentifierInfo *Id, ObjCTypeParamList *typeParamList, + SourceLocation CLoc, ObjCInterfaceDecl *PrevDecl, + bool IsInternal); void LoadExternalDefinition() const; + /// The type parameters associated with this class, if any. + ObjCTypeParamList *TypeParamList; + /// \brief Contains a pointer to the data associated with this class, /// which will be NULL if this class has not yet been defined. /// @@ -771,12 +893,27 @@ public: static ObjCInterfaceDecl *Create(const ASTContext &C, DeclContext *DC, SourceLocation atLoc, IdentifierInfo *Id, + ObjCTypeParamList *typeParamList, ObjCInterfaceDecl *PrevDecl, SourceLocation ClassLoc = SourceLocation(), bool isInternal = false); static ObjCInterfaceDecl *CreateDeserialized(const ASTContext &C, unsigned ID); + /// Retrieve the type parameters of this class. + /// + /// This function looks for a type parameter list for the given + /// class; if the class has been declared (with @class) but not + /// defined (with @interface), it will search for a declaration that + /// has type parameters, skipping any declarations that do not. + ObjCTypeParamList *getTypeParamList() const; + + /// Retrieve the type parameters written on this particular declaration of + /// the class. + ObjCTypeParamList *getTypeParamListAsWritten() const { + return TypeParamList; + } + SourceRange getSourceRange() const override LLVM_READONLY { if (isThisDeclarationADefinition()) return ObjCContainerDecl::getSourceRange(); @@ -1719,6 +1856,9 @@ class ObjCCategoryDecl : public ObjCContainerDecl { /// Interface belonging to this category ObjCInterfaceDecl *ClassInterface; + /// The type parameters associated with this category, if any. + ObjCTypeParamList *TypeParamList; + /// referenced protocols in this category. ObjCProtocolList ReferencedProtocols; @@ -1736,13 +1876,9 @@ class ObjCCategoryDecl : public ObjCContainerDecl { ObjCCategoryDecl(DeclContext *DC, SourceLocation AtLoc, SourceLocation ClassNameLoc, SourceLocation CategoryNameLoc, IdentifierInfo *Id, ObjCInterfaceDecl *IDecl, + ObjCTypeParamList *typeParamList, SourceLocation IvarLBraceLoc=SourceLocation(), - SourceLocation IvarRBraceLoc=SourceLocation()) - : ObjCContainerDecl(ObjCCategory, DC, Id, ClassNameLoc, AtLoc), - ClassInterface(IDecl), NextClassCategory(nullptr), - CategoryNameLoc(CategoryNameLoc), - IvarLBraceLoc(IvarLBraceLoc), IvarRBraceLoc(IvarRBraceLoc) { - } + SourceLocation IvarRBraceLoc=SourceLocation()); public: @@ -1752,6 +1888,7 @@ public: SourceLocation CategoryNameLoc, IdentifierInfo *Id, ObjCInterfaceDecl *IDecl, + ObjCTypeParamList *typeParamList, SourceLocation IvarLBraceLoc=SourceLocation(), SourceLocation IvarRBraceLoc=SourceLocation()); static ObjCCategoryDecl *CreateDeserialized(ASTContext &C, unsigned ID); @@ -1759,6 +1896,10 @@ public: ObjCInterfaceDecl *getClassInterface() { return ClassInterface; } const ObjCInterfaceDecl *getClassInterface() const { return ClassInterface; } + /// Retrieve the type parameter list associated with this category or + /// extension. + ObjCTypeParamList *getTypeParamList() const { return TypeParamList; } + ObjCCategoryImplDecl *getImplementation() const; void setImplementation(ObjCCategoryImplDecl *ImplD); diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index b118503b67..8633c0f418 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -1382,7 +1382,11 @@ DEF_TRAVERSE_DECL(ObjCCompatibleAliasDecl, {// FIXME: implement }) DEF_TRAVERSE_DECL(ObjCCategoryDecl, {// FIXME: implement - }) + if (ObjCTypeParamList *typeParamList = D->getTypeParamList()) { + for (auto typeParam : *typeParamList) + TRY_TO(TraverseObjCTypeParamDecl(typeParam)); + } +}) DEF_TRAVERSE_DECL(ObjCCategoryImplDecl, {// FIXME: implement }) @@ -1391,7 +1395,11 @@ DEF_TRAVERSE_DECL(ObjCImplementationDecl, {// FIXME: implement }) DEF_TRAVERSE_DECL(ObjCInterfaceDecl, {// FIXME: implement - }) + if (ObjCTypeParamList *typeParamList = D->getTypeParamListAsWritten()) { + for (auto typeParam : *typeParamList) + TRY_TO(TraverseObjCTypeParamDecl(typeParam)); + } +}) DEF_TRAVERSE_DECL(ObjCProtocolDecl, {// FIXME: implement }) @@ -1410,6 +1418,15 @@ DEF_TRAVERSE_DECL(ObjCMethodDecl, { return true; }) +DEF_TRAVERSE_DECL(ObjCTypeParamDecl, { + if (D->hasExplicitBound()) { + TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc())); + // We shouldn't traverse D->getTypeForDecl(); it's a result of + // declaring the type alias, not something that was written in the + // source. + } +}) + DEF_TRAVERSE_DECL(ObjCPropertyDecl, { if (D->getTypeSourceInfo()) TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc())); diff --git a/include/clang/Basic/DeclNodes.td b/include/clang/Basic/DeclNodes.td index dece8f9ed2..e1a23120c1 100644 --- a/include/clang/Basic/DeclNodes.td +++ b/include/clang/Basic/DeclNodes.td @@ -21,6 +21,7 @@ def Named : Decl<1>; def TypedefName : DDecl; def Typedef : DDecl; def TypeAlias : DDecl; + def ObjCTypeParam : DDecl; def UnresolvedUsingTypename : DDecl; def Tag : DDecl, DeclContext; def Enum : DDecl; diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 6a11d240c6..05bce63835 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -1017,4 +1017,17 @@ def err_module_expected_semi : Error< "expected ';' after module name">; } +let CategoryName = "Generics Issue" in { + +def err_objc_expected_type_parameter : Error< + "expected type parameter name">; + +def err_objc_parameterized_class_without_base : Error< + "parameterized Objective-C class %0 must have a superclass">; + +def err_objc_parameterized_implementation : Error< + "@implementation cannot have type parameters">; + +} + } // end of Parser diagnostics diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 7a2a49cfa8..3722b512cc 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -7743,6 +7743,42 @@ def warn_nullability_missing : Warning< "%select{pointer|block pointer|member pointer}0 is missing a nullability " "type specifier (_Nonnull, _Nullable, or _Null_unspecified)">, InGroup; +} + +let CategoryName = "Generics Issue" in { + +def err_objc_type_param_bound_nonobject : Error< + "type bound %0 for type parameter %1 is not an Objective-C pointer type">; + +def err_objc_type_param_bound_missing_pointer : Error< + "missing '*' in type bound %0 for type parameter %1">; + +def err_objc_type_param_redecl : Error< + "redeclaration of type parameter %0">; + +def err_objc_type_param_arity_mismatch : Error< + "%select{forward class declaration|class definition|category|extension}0 has " + "too %select{few|many}1 type parameters (expected %2, have %3)">; + +def err_objc_type_param_bound_conflict : Error< + "type bound %0 for type parameter %1 conflicts with " + "%select{implicit|previous}2 bound %3%select{for type parameter %5|}4">; + +def note_objc_type_param_here : Note<"type parameter %0 declared here">; + +def err_objc_type_param_bound_missing : Error< + "missing type bound %0 for type parameter %1 in %select{@interface|@class}2">; + +def err_objc_parameterized_category_nonclass : Error< + "%select{extension|category}0 of non-parameterized class %1 cannot have type " + "parameters">; + +def err_objc_parameterized_forward_class : Error< + "forward declaration of non-parameterized class %0 cannot have type " + "parameters">; + +def err_objc_parameterized_forward_class_first : Error< + "class %0 previously declared with type parameters">; } diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 97a0aa482d..0da9d58157 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -46,6 +46,8 @@ namespace clang { class PoisonSEHIdentifiersRAIIObject; class VersionTuple; class OMPClause; + class ObjCTypeParamList; + class ObjCTypeParameter; /// Parser - This implements a parser for the C family of languages. After /// parsing units of the grammar, productions are invoked to handle whatever has @@ -1246,6 +1248,13 @@ private: DeclGroupPtrTy ParseObjCAtClassDeclaration(SourceLocation atLoc); Decl *ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc, ParsedAttributes &prefixAttrs); + ObjCTypeParamList *parseObjCTypeParamList(); + ObjCTypeParamList *parseObjCTypeParamListOrProtocolRefs( + SourceLocation &lAngleLoc, + SmallVectorImpl &protocolIdents, + SourceLocation &rAngleLoc, + bool mayBeProtocolList = true); + void HelperActionsForIvarDeclarations(Decl *interfaceDecl, SourceLocation atLoc, BalancedDelimiterTracker &T, SmallVectorImpl &AllIvarDecls, @@ -2469,7 +2478,8 @@ private: typedef SmallVector TemplateArgList; bool ParseGreaterThanInTemplateList(SourceLocation &RAngleLoc, - bool ConsumeLastToken); + bool ConsumeLastToken, + bool ObjCGenericList); bool ParseTemplateIdAfterTemplateName(TemplateTy Template, SourceLocation TemplateNameLoc, const CXXScopeSpec &SS, diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 72a0e0b19e..cd0eb26aba 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7088,9 +7088,20 @@ public: }; ObjCContainerKind getObjCContainerKind() const; + DeclResult actOnObjCTypeParam(Scope *S, IdentifierInfo *paramName, + SourceLocation paramLoc, + SourceLocation colonLoc, + ParsedType typeBound); + + ObjCTypeParamList *actOnObjCTypeParamList(Scope *S, SourceLocation lAngleLoc, + ArrayRef typeParams, + SourceLocation rAngleLoc); + void popObjCTypeParamList(Scope *S, ObjCTypeParamList *typeParamList); + Decl *ActOnStartClassInterface(SourceLocation AtInterfaceLoc, IdentifierInfo *ClassName, SourceLocation ClassLoc, + ObjCTypeParamList *typeParamList, IdentifierInfo *SuperName, SourceLocation SuperLoc, Decl * const *ProtoRefs, @@ -7124,6 +7135,7 @@ public: Decl *ActOnStartCategoryInterface(SourceLocation AtInterfaceLoc, IdentifierInfo *ClassName, SourceLocation ClassLoc, + ObjCTypeParamList *typeParamList, IdentifierInfo *CategoryName, SourceLocation CategoryLoc, Decl * const *ProtoRefs, @@ -7147,9 +7159,10 @@ public: ArrayRef Decls); DeclGroupPtrTy ActOnForwardClassDeclaration(SourceLocation Loc, - IdentifierInfo **IdentList, - SourceLocation *IdentLocs, - unsigned NumElts); + IdentifierInfo **IdentList, + SourceLocation *IdentLocs, + ArrayRef TypeParamLists, + unsigned NumElts); DeclGroupPtrTy ActOnForwardProtocolDeclaration(SourceLocation AtProtoclLoc, const IdentifierLocPair *IdentList, diff --git a/include/clang/Sema/Template.h b/include/clang/Sema/Template.h index b822b11126..416ef7b1a6 100644 --- a/include/clang/Sema/Template.h +++ b/include/clang/Sema/Template.h @@ -413,6 +413,7 @@ namespace clang { #define LINKAGESPEC(DERIVED, BASE) #define OBJCCOMPATIBLEALIAS(DERIVED, BASE) #define OBJCMETHOD(DERIVED, BASE) +#define OBJCTYPEPARAM(DERIVED, BASE) #define OBJCIVAR(DERIVED, BASE) #define OBJCPROPERTY(DERIVED, BASE) #define OBJCPROPERTYIMPL(DERIVED, BASE) diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index ee8e3f4c67..4b662077c3 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -1105,7 +1105,9 @@ namespace clang { /// \brief An OMPThreadPrivateDecl record. DECL_OMP_THREADPRIVATE, /// \brief An EmptyDecl record. - DECL_EMPTY + DECL_EMPTY, + /// \brief An ObjCTypeParamDecl record. + DECL_OBJC_TYPE_PARAM, }; /// \brief Record codes for each kind of statement or expression. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 5a91f074c4..61b7c5db8e 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -5951,6 +5951,7 @@ ObjCInterfaceDecl *ASTContext::getObjCProtocolDecl() const { = ObjCInterfaceDecl::Create(*this, getTranslationUnitDecl(), SourceLocation(), &Idents.get("Protocol"), + /*typeParamList=*/nullptr, /*PrevDecl=*/nullptr, SourceLocation(), true); } diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 90da416719..3dec23d006 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -240,6 +240,9 @@ namespace { void dumpTemplateArgument(const TemplateArgument &A, SourceRange R = SourceRange()); + // Objective-C utilities. + void dumpObjCTypeParamList(const ObjCTypeParamList *typeParams); + // Types void VisitComplexType(const ComplexType *T) { dumpTypeAsChild(T->getElementType()); @@ -463,6 +466,7 @@ namespace { // ObjC Decls void VisitObjCIvarDecl(const ObjCIvarDecl *D); void VisitObjCMethodDecl(const ObjCMethodDecl *D); + void VisitObjCTypeParamDecl(const ObjCTypeParamDecl *D); void VisitObjCCategoryDecl(const ObjCCategoryDecl *D); void VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *D); void VisitObjCProtocolDecl(const ObjCProtocolDecl *D); @@ -954,6 +958,18 @@ void ASTDumper::dumpTemplateArgument(const TemplateArgument &A, SourceRange R) { }); } +//===----------------------------------------------------------------------===// +// Objective-C Utilities +//===----------------------------------------------------------------------===// +void ASTDumper::dumpObjCTypeParamList(const ObjCTypeParamList *typeParams) { + if (!typeParams) + return; + + for (auto typeParam : *typeParams) { + dumpDecl(typeParam); + } +} + //===----------------------------------------------------------------------===// // Decl dumping methods. //===----------------------------------------------------------------------===// @@ -1457,9 +1473,17 @@ void ASTDumper::VisitObjCMethodDecl(const ObjCMethodDecl *D) { dumpStmt(D->getBody()); } +void ASTDumper::VisitObjCTypeParamDecl(const ObjCTypeParamDecl *D) { + dumpName(D); + if (D->hasExplicitBound()) + OS << " bounded"; + dumpType(D->getUnderlyingType()); +} + void ASTDumper::VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { dumpName(D); dumpDeclRef(D->getClassInterface()); + dumpObjCTypeParamList(D->getTypeParamList()); dumpDeclRef(D->getImplementation()); for (ObjCCategoryDecl::protocol_iterator I = D->protocol_begin(), E = D->protocol_end(); @@ -1482,6 +1506,7 @@ void ASTDumper::VisitObjCProtocolDecl(const ObjCProtocolDecl *D) { void ASTDumper::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) { dumpName(D); + dumpObjCTypeParamList(D->getTypeParamListAsWritten()); dumpDeclRef(D->getSuperClass(), "super"); dumpDeclRef(D->getImplementation()); diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 76e4e11915..88cca6d592 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -150,9 +150,12 @@ namespace clang { Decl *VisitImplicitParamDecl(ImplicitParamDecl *D); Decl *VisitParmVarDecl(ParmVarDecl *D); Decl *VisitObjCMethodDecl(ObjCMethodDecl *D); + Decl *VisitObjCTypeParamDecl(ObjCTypeParamDecl *D); Decl *VisitObjCCategoryDecl(ObjCCategoryDecl *D); Decl *VisitObjCProtocolDecl(ObjCProtocolDecl *D); Decl *VisitLinkageSpecDecl(LinkageSpecDecl *D); + + ObjCTypeParamList *ImportObjCTypeParamList(ObjCTypeParamList *list); Decl *VisitObjCInterfaceDecl(ObjCInterfaceDecl *D); Decl *VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D); Decl *VisitObjCImplementationDecl(ObjCImplementationDecl *D); @@ -3423,6 +3426,32 @@ Decl *ASTNodeImporter::VisitObjCMethodDecl(ObjCMethodDecl *D) { return ToMethod; } +Decl *ASTNodeImporter::VisitObjCTypeParamDecl(ObjCTypeParamDecl *D) { + // Import the major distinguishing characteristics of a category. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + SourceLocation Loc; + NamedDecl *ToD; + if (ImportDeclParts(D, DC, LexicalDC, Name, ToD, Loc)) + return nullptr; + if (ToD) + return ToD; + + TypeSourceInfo *BoundInfo = Importer.Import(D->getTypeSourceInfo()); + if (!BoundInfo) + return nullptr; + + ObjCTypeParamDecl *Result = ObjCTypeParamDecl::Create( + Importer.getToContext(), DC, + Importer.Import(D->getLocation()), + Name.getAsIdentifierInfo(), + Importer.Import(D->getColonLoc()), + BoundInfo); + Importer.Imported(D, Result); + Result->setLexicalDeclContext(LexicalDC); + return Result; +} + Decl *ASTNodeImporter::VisitObjCCategoryDecl(ObjCCategoryDecl *D) { // Import the major distinguishing characteristics of a category. DeclContext *DC, *LexicalDC; @@ -3450,6 +3479,8 @@ Decl *ASTNodeImporter::VisitObjCCategoryDecl(ObjCCategoryDecl *D) { Importer.Import(D->getCategoryNameLoc()), Name.getAsIdentifierInfo(), ToInterface, + ImportObjCTypeParamList( + D->getTypeParamList()), Importer.Import(D->getIvarLBraceLoc()), Importer.Import(D->getIvarRBraceLoc())); ToCategory->setLexicalDeclContext(LexicalDC); @@ -3716,6 +3747,27 @@ bool ASTNodeImporter::ImportDefinition(ObjCInterfaceDecl *From, return false; } +ObjCTypeParamList * +ASTNodeImporter::ImportObjCTypeParamList(ObjCTypeParamList *list) { + if (!list) + return nullptr; + + SmallVector toTypeParams; + for (auto fromTypeParam : *list) { + auto toTypeParam = cast_or_null( + Importer.Import(fromTypeParam)); + if (!toTypeParam) + return nullptr; + + toTypeParams.push_back(toTypeParam); + } + + return ObjCTypeParamList::create(Importer.getToContext(), + Importer.Import(list->getLAngleLoc()), + toTypeParams, + Importer.Import(list->getRAngleLoc())); +} + Decl *ASTNodeImporter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { // If this class has a definition in the translation unit we're coming from, // but this particular declaration is not that definition, import the @@ -3756,7 +3808,9 @@ Decl *ASTNodeImporter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { if (!ToIface) { ToIface = ObjCInterfaceDecl::Create(Importer.getToContext(), DC, Importer.Import(D->getAtStartLoc()), - Name.getAsIdentifierInfo(), + Name.getAsIdentifierInfo(), + ImportObjCTypeParamList( + D->getTypeParamListAsWritten()), /*PrevDecl=*/nullptr, Loc, D->isImplicitInterfaceDecl()); ToIface->setLexicalDeclContext(LexicalDC); diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index d20451d1ba..4fcec53d6e 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -561,6 +561,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case TypeAliasTemplate: case UnresolvedUsingTypename: case TemplateTypeParm: + case ObjCTypeParam: return IDNS_Ordinary | IDNS_Type; case UsingShadow: diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index a1600cb110..79054c7862 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -239,6 +239,26 @@ ObjCPropertyDecl *ObjCContainerDecl::FindPropertyDeclaration( void ObjCInterfaceDecl::anchor() { } +ObjCTypeParamList *ObjCInterfaceDecl::getTypeParamList() const { + // If this particular declaration has a type parameter list, return it. + if (ObjCTypeParamList *written = getTypeParamListAsWritten()) + return written; + + // If there is a definition, return its type parameter list. + if (const ObjCInterfaceDecl *def = getDefinition()) + return def->getTypeParamListAsWritten(); + + // Otherwise, look at previous declarations to determine whether any + // of them has a type parameter list, skipping over those + // declarations that do not. + for (auto decl = getPreviousDecl(); decl; decl = decl->getPreviousDecl()) { + if (ObjCTypeParamList *written = decl->getTypeParamListAsWritten()) + return written; + } + + return nullptr; +} + /// FindPropertyVisibleInPrimaryClass - Finds declaration of the property /// with name 'PropertyId' in the primary class; including those in protocols /// (direct or indirect) used by the primary class. @@ -1136,6 +1156,62 @@ ObjCMethodDecl::findPropertyDecl(bool CheckOverrides) const { return nullptr; } +//===----------------------------------------------------------------------===// +// ObjCTypeParamDecl +//===----------------------------------------------------------------------===// + +void ObjCTypeParamDecl::anchor() { } + +ObjCTypeParamDecl *ObjCTypeParamDecl::Create(ASTContext &ctx, DeclContext *dc, + SourceLocation nameLoc, + IdentifierInfo *name, + SourceLocation colonLoc, + TypeSourceInfo *boundInfo) { + return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, nameLoc, name, colonLoc, + boundInfo); +} + +ObjCTypeParamDecl *ObjCTypeParamDecl::CreateDeserialized(ASTContext &ctx, + unsigned ID) { + return new (ctx, ID) ObjCTypeParamDecl(ctx, nullptr, SourceLocation(), + nullptr, SourceLocation(), nullptr); +} + +SourceRange ObjCTypeParamDecl::getSourceRange() const { + if (hasExplicitBound()) { + return SourceRange(getLocation(), + getTypeSourceInfo()->getTypeLoc().getEndLoc()); + } + + return SourceRange(getLocation()); +} + +//===----------------------------------------------------------------------===// +// ObjCTypeParamList +//===----------------------------------------------------------------------===// +ObjCTypeParamList::ObjCTypeParamList(SourceLocation lAngleLoc, + ArrayRef typeParams, + SourceLocation rAngleLoc) + : Brackets(lAngleLoc, rAngleLoc), NumParams(typeParams.size()) +{ + std::copy(typeParams.begin(), typeParams.end(), begin()); +} + + +ObjCTypeParamList *ObjCTypeParamList::create( + ASTContext &ctx, + SourceLocation lAngleLoc, + ArrayRef typeParams, + SourceLocation rAngleLoc) { + unsigned size = sizeof(ObjCTypeParamList) + + sizeof(ObjCTypeParamDecl *) * typeParams.size(); + static_assert(alignof(ObjCTypeParamList) >= alignof(ObjCTypeParamDecl*), + "type parameter list needs greater alignment"); + unsigned align = llvm::alignOf(); + void *mem = ctx.Allocate(size, align); + return new (mem) ObjCTypeParamList(lAngleLoc, typeParams, rAngleLoc); +} + //===----------------------------------------------------------------------===// // ObjCInterfaceDecl //===----------------------------------------------------------------------===// @@ -1144,11 +1220,13 @@ ObjCInterfaceDecl *ObjCInterfaceDecl::Create(const ASTContext &C, DeclContext *DC, SourceLocation atLoc, IdentifierInfo *Id, + ObjCTypeParamList *typeParamList, ObjCInterfaceDecl *PrevDecl, SourceLocation ClassLoc, bool isInternal){ ObjCInterfaceDecl *Result = new (C, DC) - ObjCInterfaceDecl(C, DC, atLoc, Id, ClassLoc, PrevDecl, isInternal); + ObjCInterfaceDecl(C, DC, atLoc, Id, typeParamList, ClassLoc, PrevDecl, + isInternal); Result->Data.setInt(!C.getLangOpts().Modules); C.getObjCInterfaceType(Result, PrevDecl); return Result; @@ -1159,6 +1237,7 @@ ObjCInterfaceDecl *ObjCInterfaceDecl::CreateDeserialized(const ASTContext &C, ObjCInterfaceDecl *Result = new (C, ID) ObjCInterfaceDecl(C, nullptr, SourceLocation(), nullptr, + nullptr, SourceLocation(), nullptr, false); Result->Data.setInt(!C.getLangOpts().Modules); @@ -1167,11 +1246,13 @@ ObjCInterfaceDecl *ObjCInterfaceDecl::CreateDeserialized(const ASTContext &C, ObjCInterfaceDecl::ObjCInterfaceDecl(const ASTContext &C, DeclContext *DC, SourceLocation AtLoc, IdentifierInfo *Id, + ObjCTypeParamList *typeParamList, SourceLocation CLoc, ObjCInterfaceDecl *PrevDecl, bool IsInternal) : ObjCContainerDecl(ObjCInterface, DC, Id, CLoc, AtLoc), - redeclarable_base(C), TypeForDecl(nullptr), Data() { + redeclarable_base(C), TypeForDecl(nullptr), TypeParamList(typeParamList), + Data() { setPreviousDecl(PrevDecl); // Copy the 'data' pointer over. @@ -1179,6 +1260,12 @@ ObjCInterfaceDecl::ObjCInterfaceDecl(const ASTContext &C, DeclContext *DC, Data = PrevDecl->Data; setImplicit(IsInternal); + + // Update the declaration context of the type parameters. + if (typeParamList) { + for (auto typeParam : *typeParamList) + typeParam->setDeclContext(this); + } } void ObjCInterfaceDecl::LoadExternalDefinition() const { @@ -1648,17 +1735,39 @@ ObjCProtocolDecl::getObjCRuntimeNameAsString() const { void ObjCCategoryDecl::anchor() { } +ObjCCategoryDecl::ObjCCategoryDecl(DeclContext *DC, SourceLocation AtLoc, + SourceLocation ClassNameLoc, + SourceLocation CategoryNameLoc, + IdentifierInfo *Id, ObjCInterfaceDecl *IDecl, + ObjCTypeParamList *typeParamList, + SourceLocation IvarLBraceLoc, + SourceLocation IvarRBraceLoc) + : ObjCContainerDecl(ObjCCategory, DC, Id, ClassNameLoc, AtLoc), + ClassInterface(IDecl), TypeParamList(typeParamList), + NextClassCategory(nullptr), CategoryNameLoc(CategoryNameLoc), + IvarLBraceLoc(IvarLBraceLoc), IvarRBraceLoc(IvarRBraceLoc) +{ + // Set the declaration context of each of the type parameters. + if (typeParamList) { + for (auto typeParam : *typeParamList) { + typeParam->setDeclContext(this); + } + } +} + ObjCCategoryDecl *ObjCCategoryDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation AtLoc, SourceLocation ClassNameLoc, SourceLocation CategoryNameLoc, IdentifierInfo *Id, ObjCInterfaceDecl *IDecl, + ObjCTypeParamList *typeParamList, SourceLocation IvarLBraceLoc, SourceLocation IvarRBraceLoc) { ObjCCategoryDecl *CatDecl = new (C, DC) ObjCCategoryDecl(DC, AtLoc, ClassNameLoc, CategoryNameLoc, Id, - IDecl, IvarLBraceLoc, IvarRBraceLoc); + IDecl, typeParamList, IvarLBraceLoc, + IvarRBraceLoc); if (IDecl) { // Link this category into its class's category list. CatDecl->NextClassCategory = IDecl->getCategoryListRaw(); @@ -1676,7 +1785,7 @@ ObjCCategoryDecl *ObjCCategoryDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) ObjCCategoryDecl(nullptr, SourceLocation(), SourceLocation(), SourceLocation(), - nullptr, nullptr); + nullptr, nullptr, nullptr); } ObjCCategoryImplDecl *ObjCCategoryDecl::getImplementation() const { diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index d33093b8b5..3e5e7c0c35 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -44,6 +44,8 @@ namespace { void PrintObjCMethodType(ASTContext &Ctx, Decl::ObjCDeclQualifier Quals, QualType T); + void PrintObjCTypeParams(ObjCTypeParamList *Params); + public: DeclPrinter(raw_ostream &Out, const PrintingPolicy &Policy, unsigned Indentation = 0, bool PrintInstantiation = false) @@ -962,6 +964,25 @@ void DeclPrinter::PrintObjCMethodType(ASTContext &Ctx, Out << ')'; } +void DeclPrinter::PrintObjCTypeParams(ObjCTypeParamList *Params) { + Out << "<"; + unsigned First = true; + for (auto *Param : *Params) { + if (First) { + First = false; + } else { + Out << ", "; + } + + Out << Param->getDeclName().getAsString(); + + if (Param->hasExplicitBound()) { + Out << " : " << Param->getUnderlyingType().getAsString(Policy); + } + } + Out << ">"; +} + void DeclPrinter::VisitObjCMethodDecl(ObjCMethodDecl *OMD) { if (OMD->isInstanceMethod()) Out << "- "; @@ -1037,14 +1058,24 @@ void DeclPrinter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *OID) { ObjCInterfaceDecl *SID = OID->getSuperClass(); if (!OID->isThisDeclarationADefinition()) { - Out << "@class " << I << ";"; + Out << "@class " << I; + + if (auto TypeParams = OID->getTypeParamListAsWritten()) { + PrintObjCTypeParams(TypeParams); + } + + Out << ";"; return; } bool eolnOut = false; + Out << "@interface " << I; + + if (auto TypeParams = OID->getTypeParamListAsWritten()) { + PrintObjCTypeParams(TypeParams); + } + if (SID) - Out << "@interface " << I << " : " << *SID; - else - Out << "@interface " << I; + Out << " : " << OID->getSuperClass()->getName(); // Protocols? const ObjCList &Protocols = OID->getReferencedProtocols(); @@ -1107,7 +1138,11 @@ void DeclPrinter::VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *PID) { } void DeclPrinter::VisitObjCCategoryDecl(ObjCCategoryDecl *PID) { - Out << "@interface " << *PID->getClassInterface() << '(' << *PID << ")\n"; + Out << "@interface " << *PID->getClassInterface(); + if (auto TypeParams = PID->getTypeParamList()) { + PrintObjCTypeParams(TypeParams); + } + Out << "(" << *PID << ")\n"; if (PID->ivar_size() > 0) { Out << "{\n"; Indentation += Policy.Indentation; diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 839c2e474c..22e9a1a229 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -79,6 +79,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::Captured: case Decl::ClassScopeFunctionSpecialization: case Decl::UsingShadow: + case Decl::ObjCTypeParam: llvm_unreachable("Declaration should not be in declstmts!"); case Decl::Function: // void X(); case Decl::Record: // struct/union/class X; diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index f975f8715e..3ef187af8d 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -96,14 +96,17 @@ Parser::DeclGroupPtrTy Parser::ParseObjCAtDirectives() { /// /// objc-class-declaration: -/// '@' 'class' identifier-list ';' +/// '@' 'class' objc-class-forward-decl (',' objc-class-forward-decl)* ';' +/// +/// objc-class-forward-decl: +/// identifier objc-type-parameter-list[opt] /// Parser::DeclGroupPtrTy Parser::ParseObjCAtClassDeclaration(SourceLocation atLoc) { ConsumeToken(); // the identifier "class" SmallVector ClassNames; SmallVector ClassLocs; - + SmallVector ClassTypeParams; while (1) { MaybeSkipAttributes(tok::objc_class); @@ -116,6 +119,14 @@ Parser::ParseObjCAtClassDeclaration(SourceLocation atLoc) { ClassLocs.push_back(Tok.getLocation()); ConsumeToken(); + // Parse the optional objc-type-parameter-list. + ObjCTypeParamList *TypeParams = nullptr; + if (Tok.is(tok::less)) { + TypeParams = parseObjCTypeParamList(); + if (TypeParams) + Actions.popObjCTypeParamList(getCurScope(), TypeParams); + } + ClassTypeParams.push_back(TypeParams); if (!TryConsumeToken(tok::comma)) break; } @@ -126,6 +137,7 @@ Parser::ParseObjCAtClassDeclaration(SourceLocation atLoc) { return Actions.ActOnForwardClassDeclaration(atLoc, ClassNames.data(), ClassLocs.data(), + ClassTypeParams, ClassNames.size()); } @@ -154,15 +166,15 @@ void Parser::CheckNestedObjCContexts(SourceLocation AtLoc) /// objc-category-interface /// /// objc-class-interface: -/// '@' 'interface' identifier objc-superclass[opt] -/// objc-protocol-refs[opt] +/// '@' 'interface' identifier objc-type-parameter-list[opt] +/// objc-superclass[opt] objc-protocol-refs[opt] /// objc-class-instance-variables[opt] /// objc-interface-decl-list /// @end /// /// objc-category-interface: -/// '@' 'interface' identifier '(' identifier[opt] ')' -/// objc-protocol-refs[opt] +/// '@' 'interface' identifier objc-type-parameter-list[opt] +/// '(' identifier[opt] ')' objc-protocol-refs[opt] /// objc-interface-decl-list /// @end /// @@ -202,7 +214,20 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc, // We have a class or category name - consume it. IdentifierInfo *nameId = Tok.getIdentifierInfo(); SourceLocation nameLoc = ConsumeToken(); - if (Tok.is(tok::l_paren) && + + // Parse the objc-type-parameter-list or objc-protocol-refs. For the latter + // case, LAngleLoc will be valid and ProtocolIdents will capture the + // protocol references (that have not yet been resolved). + SourceLocation LAngleLoc, EndProtoLoc; + SmallVector ProtocolIdents; + ObjCTypeParamList *typeParameterList = nullptr; + if (Tok.is(tok::less)) { + typeParameterList = parseObjCTypeParamListOrProtocolRefs(LAngleLoc, + ProtocolIdents, + EndProtoLoc); + } + + if (Tok.is(tok::l_paren) && !isKnownToBeTypeSpecifier(GetLookAheadToken(1))) { // we have a category. BalancedDelimiterTracker T(*this, tok::l_paren); @@ -237,7 +262,7 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc, } // Next, we need to check for any protocol references. - SourceLocation LAngleLoc, EndProtoLoc; + assert(LAngleLoc.isInvalid() && "Cannot have already parsed protocols"); SmallVector ProtocolRefs; SmallVector ProtocolLocs; if (Tok.is(tok::less) && @@ -248,6 +273,7 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc, Decl *CategoryType = Actions.ActOnStartCategoryInterface(AtLoc, nameId, nameLoc, + typeParameterList, categoryId, categoryLoc, ProtocolRefs.data(), ProtocolRefs.size(), @@ -258,6 +284,10 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc, ParseObjCClassInstanceVariables(CategoryType, tok::objc_private, AtLoc); ParseObjCInterfaceDeclList(tok::objc_not_keyword, CategoryType); + + if (typeParameterList) + Actions.popObjCTypeParamList(getCurScope(), typeParameterList); + return CategoryType; } // Parse a class interface. @@ -281,21 +311,44 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc, } superClassId = Tok.getIdentifierInfo(); superClassLoc = ConsumeToken(); + } else if (typeParameterList) { + // An objc-type-parameter-list is ambiguous with an objc-protocol-refs + // in an @interface without a specified superclass, so such classes + // are ill-formed. We have determined that we have an + // objc-type-parameter-list but no superclass, so complain and record + // as if we inherited from NSObject. + SourceLocation insertLoc = PP.getLocForEndOfToken(PrevTokLocation); + Diag(insertLoc, diag::err_objc_parameterized_class_without_base) + << nameId + << FixItHint::CreateInsertion(insertLoc, " : NSObject"); + superClassId = PP.getIdentifierInfo("NSObject"); + superClassLoc = Tok.getLocation(); } + // Next, we need to check for any protocol references. SmallVector ProtocolRefs; SmallVector ProtocolLocs; - SourceLocation LAngleLoc, EndProtoLoc; - if (Tok.is(tok::less) && - ParseObjCProtocolReferences(ProtocolRefs, ProtocolLocs, true, true, - LAngleLoc, EndProtoLoc)) + if (LAngleLoc.isValid()) { + // We already parsed the protocols named when we thought we had a + // type parameter list. Translate them into actual protocol references. + for (const auto &pair : ProtocolIdents) { + ProtocolLocs.push_back(pair.second); + } + Actions.FindProtocolDeclaration(/*WarnOnDeclarations=*/true, + /*ForObjCContainer=*/true, + &ProtocolIdents[0], ProtocolIdents.size(), + ProtocolRefs); + } else if (Tok.is(tok::less) && + ParseObjCProtocolReferences(ProtocolRefs, ProtocolLocs, true, true, + LAngleLoc, EndProtoLoc)) { return nullptr; + } if (Tok.isNot(tok::less)) Actions.ActOnTypedefedProtocols(ProtocolRefs, superClassId, superClassLoc); Decl *ClsType = - Actions.ActOnStartClassInterface(AtLoc, nameId, nameLoc, + Actions.ActOnStartClassInterface(AtLoc, nameId, nameLoc, typeParameterList, superClassId, superClassLoc, ProtocolRefs.data(), ProtocolRefs.size(), ProtocolLocs.data(), @@ -305,6 +358,10 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc, ParseObjCClassInstanceVariables(ClsType, tok::objc_protected, AtLoc); ParseObjCInterfaceDeclList(tok::objc_interface, ClsType); + + if (typeParameterList) + Actions.popObjCTypeParamList(getCurScope(), typeParameterList); + return ClsType; } @@ -339,6 +396,172 @@ static void addContextSensitiveTypeNullability(Parser &P, } } +/// Parse an Objective-C type parameter list, if present, or capture +/// the locations of the protocol identifiers for a list of protocol +/// references. +/// +/// objc-type-parameter-list: +/// '<' objc-type-parameter (',' objc-type-parameter)* '>' +/// +/// objc-type-parameter: +/// identifier objc-type-parameter-bound[opt] +/// +/// objc-type-parameter-bound: +/// ':' type-name +/// +/// \param lAngleLoc The location of the starting '<'. +/// +/// \param protocolIdents Will capture the list of identifiers, if the +/// angle brackets contain a list of protocol references rather than a +/// type parameter list. +/// +/// \param rAngleLoc The location of the ending '>'. +ObjCTypeParamList *Parser::parseObjCTypeParamListOrProtocolRefs( + SourceLocation &lAngleLoc, + SmallVectorImpl &protocolIdents, + SourceLocation &rAngleLoc, + bool mayBeProtocolList) { + assert(Tok.is(tok::less) && "Not at the beginning of a type parameter list"); + + // Within the type parameter list, don't treat '>' as an operator. + GreaterThanIsOperatorScope G(GreaterThanIsOperator, false); + + // Local function to "flush" the protocol identifiers, turning them into + // type parameters. + SmallVector typeParams; + auto makeProtocolIdentsIntoTypeParameters = [&]() { + for (const auto &pair : protocolIdents) { + DeclResult typeParam = Actions.actOnObjCTypeParam(getCurScope(), + pair.first, + pair.second, + SourceLocation(), + ParsedType()); + if (typeParam.isUsable()) + typeParams.push_back(typeParam.get()); + } + + protocolIdents.clear(); + mayBeProtocolList = false; + }; + + bool invalid = false; + lAngleLoc = ConsumeToken(); + do { + // Parse the identifier. + if (!Tok.is(tok::identifier)) { + // Code completion. + if (Tok.is(tok::code_completion)) { + // FIXME: If these aren't protocol references, we'll need different + // completions. + Actions.CodeCompleteObjCProtocolReferences(protocolIdents.data(), + protocolIdents.size()); + cutOffParsing(); + + // FIXME: Better recovery here?. + return nullptr; + } + + Diag(Tok, diag::err_objc_expected_type_parameter); + invalid = true; + break; + } + + IdentifierInfo *paramName = Tok.getIdentifierInfo(); + SourceLocation paramLoc = ConsumeToken(); + + // If there is a bound, parse it. + SourceLocation colonLoc; + TypeResult boundType; + if (TryConsumeToken(tok::colon, colonLoc)) { + // Once we've seen a bound, we know this is not a list of protocol + // references. + if (mayBeProtocolList) { + // Up until now, we have been queuing up parameters because they + // might be protocol references. Turn them into parameters now. + makeProtocolIdentsIntoTypeParameters(); + } + + // type-name + boundType = ParseTypeName(); + if (boundType.isInvalid()) + invalid = true; + } else if (mayBeProtocolList) { + // If this could still be a protocol list, just capture the identifier. + // We don't want to turn it into a parameter. + protocolIdents.push_back(std::make_pair(paramName, paramLoc)); + continue; + } + + // Create the type parameter. + DeclResult typeParam = Actions.actOnObjCTypeParam(getCurScope(), + paramName, + paramLoc, + colonLoc, + boundType.isUsable() + ? boundType.get() + : ParsedType()); + if (typeParam.isUsable()) + typeParams.push_back(typeParam.get()); + } while (TryConsumeToken(tok::comma)); + + // Parse the '>'. + if (invalid) { + SkipUntil(tok::greater, tok::at, StopBeforeMatch); + if (Tok.is(tok::greater)) + ConsumeToken(); + } else if (ParseGreaterThanInTemplateList(rAngleLoc, + /*ConsumeLastToken=*/true, + /*ObjCGenericList=*/true)) { + Diag(lAngleLoc, diag::note_matching) << "'<'"; + SkipUntil({tok::greater, tok::greaterequal, tok::at, tok::minus, + tok::minus, tok::plus, tok::colon, tok::l_paren, tok::l_brace, + tok::comma, tok::semi }, + StopBeforeMatch); + if (Tok.is(tok::greater)) + ConsumeToken(); + } + + if (mayBeProtocolList) { + // A type parameter list must be followed by either a ':' (indicating the + // presence of a superclass) or a '(' (indicating that this is a category + // or extension). This disambiguates between an objc-type-parameter-list + // and a objc-protocol-refs. + if (Tok.isNot(tok::colon) && Tok.isNot(tok::l_paren)) { + // Returning null indicates that we don't have a type parameter list. + // The results the caller needs to handle the protocol references are + // captured in the reference parameters already. + return nullptr; + } + + // We have a type parameter list that looks like a list of protocol + // references. Turn that parameter list into type parameters. + makeProtocolIdentsIntoTypeParameters(); + } + + // Form the type parameter list. + ObjCTypeParamList *list = Actions.actOnObjCTypeParamList( + getCurScope(), + lAngleLoc, + typeParams, + rAngleLoc); + + // Clear out the angle locations; they're used by the caller to indicate + // whether there are any protocol references. + lAngleLoc = SourceLocation(); + rAngleLoc = SourceLocation(); + return list; +} + +/// Parse an objc-type-parameter-list. +ObjCTypeParamList *Parser::parseObjCTypeParamList() { + SourceLocation lAngleLoc; + SmallVector protocolIdents; + SourceLocation rAngleLoc; + return parseObjCTypeParamListOrProtocolRefs(lAngleLoc, protocolIdents, + rAngleLoc, + /*mayBeProtocolList=*/false); +} + /// objc-interface-decl-list: /// empty /// objc-interface-decl-list objc-property-decl [OBJC2] @@ -1311,7 +1534,8 @@ ParseObjCProtocolReferences(SmallVectorImpl &Protocols, } // Consume the '>'. - if (ParseGreaterThanInTemplateList(EndLoc, /*ConsumeLastToken=*/true)) + if (ParseGreaterThanInTemplateList(EndLoc, /*ConsumeLastToken=*/true, + /*ObjCGenericList=*/false)) return true; // Convert the list of protocols identifiers into a list of protocol decls. @@ -1598,6 +1822,22 @@ Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) { SourceLocation nameLoc = ConsumeToken(); // consume class or category name Decl *ObjCImpDecl = nullptr; + // Neither a type parameter list nor a list of protocol references is + // permitted here. Parse and diagnose them. + if (Tok.is(tok::less)) { + SourceLocation lAngleLoc, rAngleLoc; + SmallVector protocolIdents; + SourceLocation diagLoc = Tok.getLocation(); + if (parseObjCTypeParamListOrProtocolRefs(lAngleLoc, protocolIdents, + rAngleLoc)) { + Diag(diagLoc, diag::err_objc_parameterized_implementation) + << SourceRange(diagLoc, PrevTokLocation); + } else if (lAngleLoc.isValid()) { + Diag(lAngleLoc, diag::err_unexpected_protocol_qualifier) + << FixItHint::CreateRemoval(SourceRange(lAngleLoc, rAngleLoc)); + } + } + if (Tok.is(tok::l_paren)) { // we have a category implementation. ConsumeParen(); diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 2a9becbc85..3a964dd205 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -738,11 +738,16 @@ void Parser::DiagnoseMisplacedEllipsisInDeclarator(SourceLocation EllipsisLoc, /// /// \param RAngleLoc the location of the consumed '>'. /// -/// \param ConsumeLastToken if true, the '>' is not consumed. +/// \param ConsumeLastToken if true, the '>' is consumed. +/// +/// \param ObjCGenericList if true, this is the '>' closing an Objective-C +/// type parameter or type argument list, rather than a C++ template parameter +/// or argument list. /// /// \returns true, if current token does not start with '>', false otherwise. bool Parser::ParseGreaterThanInTemplateList(SourceLocation &RAngleLoc, - bool ConsumeLastToken) { + bool ConsumeLastToken, + bool ObjCGenericList) { // What will be left once we've consumed the '>'. tok::TokenKind RemainingToken; const char *ReplacementStr = "> >"; @@ -783,40 +788,44 @@ bool Parser::ParseGreaterThanInTemplateList(SourceLocation &RAngleLoc, // the token isn't '>>' or '>>>'. // '>>>' is for CUDA, where this sequence of characters is parsed into // tok::greatergreatergreater, rather than two separate tokens. - + // + // We always allow this for Objective-C type parameter and type argument + // lists. RAngleLoc = Tok.getLocation(); - - // The source range of the '>>' or '>=' at the start of the token. - CharSourceRange ReplacementRange = - CharSourceRange::getCharRange(RAngleLoc, - Lexer::AdvanceToTokenCharacter(RAngleLoc, 2, PP.getSourceManager(), - getLangOpts())); - - // A hint to put a space between the '>>'s. In order to make the hint as - // clear as possible, we include the characters either side of the space in - // the replacement, rather than just inserting a space at SecondCharLoc. - FixItHint Hint1 = FixItHint::CreateReplacement(ReplacementRange, - ReplacementStr); - - // A hint to put another space after the token, if it would otherwise be - // lexed differently. - FixItHint Hint2; Token Next = NextToken(); - if ((RemainingToken == tok::greater || - RemainingToken == tok::greatergreater) && - Next.isOneOf(tok::greater, tok::greatergreater, - tok::greatergreatergreater, tok::equal, tok::greaterequal, - tok::greatergreaterequal, tok::equalequal) && - areTokensAdjacent(Tok, Next)) - Hint2 = FixItHint::CreateInsertion(Next.getLocation(), " "); - - unsigned DiagId = diag::err_two_right_angle_brackets_need_space; - if (getLangOpts().CPlusPlus11 && - Tok.isOneOf(tok::greatergreater, tok::greatergreatergreater)) - DiagId = diag::warn_cxx98_compat_two_right_angle_brackets; - else if (Tok.is(tok::greaterequal)) - DiagId = diag::err_right_angle_bracket_equal_needs_space; - Diag(Tok.getLocation(), DiagId) << Hint1 << Hint2; + if (!ObjCGenericList) { + // The source range of the '>>' or '>=' at the start of the token. + CharSourceRange ReplacementRange = + CharSourceRange::getCharRange(RAngleLoc, + Lexer::AdvanceToTokenCharacter(RAngleLoc, 2, PP.getSourceManager(), + getLangOpts())); + + // A hint to put a space between the '>>'s. In order to make the hint as + // clear as possible, we include the characters either side of the space in + // the replacement, rather than just inserting a space at SecondCharLoc. + FixItHint Hint1 = FixItHint::CreateReplacement(ReplacementRange, + ReplacementStr); + + // A hint to put another space after the token, if it would otherwise be + // lexed differently. + FixItHint Hint2; + if ((RemainingToken == tok::greater || + RemainingToken == tok::greatergreater) && + (Next.isOneOf(tok::greater, tok::greatergreater, + tok::greatergreatergreater, tok::equal, + tok::greaterequal, tok::greatergreaterequal, + tok::equalequal)) && + areTokensAdjacent(Tok, Next)) + Hint2 = FixItHint::CreateInsertion(Next.getLocation(), " "); + + unsigned DiagId = diag::err_two_right_angle_brackets_need_space; + if (getLangOpts().CPlusPlus11 && + (Tok.is(tok::greatergreater) || Tok.is(tok::greatergreatergreater))) + DiagId = diag::warn_cxx98_compat_two_right_angle_brackets; + else if (Tok.is(tok::greaterequal)) + DiagId = diag::err_right_angle_bracket_equal_needs_space; + Diag(Tok.getLocation(), DiagId) << Hint1 << Hint2; + } // Strip the initial '>' from the token. if (RemainingToken == tok::equal && Next.is(tok::equal) && @@ -895,7 +904,8 @@ Parser::ParseTemplateIdAfterTemplateName(TemplateTy Template, } } - return ParseGreaterThanInTemplateList(RAngleLoc, ConsumeLastToken); + return ParseGreaterThanInTemplateList(RAngleLoc, ConsumeLastToken, + /*ObjCGenericList=*/false); } /// \brief Replace the tokens that form a simple-template-id with an diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index ff6a3eed8c..e4e99ab993 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -3029,7 +3029,9 @@ CXCursorKind clang::getCursorKindForDecl(const Decl *D) { case Decl::Import: return CXCursor_ModuleImportDecl; - + + case Decl::ObjCTypeParam: return CXCursor_TemplateTypeParameter; + default: if (const TagDecl *TD = dyn_cast(D)) { switch (TD->getTagKind()) { diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index d0b299877e..574a7c8857 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -20,12 +20,15 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/Basic/SourceManager.h" +#include "clang/Lex/Preprocessor.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/ExternalSemaSource.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "TypeLocBuilder.h" using namespace clang; @@ -461,9 +464,214 @@ static void diagnoseUseOfProtocols(Sema &TheSema, } } +DeclResult Sema::actOnObjCTypeParam(Scope *S, IdentifierInfo *paramName, + SourceLocation paramLoc, + SourceLocation colonLoc, + ParsedType parsedTypeBound) { + // If there was an explicitly-provided type bound, check it. + TypeSourceInfo *typeBoundInfo = nullptr; + if (parsedTypeBound) { + // The type bound can be any Objective-C pointer type. + QualType typeBound = GetTypeFromParser(parsedTypeBound, &typeBoundInfo); + if (typeBound->isObjCObjectPointerType()) { + // okay + } else if (typeBound->isObjCObjectType()) { + // The user forgot the * on an Objective-C pointer type, e.g., + // "T : NSView". + SourceLocation starLoc = PP.getLocForEndOfToken( + typeBoundInfo->getTypeLoc().getEndLoc()); + Diag(typeBoundInfo->getTypeLoc().getBeginLoc(), + diag::err_objc_type_param_bound_missing_pointer) + << typeBound << paramName + << FixItHint::CreateInsertion(starLoc, " *"); + + // Create a new type location builder so we can update the type + // location information we have. + TypeLocBuilder builder; + builder.pushFullCopy(typeBoundInfo->getTypeLoc()); + + // Create the Objective-C pointer type. + typeBound = Context.getObjCObjectPointerType(typeBound); + ObjCObjectPointerTypeLoc newT + = builder.push(typeBound); + newT.setStarLoc(starLoc); + + // Form the new type source information. + typeBoundInfo = builder.getTypeSourceInfo(Context, typeBound); + } else { + // Not a + Diag(typeBoundInfo->getTypeLoc().getBeginLoc(), + diag::err_objc_type_param_bound_nonobject) + << typeBound << paramName; + + // Forget the bound; we'll default to id later. + typeBoundInfo = nullptr; + } + } + + // If there was no explicit type bound (or we removed it due to an error), + // use 'id' instead. + if (!typeBoundInfo) { + colonLoc = SourceLocation(); + typeBoundInfo = Context.getTrivialTypeSourceInfo(Context.getObjCIdType()); + } + + // Create the type parameter. + return ObjCTypeParamDecl::Create(Context, CurContext, paramLoc, paramName, + colonLoc, typeBoundInfo); +} + +ObjCTypeParamList *Sema::actOnObjCTypeParamList(Scope *S, + SourceLocation lAngleLoc, + ArrayRef typeParamsIn, + SourceLocation rAngleLoc) { + // We know that the array only contains Objective-C type parameters. + ArrayRef + typeParams( + reinterpret_cast(typeParamsIn.data()), + typeParamsIn.size()); + + // Diagnose redeclarations of type parameters. + // We do this now because Objective-C type parameters aren't pushed into + // scope until later (after the instance variable block), but we want the + // diagnostics to occur right after we parse the type parameter list. + llvm::SmallDenseMap knownParams; + for (auto typeParam : typeParams) { + auto known = knownParams.find(typeParam->getIdentifier()); + if (known != knownParams.end()) { + Diag(typeParam->getLocation(), diag::err_objc_type_param_redecl) + << typeParam->getIdentifier() + << SourceRange(known->second->getLocation()); + + typeParam->setInvalidDecl(); + } else { + knownParams.insert(std::make_pair(typeParam->getIdentifier(), typeParam)); + + // Push the type parameter into scope. + PushOnScopeChains(typeParam, S, /*AddToContext=*/false); + } + } + + // Create the parameter list. + return ObjCTypeParamList::create(Context, lAngleLoc, typeParams, rAngleLoc); +} + +void Sema::popObjCTypeParamList(Scope *S, ObjCTypeParamList *typeParamList) { + for (auto typeParam : *typeParamList) { + if (!typeParam->isInvalidDecl()) { + S->RemoveDecl(typeParam); + IdResolver.RemoveDecl(typeParam); + } + } +} + +namespace { + /// The context in which an Objective-C type parameter list occurs, for use + /// in diagnostics. + enum class TypeParamListContext { + ForwardDeclaration, + Definition, + Category, + Extension + }; +} + +/// Check consistency between two Objective-C type parameter lists, e.g., +/// between a category/extension and an @interface or between an @class and an +/// @interface. +static bool checkTypeParamListConsistency(Sema &S, + ObjCTypeParamList *prevTypeParams, + ObjCTypeParamList *newTypeParams, + TypeParamListContext newContext) { + // If the sizes don't match, complain about that. + if (prevTypeParams->size() != newTypeParams->size()) { + SourceLocation diagLoc; + if (newTypeParams->size() > prevTypeParams->size()) { + diagLoc = newTypeParams->begin()[prevTypeParams->size()]->getLocation(); + } else { + diagLoc = S.PP.getLocForEndOfToken(newTypeParams->back()->getLocEnd()); + } + + S.Diag(diagLoc, diag::err_objc_type_param_arity_mismatch) + << static_cast(newContext) + << (newTypeParams->size() > prevTypeParams->size()) + << prevTypeParams->size() + << newTypeParams->size(); + + return true; + } + + // Match up the type parameters. + for (unsigned i = 0, n = prevTypeParams->size(); i != n; ++i) { + ObjCTypeParamDecl *prevTypeParam = prevTypeParams->begin()[i]; + ObjCTypeParamDecl *newTypeParam = newTypeParams->begin()[i]; + + // If the bound types match, there's nothing to do. + if (S.Context.hasSameType(prevTypeParam->getUnderlyingType(), + newTypeParam->getUnderlyingType())) + continue; + + // If the new type parameter's bound was explicit, complain about it being + // different from the original. + if (newTypeParam->hasExplicitBound()) { + SourceRange newBoundRange = newTypeParam->getTypeSourceInfo() + ->getTypeLoc().getSourceRange(); + S.Diag(newBoundRange.getBegin(), diag::err_objc_type_param_bound_conflict) + << newTypeParam->getUnderlyingType() + << newTypeParam->getDeclName() + << prevTypeParam->hasExplicitBound() + << prevTypeParam->getUnderlyingType() + << (newTypeParam->getDeclName() == prevTypeParam->getDeclName()) + << prevTypeParam->getDeclName() + << FixItHint::CreateReplacement( + newBoundRange, + prevTypeParam->getUnderlyingType().getAsString( + S.Context.getPrintingPolicy())); + + S.Diag(prevTypeParam->getLocation(), diag::note_objc_type_param_here) + << prevTypeParam->getDeclName(); + + // Override the new type parameter's bound type with the previous type, + // so that it's consistent. + newTypeParam->setTypeSourceInfo( + S.Context.getTrivialTypeSourceInfo(prevTypeParam->getUnderlyingType())); + continue; + } + + // The new type parameter got the implicit bound of 'id'. That's okay for + // categories and extensions (overwrite it later), but not for forward + // declarations and @interfaces, because those must be standalone. + if (newContext == TypeParamListContext::ForwardDeclaration || + newContext == TypeParamListContext::Definition) { + // Diagnose this problem for forward declarations and definitions. + SourceLocation insertionLoc + = S.PP.getLocForEndOfToken(newTypeParam->getLocation()); + std::string newCode + = " : " + prevTypeParam->getUnderlyingType().getAsString( + S.Context.getPrintingPolicy()); + S.Diag(newTypeParam->getLocation(), + diag::err_objc_type_param_bound_missing) + << prevTypeParam->getUnderlyingType() + << newTypeParam->getDeclName() + << (newContext == TypeParamListContext::ForwardDeclaration) + << FixItHint::CreateInsertion(insertionLoc, newCode); + + S.Diag(prevTypeParam->getLocation(), diag::note_objc_type_param_here) + << prevTypeParam->getDeclName(); + } + + // Update the new type parameter's bound to match the previous one. + newTypeParam->setTypeSourceInfo( + S.Context.getTrivialTypeSourceInfo(prevTypeParam->getUnderlyingType())); + } + + return false; +} + Decl *Sema:: ActOnStartClassInterface(SourceLocation AtInterfaceLoc, IdentifierInfo *ClassName, SourceLocation ClassLoc, + ObjCTypeParamList *typeParamList, IdentifierInfo *SuperName, SourceLocation SuperLoc, Decl * const *ProtoRefs, unsigned NumProtoRefs, const SourceLocation *ProtoLocs, @@ -498,10 +706,47 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc, ClassName = PrevIDecl->getIdentifier(); } + // If there was a forward declaration with type parameters, check + // for consistency. + if (PrevIDecl) { + if (ObjCTypeParamList *prevTypeParamList = PrevIDecl->getTypeParamList()) { + if (typeParamList) { + // Both have type parameter lists; check for consistency. + if (checkTypeParamListConsistency(*this, prevTypeParamList, + typeParamList, + TypeParamListContext::Definition)) { + typeParamList = nullptr; + } + } else { + Diag(ClassLoc, diag::err_objc_parameterized_forward_class_first) + << ClassName; + Diag(prevTypeParamList->getLAngleLoc(), diag::note_previous_decl) + << ClassName; + + // Clone the type parameter list. + SmallVector clonedTypeParams; + for (auto typeParam : *prevTypeParamList) { + clonedTypeParams.push_back( + ObjCTypeParamDecl::Create( + Context, + CurContext, + SourceLocation(), + typeParam->getIdentifier(), + SourceLocation(), + Context.getTrivialTypeSourceInfo(typeParam->getUnderlyingType()))); + } + + typeParamList = ObjCTypeParamList::create(Context, + SourceLocation(), + clonedTypeParams, + SourceLocation()); + } + } + } + ObjCInterfaceDecl *IDecl = ObjCInterfaceDecl::Create(Context, CurContext, AtInterfaceLoc, ClassName, - PrevIDecl, ClassLoc); - + typeParamList, PrevIDecl, ClassLoc); if (PrevIDecl) { // Class already seen. Was it a definition? if (ObjCInterfaceDecl *Def = PrevIDecl->getDefinition()) { @@ -906,6 +1151,7 @@ Sema::ActOnForwardProtocolDeclaration(SourceLocation AtProtocolLoc, Decl *Sema:: ActOnStartCategoryInterface(SourceLocation AtInterfaceLoc, IdentifierInfo *ClassName, SourceLocation ClassLoc, + ObjCTypeParamList *typeParamList, IdentifierInfo *CategoryName, SourceLocation CategoryLoc, Decl * const *ProtoRefs, @@ -925,7 +1171,8 @@ ActOnStartCategoryInterface(SourceLocation AtInterfaceLoc, // the enclosing method declarations. We mark the decl invalid // to make it clear that this isn't a valid AST. CDecl = ObjCCategoryDecl::Create(Context, CurContext, AtInterfaceLoc, - ClassLoc, CategoryLoc, CategoryName,IDecl); + ClassLoc, CategoryLoc, CategoryName, + IDecl, typeParamList); CDecl->setInvalidDecl(); CurContext->addDecl(CDecl); @@ -951,8 +1198,28 @@ ActOnStartCategoryInterface(SourceLocation AtInterfaceLoc, } } + // If we have a type parameter list, check it. + if (typeParamList) { + if (auto prevTypeParamList = IDecl->getTypeParamList()) { + if (checkTypeParamListConsistency(*this, prevTypeParamList, typeParamList, + CategoryName + ? TypeParamListContext::Category + : TypeParamListContext::Extension)) + typeParamList = nullptr; + } else { + Diag(typeParamList->getLAngleLoc(), + diag::err_objc_parameterized_category_nonclass) + << (CategoryName != nullptr) + << ClassName + << typeParamList->getSourceRange(); + + typeParamList = nullptr; + } + } + CDecl = ObjCCategoryDecl::Create(Context, CurContext, AtInterfaceLoc, - ClassLoc, CategoryLoc, CategoryName, IDecl); + ClassLoc, CategoryLoc, CategoryName, IDecl, + typeParamList); // FIXME: PushOnScopeChains? CurContext->addDecl(CDecl); @@ -987,7 +1254,8 @@ Decl *Sema::ActOnStartCategoryImplementation( // Create and install one. CatIDecl = ObjCCategoryDecl::Create(Context, CurContext, AtCatImplLoc, ClassLoc, CatLoc, - CatName, IDecl); + CatName, IDecl, + /*typeParamList=*/nullptr); CatIDecl->setImplicit(); } } @@ -1101,7 +1369,8 @@ Decl *Sema::ActOnStartClassImplementation( // FIXME: Do we support attributes on the @implementation? If so we should // copy them over. IDecl = ObjCInterfaceDecl::Create(Context, CurContext, AtClassImplLoc, - ClassName, /*PrevDecl=*/nullptr, ClassLoc, + ClassName, /*typeParamList=*/nullptr, + /*PrevDecl=*/nullptr, ClassLoc, true); IDecl->startDefinition(); if (SDecl) { @@ -2083,6 +2352,7 @@ Sema::DeclGroupPtrTy Sema::ActOnForwardClassDeclaration(SourceLocation AtClassLoc, IdentifierInfo **IdentList, SourceLocation *IdentLocs, + ArrayRef TypeParamLists, unsigned NumElts) { SmallVector DeclsInGroup; for (unsigned i = 0; i != NumElts; ++i) { @@ -2137,9 +2407,33 @@ Sema::ActOnForwardClassDeclaration(SourceLocation AtClassLoc, ClassName = PrevIDecl->getIdentifier(); } + // If this forward declaration has type parameters, compare them with the + // type parameters of the previous declaration. + ObjCTypeParamList *TypeParams = TypeParamLists[i]; + if (PrevIDecl && TypeParams) { + if (ObjCTypeParamList *PrevTypeParams = PrevIDecl->getTypeParamList()) { + // Check for consistency with the previous declaration. + if (checkTypeParamListConsistency( + *this, PrevTypeParams, TypeParams, + TypeParamListContext::ForwardDeclaration)) { + TypeParams = nullptr; + } + } else if (ObjCInterfaceDecl *Def = PrevIDecl->getDefinition()) { + // The @interface does not have type parameters. Complain. + Diag(IdentLocs[i], diag::err_objc_parameterized_forward_class) + << ClassName + << TypeParams->getSourceRange(); + Diag(Def->getLocation(), diag::note_defined_here) + << ClassName; + + TypeParams = nullptr; + } + } + ObjCInterfaceDecl *IDecl = ObjCInterfaceDecl::Create(Context, CurContext, AtClassLoc, - ClassName, PrevIDecl, IdentLocs[i]); + ClassName, TypeParams, PrevIDecl, + IdentLocs[i]); IDecl->setAtEndRange(IdentLocs[i]); PushOnScopeChains(IDecl, TUScope); diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index c52b6f55a9..0e1751a503 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -133,7 +133,7 @@ ExprResult Sema::BuildObjCStringLiteral(SourceLocation AtLoc, StringLiteral *S){ ObjCInterfaceDecl::Create (Context, Context.getTranslationUnitDecl(), SourceLocation(), NSIdent, - nullptr, SourceLocation()); + nullptr, nullptr, SourceLocation()); Ty = Context.getObjCInterfaceType(NSStringIDecl); Context.setObjCNSStringType(Ty); } @@ -208,7 +208,8 @@ static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc, S.NSNumberDecl = ObjCInterfaceDecl::Create(CX, CX.getTranslationUnitDecl(), SourceLocation(), NSNumberId, - nullptr, SourceLocation()); + nullptr, nullptr, + SourceLocation()); } else { // Otherwise, require a declaration of NSNumber. S.Diag(Loc, diag::err_undeclared_nsnumber); @@ -475,7 +476,8 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { NSStringDecl = ObjCInterfaceDecl::Create(Context, TU, SourceLocation(), NSStringId, - nullptr, SourceLocation()); + nullptr, nullptr, + SourceLocation()); } else { Diag(SR.getBegin(), diag::err_undeclared_nsstring); return ExprError(); @@ -591,7 +593,8 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { DeclContext *TU = Context.getTranslationUnitDecl(); NSValueDecl = ObjCInterfaceDecl::Create(Context, TU, SourceLocation(), NSValueId, - nullptr, SourceLocation()); + nullptr, nullptr, + SourceLocation()); } else { // Otherwise, require a declaration of NSValue. Diag(SR.getBegin(), diag::err_undeclared_nsvalue); @@ -755,7 +758,7 @@ ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) { Context.getTranslationUnitDecl(), SourceLocation(), NSAPIObj->getNSClassId(NSAPI::ClassId_NSArray), - nullptr, SourceLocation()); + nullptr, nullptr, SourceLocation()); if (!NSArrayDecl) { Diag(SR.getBegin(), diag::err_undeclared_nsarray); @@ -870,7 +873,7 @@ ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR, Context.getTranslationUnitDecl(), SourceLocation(), NSAPIObj->getNSClassId(NSAPI::ClassId_NSDictionary), - nullptr, SourceLocation()); + nullptr, nullptr, SourceLocation()); if (!NSDictionaryDecl) { Diag(SR.getBegin(), diag::err_undeclared_nsdictionary); diff --git a/lib/Serialization/ASTCommon.cpp b/lib/Serialization/ASTCommon.cpp index 85c574c202..b1bf4a6bff 100644 --- a/lib/Serialization/ASTCommon.cpp +++ b/lib/Serialization/ASTCommon.cpp @@ -221,6 +221,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { // redeclarable. case Decl::ImplicitParam: case Decl::ParmVar: + case Decl::ObjCTypeParam: return false; } diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index b23c33c551..e01ea2b8a5 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -350,8 +350,11 @@ namespace clang { RedeclarableTemplateDecl *Existing, DeclID DsID); + ObjCTypeParamList *ReadObjCTypeParamList(); + // FIXME: Reorder according to DeclNodes.td? void VisitObjCMethodDecl(ObjCMethodDecl *D); + void VisitObjCTypeParamDecl(ObjCTypeParamDecl *D); void VisitObjCContainerDecl(ObjCContainerDecl *D); void VisitObjCInterfaceDecl(ObjCInterfaceDecl *D); void VisitObjCIvarDecl(ObjCIvarDecl *D); @@ -899,18 +902,46 @@ void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) { MD->setParamsAndSelLocs(Reader.getContext(), Params, SelLocs); } +void ASTDeclReader::VisitObjCTypeParamDecl(ObjCTypeParamDecl *D) { + VisitTypedefNameDecl(D); + D->ColonLoc = ReadSourceLocation(Record, Idx); +} + void ASTDeclReader::VisitObjCContainerDecl(ObjCContainerDecl *CD) { VisitNamedDecl(CD); CD->setAtStartLoc(ReadSourceLocation(Record, Idx)); CD->setAtEndRange(ReadSourceRange(Record, Idx)); } +ObjCTypeParamList *ASTDeclReader::ReadObjCTypeParamList() { + unsigned numParams = Record[Idx++]; + if (numParams == 0) + return nullptr; + + SmallVector typeParams; + typeParams.reserve(numParams); + for (unsigned i = 0; i != numParams; ++i) { + auto typeParam = ReadDeclAs(Record, Idx); + if (!typeParam) + return nullptr; + + typeParams.push_back(typeParam); + } + + SourceLocation lAngleLoc = ReadSourceLocation(Record, Idx); + SourceLocation rAngleLoc = ReadSourceLocation(Record, Idx); + + return ObjCTypeParamList::create(Reader.getContext(), lAngleLoc, + typeParams, rAngleLoc); +} + void ASTDeclReader::VisitObjCInterfaceDecl(ObjCInterfaceDecl *ID) { RedeclarableResult Redecl = VisitRedeclarable(ID); VisitObjCContainerDecl(ID); TypeIDForTypeDecl = Reader.getGlobalTypeID(F, Record[Idx++]); mergeRedeclarable(ID, Redecl); - + + ID->TypeParamList = ReadObjCTypeParamList(); if (Record[Idx++]) { // Read the definition. ID->allocateDefinitionData(); @@ -1020,6 +1051,7 @@ void ASTDeclReader::VisitObjCCategoryDecl(ObjCCategoryDecl *CD) { Reader.CategoriesDeserialized.insert(CD); CD->ClassInterface = ReadDeclAs(Record, Idx); + CD->TypeParamList = ReadObjCTypeParamList(); unsigned NumProtoRefs = Record[Idx++]; SmallVector ProtoRefs; ProtoRefs.reserve(NumProtoRefs); @@ -3259,6 +3291,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { case DECL_EMPTY: D = EmptyDecl::CreateDeserialized(Context, ID); break; + case DECL_OBJC_TYPE_PARAM: + D = ObjCTypeParamDecl::CreateDeserialized(Context, ID); + break; } assert(D && "Unknown declaration reading AST file"); diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 6c5bc5bbd4..5e9151eed5 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -117,6 +117,7 @@ namespace clang { // FIXME: Put in the same order is DeclNodes.td? void VisitObjCMethodDecl(ObjCMethodDecl *D); + void VisitObjCTypeParamDecl(ObjCTypeParamDecl *D); void VisitObjCContainerDecl(ObjCContainerDecl *D); void VisitObjCInterfaceDecl(ObjCInterfaceDecl *D); void VisitObjCIvarDecl(ObjCIvarDecl *D); @@ -131,6 +132,22 @@ namespace clang { void VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D); void VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D); + /// Add an Objective-C type parameter list to the given record. + void AddObjCTypeParamList(ObjCTypeParamList *typeParams) { + // Empty type parameter list. + if (!typeParams) { + Record.push_back(0); + return; + } + + Record.push_back(typeParams->size()); + for (auto typeParam : *typeParams) { + Writer.AddDeclRef(typeParam, Record); + } + Writer.AddSourceLocation(typeParams->getLAngleLoc(), Record); + Writer.AddSourceLocation(typeParams->getRAngleLoc(), Record); + } + void AddFunctionDefinition(const FunctionDecl *FD) { assert(FD->doesThisDeclarationHaveABody()); if (auto *CD = dyn_cast(FD)) { @@ -562,6 +579,13 @@ void ASTDeclWriter::VisitObjCMethodDecl(ObjCMethodDecl *D) { Code = serialization::DECL_OBJC_METHOD; } +void ASTDeclWriter::VisitObjCTypeParamDecl(ObjCTypeParamDecl *D) { + VisitTypedefNameDecl(D); + Writer.AddSourceLocation(D->ColonLoc, Record); + + Code = serialization::DECL_OBJC_TYPE_PARAM; +} + void ASTDeclWriter::VisitObjCContainerDecl(ObjCContainerDecl *D) { VisitNamedDecl(D); Writer.AddSourceLocation(D->getAtStartLoc(), Record); @@ -573,6 +597,7 @@ void ASTDeclWriter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { VisitRedeclarable(D); VisitObjCContainerDecl(D); Writer.AddTypeRef(QualType(D->getTypeForDecl(), 0), Record); + AddObjCTypeParamList(D->TypeParamList); Record.push_back(D->isThisDeclarationADefinition()); if (D->isThisDeclarationADefinition()) { @@ -660,6 +685,7 @@ void ASTDeclWriter::VisitObjCCategoryDecl(ObjCCategoryDecl *D) { Writer.AddSourceLocation(D->getIvarLBraceLoc(), Record); Writer.AddSourceLocation(D->getIvarRBraceLoc(), Record); Writer.AddDeclRef(D->getClassInterface(), Record); + AddObjCTypeParamList(D->TypeParamList); Record.push_back(D->protocol_size()); for (const auto *I : D->protocols()) Writer.AddDeclRef(I, Record); diff --git a/test/Index/comment-objc-parameterized-classes.m b/test/Index/comment-objc-parameterized-classes.m new file mode 100644 index 0000000000..173fcecf33 --- /dev/null +++ b/test/Index/comment-objc-parameterized-classes.m @@ -0,0 +1,19 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: c-index-test -test-load-source all -comments-xml-schema=%S/../../bindings/xml/comment-xml-schema.rng -target x86_64-apple-darwin10 %s > %t/out +// RUN: FileCheck %s < %t/out + +// Ensure that XML we generate is not invalid. +// RUN: FileCheck %s -check-prefix=WRONG < %t/out +// WRONG-NOT: CommentXMLInvalid + +@protocol NSObject +@end + +@interface NSObject +@end + +// CHECK: @interface A <T : id, U : NSObject *> : NSObject +/// A +@interface A : NSObject +@end diff --git a/test/PCH/objc_parameterized_classes.m b/test/PCH/objc_parameterized_classes.m new file mode 100644 index 0000000000..a541c33fbf --- /dev/null +++ b/test/PCH/objc_parameterized_classes.m @@ -0,0 +1,30 @@ +// 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 + +__attribute__((objc_root_class)) +@interface NSObject +@end + +@interface PC1 : NSObject +// expected-note@-2{{type parameter 'U' declared here}} +@end + +@interface PC1 (Cat1) +@end + +#else + +@interface PC1 (Cat2) // expected-error{{type bound 'id' for type parameter 'U' conflicts with previous bound 'NSObject *'}} + // expected-note@15{{type parameter 'U' declared here}} +@end + +#endif diff --git a/test/Parser/objc-error-qualified-implementation.m b/test/Parser/objc-error-qualified-implementation.m index 664737223e..179e2d2747 100644 --- a/test/Parser/objc-error-qualified-implementation.m +++ b/test/Parser/objc-error-qualified-implementation.m @@ -17,7 +17,7 @@ @interface K @end -@implementation K

'}} // rdar://13920026 diff --git a/test/SemaObjC/parameterized_classes.m b/test/SemaObjC/parameterized_classes.m new file mode 100644 index 0000000000..e6eae44214 --- /dev/null +++ b/test/SemaObjC/parameterized_classes.m @@ -0,0 +1,192 @@ +// RUN: %clang_cc1 %s -verify + +@protocol NSObject +@end + +__attribute__((objc_root_class)) +@interface NSObject // expected-note{{'NSObject' defined here}} +@end + +@interface NSString : NSObject +@end + +// -------------------------------------------------------------------------- +// Parsing parameterized classes. +// -------------------------------------------------------------------------- + +// Parse type parameters with a bound +@interface PC1 : NSObject +// expected-note@-1{{type parameter 'T' declared here}} +// expected-note@-2{{type parameter 'U' declared here}} +@end + +// Parse a type parameter with a bound that terminates in '>>'. +@interface PC2> : NSObject // expected-error{{a space is required between consecutive right angle brackets (use '> >')}} +@end + +// Parse multiple type parameters. +@interface PC3 : NSObject +@end + +// Parse multiple type parameters--grammatically ambiguous with protocol refs. +@interface PC4 : NSObject +@end + +// Parse a type parameter list without a superclass. +@interface PC5 // expected-error{{parameterized Objective-C class 'PC5' must have a superclass}} +@end + +// Parse a type parameter with name conflicts. +@interface PC6 : NSObject // expected-error{{redeclaration of type parameter 'T'}} +@end + +// Parse Objective-C protocol references. +@interface PC7 // expected-error{{cannot find protocol declaration for 'T'}} +@end + +// Parse both type parameters and protocol references. +@interface PC8 : NSObject +@end + +// Type parameters with improper bounds. +@interface PC9 : NSObject // expected-error{{missing '*' in type bound 'NSString' for type parameter 'U'}} +@end + +// -------------------------------------------------------------------------- +// Parsing parameterized forward declarations classes. +// -------------------------------------------------------------------------- + +// Okay: forward declaration without type parameters. +@class PC10; + +// Okay: forward declarations with type parameters. +@class PC10, PC11; // expected-note{{type parameter 'T' declared here}} + +// Okay: forward declaration without type parameters following ones +// with type parameters. +@class PC10, PC11; + +// Okay: definition of class with type parameters that was formerly +// declared with the same type parameters. +@interface PC10 : NSObject +@end + +// Mismatched parameters in declaration of @interface following @class. +@interface PC11 : NSObject // expected-error{{missing type bound 'NSObject *' for type parameter 'T' in @interface}} +@end + +@interface PC12 : NSObject // expected-note{{type parameter 'T' declared here}} +@end + +@class PC12; + +// Mismatched parameters in subsequent forward declarations. +@class PC13; // expected-note{{type parameter 'T' declared here}} +@class PC13; +@class PC13; // expected-error{{missing type bound 'NSObject *' for type parameter 'U' in @class}} + +// Mismatch parameters in declaration of @class following @interface. +@class PC12; // expected-error{{missing type bound 'NSObject *' for type parameter 'T' in @class}} + +// Parameterized forward declaration a class that is not parameterized. +@class NSObject; // expected-error{{forward declaration of non-parameterized class 'NSObject' cannot have type parameters}} + +// Parameterized forward declaration preceding the definition (that is +// not parameterized). +@class NSNumber; // expected-note{{'NSNumber' declared here}} +@interface NSNumber : NSObject // expected-error{{class 'NSNumber' previously declared with type parameters}} +@end + +@class PC14; + +// Okay: definition of class with type parameters that was formerly +// declared without type parameters. +@interface PC14 : NSObject +@end + +// -------------------------------------------------------------------------- +// Parsing parameterized categories and extensions. +// -------------------------------------------------------------------------- + +// Inferring type bounds +@interface PC1 (Cat1) +@end + +// Matching type bounds +@interface PC1 (Cat2) +@end + +// Inferring type bounds +@interface PC1 () +@end + +// Matching type bounds +@interface PC1 () +@end + +// Missing type parameters. +@interface PC1 () // expected-error{{extension has too few type parameters (expected 2, have 1)}} +@end + +// Extra type parameters. +@interface PC1 (Cat3) // expected-error{{category has too many type parameters (expected 2, have 3)}} +@end + +// Mismatched bounds. +@interface PC1 () // expected-error{{type bound 'id' for type parameter 'X' conflicts with previous bound 'NSObject *'for type parameter 'U'}} +@end + +// Parameterized category/extension of non-parameterized class. +@interface NSObject (Cat1) // expected-error{{category of non-parameterized class 'NSObject' cannot have type parameters}} +@end + +@interface NSObject () // expected-error{{extension of non-parameterized class 'NSObject' cannot have type parameters}} +@end + +// -------------------------------------------------------------------------- +// @implementations cannot have type parameters +// -------------------------------------------------------------------------- +@implementation PC1 // expected-error{{@implementation cannot have type parameters}} +@end + +@implementation PC2 // expected-error{{@implementation declaration cannot be protocol qualified}} +@end + +@implementation PC1 (Cat1) // expected-error{{@implementation cannot have type parameters}} +@end + +@implementation PC1 (Cat2) // expected-error{{@implementation cannot have type parameters}} +@end + +// -------------------------------------------------------------------------- +// Interfaces involving type parameters +// -------------------------------------------------------------------------- +@interface PC20 : NSObject { + T object; +} + +- (U)method:(V)param; // expected-note{{passing argument to parameter 'param' here}} +@end + +@interface PC20 (Cat1) +- (U)catMethod:(V)param; // expected-note{{passing argument to parameter 'param' here}} +@end + +@interface PC20() +- (X)extMethod:(Y)param; // expected-note{{passing argument to parameter 'param' here}} +@end + +void test_PC20_unspecialized(PC20 *pc20) { + // FIXME: replace type parameters with underlying types? + int *ip = [pc20 method: 0]; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'U' (aka 'NSObject *')}} + [pc20 method: ip]; // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'V' (aka 'NSString *')}} + + ip = [pc20 catMethod: 0]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'U' (aka 'NSObject *')}} + [pc20 catMethod: ip]; // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'V' (aka 'NSString *')}} + + ip = [pc20 extMethod: 0]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'X' (aka 'id')}} + [pc20 extMethod: ip]; // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'Y' (aka 'NSObject *')}} +} diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index bf5b58253a..6c887e1889 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -5038,6 +5038,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::ClassScopeFunctionSpecialization: case Decl::Import: case Decl::OMPThreadPrivate: + case Decl::ObjCTypeParam: return C; // Declaration kinds that don't make any sense here, but are @@ -6321,6 +6322,7 @@ static CXLanguageKind getDeclLanguage(const Decl *D) { case Decl::ObjCProperty: case Decl::ObjCPropertyImpl: case Decl::ObjCProtocol: + case Decl::ObjCTypeParam: return CXLanguage_ObjC; case Decl::CXXConstructor: case Decl::CXXConversion: -- 2.40.0