]> granicus.if.org Git - clang/commitdiff
Implement parsing of nested-name-specifiers that involve template-ids, e.g.,
authorDouglas Gregor <dgregor@apple.com>
Wed, 25 Feb 2009 19:37:18 +0000 (19:37 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 25 Feb 2009 19:37:18 +0000 (19:37 +0000)
  std::vector<int>::allocator_type

When we parse a template-id that names a type, it will become either a
template-id annotation (which is a parsed representation of a
template-id that has not yet been through semantic analysis) or a
typename annotation (where semantic analysis has resolved the
template-id to an actual type), depending on the context. We only
produce a type in contexts where we know that we only need type
information, e.g., in a type specifier. Otherwise, we create a
template-id annotation that can later be "upgraded" by transforming it
into a typename annotation when the parser needs a type. This occurs,
for example, when we've parsed "std::vector<int>" above and then see
the '::' after it. However, it means that when writing something like
this:

  template<> class Outer::Inner<int> { ... };

We have two tokens to represent Outer::Inner<int>: one token for the
nested name specifier Outer::, and one template-id annotation token
for Inner<int>, which will be passed to semantic analysis to define
the class template specialization.

Most of the churn in the template tests in this patch come from an
improvement in our error recovery from ill-formed template-ids.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@65467 91177308-0d34-0410-b5e6-96231b3b80d8

25 files changed:
docs/InternalsManual.html
include/clang/Basic/DiagnosticParseKinds.def
include/clang/Basic/TemplateKinds.h [new file with mode: 0644]
include/clang/Lex/Preprocessor.h
include/clang/Lex/Token.h
include/clang/Parse/Action.h
include/clang/Parse/Ownership.h
include/clang/Parse/Parser.h
lib/AST/DeclTemplate.cpp
lib/Parse/MinimalAction.cpp
lib/Parse/ParseDecl.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Parse/ParseExprCXX.cpp
lib/Parse/ParseTemplate.cpp
lib/Parse/Parser.cpp
lib/Sema/Sema.h
lib/Sema/SemaCXXScopeSpec.cpp
lib/Sema/SemaTemplate.cpp
test/SemaTemplate/class-template-spec.cpp
test/SemaTemplate/default-arguments.cpp
test/SemaTemplate/nested-name-spec-template.cpp [new file with mode: 0644]
test/SemaTemplate/temp_arg.cpp
test/SemaTemplate/temp_arg_nontype.cpp
test/SemaTemplate/temp_arg_template.cpp
test/SemaTemplate/temp_arg_type.cpp

index 7b6d201053c4296a9d9958a0d38b233d0feaad90..9250fd1dd777f0e50f2caf2a4dc9b833b6c611ed 100644 (file)
@@ -628,12 +628,10 @@ by the Action::ActOnCXXGlobalScopeSpecifier and
 Action::ActOnCXXNestedNameSpecifier callbacks.  In the case of Sema, this is a
 <tt>DeclContext*</tt>.</li>
 
-<li><b>tok::annot_template_id</b>: This annotation token represents a C++
-template-id such as "foo&lt;int, 4&gt;", which may refer to a function or type
-depending on whether foo is a function template or class template.  The
-AnnotationValue pointer is a pointer to a malloc'd TemplateIdAnnotation object.
-FIXME: I don't think the parsing logic is right for this.  Shouldn't type
-templates be turned into annot_typename??</li>
+<li><b>tok::annot_template_id</b>: This annotation token represents a
+C++ template-id such as "foo&lt;int, 4&gt;", where "foo" is the name
+of a template. The AnnotationValue pointer is a pointer to a malloc'd
+TemplateIdAnnotation object. Depending on the context, a parsed template-id that names a type might become a typename annotation token (if all we care about is the named type, e.g., because it occurs in a type specifier) or might remain a template-id token (if we want to retain more source location information or produce a new type, e.g., in a declaration of a class template specialization). template-id annotation tokens that refer to a type can be "upgraded" to typename annotation tokens by the parser.</li>
 
 </ol>
 
index 6d87c70a1764cc075b50e810609e47576de7898b..e8485c69bfd1792d1be1c411ea4ba19ae21871a0 100644 (file)
@@ -274,6 +274,10 @@ DIAG(err_expected_class_before, ERROR,
       "expected 'class' before '%0'")
 DIAG(err_template_spec_syntax_non_template, ERROR,
      "identifier followed by '<' indicates a class template specialization but %0 %select{does not refer to a template|refers to a function template|<unused>|refers to a template template parameter}1")
+DIAG(err_id_after_template_in_nested_name_spec, ERROR,
+     "expected template name after 'template' keyword in nested name specifier")
+DIAG(err_less_after_template_name_in_nested_name_spec, ERROR,
+     "expected '<' after 'template %0' in nested name specifier")
 
 // Language specific pragmas
 
diff --git a/include/clang/Basic/TemplateKinds.h b/include/clang/Basic/TemplateKinds.h
new file mode 100644 (file)
index 0000000..dbaf5bd
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- TemplateKinds.h - Enum values for C++ Template Kinds ---*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the TemplateNameKind enum.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_TEMPLATEKINDS_H
+#define LLVM_CLANG_TEMPLATEKINDS_H
+
+namespace clang {
+
+/// \brief Specifies the kind of template name that an identifier refers to.
+enum TemplateNameKind {
+  /// The name does not refer to a template.
+  TNK_Non_template = 0,
+  /// 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,
+  /// The name is dependent and is known to be a template name based
+  /// on syntax, e.g., "Alloc::template rebind<Other>".
+  TNK_Dependent_template_name
+};
+
+}
+#endif
+
+
index a0ed1767df34f12376a9477e031ddd7a09326e95..9d99142480bfc19ddca53be5473a0c0775e8947e 100644 (file)
@@ -431,7 +431,21 @@ public:
     if (CachedLexPos != 0 && isBacktrackEnabled())
       AnnotatePreviousCachedTokens(Tok);
   }
-  
+
+  /// \brief Replace the last token with an annotation token. 
+  ///
+  /// Like AnnotateCachedTokens(), this routine replaces an
+  /// already-parsed (and resolved) token with an annotation
+  /// token. However, this routine only replaces the last token with
+  /// the annotation token; it does not affect any other cached
+  /// tokens. This function has no effect if backtracking is not
+  /// enabled.
+  void ReplaceLastTokenWithAnnotation(const Token &Tok) {
+    assert(Tok.isAnnotation() && "Expected annotation token");
+    if (CachedLexPos != 0 && isBacktrackEnabled())
+      CachedTokens[CachedLexPos-1] = Tok;
+  }
+
   /// Diag - Forwarding function for diagnostics.  This emits a diagnostic at
   /// the specified Token's location, translating the token's start
   /// position in the current buffer into a SourcePosition object for rendering.
index 73e087ecb6d949cb5c2108d5c996a56cfcd2181e..5bbb63add9416688fcee400f56093ad201890cf1 100644 (file)
@@ -14,6 +14,7 @@
 #ifndef LLVM_CLANG_TOKEN_H
 #define LLVM_CLANG_TOKEN_H
 
+#include "clang/Basic/TemplateKinds.h"
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Basic/SourceLocation.h"
 
