From 1734317845d60307d474b5da8a8d33adbaf5e723 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 1 Apr 2009 00:28:59 +0000 Subject: [PATCH] Parsing, semantic analysis, and template instantiation for typename specifiers that terminate in a simple-template-id, e.g., typename MetaFun::template apply Also, implement template instantiation for dependent nested-name-specifiers that involve unresolved identifiers, e.g., typename T::type::type git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@68166 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 3 ++ include/clang/AST/NestedNameSpecifier.h | 41 +++++++++++---------- include/clang/AST/TemplateName.h | 9 ++++- include/clang/AST/Type.h | 36 +++++++++++++++--- include/clang/Basic/DiagnosticParseKinds.td | 6 +++ include/clang/Basic/IdentifierTable.h | 27 ++++++++++++++ include/clang/Parse/Action.h | 16 +++++++- lib/AST/ASTContext.cpp | 33 +++++++++++++++++ lib/AST/NestedNameSpecifier.cpp | 26 ++++++------- lib/AST/TemplateName.cpp | 8 ++-- lib/AST/Type.cpp | 9 ++++- lib/Parse/Parser.cpp | 39 +++++++++++++++----- lib/Sema/Sema.h | 13 +++++++ lib/Sema/SemaTemplate.cpp | 16 ++++++++ lib/Sema/SemaTemplateInstantiate.cpp | 34 ++++++++++++----- test/SemaTemplate/typename-specifier-2.cpp | 30 +++++++++++++++ 16 files changed, 282 insertions(+), 64 deletions(-) create mode 100644 test/SemaTemplate/typename-specifier-2.cpp diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 6dd93fd9f2..503c4639cc 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -302,6 +302,9 @@ public: QualType getTypenameType(NestedNameSpecifier *NNS, const IdentifierInfo *Name, QualType Canon = QualType()); + QualType getTypenameType(NestedNameSpecifier *NNS, + const TemplateSpecializationType *TemplateId, + QualType Canon = QualType()); /// getObjCQualifiedInterfaceType - Return a /// ObjCQualifiedInterfaceType type for the given interface decl and diff --git a/include/clang/AST/NestedNameSpecifier.h b/include/clang/AST/NestedNameSpecifier.h index 864459b8ce..96eebe8be6 100644 --- a/include/clang/AST/NestedNameSpecifier.h +++ b/include/clang/AST/NestedNameSpecifier.h @@ -40,7 +40,11 @@ class Type; class NestedNameSpecifier : public llvm::FoldingSetNode { /// \brief The nested name specifier that precedes this nested name /// specifier. - NestedNameSpecifier *Prefix; + /// + /// The pointer is the nested-name-specifier that precedes this + /// one. The integer stores one of the first four values of type + /// SpecifierKind. + llvm::PointerIntPair Prefix; /// \brief The last component in the nested name specifier, which /// can be an identifier, a declaration, or a type. @@ -48,9 +52,8 @@ class NestedNameSpecifier : public llvm::FoldingSetNode { /// When the pointer is NULL, this specifier represents the global /// specifier '::'. Otherwise, the pointer is one of /// IdentifierInfo*, Namespace*, or Type*, depending on the kind of - /// specifier. The integer stores one ofthe first four values of - /// type SpecifierKind. - llvm::PointerIntPair Specifier; + /// specifier as encoded within the prefix. + void* Specifier; public: /// \brief The kind of specifier that completes this nested name @@ -71,7 +74,7 @@ public: private: /// \brief Builds the global specifier. - NestedNameSpecifier() : Prefix(0), Specifier(0, 0) { } + NestedNameSpecifier() : Prefix(0, 0), Specifier(0) { } /// \brief Copy constructor used internally to clone nested name /// specifiers. @@ -84,7 +87,8 @@ private: /// \brief Either find or insert the given nested name specifier /// mockup in the given context. - static NestedNameSpecifier *FindOrInsert(ASTContext &Context, const NestedNameSpecifier &Mockup); + static NestedNameSpecifier *FindOrInsert(ASTContext &Context, + const NestedNameSpecifier &Mockup); public: /// \brief Builds a specifier combining a prefix and an identifier. @@ -117,20 +121,20 @@ public: /// nested name specifier that represents "foo::bar::", the current /// specifier will contain "bar::" and the prefix will contain /// "foo::". - NestedNameSpecifier *getPrefix() const { return Prefix; } + NestedNameSpecifier *getPrefix() const { return Prefix.getPointer(); } /// \brief Determine what kind of nested name specifier is stored. SpecifierKind getKind() const { - if (Specifier.getPointer() == 0) + if (Specifier == 0) return Global; - return (SpecifierKind)Specifier.getInt(); + return (SpecifierKind)Prefix.getInt(); } /// \brief Retrieve the identifier stored in this nested name /// specifier. IdentifierInfo *getAsIdentifier() const { - if (Specifier.getInt() == Identifier) - return (IdentifierInfo *)Specifier.getPointer(); + if (Prefix.getInt() == Identifier) + return (IdentifierInfo *)Specifier; return 0; } @@ -138,17 +142,17 @@ public: /// \brief Retrieve the namespace stored in this nested name /// specifier. NamespaceDecl *getAsNamespace() const { - if (Specifier.getInt() == Namespace) - return (NamespaceDecl *)Specifier.getPointer(); + if (Prefix.getInt() == Namespace) + return (NamespaceDecl *)Specifier; return 0; } /// \brief Retrieve the type stored in this nested name specifier. Type *getAsType() const { - if (Specifier.getInt() == TypeSpec || - Specifier.getInt() == TypeSpecWithTemplate) - return (Type *)Specifier.getPointer(); + if (Prefix.getInt() == TypeSpec || + Prefix.getInt() == TypeSpecWithTemplate) + return (Type *)Specifier; return 0; } @@ -162,9 +166,8 @@ public: void print(llvm::raw_ostream &OS) const; void Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddPointer(Prefix); - ID.AddPointer(Specifier.getPointer()); - ID.AddInteger(Specifier.getInt()); + ID.AddPointer(Prefix.getOpaqueValue()); + ID.AddPointer(Specifier); } void Destroy(ASTContext &Context); diff --git a/include/clang/AST/TemplateName.h b/include/clang/AST/TemplateName.h index 86c443985d..8a83108f8c 100644 --- a/include/clang/AST/TemplateName.h +++ b/include/clang/AST/TemplateName.h @@ -97,7 +97,14 @@ public: bool isDependent() const; /// \brief Print the template name. - void print(llvm::raw_ostream &OS) const; + /// + /// \param OS the output stream to which the template name will be + /// printed. + /// + /// \param SuppressNNS if true, don't print the + /// nested-name-specifier that precedes the template name (if it has + /// one). + void print(llvm::raw_ostream &OS, bool SuppressNNS = false) const; /// \brief Debugging aid that dumps the template name to standard /// error. diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 4d92f45391..c3195eb668 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -15,12 +15,14 @@ #define LLVM_CLANG_AST_TYPE_H #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/IdentifierTable.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/TemplateName.h" #include "llvm/Support/Casting.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/PointerUnion.h" #include "llvm/Bitcode/SerializationFwd.h" using llvm::isa; @@ -1649,9 +1651,11 @@ class TypenameType : public Type, public llvm::FoldingSetNode { /// \brief The nested name specifier containing the qualifier. NestedNameSpecifier *NNS; + typedef llvm::PointerUnion NameType; + /// \brief The type that this typename specifier refers to. - /// FIXME: Also need to represent the "template simple-template-id" case. - const IdentifierInfo *Name; + NameType Name; TypenameType(NestedNameSpecifier *NNS, const IdentifierInfo *Name, QualType CanonType) @@ -1660,14 +1664,34 @@ class TypenameType : public Type, public llvm::FoldingSetNode { "TypenameType requires a dependent nested-name-specifier"); } + TypenameType(NestedNameSpecifier *NNS, const TemplateSpecializationType *Ty, + QualType CanonType) + : Type(Typename, CanonType, true), NNS(NNS), Name(Ty) { + assert(NNS->isDependent() && + "TypenameType requires a dependent nested-name-specifier"); + } + friend class ASTContext; // ASTContext creates these public: /// \brief Retrieve the qualification on this type. NestedNameSpecifier *getQualifier() const { return NNS; } - /// \brief Retrieve the type named by the typename specifier. - const IdentifierInfo *getName() const { return Name; } + /// \brief Retrieve the type named by the typename specifier as an + /// identifier. + /// + /// This routine will return a non-NULL identifier pointer when the + /// form of the original typename was terminated by an identifier, + /// e.g., "typename T::type". + const IdentifierInfo *getIdentifier() const { + return Name.dyn_cast(); + } + + /// \brief Retrieve the type named by the typename specifier as a + /// type specialization. + const TemplateSpecializationType *getTemplateId() const { + return Name.dyn_cast(); + } virtual void getAsStringInternal(std::string &InnerString) const; @@ -1676,9 +1700,9 @@ public: } static void Profile(llvm::FoldingSetNodeID &ID, NestedNameSpecifier *NNS, - const IdentifierInfo *Name) { + NameType Name) { ID.AddPointer(NNS); - ID.AddPointer(Name); + ID.AddPointer(Name.getOpaqueValue()); } static bool classof(const Type *T) { diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index ad996b7eeb..a040d3532a 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -226,6 +226,8 @@ def err_template_spec_syntax_non_template : Error< "template||refers to a template template parameter}1">; def err_id_after_template_in_nested_name_spec : Error< "expected template name after 'template' keyword in nested name specifier">; +def err_id_after_template_in_typename_spec : Error< + "expected template name after 'template' keyword in typename specifier">; def err_less_after_template_name_in_nested_name_spec : Error< "expected '<' after 'template %0' in nested name specifier">; def err_two_right_angle_brackets_need_space : Error< @@ -236,6 +238,10 @@ def warn_cxx0x_right_shift_in_template_arg : Warning< def err_expected_qualified_after_typename : Error< "expected a qualified name after 'typename'">; +def err_typename_refers_to_non_type_template : Error< + "typename specifier refers to a non-template">; +def err_expected_type_name_after_typename : Error< + "expected an identifier or template-id after '::'">; // Language specific pragmas // - Generic warnings diff --git a/include/clang/Basic/IdentifierTable.h b/include/clang/Basic/IdentifierTable.h index 02296c0e29..cb55d257a1 100644 --- a/include/clang/Basic/IdentifierTable.h +++ b/include/clang/Basic/IdentifierTable.h @@ -21,6 +21,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/Bitcode/SerializationFwd.h" +#include "llvm/Support/PointerLikeTypeTraits.h" #include #include @@ -512,5 +513,31 @@ struct DenseMapInfo { static bool isPod() { return true; } }; +// Provide PointerLikeTypeTraits for IdentifierInfo pointers, which +// are not guaranteed to be 8-byte aligned. +template<> +class PointerLikeTypeTraits { +public: + static inline void *getAsVoidPointer(clang::IdentifierInfo* P) { + return P; + } + static inline clang::IdentifierInfo *getFromVoidPointer(void *P) { + return static_cast(P); + } + enum { NumLowBitsAvailable = 1 }; +}; + +template<> +class PointerLikeTypeTraits { +public: + static inline const void *getAsVoidPointer(const clang::IdentifierInfo* P) { + return P; + } + static inline const clang::IdentifierInfo *getFromVoidPointer(const void *P) { + return static_cast(P); + } + enum { NumLowBitsAvailable = 1 }; +}; + } // end namespace llvm #endif diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 6186126e09..d53e1fb49c 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -1305,7 +1305,7 @@ public: } /// \brief Called when the parser has parsed a C++ typename - /// specifier, e.g., "typename T::type". + /// specifier that ends in an identifier, e.g., "typename T::type". /// /// \param TypenameLoc the location of the 'typename' keyword /// \param SS the nested-name-specifier following the typename (e.g., 'T::'). @@ -1317,6 +1317,20 @@ public: return TypeResult(); } + /// \brief Called when the parser has parsed a C++ typename + /// specifier that ends in a template-id, e.g., + /// "typename MetaFun::template apply". + /// + /// \param TypenameLoc the location of the 'typename' keyword + /// \param SS the nested-name-specifier following the typename (e.g., 'T::'). + /// \param TemplateLoc the location of the 'template' keyword, if any. + /// \param Ty the type that the typename specifier refers to. + virtual TypeResult + ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS, + SourceLocation TemplateLoc, TypeTy *Ty) { + return TypeResult(); + } + //===----------------------- Obj-C Declarations -------------------------===// // ActOnStartClassInterface - this action is called immediately after parsing diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 422988d05d..cede0e5563 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1454,6 +1454,39 @@ QualType ASTContext::getTypenameType(NestedNameSpecifier *NNS, return QualType(T, 0); } +QualType +ASTContext::getTypenameType(NestedNameSpecifier *NNS, + const TemplateSpecializationType *TemplateId, + QualType Canon) { + assert(NNS->isDependent() && "nested-name-specifier must be dependent"); + + if (Canon.isNull()) { + NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS); + QualType CanonType = getCanonicalType(QualType(TemplateId, 0)); + if (CanonNNS != NNS || CanonType != QualType(TemplateId, 0)) { + const TemplateSpecializationType *CanonTemplateId + = CanonType->getAsTemplateSpecializationType(); + assert(CanonTemplateId && + "Canonical type must also be a template specialization type"); + Canon = getTypenameType(CanonNNS, CanonTemplateId); + } + } + + llvm::FoldingSetNodeID ID; + TypenameType::Profile(ID, NNS, TemplateId); + + void *InsertPos = 0; + TypenameType *T + = TypenameTypes.FindNodeOrInsertPos(ID, InsertPos); + if (T) + return QualType(T, 0); + + T = new (*this) TypenameType(NNS, TemplateId, Canon); + Types.push_back(T); + TypenameTypes.InsertNode(T, InsertPos); + return QualType(T, 0); +} + /// CmpProtocolNames - Comparison predicate for sorting protocols /// alphabetically. static bool CmpProtocolNames(const ObjCProtocolDecl *LHS, diff --git a/lib/AST/NestedNameSpecifier.cpp b/lib/AST/NestedNameSpecifier.cpp index 2db8c76343..c94a4da7b6 100644 --- a/lib/AST/NestedNameSpecifier.cpp +++ b/lib/AST/NestedNameSpecifier.cpp @@ -30,7 +30,7 @@ NestedNameSpecifier::FindOrInsert(ASTContext &Context, NestedNameSpecifier *NNS = Context.NestedNameSpecifiers.FindNodeOrInsertPos(ID, InsertPos); if (!NNS) { - NNS = new (Context) NestedNameSpecifier(Mockup); + NNS = new (Context, 4) NestedNameSpecifier(Mockup); Context.NestedNameSpecifiers.InsertNode(NNS, InsertPos); } @@ -44,9 +44,9 @@ NestedNameSpecifier::Create(ASTContext &Context, NestedNameSpecifier *Prefix, assert(Prefix && Prefix->isDependent() && "Prefix must be dependent"); NestedNameSpecifier Mockup; - Mockup.Prefix = Prefix; - Mockup.Specifier.setPointer(II); - Mockup.Specifier.setInt(Identifier); + Mockup.Prefix.setPointer(Prefix); + Mockup.Prefix.setInt(Identifier); + Mockup.Specifier = II; return FindOrInsert(Context, Mockup); } @@ -58,9 +58,9 @@ NestedNameSpecifier::Create(ASTContext &Context, NestedNameSpecifier *Prefix, (Prefix->getAsType() == 0 && Prefix->getAsIdentifier() == 0)) && "Broken nested name specifier"); NestedNameSpecifier Mockup; - Mockup.Prefix = Prefix; - Mockup.Specifier.setPointer(NS); - Mockup.Specifier.setInt(Namespace); + Mockup.Prefix.setPointer(Prefix); + Mockup.Prefix.setInt(Namespace); + Mockup.Specifier = NS; return FindOrInsert(Context, Mockup); } @@ -69,15 +69,15 @@ NestedNameSpecifier::Create(ASTContext &Context, NestedNameSpecifier *Prefix, bool Template, Type *T) { assert(T && "Type cannot be NULL"); NestedNameSpecifier Mockup; - Mockup.Prefix = Prefix; - Mockup.Specifier.setPointer(T); - Mockup.Specifier.setInt(Template? TypeSpecWithTemplate : TypeSpec); + Mockup.Prefix.setPointer(Prefix); + Mockup.Prefix.setInt(Template? TypeSpecWithTemplate : TypeSpec); + Mockup.Specifier = T; return FindOrInsert(Context, Mockup); } NestedNameSpecifier *NestedNameSpecifier::GlobalSpecifier(ASTContext &Context) { if (!Context.GlobalNestedNameSpecifier) - Context.GlobalNestedNameSpecifier = new (Context) NestedNameSpecifier(); + Context.GlobalNestedNameSpecifier = new (Context, 4) NestedNameSpecifier(); return Context.GlobalNestedNameSpecifier; } @@ -105,8 +105,8 @@ bool NestedNameSpecifier::isDependent() const { /// \brief Print this nested name specifier to the given output /// stream. void NestedNameSpecifier::print(llvm::raw_ostream &OS) const { - if (Prefix) - Prefix->print(OS); + if (getPrefix()) + getPrefix()->print(OS); switch (getKind()) { case Identifier: diff --git a/lib/AST/TemplateName.cpp b/lib/AST/TemplateName.cpp index 659796d27b..16b96a01cd 100644 --- a/lib/AST/TemplateName.cpp +++ b/lib/AST/TemplateName.cpp @@ -38,16 +38,18 @@ bool TemplateName::isDependent() const { return true; } -void TemplateName::print(llvm::raw_ostream &OS) const { +void TemplateName::print(llvm::raw_ostream &OS, bool SuppressNNS) const { if (TemplateDecl *Template = Storage.dyn_cast()) OS << Template->getIdentifier()->getName(); else if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName()) { - QTN->getQualifier()->print(OS); + if (!SuppressNNS) + QTN->getQualifier()->print(OS); if (QTN->hasTemplateKeyword()) OS << "template "; OS << QTN->getTemplateDecl()->getIdentifier()->getName(); } else if (DependentTemplateName *DTN = getAsDependentTemplateName()) { - DTN->getQualifier()->print(OS); + if (!SuppressNNS) + DTN->getQualifier()->print(OS); OS << "template "; OS << DTN->getName()->getName(); } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 669eb7ce9c..b9bd0bae04 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1450,7 +1450,14 @@ void TypenameType::getAsStringInternal(std::string &InnerString) const { llvm::raw_string_ostream OS(MyString); OS << "typename "; NNS->print(OS); - OS << Name->getName(); + + if (const IdentifierInfo *Ident = getIdentifier()) + OS << Ident->getName(); + else if (const TemplateSpecializationType *Spec = getTemplateId()) { + Spec->getTemplateName().print(OS, true); + OS << TemplateSpecializationType::PrintTemplateArgumentList( + Spec->getArgs(), Spec->getNumArgs()); + } } if (InnerString.empty()) diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 56e217a329..17eca37ef3 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -828,7 +828,7 @@ bool Parser::TryAnnotateTypeOrScopeToken() { // typename-specifier: // 'typename' '::' [opt] nested-name-specifier identifier // 'typename' '::' [opt] nested-name-specifier template [opt] - // simple-template-id [TODO] + // simple-template-id SourceLocation TypenameLoc = ConsumeToken(); CXXScopeSpec SS; bool HadNestedNameSpecifier = ParseOptionalCXXScopeSpecifier(SS); @@ -842,16 +842,35 @@ bool Parser::TryAnnotateTypeOrScopeToken() { // FIXME: check whether the next token is '<', first! Ty = Actions.ActOnTypenameType(TypenameLoc, SS, *Tok.getIdentifierInfo(), Tok.getLocation()); - // FIXME: better error recovery! - Tok.setKind(tok::annot_typename); - Tok.setAnnotationValue(Ty.get()); - Tok.setAnnotationEndLoc(Tok.getLocation()); - Tok.setLocation(TypenameLoc); - PP.AnnotateCachedTokens(Tok); - return true; - } + } else if (Tok.is(tok::annot_template_id)) { + TemplateIdAnnotation *TemplateId + = static_cast(Tok.getAnnotationValue()); + if (TemplateId->Kind == TNK_Function_template) { + Diag(Tok, diag::err_typename_refers_to_non_type_template) + << Tok.getAnnotationRange(); + return false; + } - return false; + if (AnnotateTemplateIdTokenAsType(0)) + return false; + + assert(Tok.is(tok::annot_typename) && + "AnnotateTemplateIdTokenAsType isn't working properly"); + Ty = Actions.ActOnTypenameType(TypenameLoc, SS, SourceLocation(), + Tok.getAnnotationValue()); + } else { + Diag(Tok, diag::err_expected_type_name_after_typename) + << SS.getRange(); + return false; + } + + // FIXME: better error recovery! + Tok.setKind(tok::annot_typename); + Tok.setAnnotationValue(Ty.get()); + Tok.setAnnotationEndLoc(Tok.getLocation()); + Tok.setLocation(TypenameLoc); + PP.AnnotateCachedTokens(Tok); + return true; } CXXScopeSpec SS; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 3a18f9676b..7f686369a8 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1816,6 +1816,19 @@ public: virtual TypeResult ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS, const IdentifierInfo &II, SourceLocation IdLoc); + + /// \brief Called when the parser has parsed a C++ typename + /// specifier that ends in a template-id, e.g., + /// "typename MetaFun::template apply". + /// + /// \param TypenameLoc the location of the 'typename' keyword + /// \param SS the nested-name-specifier following the typename (e.g., 'T::'). + /// \param TemplateLoc the location of the 'template' keyword, if any. + /// \param Ty the type that the typename specifier refers to. + virtual TypeResult + ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS, + SourceLocation TemplateLoc, TypeTy *Ty); + QualType CheckTypenameType(NestedNameSpecifier *NNS, const IdentifierInfo &II, SourceRange Range); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 591f323347..5db19a59ac 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2105,6 +2105,22 @@ Sema::ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS, return T.getAsOpaquePtr(); } +Sema::TypeResult +Sema::ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS, + SourceLocation TemplateLoc, TypeTy *Ty) { + QualType T = QualType::getFromOpaquePtr(Ty); + NestedNameSpecifier *NNS + = static_cast(SS.getScopeRep()); + const TemplateSpecializationType *TemplateId + = T->getAsTemplateSpecializationType(); + assert(TemplateId && "Expected a template specialization type"); + + if (NNS->isDependent()) + return Context.getTypenameType(NNS, TemplateId).getAsOpaquePtr(); + + return Context.getQualifiedNameType(NNS, T).getAsOpaquePtr(); +} + /// \brief Build the type that describes a C++ typename specifier, /// e.g., "typename T::type". QualType diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 2f12716b72..a041d06bf2 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -497,6 +497,15 @@ InstantiateQualifiedNameType(const QualifiedNameType *T, QualType TemplateTypeInstantiator:: InstantiateTypenameType(const TypenameType *T, unsigned Quals) const { + if (const TemplateSpecializationType *TemplateId = T->getTemplateId()) { + // When the typename type refers to a template-id, the template-id + // is dependent and has enough information to instantiate the + // result of the typename type. Since we don't care about keeping + // the spelling of the typename type in template instantiations, + // we just instantiate the template-id. + return InstantiateTemplateSpecializationType(TemplateId, Quals); + } + NestedNameSpecifier *NNS = SemaRef.InstantiateNestedNameSpecifier(T->getQualifier(), SourceRange(Loc), @@ -504,7 +513,7 @@ InstantiateTypenameType(const TypenameType *T, unsigned Quals) const { if (!NNS) return QualType(); - return SemaRef.CheckTypenameType(NNS, *T->getName(), SourceRange(Loc)); + return SemaRef.CheckTypenameType(NNS, *T->getIdentifier(), SourceRange(Loc)); } QualType @@ -801,10 +810,20 @@ Sema::InstantiateNestedNameSpecifier(NestedNameSpecifier *NNS, } switch (NNS->getKind()) { - case NestedNameSpecifier::Identifier: - // FIXME: Implement this lookup! - assert(false && "Cannot instantiate this nested-name-specifier"); + case NestedNameSpecifier::Identifier: { + assert(Prefix && + "Can't have an identifier nested-name-specifier with no prefix"); + CXXScopeSpec SS; + // FIXME: The source location information is all wrong. + SS.setRange(Range); + SS.setScopeRep(Prefix); + return static_cast( + ActOnCXXNestedNameSpecifier(0, SS, + Range.getEnd(), + Range.getEnd(), + *NNS->getAsIdentifier())); break; + } case NestedNameSpecifier::Namespace: case NestedNameSpecifier::Global: @@ -816,9 +835,6 @@ Sema::InstantiateNestedNameSpecifier(NestedNameSpecifier *NNS, if (!T->isDependentType()) return NNS; - // FIXME: We won't be able to perform the instantiation here when - // the template-name is dependent, e.g., we have something like - // "T::template apply::type". T = InstantiateType(T, TemplateArgs, NumTemplateArgs, Range.getBegin(), DeclarationName()); if (T.isNull()) @@ -826,9 +842,7 @@ Sema::InstantiateNestedNameSpecifier(NestedNameSpecifier *NNS, if (T->isRecordType() || (getLangOptions().CPlusPlus0x && T->isEnumeralType())) { - // Note that T.getTypePtr(), below, strips cv-qualifiers. This is - // perfectly reasonable, since cv-qualified types in - // nested-name-specifiers don't matter. + assert(T.getCVRQualifiers() == 0 && "Can't get cv-qualifiers here"); return NestedNameSpecifier::Create(Context, Prefix, NNS->getKind() == NestedNameSpecifier::TypeSpecWithTemplate, T.getTypePtr()); diff --git a/test/SemaTemplate/typename-specifier-2.cpp b/test/SemaTemplate/typename-specifier-2.cpp new file mode 100644 index 0000000000..3aebb9a524 --- /dev/null +++ b/test/SemaTemplate/typename-specifier-2.cpp @@ -0,0 +1,30 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +template +struct bind_metafun { + typedef typename MetaFun::template apply type; +}; + +struct add_pointer { + template + struct apply { + typedef T* type; + }; +}; + +int i; +// FIXME: if we make the declarator below a pointer (e.g., with *ip), +// the error message isn't so good because we don't get the handy +// 'aka' telling us that we're dealing with an int**. Should we fix +// getDesugaredType to dig through pointers and such? +bind_metafun::type::type ip = &i; +bind_metafun::type::type fp = &i; // expected-error{{incompatible type initializing 'int *', expected 'bind_metafun::type::type' (aka 'float *')}} + + +template +struct extract_type_type { + typedef typename T::type::type t; +}; + +double d; +extract_type_type >::t dp = &d; -- 2.40.0