]> granicus.if.org Git - clang/commitdiff
Parsing, semantic analysis, and template instantiation for typename
authorDouglas Gregor <dgregor@apple.com>
Wed, 1 Apr 2009 00:28:59 +0000 (00:28 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 1 Apr 2009 00:28:59 +0000 (00:28 +0000)
specifiers that terminate in a simple-template-id, e.g.,

  typename MetaFun::template apply<T1, T2>

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

16 files changed:
include/clang/AST/ASTContext.h
include/clang/AST/NestedNameSpecifier.h
include/clang/AST/TemplateName.h
include/clang/AST/Type.h
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/IdentifierTable.h
include/clang/Parse/Action.h
lib/AST/ASTContext.cpp
lib/AST/NestedNameSpecifier.cpp
lib/AST/TemplateName.cpp
lib/AST/Type.cpp
lib/Parse/Parser.cpp
lib/Sema/Sema.h
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateInstantiate.cpp
test/SemaTemplate/typename-specifier-2.cpp [new file with mode: 0644]

index 6dd93fd9f2eee49f5b8019528ffdfbaed356e6e1..503c4639ccd906b8c1f3bcd6896efa2210371267 100644 (file)
@@ -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
index 864459b8cede1ad583b6884e3600e14420541bbe..96eebe8be6ee22c4b6db13b64bf72e9a6bd18678 100644 (file)
@@ -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<NestedNameSpecifier *, 2> 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<void*, 2> 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);
index 86c443985de24c23f033e50885df038a2f979be9..8a83108f8cab201958cbb7ed9ee93f48173b8525 100644 (file)
@@ -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.
index 4d92f45391e07c26205101b0d134392878a439b2..c3195eb66809725531e033b1fffe7044af267a92 100644 (file)
 #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<const IdentifierInfo *, 
+                             const TemplateSpecializationType *> 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<const IdentifierInfo *>(); 
+  }
+
+  /// \brief Retrieve the type named by the typename specifier as a
+  /// type specialization.
+  const TemplateSpecializationType *getTemplateId() const {
+    return Name.dyn_cast<const TemplateSpecializationType *>();
+  }
 
   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) { 
index ad996b7eeb5c69ea3daeca2088de9c14dfed5f67..a040d3532aefb2d022fe91c6cd05912abedb8d9e 100644 (file)
@@ -226,6 +226,8 @@ def err_template_spec_syntax_non_template : Error<
   "template|<unused>|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
index 02296c0e296bffbc36b27396faefe1043f6765dd..cb55d257a159e51ca23442b76e0980c9b63224fc 100644 (file)
@@ -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 <string> 
 #include <cassert> 
 
@@ -512,5 +513,31 @@ struct DenseMapInfo<clang::Selector> {
   static bool isPod() { return true; }
 };
 
+// Provide PointerLikeTypeTraits for IdentifierInfo pointers, which
+// are not guaranteed to be 8-byte aligned.
+template<>
+class PointerLikeTypeTraits<clang::IdentifierInfo*> {
+public:
+  static inline void *getAsVoidPointer(clang::IdentifierInfo* P) {
+    return P; 
+  }
+  static inline clang::IdentifierInfo *getFromVoidPointer(void *P) {
+    return static_cast<clang::IdentifierInfo*>(P);
+  }
+  enum { NumLowBitsAvailable = 1 };
+};
+
+template<>
+class PointerLikeTypeTraits<const clang::IdentifierInfo*> {
+public:
+  static inline const void *getAsVoidPointer(const clang::IdentifierInfo* P) {
+    return P; 
+  }
+  static inline const clang::IdentifierInfo *getFromVoidPointer(const void *P) {
+    return static_cast<const clang::IdentifierInfo*>(P);
+  }
+  enum { NumLowBitsAvailable = 1 };
+};
+
 }  // end namespace llvm
 #endif
index 6186126e0995d28876b8372bff6ff5d7bf1f6c75..d53e1fb49c55606985b334d173fbe11d59da531b 100644 (file)
@@ -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<T1, T2>".
+  ///
+  /// \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
index 422988d05d078f583b36894d6016fb96825ae6db..cede0e556391cfee3d53f4161767e0bf644b47cf 100644 (file)
@@ -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,
index 2db8c763431849364c2efa9c68de20061bb2d8a2..c94a4da7b610c5addbc6bbbb34196d384fe24f41 100644 (file)
@@ -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:
index 659796d27b2e28c6d53cbb0ec706aaf67742da94..16b96a01cd9c53aef1f6e4e276a2856b54a288b0 100644 (file)
@@ -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<TemplateDecl *>())
     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();
   }
index 669eb7ce9c3d698d351436499421d4982b9e8001..b9bd0bae046c80673fdda62b8942fd157ddaadfd 100644 (file)
@@ -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())
index 56e217a32901ad465bc2153b911368d7c29fc74c..17eca37ef3c1239fcd7962c19455f3ee4db36dd1 100644 (file)
@@ -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<TemplateIdAnnotation *>(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;
index 3a18f9676b032ef41cb7786100a36ac8fcc719f1..7f686369a8df4f45dfbc9fb186d038563d6074c1 100644 (file)
@@ -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<T1, T2>".
+  ///
+  /// \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);
index 591f32334714713b959dcb987c7077d1a0c9f9cc..5db19a59ac48d79c869586d6af7913ebca735096 100644 (file)
@@ -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<NestedNameSpecifier *>(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
index 2f12716b722fb8959201dc2d99cb1ff32707d3f3..a041d06bf20ce016c9da9a83aea1858a5be5729f 100644 (file)
@@ -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<NestedNameSpecifier *>(
+                                 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<U>::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 (file)
index 0000000..3aebb9a
--- /dev/null
@@ -0,0 +1,30 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+template<typename MetaFun, typename T>
+struct bind_metafun {
+  typedef typename MetaFun::template apply<T> type;
+};
+
+struct add_pointer {
+  template<typename T>
+  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<add_pointer, int>::type::type ip = &i;
+bind_metafun<add_pointer, float>::type::type fp = &i; // expected-error{{incompatible type initializing 'int *', expected 'bind_metafun<struct add_pointer, float>::type::type' (aka 'float *')}}
+
+
+template<typename T>
+struct extract_type_type {
+  typedef typename T::type::type t;
+};
+
+double d;
+extract_type_type<bind_metafun<add_pointer, double> >::t dp = &d;