@@ -247,24 +248,62 @@ struct PPConditionalInfo {
 
 /// TemplateIdAnnotation - Information about a template-id annotation
 /// token, which contains the template declaration, template
-/// arguments, and the source locations for important tokens.
+/// arguments, whether those template arguments were types or
+/// expressions, and the source locations for important tokens. All of
+/// the information about template arguments is allocated directly
+/// after this structure.
 struct TemplateIdAnnotation {
   /// TemplateNameLoc - The location of the template name within the
   /// source.
   SourceLocation TemplateNameLoc;
 
-  /// Template - The declaration of the template corresponding to the
+  /// FIXME: Temporarily stores the name of a specialization
+  IdentifierInfo *Name;
+
+  /// The declaration of the template corresponding to the
   /// template-name. This is an Action::DeclTy*.
   void *Template; 
 
-  /// LAngleLoc - The location of the '<' before the template argument
+  /// The kind of template that Template refers to.
+  TemplateNameKind Kind;
+
+  /// The location of the '<' before the template argument
   /// list. 
   SourceLocation LAngleLoc;
 
-  /// NumArgs - The number of template arguments. The arguments
-  /// themselves are Action::TemplateArgTy pointers allocated directly
-  /// following the TemplateIdAnnotation structure.
+  /// The location of the '>' after the template argument
+  /// list. 
+  SourceLocation RAngleLoc;
+
+  /// NumArgs - The number of template arguments.
   unsigned NumArgs; 
+
+  /// \brief Retrieves a pointer to the template arguments
+  void **getTemplateArgs() { return (void **)(this + 1); }
+
+  /// \brief Retrieves a pointer to the array of template argument
+  /// locations.
+  SourceLocation *getTemplateArgLocations() { 
+    return (SourceLocation *)(getTemplateArgs() + NumArgs);
+  }
+
+  /// \brief Retrieves a pointer to the array of flags that states
+  /// whether the template arguments are types.
+  bool *getTemplateArgIsType() { 
+    return (bool *)(getTemplateArgLocations() + NumArgs);
+  }
+
+  static TemplateIdAnnotation* Allocate(unsigned NumArgs) {
+    TemplateIdAnnotation *TemplateId 
+      = (TemplateIdAnnotation *)malloc(sizeof(TemplateIdAnnotation) + 
+                                       sizeof(void*) * NumArgs + 
+                                       sizeof(SourceLocation) * NumArgs +
+                                       sizeof(bool) * NumArgs);
+    TemplateId->NumArgs = NumArgs;
+    return TemplateId;
+  }
+
+  void Destroy() { free(this); }
 };
 
 }  // end namespace clang
index 5103c16ee3475d9f8ace7b57875f9571ce018913..ebcd79caaee8bcbe71eaa0b95bd7153d6014b1ab 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TemplateKinds.h"
 #include "clang/Basic/TypeTraits.h"
 #include "clang/Parse/AccessSpecifier.h"
 #include "clang/Parse/Ownership.h"
@@ -134,20 +135,6 @@ public:
   virtual bool isCurrentClassName(const 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 = 0,
-    /// 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
@@ -178,6 +165,22 @@ public:
     return 0;
   }
 
+  /// ActOnCXXNestedNameSpecifier - Called during parsing of a
+  /// nested-name-specifier that involves a template-id, e.g.,
+  /// "foo::bar<int, float>::", and now we need to build a scope
+  /// specifier. \p SS is empty or the previously parsed nested-name
+  /// part ("foo::"), \p Type is the already-parsed class template
+  /// specialization (or other template-id that names a type), \p
+  /// TypeRange is the source range where the type is located, and \p
+  /// CCLoc is the location of the trailing '::'.
+  virtual CXXScopeTy *ActOnCXXNestedNameSpecifier(Scope *S,
+                                                  const CXXScopeSpec &SS,
+                                                  TypeTy *Type,
+                                                  SourceRange TypeRange,
+                                                  SourceLocation CCLoc) {
+    return 0; 
+  }
+
   /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
   /// scope or nested-name-specifier) is parsed, part of a declarator-id.
   /// After this method is called, according to [C++ 3.4.3p3], names should be
index 684203d6d1a0f00235e76f15e25a3a1b87cdae21..9665576a0504d5c32b5100b40a98e0441e7980d7 100644 (file)
@@ -538,7 +538,7 @@ namespace clang
     void **Args;
     bool *ArgIsType;
     mutable unsigned Count;
-
+    
 #if !defined(DISABLE_SMART_POINTERS)
     void destroy() {
       if (!Count)
index 2432f445d9c8a7860a515afb5d8b6b26290ee958..9ef23d48657ae31b90b4705ba2200201730197ae 100644 (file)
@@ -993,7 +993,6 @@ private:
   //===--------------------------------------------------------------------===//
   // C++ 14: Templates [temp]
   typedef llvm::SmallVector<DeclTy *, 4> TemplateParameterList;
-  typedef Action::TemplateNameKind TemplateNameKind;
 
   // C++ 14.1: Template Parameters [temp.param]
   DeclTy *ParseTemplateDeclarationOrSpecialization(unsigned Context);
@@ -1023,7 +1022,10 @@ private:
                                         SourceLocation &RAngleLoc);
 
   void AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK,
-                               const CXXScopeSpec *SS = 0);
+                               const CXXScopeSpec *SS,
+                               SourceLocation TemplateKWLoc = SourceLocation(),
+                               bool AllowTypeAnnotation = true);
+  bool AnnotateTemplateIdTokenAsType(const CXXScopeSpec *SS = 0);
   bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
                                  TemplateArgIsTypeList &TemplateArgIsType,
                                  TemplateArgLocationList &TemplateArgLocations);
