From: Douglas Gregor Date: Mon, 9 Feb 2009 18:46:07 +0000 (+0000) Subject: Start processing template-ids as types when the template-name refers X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=55f6b14230c94272efbbcdd89a92224c8db9f225;p=clang Start processing template-ids as types when the template-name refers to a class template. For example, the template-id 'vector' now has a nice, sugary type in the type system. What we can do now: - Parse template-ids like 'vector' (where 'vector' names a class template) and form proper types for them in the type system. - Parse icky template-ids like 'A<5>' and 'A<(5 > 0)>' properly, using (sadly) a bool in the parser to tell it whether '>' should be treated as an operator or not. This is a baby-step, with major problems and limitations: - There are currently two ways that we handle template arguments (whether they are types or expressions). These will be merged, and, most likely, TemplateArg will disappear. - We don't have any notion of the declaration of class template specializations or of template instantiations, so all template-ids are fancy names for 'int' :) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@64153 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 7198332b29..2329ff5fe8 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -69,6 +69,8 @@ class ASTContext { llvm::FoldingSet FunctionTypeNoProtos; llvm::FoldingSet FunctionTypeProtos; llvm::FoldingSet TemplateTypeParmTypes; + llvm::FoldingSet + ClassTemplateSpecializationTypes; llvm::FoldingSet ObjCQualifiedInterfaceTypes; llvm::FoldingSet ObjCQualifiedIdTypes; /// ASTRecordLayouts - A cache mapping from RecordDecls to ASTRecordLayouts. @@ -262,6 +264,11 @@ public: QualType getTemplateTypeParmType(unsigned Depth, unsigned Index, IdentifierInfo *Name = 0); + QualType getClassTemplateSpecializationType(TemplateDecl *Template, + unsigned NumArgs, + uintptr_t *Args, bool *ArgIsType, + QualType Canon); + /// getObjCQualifiedInterfaceType - Return a /// ObjCQualifiedInterfaceType type for the given interface decl and /// the conforming protocol list. diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index 2183298f9d..daf4210c44 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -14,6 +14,8 @@ #ifndef LLVM_CLANG_AST_DECLTEMPLATE_H #define LLVM_CLANG_AST_DECLTEMPLATE_H +#include "clang/AST/DeclCXX.h" + namespace clang { class TemplateParameterList; @@ -158,7 +160,7 @@ protected: public: /// Get the underlying class declarations of the template. CXXRecordDecl *getTemplatedDecl() const { - return static_cast(TemplatedDecl); + return static_cast(TemplatedDecl); } /// Create a class teplate node. @@ -334,6 +336,35 @@ protected: friend Decl* Decl::Create(llvm::Deserializer& D, ASTContext& C); }; +class TemplateArg { + enum { + TypeArg, + ExprArg + } Kind; + + uintptr_t Ptr; + +public: + explicit TemplateArg(QualType Type) + : Kind(TypeArg), Ptr(reinterpret_cast(Type.getAsOpaquePtr())) { } + explicit TemplateArg(Expr *E) + : Kind(ExprArg), Ptr(reinterpret_cast(E)) { } + + QualType getAsType() const { + if (Kind == TypeArg) + return QualType::getFromOpaquePtr(reinterpret_cast(Ptr)); + return QualType(); + } + + Expr *getAsExpr() const { + if (Kind == ExprArg) return reinterpret_cast(Ptr); + return 0; + } + + void Destroy(ASTContext &C); +}; + + } /* end of namespace clang */ #endif diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 666d952645..8b9826e347 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -30,6 +30,7 @@ namespace clang { class ASTContext; class Type; class TypedefDecl; + class TemplateDecl; class TemplateTypeParmDecl; class NonTypeTemplateParmDecl; class TemplateTemplateParamDecl; @@ -69,6 +70,7 @@ namespace clang { class ObjCQualifiedIdType; class ObjCQualifiedInterfaceType; class StmtIteratorBase; + class ClassTemplateSpecializationType; /// QualType - For efficiency, we don't store CVR-qualified types as nodes on /// their own: instead each reference to a type stores the qualifiers. This @@ -245,7 +247,7 @@ public: Vector, ExtVector, FunctionNoProto, FunctionProto, TypeName, Tagged, ASQual, - TemplateTypeParm, + TemplateTypeParm, ClassTemplateSpecialization, ObjCInterface, ObjCQualifiedInterface, ObjCQualifiedId, TypeOfExp, TypeOfTyp, // GNU typeof extension. @@ -391,6 +393,9 @@ public: const ObjCQualifiedIdType *getAsObjCQualifiedIdType() const; const TemplateTypeParmType *getAsTemplateTypeParmType() const; + const ClassTemplateSpecializationType * + getClassTemplateSpecializationType() const; + /// getAsPointerToObjCInterfaceType - If this is a pointer to an ObjC /// interface, return the interface type, otherwise return null. const ObjCInterfaceType *getAsPointerToObjCInterfaceType() const; @@ -400,8 +405,6 @@ public: /// This method should never be used when type qualifiers are meaningful. const Type *getArrayElementTypeNoTypeQual() const; - - /// getDesugaredType - Return the specified type with any "sugar" removed from /// the type. This takes off typedefs, typeof's etc. If the outer level of /// the type is already concrete, it returns it unmodified. This is similar @@ -1395,6 +1398,127 @@ protected: friend class Type; }; +/// \brief Represents the type of a class template specialization as +/// written in the source code. +/// +/// Class template specialization types represent the syntactic form +/// of a template-id that refers to a type, e.g., @c vector. All +/// class template specialization types are syntactic sugar, whose +/// canonical type will point to some other type node that represents +/// the instantiation or class template specialization. For example, a +/// class template specialization type of @c vector will refer to +/// a tag type for the instantiation +/// @c std::vector>. +class ClassTemplateSpecializationType + : public Type, public llvm::FoldingSetNode { + + // FIXME: Do we want templates to have a representation in the type + // system? It will probably help with dependent templates and + // possibly with template-names preceded by a nested-name-specifier. + TemplateDecl *Template; + + unsigned NumArgs; + + ClassTemplateSpecializationType(TemplateDecl *T, unsigned NumArgs, + uintptr_t *Args, bool *ArgIsType, + QualType Canon); + + /// \brief Retrieve the number of packed words that precede the + /// actual arguments. + /// + /// The flags that specify whether each argument is a type or an + /// expression are packed into the + /// ClassTemplateSpecializationType. This routine computes the + /// number of pointer-sized words we need to store this information, + /// based on the number of template arguments + static unsigned getNumPackedWords(unsigned NumArgs) { + const unsigned BitsPerWord = sizeof(uintptr_t) * CHAR_BIT; + return NumArgs / BitsPerWord + (NumArgs % BitsPerWord > 0); + } + + /// \brief Pack the given boolean values into words. + static void + packBooleanValues(unsigned NumArgs, bool *Values, uintptr_t *Words); + + friend class ASTContext; // ASTContext creates these + +public: + /// \brief Retrieve the template that we are specializing. + TemplateDecl *getTemplate() const { return Template; } + + /// \briefe Retrieve the number of template arguments. + unsigned getNumArgs() const { return NumArgs; } + + /// \brief Retrieve a specific template argument as a type. + /// \precondition @c isArgType(Arg) + QualType getArgAsType(unsigned Arg) const { + assert(isArgType(Arg) && "Argument is not a type"); + return QualType::getFromOpaquePtr( + reinterpret_cast(getArgAsOpaqueValue(Arg))); + } + + /// \brief Retrieve a specific template argument as an expression. + /// \precondition @c !isArgType(Arg) + Expr *getArgAsExpr(unsigned Arg) const { + assert(!isArgType(Arg) && "Argument is not an expression"); + return reinterpret_cast(getArgAsOpaqueValue(Arg)); + } + + /// \brief Retrieve the specified template argument as an opaque value. + uintptr_t getArgAsOpaqueValue(unsigned Arg) const; + + /// \brief Determine whether the given template argument is a type. + bool isArgType(unsigned Arg) const; + + virtual void getAsStringInternal(std::string &InnerString) const; + + void Profile(llvm::FoldingSetNodeID &ID) { + // Add the template + ID.AddPointer(Template); + + // Add the packed words describing what kind of template arguments + // we have. + uintptr_t *Data = reinterpret_cast(this + 1); + for (unsigned Packed = 0, NumPacked = getNumPackedWords(NumArgs); + Packed != NumPacked; ++Packed) + ID.AddInteger(Data[Packed]); + + // Add the template arguments themselves. + for (unsigned Arg = 0; Arg < NumArgs; ++Arg) + ID.AddInteger(getArgAsOpaqueValue(Arg)); + } + + static void Profile(llvm::FoldingSetNodeID &ID, TemplateDecl *T, + unsigned NumArgs, uintptr_t *Args, bool *ArgIsType) { + // Add the template + ID.AddPointer(T); + + // Add the packed words describing what kind of template arguments + // we have. + unsigned NumPackedWords = getNumPackedWords(NumArgs); + unsigned NumPackedBytes = NumPackedWords * sizeof(uintptr_t); + uintptr_t *PackedWords + = reinterpret_cast(alloca(NumPackedBytes)); + packBooleanValues(NumArgs, ArgIsType, PackedWords); + for (unsigned Packed = 0; Packed != NumPackedWords; ++Packed) + ID.AddInteger(PackedWords[Packed]); + + // Add the template arguments themselves. + for (unsigned Arg = 0; Arg < NumArgs; ++Arg) + ID.AddInteger(Args[Arg]); + } + + static bool classof(const Type *T) { + return T->getTypeClass() == ClassTemplateSpecialization; + } + static bool classof(const ClassTemplateSpecializationType *T) { return true; } + +protected: + virtual void EmitImpl(llvm::Serializer& S) const; + static Type* CreateImpl(ASTContext& Context, llvm::Deserializer& D); + friend class Type; +}; + /// ObjCInterfaceType - Interfaces are the core concept in Objective-C for /// object oriented design. They basically correspond to C++ classes. There /// are two kinds of interface types, normal interfaces like "NSString" and diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index f735f3e792..b13b8ef228 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -108,7 +108,7 @@ public: typedef ASTMultiPtr<&ActionBase::DeleteExpr> MultiExprArg; typedef ASTMultiPtr<&ActionBase::DeleteStmt> MultiStmtArg; typedef ASTMultiPtr<&ActionBase::DeleteTemplateParams> MultiTemplateParamsArg; - typedef ASTMultiPtr<&ActionBase::DeleteTemplateArg> MultiTemplateArgArg; + typedef ASTMultiPtr<&ActionBase::DeleteTemplateArg> MultiTemplateArgsArg; // Utilities for Action implementations to return smart results. @@ -149,13 +149,28 @@ public: virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S, const CXXScopeSpec *SS = 0) = 0; - /// isTemplateName - Determines whether the identifier II is a - /// template name in the current scope, and returns the template - /// declaration if II names a template. An optional CXXScope can be - /// passed to indicate the C++ scope in which the identifier will be - /// found. - virtual DeclTy *isTemplateName(IdentifierInfo &II, Scope *S, - const CXXScopeSpec *SS = 0) = 0; + /// \brief Specifies the kind of template name. Returned from + /// isTemplateName. + enum TemplateNameKind { + /// The name does not refer to a template. + TNK_Non_template, + /// The name refers to a function template or a set of overloaded + /// functions that includes at least one function template. + TNK_Function_template, + /// The name refers to a class template. + TNK_Class_template, + /// The name referes to a template template parameter. + TNK_Template_template_parm + }; + + /// \brief Determines whether the identifier II is a template name + /// in the current scope. If so, the kind of template name is + /// returned, and \p TemplateDecl receives the declaration. An + /// optional CXXScope can be passed to indicate the C++ scope in + /// which the identifier will be found. + virtual TemplateNameKind isTemplateName(IdentifierInfo &II, Scope *S, + DeclTy *&TemplateDecl, + const CXXScopeSpec *SS = 0) = 0; /// ActOnCXXGlobalScopeSpecifier - Return the object that represents the /// global scope ('::'). @@ -1111,8 +1126,16 @@ public: return 0; } - // \brief Process the declaration or definition of a class template - // with the given template parameter lists. + virtual OwningTemplateArgResult ActOnTypeTemplateArgument(TypeTy *Type) { + return TemplateArgError(); + } + + virtual OwningTemplateArgResult ActOnExprTemplateArgument(ExprArg Value) { + return TemplateArgError(); + } + + /// \brief Process the declaration or definition of a class template + /// with the given template parameter lists. virtual DeclTy * ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK, SourceLocation KWLoc, const CXXScopeSpec &SS, @@ -1122,7 +1145,25 @@ public: return 0; } - + /// \brief Form a class template specialization from a template and + /// a list of template arguments. + /// + /// \param Template A template whose specialization results in a + /// type, e.g., a class template or template template parameter. + /// + /// \todo "Class template specialization" is the standard term for + /// the types that we're forming, but the name + /// ActOnClassTemplateSpecialization sounds like we're declaring a + /// new class template specialization. + virtual TypeTy * + ActOnClassTemplateSpecialization(DeclTy *Template, + SourceLocation LAngleLoc, + MultiTemplateArgsArg TemplateArgs, + SourceLocation RAngleLoc, + const CXXScopeSpec *SS = 0) { + return 0; + }; + //===----------------------- Obj-C Declarations -------------------------===// // ActOnStartClassInterface - this action is called immediately after parsing @@ -1372,13 +1413,9 @@ public: virtual bool isCurrentClassName(const IdentifierInfo& II, Scope *S, const CXXScopeSpec *SS); - /// isTemplateName - Determines whether the identifier II is a - /// template name in the current scope, and returns the template - /// declaration if II names a template. An optional CXXScope can be - /// passed to indicate the C++ scope in which the identifier will be - /// found. - virtual DeclTy *isTemplateName(IdentifierInfo &II, Scope *S, - const CXXScopeSpec *SS = 0); + virtual TemplateNameKind isTemplateName(IdentifierInfo &II, Scope *S, + DeclTy *&TemplateDecl, + const CXXScopeSpec *SS = 0); /// ActOnDeclarator - If this is a typedef declarator, we modify the /// IdentifierInfo::FETokenInfo field to keep track of this fact, until S is diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index da29208ccb..7a73fcba53 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -58,6 +58,44 @@ class Parser { PragmaHandler *PackHandler; + /// Whether the '>' token acts as an operator or not. This will be + /// true except when we are parsing an expression within a C++ + /// template argument list, where the '>' closes the template + /// argument list. + bool GreaterThanIsOperator; + + /// \brief RAII object that makes '>' behave like the closing angle + /// bracket for a template argument list. + struct MakeGreaterThanTemplateArgumentListTerminator { + bool &GreaterThanIsOperator; + bool OldGreaterThanIsOperator; + + MakeGreaterThanTemplateArgumentListTerminator(bool >IO) + : GreaterThanIsOperator(GTIO), OldGreaterThanIsOperator(GTIO) { + GTIO = false; + } + + ~MakeGreaterThanTemplateArgumentListTerminator() { + GreaterThanIsOperator = OldGreaterThanIsOperator; + } + }; + + /// \brief RAII object that makes '>' behave like an + /// operator. Occurs, for example, inside parentheses. + struct MakeGreaterThanAnOperator { + bool &GreaterThanIsOperator; + bool OldGreaterThanIsOperator; + + MakeGreaterThanAnOperator(bool >IO) + : GreaterThanIsOperator(GTIO), OldGreaterThanIsOperator(GTIO) { + GTIO = true; + } + + ~MakeGreaterThanAnOperator() { + GreaterThanIsOperator = OldGreaterThanIsOperator; + } + }; + public: Parser(Preprocessor &PP, Action &Actions); ~Parser(); @@ -976,6 +1014,7 @@ private: //===--------------------------------------------------------------------===// // C++ 14: Templates [temp] typedef llvm::SmallVector TemplateParameterList; + typedef Action::TemplateNameKind TemplateNameKind; // C++ 14.1: Template Parameters [temp.param] DeclTy *ParseTemplateDeclaration(unsigned Context); @@ -991,7 +1030,8 @@ private: DeclTy *ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position); // C++ 14.3: Template arguments [temp.arg] typedef llvm::SmallVector TemplateArgList; - void AnnotateTemplateIdToken(DeclTy *Template, const CXXScopeSpec *SS = 0); + void AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK, + const CXXScopeSpec *SS = 0); bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs); OwningTemplateArgResult ParseTemplateArgument(); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index a87a902d06..72aef4269b 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1220,6 +1220,34 @@ QualType ASTContext::getTemplateTypeParmType(unsigned Depth, unsigned Index, return QualType(TypeParm, 0); } +QualType +ASTContext::getClassTemplateSpecializationType(TemplateDecl *Template, + unsigned NumArgs, + uintptr_t *Args, bool *ArgIsType, + QualType Canon) { + llvm::FoldingSetNodeID ID; + ClassTemplateSpecializationType::Profile(ID, Template, NumArgs, Args, + ArgIsType); + void *InsertPos = 0; + ClassTemplateSpecializationType *Spec + = ClassTemplateSpecializationTypes.FindNodeOrInsertPos(ID, InsertPos); + + if (Spec) + return QualType(Spec, 0); + + void *Mem = Allocate(sizeof(ClassTemplateSpecializationType) + + (sizeof(uintptr_t) * + (ClassTemplateSpecializationType:: + getNumPackedWords(NumArgs) + + NumArgs)), 8); + Spec = new (Mem) ClassTemplateSpecializationType(Template, NumArgs, Args, + ArgIsType, Canon); + Types.push_back(Spec); + ClassTemplateSpecializationTypes.InsertNode(Spec, InsertPos); + + return QualType(Spec, 0); +} + /// CmpProtocolNames - Comparison predicate for sorting protocols /// alphabetically. static bool CmpProtocolNames(const ObjCProtocolDecl *LHS, diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index d005ca3b06..ab5a3bc0b2 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -13,6 +13,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/IdentifierTable.h" #include "llvm/ADT/STLExtras.h" @@ -43,6 +44,11 @@ TemplateParameterList::Create(ASTContext &C, SourceLocation TemplateLoc, NumParams, RAngleLoc); } +void TemplateArg::Destroy(ASTContext &C) { + if (Kind == ExprArg) + getAsExpr()->Destroy(C); +} + //===----------------------------------------------------------------------===// // TemplateDecl Implementation //===----------------------------------------------------------------------===// diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 106d0eb453..6ec5062f93 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -489,6 +489,14 @@ const TemplateTypeParmType *Type::getAsTemplateTypeParmType() const { return dyn_cast(CanonicalType); } +const ClassTemplateSpecializationType * +Type::getClassTemplateSpecializationType() const { + // There is no sugar for class template specialization types, so + // just return the canonical type pointer if it is the right class. + return dyn_cast(CanonicalType); +} + + bool Type::isIntegerType() const { if (const BuiltinType *BT = dyn_cast(CanonicalType)) return BT->getKind() >= BuiltinType::Bool && @@ -882,6 +890,54 @@ bool EnumType::classof(const TagType *TT) { return isa(TT->getDecl()); } +void +ClassTemplateSpecializationType:: +packBooleanValues(unsigned NumArgs, bool *Values, uintptr_t *Words) { + const unsigned BitsPerWord = sizeof(uintptr_t) * CHAR_BIT; + + for (unsigned PW = 0, NumPackedWords = getNumPackedWords(NumArgs), Arg = 0; + PW != NumPackedWords; ++PW) { + uintptr_t Word = 0; + for (unsigned Bit = 0; Bit < BitsPerWord && Arg < NumArgs; ++Bit, ++Arg) { + Word <<= 1; + Word |= Values[Arg]; + } + Words[PW] = Word; + } +} + +ClassTemplateSpecializationType:: +ClassTemplateSpecializationType(TemplateDecl *T, unsigned NumArgs, + uintptr_t *Args, bool *ArgIsType, + QualType Canon) + : Type(ClassTemplateSpecialization, Canon, /*FIXME:Dependent=*/false), + Template(T), NumArgs(NumArgs) +{ + uintptr_t *Data = reinterpret_cast(this + 1); + + // Pack the argument-is-type values into the words just after the + // class template specialization type. + packBooleanValues(NumArgs, ArgIsType, Data); + + // Copy the template arguments after the packed words. + Data += getNumPackedWords(NumArgs); + for (unsigned Arg = 0; Arg < NumArgs; ++Arg) + Data[Arg] = Args[Arg]; +} + +uintptr_t +ClassTemplateSpecializationType::getArgAsOpaqueValue(unsigned Arg) const { + const uintptr_t *Data = reinterpret_cast(this + 1); + Data += getNumPackedWords(NumArgs); + return Data[Arg]; +} + +bool ClassTemplateSpecializationType::isArgType(unsigned Arg) const { + const unsigned BitsPerWord = sizeof(uintptr_t) * CHAR_BIT; + const uintptr_t *Data = reinterpret_cast(this + 1); + Data += Arg / BitsPerWord; + return (*Data >> (Arg % BitsPerWord)) & 0x01; +} //===----------------------------------------------------------------------===// // Type Printing @@ -1146,6 +1202,47 @@ void TemplateTypeParmType::getAsStringInternal(std::string &InnerString) const { InnerString = Name->getName() + InnerString; } +void +ClassTemplateSpecializationType:: +getAsStringInternal(std::string &InnerString) const { + std::string SpecString = Template->getNameAsString(); + SpecString += '<'; + for (unsigned Arg = 0; Arg < NumArgs; ++Arg) { + if (Arg) + SpecString += ", "; + + // Print the argument into a string. + std::string ArgString; + if (isArgType(Arg)) + getArgAsType(Arg).getAsStringInternal(ArgString); + else { + llvm::raw_string_ostream s(ArgString); + getArgAsExpr(Arg)->printPretty(s); + } + + // If this is the first argument and its string representation + // begins with the global scope specifier ('::foo'), add a space + // to avoid printing the diagraph '<:'. + if (!Arg && !ArgString.empty() && ArgString[0] == ':') + SpecString += ' '; + + SpecString += ArgString; + } + + // If the last character of our string is '>', add another space to + // keep the two '>''s separate tokens. We don't *have* to do this in + // C++0x, but it's still good hygiene. + if (SpecString[SpecString.size() - 1] == '>') + SpecString += ' '; + + SpecString += '>'; + + if (InnerString.empty()) + InnerString.swap(SpecString); + else + InnerString = SpecString + ' ' + InnerString; +} + void ObjCInterfaceType::getAsStringInternal(std::string &InnerString) const { if (!InnerString.empty()) // Prefix the basic type, e.g. 'typedefname X'. InnerString = ' ' + InnerString; diff --git a/lib/AST/TypeSerialization.cpp b/lib/AST/TypeSerialization.cpp index 57598d3afc..90bc67ad4d 100644 --- a/lib/AST/TypeSerialization.cpp +++ b/lib/AST/TypeSerialization.cpp @@ -13,6 +13,7 @@ #include "clang/AST/Type.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ASTContext.h" #include "llvm/Bitcode/Serialize.h" @@ -376,6 +377,48 @@ Type* TemplateTypeParmType::CreateImpl(ASTContext& Context, Deserializer& D) { return Context.getTemplateTypeParmType(Depth, Index, Name).getTypePtr(); } +//===----------------------------------------------------------------------===// +// ClassTemplateSpecializationType +//===----------------------------------------------------------------------===// + +void ClassTemplateSpecializationType::EmitImpl(Serializer& S) const { + S.Emit(getCanonicalTypeInternal()); + S.EmitPtr(Template); + S.EmitInt(NumArgs); + for (unsigned Arg = 0; Arg < NumArgs; ++Arg) { + S.EmitBool(isArgType(Arg)); + if (isArgType(Arg)) + S.Emit(getArgAsType(Arg)); + else + S.EmitOwnedPtr(getArgAsExpr(Arg)); + } +} + +Type* +ClassTemplateSpecializationType:: +CreateImpl(ASTContext& Context, Deserializer& D) { + llvm::SmallVector Args; + llvm::SmallVector ArgIsType; + + QualType Canon = QualType::ReadVal(D); + TemplateDecl *Template = cast(D.ReadPtr()); + unsigned NumArgs = D.ReadInt(); + + for (unsigned Arg = 0; Arg < NumArgs; ++Arg) { + bool IsType = D.ReadBool(); + ArgIsType.push_back(IsType); + if (IsType) + Args.push_back( + reinterpret_cast(QualType::ReadVal(D).getAsOpaquePtr())); + else + Args.push_back(reinterpret_cast(D.ReadOwnedPtr(Context))); + } + + return Context.getClassTemplateSpecializationType(Template, NumArgs, + &Args[0], &ArgIsType[0], + Canon).getTypePtr(); +} + //===----------------------------------------------------------------------===// // VariableArrayType //===----------------------------------------------------------------------===// diff --git a/lib/CodeGen/CodeGenTypes.cpp b/lib/CodeGen/CodeGenTypes.cpp index 0b74e1e636..37b7c5f68c 100644 --- a/lib/CodeGen/CodeGenTypes.cpp +++ b/lib/CodeGen/CodeGenTypes.cpp @@ -176,6 +176,7 @@ const llvm::Type *CodeGenTypes::ConvertNewType(QualType T) { switch (Ty.getTypeClass()) { case Type::TypeName: // typedef isn't canonical. case Type::TemplateTypeParm:// template type parameters never generated + case Type::ClassTemplateSpecialization: // these types are always sugar case Type::DependentSizedArray: // dependent types are never generated case Type::TypeOfExp: // typeof isn't canonical. case Type::TypeOfTyp: // typeof isn't canonical. diff --git a/lib/Parse/MinimalAction.cpp b/lib/Parse/MinimalAction.cpp index 2d6c9a6ba5..ce15cf9dda 100644 --- a/lib/Parse/MinimalAction.cpp +++ b/lib/Parse/MinimalAction.cpp @@ -95,14 +95,11 @@ bool MinimalAction::isCurrentClassName(const IdentifierInfo &, Scope *, return false; } - /// isTemplateName - Determines whether the identifier II is a - /// template name in the current scope, and returns the template - /// declaration if II names a template. An optional CXXScope can be - /// passed to indicate the C++ scope in which the identifier will be - /// found. -Action::DeclTy *MinimalAction::isTemplateName(IdentifierInfo &II, Scope *S, - const CXXScopeSpec *SS ) { - return 0; +Action::TemplateNameKind +MinimalAction::isTemplateName(IdentifierInfo &II, Scope *S, + DeclTy *&TemplateDecl, + const CXXScopeSpec *SS) { + return TNK_Non_template; } /// ActOnDeclarator - If this is a typedef declarator, we modify the diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 73a09ee39c..5d601bc4f1 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -499,6 +499,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, Token Next = NextToken(); TypeTy *TypeRep = Actions.getTypeName(*Next.getIdentifierInfo(), Next.getLocation(), CurScope, &SS); + if (TypeRep == 0) goto DoneWithDeclSpec; @@ -553,9 +554,23 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // It has to be available as a typedef too! TypeTy *TypeRep = Actions.getTypeName(*Tok.getIdentifierInfo(), Tok.getLocation(), CurScope); + + if (TypeRep == 0 && getLang().CPlusPlus && NextToken().is(tok::less)) { + // If we have a template name, annotate the token and try again. + DeclTy *Template = 0; + if (TemplateNameKind TNK = + Actions.isTemplateName(*Tok.getIdentifierInfo(), CurScope, + Template)) { + AnnotateTemplateIdToken(Template, TNK, 0); + continue; + } + } + if (TypeRep == 0) goto DoneWithDeclSpec; + + // C++: If the identifier is actually the name of the class type // being defined and the next token is a '(', then this is a // constructor declaration. We're done with the decl-specifiers @@ -1752,11 +1767,12 @@ void Parser::ParseDirectDeclarator(Declarator &D) { // If this identifier is followed by a '<', we may have a template-id. DeclTy *Template; + Action::TemplateNameKind TNK; if (getLang().CPlusPlus && NextToken().is(tok::less) && - (Template = Actions.isTemplateName(*Tok.getIdentifierInfo(), - CurScope))) { + (TNK = Actions.isTemplateName(*Tok.getIdentifierInfo(), + CurScope, Template))) { IdentifierInfo *II = Tok.getIdentifierInfo(); - AnnotateTemplateIdToken(Template, 0); + AnnotateTemplateIdToken(Template, TNK, 0); // FIXME: Set the declarator to a template-id. How? I don't // know... for now, just use the identifier. D.SetIdentifier(II, Tok.getLocation()); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 332ad7789e..0f04d13f6e 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -55,8 +55,17 @@ namespace prec { /// getBinOpPrecedence - Return the precedence of the specified binary operator /// token. This returns: /// -static prec::Level getBinOpPrecedence(tok::TokenKind Kind) { +static prec::Level getBinOpPrecedence(tok::TokenKind Kind, + bool GreaterThanIsOperator) { switch (Kind) { + case tok::greater: + // The '>' token can act as either an operator or as the ending + // token for a template argument list. + // FIXME: '>>' is similar, for error recovery and C++0x. + if (GreaterThanIsOperator) + return prec::Relational; + return prec::Unknown; + default: return prec::Unknown; case tok::comma: return prec::Comma; case tok::equal: @@ -80,8 +89,7 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind) { case tok::equalequal: return prec::Equality; case tok::lessequal: case tok::less: - case tok::greaterequal: - case tok::greater: return prec::Relational; + case tok::greaterequal: return prec::Relational; case tok::lessless: case tok::greatergreater: return prec::Shift; case tok::plus: @@ -266,7 +274,7 @@ Parser::OwningExprResult Parser::ParseConstantExpression() { /// LHS and has a precedence of at least MinPrec. Parser::OwningExprResult Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) { - unsigned NextTokPrec = getBinOpPrecedence(Tok.getKind()); + unsigned NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator); SourceLocation ColonLoc; while (1) { @@ -316,7 +324,7 @@ Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) { // Remember the precedence of this operator and get the precedence of the // operator immediately to the right of the RHS. unsigned ThisPrec = NextTokPrec; - NextTokPrec = getBinOpPrecedence(Tok.getKind()); + NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator); // Assignment and conditional expressions are right-associative. bool isRightAssoc = ThisPrec == prec::Conditional || @@ -335,7 +343,7 @@ Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) { if (RHS.isInvalid()) return move(RHS); - NextTokPrec = getBinOpPrecedence(Tok.getKind()); + NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator); } assert(NextTokPrec <= ThisPrec && "Recursion didn't work!"); @@ -1104,6 +1112,7 @@ Parser::OwningExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, TypeTy *&CastTy, SourceLocation &RParenLoc) { assert(Tok.is(tok::l_paren) && "Not a paren expr!"); + MakeGreaterThanAnOperator G(GreaterThanIsOperator); SourceLocation OpenLoc = ConsumeParen(); OwningExprResult Result(Actions, true); CastTy = 0; diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 8836106e39..2e2cf5357f 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -15,6 +15,7 @@ #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/DeclSpec.h" #include "clang/Parse/Scope.h" +#include "AstGuard.h" using namespace clang; @@ -354,7 +355,8 @@ Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) { /// AnnotateTemplateIdToken - The current token is an identifier that /// refers to the template declaration Template, and is followed by a /// '<'. Turn this template-id into a template-id annotation token. -void Parser::AnnotateTemplateIdToken(DeclTy *Template, const CXXScopeSpec *SS) { +void Parser::AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK, + const CXXScopeSpec *SS) { assert(getLang().CPlusPlus && "Can only annotate template-ids in C++"); assert(Template && Tok.is(tok::identifier) && NextToken().is(tok::less) && "Parser isn't at the beginning of a template-id"); @@ -366,13 +368,16 @@ void Parser::AnnotateTemplateIdToken(DeclTy *Template, const CXXScopeSpec *SS) { SourceLocation LAngleLoc = ConsumeToken(); // Parse the optional template-argument-list. - TemplateArgList TemplateArgs; - if (Tok.isNot(tok::greater) && ParseTemplateArgumentList(TemplateArgs)) { - // Try to find the closing '>'. - SkipUntil(tok::greater, true, true); - - // FIXME: What's our recovery strategy for failed template-argument-lists? - return; + ASTVector<&ActionBase::DeleteTemplateArg, 8> TemplateArgs(Actions); + { + MakeGreaterThanTemplateArgumentListTerminator G(GreaterThanIsOperator); + if (Tok.isNot(tok::greater) && ParseTemplateArgumentList(TemplateArgs)) { + // Try to find the closing '>'. + SkipUntil(tok::greater, true, true); + + // FIXME: What's our recovery strategy for failed template-argument-lists? + return; + } } if (Tok.isNot(tok::greater)) @@ -382,24 +387,41 @@ void Parser::AnnotateTemplateIdToken(DeclTy *Template, const CXXScopeSpec *SS) { // token, because we'll be replacing it with the template-id. SourceLocation RAngleLoc = Tok.getLocation(); - Tok.setKind(tok::annot_template_id); + // Build the annotation token. + if (TNK == Action::TNK_Function_template) { + // This is a function template. We'll be building a template-id + // annotation token. + TemplateArgs.take(); // Annotation token takes ownership + Tok.setKind(tok::annot_template_id); + TemplateIdAnnotation *TemplateId + = (TemplateIdAnnotation *)malloc(sizeof(TemplateIdAnnotation) + + sizeof(TemplateArgTy*) * TemplateArgs.size()); + TemplateId->TemplateNameLoc = TemplateNameLoc; + TemplateId->Template = Template; + TemplateId->LAngleLoc = LAngleLoc; + TemplateId->NumArgs = TemplateArgs.size(); + TemplateArgTy **Args = (TemplateArgTy**)(TemplateId + 1); + for (unsigned Arg = 0, ArgEnd = TemplateArgs.size(); Arg != ArgEnd; ++Arg) + Args[Arg] = TemplateArgs[Arg]; + Tok.setAnnotationValue(TemplateId); + } else { + // This is a type template, e.g., a class template, template + // template parameter, or template alias. We'll be building a + // "typename" annotation token. + TypeTy *Ty + = Actions.ActOnClassTemplateSpecialization(Template,LAngleLoc, + move_arg(TemplateArgs), + RAngleLoc, SS); + Tok.setKind(tok::annot_typename); + Tok.setAnnotationValue(Ty); + } + + // Common fields for the annotation token Tok.setAnnotationEndLoc(RAngleLoc); Tok.setLocation(TemplateNameLoc); if (SS && SS->isNotEmpty()) Tok.setLocation(SS->getBeginLoc()); - TemplateIdAnnotation *TemplateId - = (TemplateIdAnnotation *)malloc(sizeof(TemplateIdAnnotation) + - sizeof(TemplateArgTy*) * TemplateArgs.size()); - TemplateId->TemplateNameLoc = TemplateNameLoc; - TemplateId->Template = Template; - TemplateId->LAngleLoc = LAngleLoc; - TemplateId->NumArgs = TemplateArgs.size(); - TemplateArgTy **Args = (TemplateArgTy**)(TemplateId + 1); - for (unsigned Arg = 0, ArgEnd = TemplateArgs.size(); Arg != ArgEnd; ++Arg) - Args[Arg] = TemplateArgs[Arg]; - Tok.setAnnotationValue(TemplateId); - // In case the tokens were cached, have Preprocessor replace them with the // annotation token. PP.AnnotateCachedTokens(Tok); @@ -412,8 +434,22 @@ void Parser::AnnotateTemplateIdToken(DeclTy *Template, const CXXScopeSpec *SS) { /// type-id /// id-expression Parser::OwningTemplateArgResult Parser::ParseTemplateArgument() { - // FIXME: Implement this! - return TemplateArgError(); + // C++ [temp.arg]p2: + // In a template-argument, an ambiguity between a type-id and an + // expression is resolved to a type-id, regardless of the form of + // the corresponding template-parameter. + // + // Therefore, we initially try to parse a type-id. + if (isTypeIdInParens()) { + TypeTy *TypeArg = ParseTypeName(); + return Actions.ActOnTypeTemplateArgument(TypeArg); + } + + OwningExprResult ExprArg = ParseExpression(); + if (ExprArg.isInvalid()) + return TemplateArgError(); + + return Actions.ActOnExprTemplateArgument(move(ExprArg)); } /// ParseTemplateArgumentList - Parse a C++ template-argument-list diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index f28767a0fd..7b09d2105c 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -20,7 +20,8 @@ using namespace clang; Parser::Parser(Preprocessor &pp, Action &actions) - : PP(pp), Actions(actions), Diags(PP.getDiagnostics()) { + : PP(pp), Actions(actions), Diags(PP.getDiagnostics()), + GreaterThanIsOperator(true) { Tok.setKind(tok::eof); CurScope = 0; NumCachedScopes = 0; @@ -785,10 +786,15 @@ bool Parser::TryAnnotateTypeOrScopeToken() { } // If this is a template-id, annotate the template-id token. - if (NextToken().is(tok::less)) - if (DeclTy *Template = - Actions.isTemplateName(*Tok.getIdentifierInfo(), CurScope, &SS)) - AnnotateTemplateIdToken(Template, &SS); + if (NextToken().is(tok::less)) { + DeclTy *Template; + if (TemplateNameKind TNK + = Actions.isTemplateName(*Tok.getIdentifierInfo(), + CurScope, Template, &SS)) { + AnnotateTemplateIdToken(Template, TNK, &SS); + return true; + } + } // We either have an identifier that is not a type name or we have // just created a template-id that might be a type name. Both diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index f0544eb50e..bba99fd751 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -62,6 +62,7 @@ namespace clang { class TypedefDecl; class TemplateDecl; class TemplateParameterList; + class TemplateArg; class ObjCInterfaceDecl; class ObjCCompatibleAliasDecl; class ObjCProtocolDecl; @@ -258,6 +259,9 @@ public: return OwningExprResult(*this, R.get()); } OwningStmtResult Owned(Stmt* S) { return OwningStmtResult(*this, S); } + OwningTemplateArgResult Owned(TemplateArg *Arg) { + return OwningTemplateArgResult(*this, Arg); + } virtual void ActOnEndOfTranslationUnit(); @@ -1478,8 +1482,9 @@ public: //===--------------------------------------------------------------------===// // C++ Templates [C++ 14] // - virtual DeclTy *isTemplateName(IdentifierInfo &II, Scope *S, - const CXXScopeSpec *SS = 0); + virtual TemplateNameKind isTemplateName(IdentifierInfo &II, Scope *S, + DeclTy *&TemplateDecl, + const CXXScopeSpec *SS = 0); bool DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl); TemplateDecl *AdjustDeclIfTemplate(DeclTy *&Decl); @@ -1506,6 +1511,10 @@ public: DeclTy **Params, unsigned NumParams, SourceLocation RAngleLoc); + virtual OwningTemplateArgResult ActOnTypeTemplateArgument(TypeTy *Type); + + virtual OwningTemplateArgResult ActOnExprTemplateArgument(ExprArg Value); + virtual DeclTy * ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK, SourceLocation KWLoc, const CXXScopeSpec &SS, @@ -1513,6 +1522,13 @@ public: AttributeList *Attr, MultiTemplateParamsArg TemplateParameterLists); + virtual TypeTy * + ActOnClassTemplateSpecialization(DeclTy *Template, + SourceLocation LAngleLoc, + MultiTemplateArgsArg TemplateArgs, + SourceLocation RAngleLoc, + const CXXScopeSpec *SS = 0); + bool TemplateParameterListsAreEqual(TemplateParameterList *New, TemplateParameterList *Old, bool Complain, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 6c5f90afa5..6b6a50130a 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -25,38 +25,43 @@ using namespace clang; /// declaration if II names a template. An optional CXXScope can be /// passed to indicate the C++ scope in which the identifier will be /// found. -Sema::DeclTy *Sema::isTemplateName(IdentifierInfo &II, Scope *S, - const CXXScopeSpec *SS) { - DeclContext *DC = 0; - - if (SS) { - if (SS->isInvalid()) - return 0; - DC = static_cast(SS->getScopeRep()); - } +Sema::TemplateNameKind Sema::isTemplateName(IdentifierInfo &II, Scope *S, + DeclTy *&Template, + const CXXScopeSpec *SS) { NamedDecl *IIDecl = LookupParsedName(S, SS, &II, LookupOrdinaryName); if (IIDecl) { - // FIXME: We need to represent templates via some kind of - // TemplateDecl, because what follows is a hack that only works in - // one specific case. - if (isa(IIDecl)) - return IIDecl; + if (isa(IIDecl)) { + Template = IIDecl; + if (isa(IIDecl)) + return TNK_Function_template; + else if (isa(IIDecl)) + return TNK_Class_template; + else if (isa(IIDecl)) + return TNK_Template_template_parm; + else + assert(false && "Unknown TemplateDecl"); + } + // FIXME: What follows is a gross hack. if (FunctionDecl *FD = dyn_cast(IIDecl)) { - if (FD->getType()->isDependentType()) - return FD; + if (FD->getType()->isDependentType()) { + Template = FD; + return TNK_Function_template; + } } else if (OverloadedFunctionDecl *Ovl = dyn_cast(IIDecl)) { for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(), FEnd = Ovl->function_end(); F != FEnd; ++F) { - if ((*F)->getType()->isDependentType()) - return Ovl; + if ((*F)->getType()->isDependentType()) { + Template = Ovl; + return TNK_Function_template; + } } } } - return 0; + return TNK_Non_template; } /// DiagnoseTemplateParameterShadow - Produce a diagnostic complaining @@ -226,6 +231,15 @@ Sema::ActOnTemplateParameterList(unsigned Depth, (Decl**)Params, NumParams, RAngleLoc); } +Sema::OwningTemplateArgResult Sema::ActOnTypeTemplateArgument(TypeTy *Type) { + return Owned(new (Context) TemplateArg(QualType::getFromOpaquePtr(Type))); +} + +Sema::OwningTemplateArgResult +Sema::ActOnExprTemplateArgument(ExprArg Value) { + return Owned(new (Context) TemplateArg(static_cast(Value.release()))); +} + Sema::DeclTy * Sema::ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK, SourceLocation KWLoc, const CXXScopeSpec &SS, @@ -351,6 +365,42 @@ Sema::ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK, return NewTemplate; } +Action::TypeTy * +Sema::ActOnClassTemplateSpecialization(DeclTy *TemplateD, + SourceLocation LAngleLoc, + MultiTemplateArgsArg TemplateArgsIn, + SourceLocation RAngleLoc, + const CXXScopeSpec *SS) { + TemplateDecl *Template = cast(static_cast(TemplateD)); + + // FIXME: Not happy about this. We should teach the parser to pass + // us opaque pointers + bools for template argument lists. + // FIXME: Also not happy about the fact that we leak these + // TemplateArg structures. Fixing the above will fix this, too. + llvm::SmallVector Args; + llvm::SmallVector ArgIsType; + unsigned NumArgs = TemplateArgsIn.size(); + TemplateArg **TemplateArgs + = reinterpret_cast(TemplateArgsIn.release()); + for (unsigned Arg = 0; Arg < NumArgs; ++Arg) { + if (Expr *ExprArg = TemplateArgs[Arg]->getAsExpr()) { + Args.push_back(reinterpret_cast(ExprArg)); + ArgIsType.push_back(false); + } else { + QualType T = TemplateArgs[Arg]->getAsType(); + Args.push_back(reinterpret_cast(T.getAsOpaquePtr())); + ArgIsType.push_back(true); + } + } + + // Yes, all class template specializations are just silly sugar for + // 'int'. Gotta problem wit dat? + return Context.getClassTemplateSpecializationType(Template, NumArgs, + &Args[0], &ArgIsType[0], + Context.IntTy) + .getAsOpaquePtr(); +} + /// \brief Determine whether the given template parameter lists are /// equivalent. /// diff --git a/test/SemaTemplate/class-template-id.cpp b/test/SemaTemplate/class-template-id.cpp new file mode 100644 index 0000000000..0bdc3e9875 --- /dev/null +++ b/test/SemaTemplate/class-template-id.cpp @@ -0,0 +1,17 @@ +// RUN: clang -fsyntax-only -verify %s +template struct A { }; + +typedef A A_int; + +float *foo(A *ptr, A const *ptr2) { + if (ptr) + return ptr; // expected-error{{incompatible type returning 'A *', expected 'float *'}} + else if (ptr2) + return ptr2; // expected-error{{incompatible type returning 'A const *', expected 'float *'}} + else { + // FIXME: This is completely bogus, but we're using it temporarily + // to test the syntactic sugar for class template specializations. + int *ip = ptr; + return 0; + } +}