index e10e270c7d4e17df5e0ca186190679dfbcd56383..3ed4435bb379959e1db608c193c8dcfdc5fadf50 100644 (file)
@@ -162,7 +162,7 @@ ClassTemplateSpecializationDecl(DeclContext *DC, SourceLocation L,
     NumTemplateArgs(NumTemplateArgs), SpecializationKind(TSK_Undeclared) {
   TemplateArgument *Arg = reinterpret_cast<TemplateArgument *>(this + 1);
   for (unsigned ArgIdx = 0; ArgIdx < NumTemplateArgs; ++ArgIdx, ++Arg)
-    *Arg = TemplateArgs[ArgIdx];
+    new (Arg) TemplateArgument(TemplateArgs[ArgIdx]);
 }
                   
 ClassTemplateSpecializationDecl *
index ce15cf9ddaef7a66c8d8e12926f3038df4a5fa08..9cd5b979ca66a6fe422993e8d9a04f53373ba939 100644 (file)
@@ -95,7 +95,7 @@ bool MinimalAction::isCurrentClassName(const IdentifierInfo &, Scope *,
   return false;
 }
 
-Action::TemplateNameKind 
+TemplateNameKind 
 MinimalAction::isTemplateName(IdentifierInfo &II, Scope *S,
                               DeclTy *&TemplateDecl,
                               const CXXScopeSpec *SS) {
index 6e68b20fa5e34a947644fb7de7de60161b0efaa0..bdd352f341920b7fdf226ce1fd3512170e1880ce 100644 (file)
@@ -558,21 +558,8 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
       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
@@ -610,6 +597,25 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
       // If a type specifier follows, it will be diagnosed elsewhere.
       continue;
     }
+
+      // type-name
+    case tok::annot_template_id: {
+      TemplateIdAnnotation *TemplateId 
+        = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
+      if (TemplateId->Kind != TNK_Class_template) {
+        // This template-id does not refer to a type name, so we're
+        // done with the type-specifiers.
+        goto DoneWithDeclSpec;
+      }
+
+      // Turn the template-id annotation token into a type annotation
+      // token, then try again to parse it as a type-specifier.
+      if (AnnotateTemplateIdTokenAsType())
+        DS.SetTypeSpecError();
+
+      continue;
+    }
+
     // GNU attributes support.
     case tok::kw___attribute:
       DS.AddAttributes(ParseAttributes());
@@ -1749,7 +1755,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
 ///         operator-function-id
 ///         conversion-function-id  [TODO]
 ///          '~' class-name         
-///         template-id             [TODO]
+///         template-id
 ///
 void Parser::ParseDirectDeclarator(Declarator &D) {
   DeclaratorScopeObj DeclScopeObj(*this, D.getCXXScopeSpec());
@@ -1768,21 +1774,9 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
       if (Tok.is(tok::identifier)) {
         assert(Tok.getIdentifierInfo() && "Not an identifier?");
 
-        // 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) &&
-            (TNK = Actions.isTemplateName(*Tok.getIdentifierInfo(), 
-                                          CurScope, Template))) {
-          IdentifierInfo *II = Tok.getIdentifierInfo();
-          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());
-        }
         // If this identifier is the name of the current class, it's a
         // constructor name. 
-        else if (Actions.isCurrentClassName(*Tok.getIdentifierInfo(),CurScope)){
+        if (Actions.isCurrentClassName(*Tok.getIdentifierInfo(),CurScope)){
           D.setConstructor(Actions.getTypeName(*Tok.getIdentifierInfo(),
                                                Tok.getLocation(), CurScope),
                            Tok.getLocation());
@@ -1791,6 +1785,21 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
           D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
         ConsumeToken();
         goto PastIdentifier;
+      } else if (Tok.is(tok::annot_template_id)) {
+        TemplateIdAnnotation *TemplateId 
+          = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
+
+        // FIXME: Could this template-id name a constructor?
+
+        // FIXME: This is an egregious hack, where we silently ignore
+        // the specialization (which should be a function template
+        // specialization name) and use the name instead. This hack
+        // will go away when we have support for function
+        // specializations.
+        D.SetIdentifier(TemplateId->Name, Tok.getLocation());
+        TemplateId->Destroy();
+        ConsumeToken();
+        goto PastIdentifier;
       } else if (Tok.is(tok::kw_operator)) {
         SourceLocation OperatorLoc = Tok.getLocation();
         SourceLocation EndLoc;
index e36bc40bac3b0edec02de10ac323f6f5d0507054..3ef3f93fdfd1a8d4f1fd608d95fe2308a2438d0b 100644 (file)
@@ -309,75 +309,42 @@ void Parser::ParseClassSpecifier(DeclSpec &DS,
   
   // Parse the (optional) nested-name-specifier.
   CXXScopeSpec SS;
-  if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS)) {
-    // FIXME: can we get a class template specialization or
-    // template-id token here?
-    if (Tok.isNot(tok::identifier))
+  if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS))
+    if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id))
       Diag(Tok, diag::err_expected_ident);
-  }
-
-
-  // These variables encode the simple-template-id that we might end
-  // up parsing below. We don't translate this into a type
-  // automatically because (1) we want to create a separate
-  // declaration for each specialization, and (2) we want to retain
-  // more information about source locations that types provide.
-  DeclTy *Template = 0;
-  SourceLocation LAngleLoc, RAngleLoc;
-  TemplateArgList TemplateArgs;
-  TemplateArgIsTypeList TemplateArgIsType;
-  TemplateArgLocationList TemplateArgLocations;
-  ASTTemplateArgsPtr TemplateArgsPtr(Actions, 0, 0, 0);
-  
 
   // Parse the (optional) class name or simple-template-id.
   IdentifierInfo *Name = 0;
   SourceLocation NameLoc;
+  TemplateIdAnnotation *TemplateId = 0;
   if (Tok.is(tok::identifier)) {
     Name = Tok.getIdentifierInfo();
     NameLoc = ConsumeToken();
+  } else if (Tok.is(tok::annot_template_id)) {
+    TemplateId = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
+    NameLoc = ConsumeToken();
 
-    if (Tok.is(tok::less)) {
-      // This is a simple-template-id.
-      Action::TemplateNameKind TNK 
-        = Actions.isTemplateName(*Name, CurScope, Template, &SS);
-
-      bool Invalid = false;
-
-      // Parse the enclosed template argument list.
-      if (TNK != Action::TNK_Non_template)
-        Invalid = ParseTemplateIdAfterTemplateName(Template, NameLoc,
-                                                   &SS, true, LAngleLoc, 
-                                                   TemplateArgs,
-                                                   TemplateArgIsType,
-                                                   TemplateArgLocations,
-                                                   RAngleLoc);
+    if (TemplateId->Kind != TNK_Class_template) {
+      // The template-name in the simple-template-id refers to
+      // something other than a class template. Give an appropriate
+      // error message and skip to the ';'.
+      SourceRange Range(NameLoc);
+      if (SS.isNotEmpty())
+        Range.setBegin(SS.getBeginLoc());
+
+      Diag(TemplateId->LAngleLoc, diag::err_template_spec_syntax_non_template)
+        << Name << static_cast<int>(TemplateId->Kind) << Range;
       
-      TemplateArgsPtr.reset(&TemplateArgs[0], &TemplateArgIsType[0],
-                            TemplateArgs.size());
-
-      if (TNK != Action::TNK_Class_template) {
-        // The template-name in the simple-template-id refers to
-        // something other than a class template. Give an appropriate
-        // error message and skip to the ';'.
-        SourceRange Range(NameLoc);
-        if (SS.isNotEmpty())
-          Range.setBegin(SS.getBeginLoc());
-        else if (!Invalid)
-          
-        Diag(LAngleLoc, diag::err_template_spec_syntax_non_template)
-          << Name << static_cast<int>(TNK) << Range;
-
-        DS.SetTypeSpecError();
-        SkipUntil(tok::semi, false, true);
-        return;
-      }
+      DS.SetTypeSpecError();
+      SkipUntil(tok::semi, false, true);
+      TemplateId->Destroy();
+      return;
     }
   }
 
   // There are three options here.  If we have 'struct foo;', then
   // this is a forward declaration.  If we have 'struct foo {...' or
-  // 'struct fo :...' then this is a definition. Otherwise we have
+  // 'struct foo :...' then this is a definition. Otherwise we have
   // something like 'struct foo xyz', a reference.
   Action::TagKind TK;
   if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon)))
@@ -387,35 +354,43 @@ void Parser::ParseClassSpecifier(DeclSpec &DS,
   else
     TK = Action::TK_Reference;
 
-  if (!Name && TK != Action::TK_Definition) {
+  if (!Name && !TemplateId && TK != Action::TK_Definition) {
     // We have a declaration or reference to an anonymous class.
     Diag(StartLoc, diag::err_anon_type_definition)
       << DeclSpec::getSpecifierName(TagType);
 
     // Skip the rest of this declarator, up until the comma or semicolon.
     SkipUntil(tok::comma, true);
+
+    if (TemplateId)
+      TemplateId->Destroy();
     return;
   }
 
   // Create the tag portion of the class or class template.
   DeclTy *TagOrTempDecl;
-  if (Template && TK != Action::TK_Reference)
+  if (TemplateId && TK != Action::TK_Reference) {
     // Explicit specialization or class template partial
     // specialization. Let semantic analysis decide.
-
-    // FIXME: we want a source range covering the simple-template-id.
+    ASTTemplateArgsPtr TemplateArgsPtr(Actions, 
+                                       TemplateId->getTemplateArgs(),
+                                       TemplateId->getTemplateArgIsType(),
+                                       TemplateId->NumArgs);
     TagOrTempDecl 
       = Actions.ActOnClassTemplateSpecialization(CurScope, TagType, TK,
-                                                 StartLoc, SS, /*Range*/
-                                                 Template, NameLoc, 
-                                                 LAngleLoc, TemplateArgsPtr,
-                                                 &TemplateArgLocations[0],
-                                                 RAngleLoc, Attr,
+                       StartLoc, SS,
+                       TemplateId->Template, 
+                       TemplateId->TemplateNameLoc, 
+                       TemplateId->LAngleLoc, 
+                       TemplateArgsPtr,
+                       TemplateId->getTemplateArgLocations(),
+                       TemplateId->RAngleLoc, 
+                       Attr,
                        Action::MultiTemplateParamsArg(Actions, 
                                     TemplateParams? &(*TemplateParams)[0] : 0,
                                  TemplateParams? TemplateParams->size() : 0));
-
-  else if (TemplateParams && TK != Action::TK_Reference)
+    TemplateId->Destroy();
+  else if (TemplateParams && TK != Action::TK_Reference)
     TagOrTempDecl = Actions.ActOnClassTemplate(CurScope, TagType, TK, StartLoc,
                                                SS, Name, NameLoc, Attr,
                        Action::MultiTemplateParamsArg(Actions, 
index fb60bde4f1ea49cd3ea3211dabec03bb17592ed9..50e6657ad89744d900d02ff28856aa63d5c34660 100644 (file)
@@ -42,6 +42,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS) {
     return true;
   }
 
+  bool HasScopeSpecifier = false;
+
   if (Tok.is(tok::coloncolon)) {
     // ::new and ::delete aren't nested-name-specifiers.
     tok::TokenKind NextKind = NextToken().getKind();
@@ -53,32 +55,132 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS) {
     SS.setBeginLoc(CCLoc);
     SS.setScopeRep(Actions.ActOnCXXGlobalScopeSpecifier(CurScope, CCLoc));
     SS.setEndLoc(CCLoc);
-  } else if (Tok.is(tok::identifier) && NextToken().is(tok::coloncolon)) {
-    SS.setBeginLoc(Tok.getLocation());
-  } else {
-    // Not a CXXScopeSpecifier.
-    return false;
+    HasScopeSpecifier = true;
   }
 
-  // nested-name-specifier:
-  //   type-name '::'
-  //   namespace-name '::'
-  //   nested-name-specifier identifier '::'
-  //   nested-name-specifier 'template'[opt] simple-template-id '::' [TODO]
-  while (Tok.is(tok::identifier) && NextToken().is(tok::coloncolon)) {
-    IdentifierInfo *II = Tok.getIdentifierInfo();
-    SourceLocation IdLoc = ConsumeToken();
-    assert(Tok.is(tok::coloncolon) && "NextToken() not working properly!");
-    SourceLocation CCLoc = ConsumeToken();
-    if (SS.isInvalid())
+  while (true) {
+    // nested-name-specifier:
+    //   type-name '::'
+    //   namespace-name '::'
+    //   nested-name-specifier identifier '::'
+    if (Tok.is(tok::identifier) && NextToken().is(tok::coloncolon)) {
+      // We have an identifier followed by a '::'. Lookup this name
+      // as the name in a nested-name-specifier.
+      IdentifierInfo *II = Tok.getIdentifierInfo();
+      SourceLocation IdLoc = ConsumeToken();
+      assert(Tok.is(tok::coloncolon) && "NextToken() not working properly!");
+      SourceLocation CCLoc = ConsumeToken();
+      
+      if (!HasScopeSpecifier) {
+        SS.setBeginLoc(IdLoc);
+        HasScopeSpecifier = true;
+      }
+      
+      if (SS.isInvalid())
+        continue;
+      
+      SS.setScopeRep(
+        Actions.ActOnCXXNestedNameSpecifier(CurScope, SS, IdLoc, CCLoc, *II));
+      SS.setEndLoc(CCLoc);
       continue;
+    }
 
-    SS.setScopeRep(
-         Actions.ActOnCXXNestedNameSpecifier(CurScope, SS, IdLoc, CCLoc, *II));
-    SS.setEndLoc(CCLoc);
-  }
+    // nested-name-specifier:
+    //   type-name '::'
+    //   nested-name-specifier 'template'[opt] simple-template-id '::'
+    if ((Tok.is(tok::identifier) && NextToken().is(tok::less)) ||
+        Tok.is(tok::kw_template)) {
+      // Parse the optional 'template' keyword, then make sure we have
+      // 'identifier <' after it.
+      SourceLocation TemplateKWLoc;
+      if (Tok.is(tok::kw_template)) {
+        TemplateKWLoc = ConsumeToken();
+        
+        if (Tok.isNot(tok::identifier)) {
+          Diag(Tok.getLocation(), 
+               diag::err_id_after_template_in_nested_name_spec)
+            << SourceRange(TemplateKWLoc);
+          break;
+        }
+
+        if (NextToken().isNot(tok::less)) {
+          Diag(NextToken().getLocation(),
+               diag::err_less_after_template_name_in_nested_name_spec)
+            << Tok.getIdentifierInfo()->getName()
+            << SourceRange(TemplateKWLoc, Tok.getLocation());
+          break;
+        }
+      }
+      else {
+        // FIXME: If the nested-name-specifier thus far is dependent,
+        // we need to break out of here, because this '<' is taken as
+        // an operator and not as part of a simple-template-id.
+      }
+
+      DeclTy *Template = 0;
+      TemplateNameKind TNK = TNK_Non_template;
+      // FIXME: If the nested-name-specifier thus far is dependent,
+      // set TNK = TNK_Dependent_template_name and skip the
+      // "isTemplateName" check.
+      TNK = Actions.isTemplateName(*Tok.getIdentifierInfo(),
+                                   CurScope, Template, &SS);
+      if (TNK) {
+        // We have found a template name, so annotate this this token
+        // with a template-id annotation. We do not permit the
+        // template-id to be translated into a type annotation,
+        // because some clients (e.g., the parsing of class template
+        // specializations) still want to see the original template-id
+        // token.
+        AnnotateTemplateIdToken(Template, TNK, &SS, TemplateKWLoc, false);
+        continue;
+      }
+    }
+
+    if (Tok.is(tok::annot_template_id) && NextToken().is(tok::coloncolon)) {
+      // We have 
+      //
+      //   simple-template-id '::'
+      //
+      // So we need to check whether the simple-template-id is of the
+      // right kind (it should name a type), and then convert it into
+      // a type within the nested-name-specifier.
+      TemplateIdAnnotation *TemplateId 
+        = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
+
+      if (TemplateId->Kind == TNK_Class_template) {
+        if (AnnotateTemplateIdTokenAsType(&SS))
+          SS.setScopeRep(0);
+
+        assert(Tok.is(tok::annot_typename) && 
+               "AnnotateTemplateIdTokenAsType isn't working");
+
+        Token TypeToken = Tok;
+        ConsumeToken();
+        assert(Tok.is(tok::coloncolon) && "NextToken() not working properly!");
+        SourceLocation CCLoc = ConsumeToken();
+        
+        if (!HasScopeSpecifier) {
+          SS.setBeginLoc(TypeToken.getLocation());
+          HasScopeSpecifier = true;
+        }
 
-  return true;
+        SS.setScopeRep(
+          Actions.ActOnCXXNestedNameSpecifier(CurScope, SS, 
+                                              TypeToken.getAnnotationValue(),
+                                              TypeToken.getAnnotationRange(),
+                                              CCLoc));
+        SS.setEndLoc(CCLoc);
+        continue;
+      } else
+        assert(false && "FIXME: Only class template names supported here");
+    }
+
+    // We don't have any tokens that form the beginning of a
+    // nested-name-specifier, so we're done.
+    break;
+  }
+    
+  return HasScopeSpecifier;
 }
 
 /// ParseCXXIdExpression - Handle id-expression.
index 747a4de1523cc64cdb8b81ba508a4972b2a00295..64fc8fdf47cae19b9238c44e5a550aa86f055466 100644 (file)
@@ -444,16 +444,49 @@ Parser::ParseTemplateIdAfterTemplateName(DeclTy *Template,
   return false;
 }
                                               
-/// 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.
+/// \brief Replace the tokens that form a simple-template-id with an
+/// annotation token containing the complete template-id.
+///
+/// The first token in the stream must be the name of a template that
+/// is followed by a '<'. This routine will parse the complete
+/// simple-template-id and replace the tokens with a single annotation
+/// token with one of two different kinds: if the template-id names a
+/// type (and \p AllowTypeAnnotation is true), the annotation token is
+/// a type annotation that includes the optional nested-name-specifier
+/// (\p SS). Otherwise, the annotation token is a template-id
+/// annotation that does not include the optional
+/// nested-name-specifier.
+///
+/// \param Template  the declaration of the template named by the first
+/// token (an identifier), as returned from \c Action::isTemplateName().
+///
+/// \param TemplateNameKind the kind of template that \p Template
+/// refers to, as returned from \c Action::isTemplateName().
+///
+/// \param SS if non-NULL, the nested-name-specifier that precedes
+/// this template name.
+///
+/// \param TemplateKWLoc if valid, specifies that this template-id
+/// annotation was preceded by the 'template' keyword and gives the
+/// location of that keyword. If invalid (the default), then this
+/// template-id was not preceded by a 'template' keyword.
+///
+/// \param AllowTypeAnnotation if true (the default), then a
+/// simple-template-id that refers to a class template, template
+/// template parameter, or other template that produces a type will be
+/// replaced with a type annotation token. Otherwise, the
+/// simple-template-id is always replaced with a template-id
+/// annotation token.
 void Parser::AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK,
-                                     const CXXScopeSpec *SS) {
+                                     const CXXScopeSpec *SS, 
+                                     SourceLocation TemplateKWLoc,
+                                     bool AllowTypeAnnotation) {
   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");
 
   // Consume the template-name.
+  IdentifierInfo *Name = Tok.getIdentifierInfo();
   SourceLocation TemplateNameLoc = ConsumeToken();
 
   // Parse the enclosed template argument list.
@@ -476,7 +509,7 @@ void Parser::AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK,
     return; 
 
   // Build the annotation token.
-  if (TNK == Action::TNK_Class_template) {
+  if (TNK == TNK_Class_template && AllowTypeAnnotation) {
     Action::TypeResult Type 
       = Actions.ActOnClassTemplateId(Template, TemplateNameLoc,
                                      LAngleLoc, TemplateArgsPtr,
@@ -487,34 +520,96 @@ void Parser::AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK,
 
     Tok.setKind(tok::annot_typename);
     Tok.setAnnotationValue(Type.get());
+    if (SS && SS->isNotEmpty())
+      Tok.setLocation(SS->getBeginLoc());
+    else if (TemplateKWLoc.isValid())
+      Tok.setLocation(TemplateKWLoc);
+    else 
+      Tok.setLocation(TemplateNameLoc);
   } else {
     // This is a function template. We'll be building a template-id
     // annotation token.
-    Tok.setKind(tok::annot_template_id);    
+    Tok.setKind(tok::annot_template_id);
     TemplateIdAnnotation *TemplateId 
-      = (TemplateIdAnnotation *)malloc(sizeof(TemplateIdAnnotation) + 
-                                       sizeof(void*) * TemplateArgs.size());
+      = TemplateIdAnnotation::Allocate(TemplateArgs.size());
     TemplateId->TemplateNameLoc = TemplateNameLoc;
+    TemplateId->Name = Name;
     TemplateId->Template = Template;
+    TemplateId->Kind = TNK;
     TemplateId->LAngleLoc = LAngleLoc;
-    TemplateId->NumArgs = TemplateArgs.size();
-    void **Args = (void**)(TemplateId + 1);
-    for (unsigned Arg = 0, ArgEnd = TemplateArgs.size(); Arg != ArgEnd; ++Arg)
+    TemplateId->RAngleLoc = RAngleLoc;
+    void **Args = TemplateId->getTemplateArgs();
+    bool *ArgIsType = TemplateId->getTemplateArgIsType();
+    SourceLocation *ArgLocs = TemplateId->getTemplateArgLocations();
+    for (unsigned Arg = 0, ArgEnd = TemplateArgs.size(); Arg != ArgEnd; ++Arg) {
       Args[Arg] = TemplateArgs[Arg];
+      ArgIsType[Arg] = TemplateArgIsType[Arg];
+      ArgLocs[Arg] = TemplateArgLocations[Arg];
+    }
     Tok.setAnnotationValue(TemplateId);
+    if (TemplateKWLoc.isValid())
+      Tok.setLocation(TemplateKWLoc);
+    else
+      Tok.setLocation(TemplateNameLoc);
+
+    TemplateArgsPtr.release();
   }
 
   // Common fields for the annotation token
   Tok.setAnnotationEndLoc(RAngleLoc);
-  Tok.setLocation(TemplateNameLoc);
-  if (SS && SS->isNotEmpty())
-    Tok.setLocation(SS->getBeginLoc());
 
   // In case the tokens were cached, have Preprocessor replace them with the
   // annotation token.
   PP.AnnotateCachedTokens(Tok);
 }
 
+/// \brief Replaces a template-id annotation token with a type
+/// annotation token.
+///
+/// \returns true if there was an error, false otherwise.
+bool Parser::AnnotateTemplateIdTokenAsType(const CXXScopeSpec *SS) {
+  assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens");
+
+  TemplateIdAnnotation *TemplateId 
+    = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
+  assert(TemplateId->Kind == TNK_Class_template &&
+         "Only works for class templates");
+  
+  ASTTemplateArgsPtr TemplateArgsPtr(Actions, 
+                                     TemplateId->getTemplateArgs(),
+                                     TemplateId->getTemplateArgIsType(),
+                                     TemplateId->NumArgs);
+
+  Action::TypeResult Type 
+    = Actions.ActOnClassTemplateId(TemplateId->Template, 
+                                   TemplateId->TemplateNameLoc,
+                                   TemplateId->LAngleLoc, 
+                                   TemplateArgsPtr,
+                                   TemplateId->getTemplateArgLocations(),
+                                   TemplateId->RAngleLoc, SS);
+  if (Type.isInvalid()) {
+    // FIXME: better recovery?
+    ConsumeToken();
+    TemplateId->Destroy();
+    return true;
+  }
+
+  // Create the new "type" annotation token.
+  Tok.setKind(tok::annot_typename);
+  Tok.setAnnotationValue(Type.get());
+  if (SS && SS->isNotEmpty()) // it was a C++ qualified type name.
+    Tok.setLocation(SS->getBeginLoc());
+
+  // We might be backtracking, in which case we need to replace the
+  // template-id annotation token with the type annotation within the
+  // set of cached tokens. That way, we won't try to form the same
+  // class template specialization again.
+  PP.ReplaceLastTokenWithAnnotation(Tok);
+  TemplateId->Destroy();
+
+  return false;
+}
+
 /// ParseTemplateArgument - Parse a C++ template argument (C++ [temp.names]).
 ///
 ///       template-argument: [C++ 14.2]
index 7b09d2105c2fb23cfdb3aa47831f67294031a56e..a3ed02784661939c0b5bbc14023c35ae3c0d9974 100644 (file)
@@ -778,31 +778,41 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
       // them with the annotation token.
       PP.AnnotateCachedTokens(Tok);
       return true;
-    } else if (!getLang().CPlusPlus) {
+    } 
+
+    if (!getLang().CPlusPlus) {
       // If we're in C, we can't have :: tokens at all (the lexer won't return
       // them).  If the identifier is not a type, then it can't be scope either,
       // just early exit. 
       return false;
     }
     
-    // If this is a template-id, annotate the template-id token.
+    // If this is a template-id, annotate with a template-id or type token.
     if (NextToken().is(tok::less)) {
       DeclTy *Template;
       if (TemplateNameKind TNK 
             = Actions.isTemplateName(*Tok.getIdentifierInfo(),
-                                     CurScope, Template, &SS)) {
+                                     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
-    // cases will be handled below.
+    // The current token, which is either an identifier or a
+    // template-id, is not part of the annotation. Fall through to
+    // push that token back into the stream and complete the C++ scope
+    // specifier annotation.
   }
 
-  // FIXME: check for a template-id token here, and look it up if it
-  // names a type.
+  if (Tok.is(tok::annot_template_id)) {
+    TemplateIdAnnotation *TemplateId 
+      = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
+    if (TemplateId->Kind == TNK_Class_template) {
+      // A template-id that refers to a type was parsed into a
+      // template-id annotation in a context where we weren't allowed
+      // to produce a type annotation token. Update the template-id
+      // annotation token to a type annotation token now.
+      return !AnnotateTemplateIdTokenAsType(&SS);
+    }
+  }
 
   if (SS.isEmpty())
     return false;
@@ -825,8 +835,8 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
 }
 
 /// TryAnnotateScopeToken - Like TryAnnotateTypeOrScopeToken but only
-/// annotates C++ scope specifiers.  This returns true if the token was
-/// annotated.
+/// annotates C++ scope specifiers and template-ids.  This returns
+/// true if the token was annotated.
 /// 
 /// Note that this routine emits an error if you call it with ::new or ::delete
 /// as the current tokens, so only call it in contexts where these are invalid.
@@ -838,7 +848,7 @@ bool Parser::TryAnnotateCXXScopeToken() {
 
   CXXScopeSpec SS;
   if (!ParseOptionalCXXScopeSpecifier(SS))
-    return false;
+    return Tok.is(tok::annot_template_id);
 
   // Push the current token back into the token stream (or revert it if it is
   // cached) and use an annotation scope token for current token.
index 7ee6e22bca15a3fcc6f792f4b173f9edcdb89f34..fed2bbdf7c4077744f2607a822d700a7617dc5d7 100644 (file)
@@ -1338,6 +1338,20 @@ public:
                                                   SourceLocation CCLoc,
                                                   IdentifierInfo &II);
 
+  /// ActOnCXXNestedNameSpecifier - Called during parsing of a
+  /// nested-name-specifier that involves a template-id, e.g.,
+  /// "foo::bar<int, float>::", and now we need to build a scope
+  /// specifier. \p SS is empty or the previously parsed nested-name
+  /// part ("foo::"), \p Type is the already-parsed class template
+  /// specialization (or other template-id that names a type), \p
+  /// TypeRange is the source range where the type is located, and \p
+  /// CCLoc is the location of the trailing '::'.
+  virtual CXXScopeTy *ActOnCXXNestedNameSpecifier(Scope *S,
+                                                  const CXXScopeSpec &SS,
+                                                  TypeTy *Type,
+                                                  SourceRange TypeRange,
+                                                  SourceLocation CCLoc);
+
   /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
   /// scope or nested-name-specifier) is parsed, part of a declarator-id.
   /// After this method is called, according to [C++ 3.4.3p3], names should be
index 0d8844f95abb19b4f49ede9cb9a2aed0d402c5e0..c8a86cfd7d12c2380470e8be227c936291d0641f 100644 (file)
@@ -74,6 +74,17 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
   return 0;
 }
 
+Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
+                                                    const CXXScopeSpec &SS,
+                                                    TypeTy *Ty,
+                                                    SourceRange TypeRange,
+                                                    SourceLocation CCLoc) {
+  QualType Type = QualType::getFromOpaquePtr(Ty);
+  assert(Type->isRecordType() && 
+         "Types in a nested-name-specifier always refer to a record type");
+  return cast<DeclContext>(Type->getAsRecordType()->getDecl());
+}
+
 /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
 /// scope or nested-name-specifier) is parsed, part of a declarator-id.
 /// After this method is called, according to [C++ 3.4.3p3], names should be
index 223094b06682f13c62df4007ad9b15a6f1dc8d37..e3538e8a4ce7c7b0ecc455c9bfebd4e268d85ef9 100644 (file)
@@ -26,9 +26,9 @@ 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::TemplateNameKind Sema::isTemplateName(IdentifierInfo &II, Scope *S,
-                                            DeclTy *&Template,
-                                            const CXXScopeSpec *SS) {
+TemplateNameKind Sema::isTemplateName(IdentifierInfo &II, Scope *S,
+                                      DeclTy *&Template,
+                                      const CXXScopeSpec *SS) {
   NamedDecl *IIDecl = LookupParsedName(S, SS, &II, LookupOrdinaryName);
 
   if (IIDecl) {
index 9347bf5997fdcb20edf7b3a48957e9d5edf4e9c8..86cd52c3e36dbe168d5e368fa7d94e863fd64bfc 100644 (file)
@@ -40,3 +40,5 @@ void testme(X<int_type> *x1, X<float, int> *x2) {
   x1->foo(); // okay: refers to #1
   x2->bar(); // okay: refers to #2
 }
+
+// FIXME: diagnose specializations in a different namespace
index 94976e9735c86925d91954efeb4547a58aa0f418..cc0da1942d49d4af937efdebbc92efc4efeef119 100644 (file)
@@ -5,8 +5,7 @@ template<typename T, int N = 2> struct X; // expected-note{{template is declared
 X<int, 1> *x1;
 X<int> *x2;
 
-X<> *x3; // expected-error{{too few template arguments for class template 'X'}} \
-        // FIXME: expected-error{{expected unqualified-id}}
+X<> *x3; // expected-error{{too few template arguments for class template 'X'}}
 
 template<typename U = float, int M> struct X;
 
diff --git a/test/SemaTemplate/nested-name-spec-template.cpp b/test/SemaTemplate/nested-name-spec-template.cpp
new file mode 100644 (file)
index 0000000..4007f23
--- /dev/null
@@ -0,0 +1,50 @@
+// RUN: clang -fsyntax-only -verify %s
+
+namespace N { 
+  namespace M {
+    template<typename T> struct Promote; // expected-note{{previous definition is here}}
+    
+    template<> struct Promote<short> {
+      typedef int type;
+    };
+    
+    template<> struct Promote<int> {
+      typedef int type;
+    };
+    
+    template<> struct Promote<float> {
+      typedef double type;
+    };
+    
+    Promote<short>::type *ret_intptr(int* ip) { return ip; }
+    Promote<int>::type *ret_intptr2(int* ip) { return ip; }
+  }
+
+  M::Promote<int>::type *ret_intptr3(int* ip) { return ip; }
+  M::template Promote<int>::type *ret_intptr4(int* ip) { return ip; }
+}
+
+N::M::Promote<int>::type *ret_intptr5(int* ip) { return ip; }
+::N::M::Promote<int>::type *ret_intptr6(int* ip) { return ip; }
+
+
+N::M::template; // expected-error{{expected template name after 'template' keyword in nested name specifier}} \
+               // expected-error{{expected unqualified-id}}
+
+N::M::template Promote; // expected-error{{expected '<' after 'template Promote' in nested name specifier}} \
+// expected-error{{C++ requires a type specifier for all declarations}} \
+// expected-error{{redefinition of 'Promote' as different kind of symbol}} \
+// expected-error{{no member named 'Promote'}}
+
+namespace N {
+  template<typename T> struct A;
+
+  template<>
+  struct A<int> {
+    struct X;
+  };
+}
+
+struct ::N::A<int>::X {
+  int foo;
+};
index b8bb279caf2dcab6e4f1b3041cfee1905441327f..b974c622a4f8fa940f5b71be97c78ccf16a1bdfd 100644 (file)
@@ -8,7 +8,5 @@ template<typename> class X;
 
 A<int, 0, X> * a1;
 
-A<float, 1, X, double> *a2; // expected-error{{too many template arguments for class template 'A'}} \
-          // expected-error{{unqualified-id}}
-A<float, 1> *a3; // expected-error{{too few template arguments for class template 'A'}} \
-          // expected-error{{unqualified-id}}
+A<float, 1, X, double> *a2; // expected-error{{too many template arguments for class template 'A'}}
+A<float, 1> *a3; // expected-error{{too few template arguments for class template 'A'}}
index 89dbf237b3e648ce33903f74e924e67838d954fe..d67cc4e40979b00c56026c48731284a104334cdb 100644 (file)
@@ -3,26 +3,21 @@ template<int N> struct A; // expected-note 5{{template parameter is declared her
 
 A<0> *a0;
 
-A<int()> *a1; // expected-error{{template argument for non-type template parameter is treated as type 'int (void)'}} \
-              // FIXME: expected-error{{unqualified-id}}
+A<int()> *a1; // expected-error{{template argument for non-type template parameter is treated as type 'int (void)'}}
 
-A<int> *a2; // expected-error{{template argument for non-type template parameter must be an expression}} \
-              // FIXME: expected-error{{unqualified-id}}
+A<int> *a2; // expected-error{{template argument for non-type template parameter must be an expression}}
 
 A<1 >> 2> *a3;
 
 // C++ [temp.arg.nontype]p5:
 A<A> *a4; // expected-error{{must have an integral or enumeration type}} \
-          // FIXME: the error message above is a bit lame \
-          // FIXME: expected-error{{expected unqualified-id}}
+          // FIXME: the error message above is a bit lame
 
 enum E { Enumerator = 17 };
-A<E> *a5; // expected-error{{template argument for non-type template parameter must be an expression}} \
-          // FIXME: expected-error{{unqualified-id}}
+A<E> *a5; // expected-error{{template argument for non-type template parameter must be an expression}}
 template<E Value> struct A1; // expected-note{{template parameter is declared here}}
 A1<Enumerator> *a6; // okay
-A1<17> *a7; // expected-error{{non-type template argument of type 'int' cannot be converted to a value of type 'enum E'}} \
-          // FIXME: expected-error{{expected unqualified-id}}
+A1<17> *a7; // expected-error{{non-type template argument of type 'int' cannot be converted to a value of type 'enum E'}}
 
 const long LongValue = 12345678;
 A<LongValue> *a8;
@@ -30,8 +25,7 @@ const short ShortValue = 17;
 A<ShortValue> *a9;
 
 int f(int);
-A<f(17)> *a10; // expected-error{{non-type template argument of type 'int' is not an integral constant expression}} \
-          // FIXME: expected-error{{expected unqualified-id}}
+A<f(17)> *a10; // expected-error{{non-type template argument of type 'int' is not an integral constant expression}}
 
 class X {
 public:
@@ -39,8 +33,7 @@ public:
   X(int, int);
   operator int() const;
 };
-A<X(17, 42)> *a11; // expected-error{{non-type template argument of type 'class X' must have an integral or enumeration type}} \
-                   // FIXME:expected-error{{expected unqualified-id}}
+A<X(17, 42)> *a11; // expected-error{{non-type template argument of type 'class X' must have an integral or enumeration type}}
 
 template<X const *Ptr> struct A2;
 
@@ -50,8 +43,7 @@ X array_of_Xs[10];
 A2<X_ptr> *a12;
 A2<array_of_Xs> *a13;
 A2<&an_X> *a13_2;
-A2<(&an_X)> *a13_2; // expected-error{{non-type template argument cannot be surrounded by parentheses}} \
-                    // FIXME: expected-error{{unqualified-id}}
+A2<(&an_X)> *a13_3; // expected-error{{non-type template argument cannot be surrounded by parentheses}}
 
 float f(float);
 
@@ -66,10 +58,8 @@ A3<h> *a14_1;
 A3<&h> *a14_2;
 A3<f> *a14_3;
 A3<&f> *a14_4;
-A3<h2> *a14_6;  // expected-error{{non-type template argument of type 'float (*)(float)' cannot be converted to a value of type 'int (*)(int)'}} \
-// FIXME: expected-error{{expected unqualified-id}}
-A3<g> *a14_7; // expected-error{{non-type template argument of type '<overloaded function type>' cannot be converted to a value of type 'int (*)(int)'}}\
-// FIXME: expected-error{{expected unqualified-id}}
+A3<h2> *a14_6;  // expected-error{{non-type template argument of type 'float (*)(float)' cannot be converted to a value of type 'int (*)(int)'}}
+A3<g> *a14_7; // expected-error{{non-type template argument of type '<overloaded function type>' cannot be converted to a value of type 'int (*)(int)'}}
 // FIXME: the first error includes the string <overloaded function
 // type>, which makes Doug slightly unhappy.
 
@@ -79,18 +69,15 @@ struct Y { } y;
 volatile X * X_volatile_ptr;
 template<X const &AnX> struct A4; // expected-note 2{{template parameter is declared here}}
 A4<an_X> *a15_1; // okay
-A4<*X_volatile_ptr> *a15_2; // expected-error{{reference binding of non-type template parameter of type 'class X const &' to template argument of type 'class X volatile' ignores qualifiers}} \
-                  // FIXME: expected-error{{expected unqualified-id}}
-A4<y> *15_3; //  expected-error{{non-type template parameter of reference type 'class X const &' cannot bind to template argument of type 'struct Y'}}\
-                  // FIXME: expected-error{{expected unqualified-id}}
+A4<*X_volatile_ptr> *a15_2; // expected-error{{reference binding of non-type template parameter of type 'class X const &' to template argument of type 'class X volatile' ignores qualifiers}}
+A4<y> *15_3; //  expected-error{{non-type template parameter of reference type 'class X const &' cannot bind to template argument of type 'struct Y'}} \
+            // FIXME: expected-error{{expected unqualified-id}}
 
 template<int (&fr)(int)> struct A5; // expected-note 2{{template parameter is declared here}}
 A5<h> *a16_1;
 A5<f> *a16_3;
-A5<h2> *a16_6;  // expected-error{{non-type template argument of type 'float (float)' cannot be converted to a value of type 'int (&)(int)'}} \
-// FIXME: expected-error{{expected unqualified-id}}
-A5<g> *a14_7; // expected-error{{non-type template argument of type '<overloaded function type>' cannot be converted to a value of type 'int (&)(int)'}}\
-// FIXME: expected-error{{expected unqualified-id}}
+A5<h2> *a16_6;  // expected-error{{non-type template argument of type 'float (float)' cannot be converted to a value of type 'int (&)(int)'}}
+A5<g> *a14_7; // expected-error{{non-type template argument of type '<overloaded function type>' cannot be converted to a value of type 'int (&)(int)'}}
 // FIXME: the first error includes the string <overloaded function
 // type>, which makes Doug slightly unhappy.
 
@@ -106,15 +93,12 @@ struct Z {
 template<int (Z::*pmf)(int)> struct A6; // expected-note{{template parameter is declared here}}
 A6<&Z::foo> *a17_1;
 A6<&Z::bar> *a17_2;
-A6<&Z::baz> *a17_3; // expected-error{{non-type template argument of type 'double (struct Z::*)(double)' cannot be converted to a value of type 'int (struct Z::*)(int)'}} \
-// FIXME: expected-error{{expected unqualified-id}}
+A6<&Z::baz> *a17_3; // expected-error{{non-type template argument of type 'double (struct Z::*)(double)' cannot be converted to a value of type 'int (struct Z::*)(int)'}}
 
 
 template<int Z::*pm> struct A7;  // expected-note{{template parameter is declared here}}
 template<int Z::*pm> struct A7c;
 A7<&Z::int_member> *a18_1;
 A7c<&Z::int_member> *a18_2;
-A7<&Z::float_member> *a18_3; // expected-error{{non-type template argument of type 'float struct Z::*' cannot be converted to a value of type 'int struct Z::*'}} \
-              // FIXME: expected-error{{unqualified-id}}
-A7c<(&Z::int_member)> *a18_3; // expected-error{{non-type template argument cannot be surrounded by parentheses}} \
-          // FIXME: expected-error{{expected unqualified-id}}
+A7<&Z::float_member> *a18_3; // expected-error{{non-type template argument of type 'float struct Z::*' cannot be converted to a value of type 'int struct Z::*'}}
+A7c<(&Z::int_member)> *a18_3; // expected-error{{non-type template argument cannot be surrounded by parentheses}}
index e1b9bb4874f791367ea5bd2c8081ae7f962817cd..781ada9cfe78d81d92190abee09ef107032acaed 100644 (file)
@@ -20,23 +20,18 @@ A<X> *a1;
 A<N::Z> *a2;
 A< ::N::Z> *a3;
 
-A<Y> *a4; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}} \
-          // FIXME::expected-error{{expected unqualified-id}}
-A<TooMany> *a5; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}} \
-          // FIXME::expected-error{{expected unqualified-id}}
-B<X> *a6; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}} \
-          // FIXME::expected-error{{expected unqualified-id}}
+A<Y> *a4; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
+A<TooMany> *a5; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
+B<X> *a6; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
 C<Y> *a7;
-C<Ylong> *a8; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}} \
-          // FIXME::expected-error{{expected unqualified-id}}
+C<Ylong> *a8; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
 
 template<typename T> void f(int);
 
 // FIXME: we're right to provide an error message, but it should say
 // that we need a class template. We won't get this right until name
 // lookup of 'f' returns a TemplateDecl.
-A<f> *a9; // expected-error{{template argument for template template parameter must be a template}} \
-          // expected-error{{unqualified-id}}
+A<f> *a9; // expected-error{{template argument for template template parameter must be a template}}
 
 // FIXME: The code below is ill-formed, because of the evil digraph '<:'. 
 // We should provide a much better error message than we currently do.
index 36764fc8e43b6555ed2507536cdadfa5f9d62a4b..216a283e2e99c272826965dff9382ea21a51b1e9 100644 (file)
@@ -2,11 +2,9 @@
 template<typename T> class A; // expected-note 2 {{template parameter is declared here}}
 
 // [temp.arg.type]p1
-A<0> *a1; // expected-error{{template argument for template type parameter must be a type}} \
-          // expected-error{{unqualified-id}}
+A<0> *a1; // expected-error{{template argument for template type parameter must be a type}}
 
-A<A> *a2; // expected-error{{template argument for template type parameter must be a type}} \
-          // expected-error{{unqualified-id}}
+A<A> *a2; // expected-error{{template argument for template type parameter must be a type}}
 
 A<int> *a3;
 A<int()> *a4; 
@@ -16,13 +14,12 @@ A<A<int> > *a6;
 // [temp.arg.type]p2
 void f() {
   class X { };
-  A<X> * a = 0; // expected-error{{template argument uses local type 'class X'}}\
-                // FIXME: expected-error{{expected expression}}
+  A<X> * a = 0; // expected-error{{template argument uses local type 'class X'}} \
+                // FIXME: expected-error{{use of undeclared identifier 'a'}}
 }
 
 struct { int x; } Unnamed; // expected-note{{unnamed type used in template argument was declared here}}
-A<__typeof__(Unnamed)> *a7; // expected-error{{template argument uses unnamed type}} \
-                            // FIXME: expected-error{{expected unqualified-id}}
+A<__typeof__(Unnamed)> *a7; // expected-error{{template argument uses unnamed type}}
 
 // FIXME: [temp.arg.type]p3. The check doesn't really belong here (it
 // belongs somewhere in the template instantiation section).