[cfe-commits] r60384 - in /cfe/trunk: include/clang/Basic/DiagnosticKinds.def include/clang/Parse/Parser.h lib/Parse/ParseDecl.cpp lib/Parse/ParseTemplate.cpp lib/Parse/Parser.cpp test/Parser/cxx-template-decl.cpp

Douglas Gregor doug.gregor at gmail.com
Mon Dec 1 15:54:01 PST 2008


Author: dgregor
Date: Mon Dec  1 17:54:00 2008
New Revision: 60384

URL: http://llvm.org/viewvc/llvm-project?rev=60384&view=rev
Log:
Basic support for parsing templates, from Andrew Sutton

Added:
    cfe/trunk/lib/Parse/ParseTemplate.cpp
    cfe/trunk/test/Parser/cxx-template-decl.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticKinds.def
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Parse/Parser.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticKinds.def?rev=60384&r1=60383&r2=60384&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticKinds.def (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticKinds.def Mon Dec  1 17:54:00 2008
@@ -590,6 +590,16 @@
 DIAG(ext_ellipsis_exception_spec, EXTENSION,
      "exception specification of '...' is a Microsoft extension")
 
+/// C++ Templates
+DIAG(err_expected_template, ERROR,
+      "expected template")
+DIAG(err_expected_comma_greater, ERROR,
+      "expected ',' or '>' in template-parameter-list")
+DIAG(err_expected_type_id_after, ERROR,
+      "expected type-id after '%0'")
+DIAG(err_expected_class_before, ERROR,
+      "expected 'class' before '%0'")
+
 // Language specific pragmas
 
 // #pragma pack

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=60384&r1=60383&r2=60384&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Mon Dec  1 17:54:00 2008
@@ -802,6 +802,17 @@
   // C++ 13.5: Overloaded operators [over.oper]
   OverloadedOperatorKind TryParseOperatorFunctionId();
   TypeTy *ParseConversionFunctionId();
+
+  //===--------------------------------------------------------------------===//
+  // C++ 14: Templates [temp]
+  // C++ 14.1: Template Parameters [temp.param]
+  DeclTy *ParseTemplateDeclaration(unsigned Context);
+  bool ParseTemplateParameters(DeclTy* TempDecl);
+  bool ParseTemplateParameterList(DeclTy *TmpDecl);
+  DeclTy *ParseTemplateParameter();
+  DeclTy *ParseTypeParameter();
+  DeclTy *ParseTemplateTemplateParameter();
+  DeclTy *ParseNonTypeTemplateParameter();
 };
 
 }  // end namespace clang

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=60384&r1=60383&r2=60384&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Mon Dec  1 17:54:00 2008
@@ -202,11 +202,15 @@
 ///         block-declaration ->
 ///           simple-declaration
 ///           others                   [FIXME]
+/// [C++]   template-declaration
 /// [C++]   namespace-definition
 ///         others... [FIXME]
 ///
 Parser::DeclTy *Parser::ParseDeclaration(unsigned Context) {
   switch (Tok.getKind()) {
+  case tok::kw_export:
+  case tok::kw_template:
+    return ParseTemplateDeclaration(Context);
   case tok::kw_namespace:
     return ParseNamespace(Context);
   default:
@@ -417,7 +421,8 @@
 /// [C++]   'virtual'
 /// [C++]   'explicit'
 ///
-void Parser::ParseDeclarationSpecifiers(DeclSpec &DS) {
+void Parser::ParseDeclarationSpecifiers(DeclSpec &DS)
+{
   DS.SetRangeStart(Tok.getLocation());
   while (1) {
     int isInvalid = false;
@@ -431,9 +436,11 @@
     
     switch (Tok.getKind()) {
     default: 
-      // Try to parse a type-specifier; if we found one, continue.
-      if (MaybeParseTypeSpecifier(DS, isInvalid, PrevSpec))
+      // Try to parse a type-specifier; if we found one, continue. If it's not
+      // a type, this falls through.
+      if (MaybeParseTypeSpecifier(DS, isInvalid, PrevSpec)) {
         continue;
+      }
 
     DoneWithDeclSpec:
       // If this is not a declaration specifier token, we're done reading decl
@@ -612,6 +619,7 @@
     ConsumeToken();
   }
 }
+
 /// MaybeParseTypeSpecifier - Try to parse a single type-specifier. We
 /// primarily follow the C++ grammar with additions for C99 and GNU,
 /// which together subsume the C grammar. Note that the C++

Added: cfe/trunk/lib/Parse/ParseTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTemplate.cpp?rev=60384&view=auto

==============================================================================
--- cfe/trunk/lib/Parse/ParseTemplate.cpp (added)
+++ cfe/trunk/lib/Parse/ParseTemplate.cpp Mon Dec  1 17:54:00 2008
@@ -0,0 +1,288 @@
+//===--- ParseTemplate.cpp - Template Parsing -----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file implements parsing of C++ templates.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Parse/Parser.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Parse/DeclSpec.h"
+#include "clang/Parse/Scope.h"
+
+using namespace clang;
+
+/// ParseTemplateDeclaration - Parse a template declaration, which includes 
+/// the template parameter list and either a function of class declaration.
+///
+///       template-declaration: [C++ temp]
+///         'export'[opt] 'template' '<' template-parameter-list '>' declaration
+Parser::DeclTy *Parser::ParseTemplateDeclaration(unsigned Context) {
+  assert((Tok.is(tok::kw_export) || Tok.is(tok::kw_template)) && 
+	 "Token does not start a template declaration.");
+  
+  // Consume the optional export token, if it exists, followed by the
+  // namespace token.
+  bool isExported = false;
+  if(Tok.is(tok::kw_export)) {
+    SourceLocation ExportLoc = ConsumeToken();
+    if(!Tok.is(tok::kw_template)) {
+      Diag(Tok.getLocation(), diag::err_expected_template);
+      return 0;
+    }
+    isExported = true;
+  }
+  SourceLocation TemplateLoc = ConsumeToken();
+  
+  // Try to parse the template parameters, and the declaration if successful.
+  if(ParseTemplateParameters(0)) {
+    // For some reason, this is generating a compiler error when parsing the
+    // declaration. Apparently, ParseDeclaration doesn't want to match a
+    // function-definition, but will match a function declaration.
+    // TODO: ParseDeclarationOrFunctionDefinition
+    return ParseDeclaration(Context);
+  }
+  return 0;
+}
+
+/// ParseTemplateParameters - Parses a template-parameter-list enclosed in
+/// angle brackets.
+bool Parser::ParseTemplateParameters(DeclTy* TmpDecl) {
+  // Get the template parameter list.
+  if(!Tok.is(tok::less)) {
+    Diag(Tok.getLocation(), diag::err_expected_less_after) << "template";
+    return false;
+  }
+  ConsumeToken();
+  
+  // Try to parse the template parameter list.
+  if(ParseTemplateParameterList(0)) {
+    if(!Tok.is(tok::greater)) {
+      Diag(Tok.getLocation(), diag::err_expected_greater);
+      return false;
+    }
+    ConsumeToken();
+  }
+  return true;
+}
+
+/// ParseTemplateParameterList - Parse a template parameter list. If
+/// the parsing fails badly (i.e., closing bracket was left out), this
+/// will try to put the token stream in a reasonable position (closing
+/// a statement, etc.) and return false. 
+///
+///       template-parameter-list:    [C++ temp]
+///         template-parameter
+///         template-parameter-list ',' template-parameter
+bool Parser::ParseTemplateParameterList(DeclTy* TmpDecl) {
+  // FIXME: For now, this is just going to consume the template parameters.
+  // Eventually, we should pass the template decl AST node as a parameter and
+  // apply template parameters as we find them.
+  while(1) {
+    DeclTy* TmpParam = ParseTemplateParameter();
+    if(!TmpParam) {
+      // If we failed to parse a template parameter, skip until we find
+      // a comma or closing brace.
+      SkipUntil(tok::comma, tok::greater, true, true);
+    }
+    
+    // Did we find a comma or the end of the template parmeter list?
+    if(Tok.is(tok::comma)) {
+      ConsumeToken();
+    } else if(Tok.is(tok::greater)) {
+      // Don't consume this... that's done by template parser.
+      break;
+    } else {
+      // Somebody probably forgot to close the template. Skip ahead and
+      // try to get out of the expression. This error is currently
+      // subsumed by whatever goes on in ParseTemplateParameter.
+      // TODO: This could match >>, and it would be nice to avoid those
+      // silly errors with template <vec<T>>.
+      // Diag(Tok.getLocation(), diag::err_expected_comma_greater);
+      SkipUntil(tok::greater, true, true);
+      return false;
+    }
+  }
+  return true;
+}
+
+/// ParseTemplateParameter - Parse a template-parameter (C++ [temp.param]).
+///
+///       template-parameter: [C++ temp.param]
+///         type-parameter
+///         parameter-declaration
+///
+///       type-parameter: (see below)
+///         'class' identifier[opt]
+///         'class' identifier[opt] '=' type-id
+///         'typename' identifier[opt]
+///         'typename' identifier[opt] '=' type-id
+///         'template' '<' template-parameter-list '>' 'class' identifier[opt]
+///         'template' '<' template-parameter-list '>' 'class' identifier[opt] = id-expression
+Parser::DeclTy *Parser::ParseTemplateParameter() {
+  TryAnnotateCXXScopeToken();
+
+  if(Tok.is(tok::kw_class) 
+     || (Tok.is(tok::kw_typename) && 
+	 NextToken().isNot(tok::annot_qualtypename))) {
+    return ParseTypeParameter();
+  } else if(Tok.is(tok::kw_template)) {
+    return ParseTemplateTemplateParameter();
+  } else {
+    // If it's none of the above, then it must be a parameter declaration.
+    // NOTE: This will pick up errors in the closure of the template parameter
+    // list (e.g., template < ; Check here to implement >> style closures.
+    return ParseNonTypeTemplateParameter();
+  }
+  return 0;
+}
+
+/// ParseTypeParameter - Parse a template type parameter (C++ [temp.param]).
+/// Other kinds of template parameters are parsed in
+/// ParseTemplateTemplateParameter and ParseNonTypeTemplateParameter.
+///
+///       type-parameter:     [C++ temp.param]
+///         'class' identifier[opt]
+///         'class' identifier[opt] '=' type-id
+///         'typename' identifier[opt]
+///         'typename' identifier[opt] '=' type-id
+Parser::DeclTy *Parser::ParseTypeParameter() {
+  SourceLocation keyLoc = ConsumeToken();
+
+  // Grab the template parameter name (if given)
+  IdentifierInfo* paramName = 0;
+  if(Tok.is(tok::identifier)) {
+    paramName = Tok.getIdentifierInfo();
+    ConsumeToken();
+  } else if(Tok.is(tok::equal) || Tok.is(tok::comma) ||
+	    Tok.is(tok::greater)) {
+    // Unnamed template parameter. Don't have to do anything here, just
+    // don't consume this token.
+  } else {
+    Diag(Tok.getLocation(), diag::err_expected_ident);
+    return 0;
+  }
+  
+  // Grab a default type id (if given).
+  TypeTy* defaultType = 0;
+  if(Tok.is(tok::equal)) {
+    ConsumeToken();
+    defaultType = ParseTypeName();
+    if(!defaultType)
+      return 0;
+  }
+  
+  // FIXME: Add an action for type parameters.
+  return 0;
+}
+
+/// ParseTemplateTemplateParameter - Handle the parsing of template
+/// template parameters. 
+///
+///       type-parameter:    [C++ temp.param]
+///         'template' '<' template-parameter-list '>' 'class' identifier[opt]
+///         'template' '<' template-parameter-list '>' 'class' identifier[opt] = id-expression
+Parser::DeclTy* Parser::ParseTemplateTemplateParameter() {
+  assert(Tok.is(tok::kw_template) && "Expected 'template' keyword");
+
+  // Handle the template <...> part.
+  SourceLocation TemplateLoc = ConsumeToken();
+  if(!ParseTemplateParameters(0)) {
+    return 0;
+  }
+
+  // Generate a meaningful error if the user forgot to put class before the
+  // identifier, comma, or greater.
+  if(!Tok.is(tok::kw_class)) {
+    Diag(Tok.getLocation(), diag::err_expected_class_before) 
+      << PP.getSpelling(Tok);
+    return 0;
+  }
+  SourceLocation ClassLoc = ConsumeToken();
+
+  // Get the identifier, if given.
+  IdentifierInfo* ident = 0;
+  if(Tok.is(tok::identifier)) {
+    ident = Tok.getIdentifierInfo();
+    ConsumeToken();
+  } else if(Tok.is(tok::equal) || Tok.is(tok::comma) || Tok.is(tok::greater)) {
+    // Unnamed template parameter. Don't have to do anything here, just
+    // don't consume this token.
+  } else {
+    Diag(Tok.getLocation(), diag::err_expected_ident);
+    return 0;
+  }
+
+  // Get the a default value, if given.
+  ExprResult defaultExpr;
+  if(Tok.is(tok::equal)) {
+    ConsumeToken();
+    defaultExpr = ParseCXXIdExpression();
+    if(defaultExpr.isInvalid) {
+      return 0;
+    }
+  }
+
+  // FIXME: Add an action for template template parameters.
+  return 0;
+}
+
+/// ParseNonTypeTemplateParameter - Handle the parsing of non-type
+/// template parameters (e.g., in "template<int Size> class array;"). 
+
+///       template-parameter:
+///         ...
+///         parameter-declaration
+///
+/// NOTE: It would be ideal to simply call out to ParseParameterDeclaration(),
+/// but that didn't work out to well. Instead, this tries to recrate the basic
+/// parsing of parameter declarations, but tries to constrain it for template
+/// parameters.
+/// FIXME: We need to make ParseParameterDeclaration work for non-type 
+/// template parameters, too.
+Parser::DeclTy* Parser::ParseNonTypeTemplateParameter()
+{
+  SourceLocation startLoc = Tok.getLocation();
+
+  // Parse the declaration-specifiers (i.e., the type).
+  // FIXME:: The type should probably be restricted in some way... Not all
+  // declarators (parts of declarators?) are accepted for parameters.
+  DeclSpec ds;
+  ParseDeclarationSpecifiers(ds);
+
+  // Parse this as a typename.
+  Declarator decl(ds, Declarator::TypeNameContext);
+  ParseDeclarator(decl);
+  if(ds.getTypeSpecType() == DeclSpec::TST_unspecified && !ds.getTypeRep()) {
+    // This probably shouldn't happen - and it's more of a Sema thing, but
+    // basically we didn't parse the type name because we couldn't associate
+    // it with an AST node. we should just skip to the comma or greater.
+    // TODO: This is currently a placeholder for some kind of Sema Error.
+    Diag(Tok.getLocation(), diag::err_parse_error);
+    SkipUntil(tok::comma, tok::greater, true, true);
+    return 0;
+  }
+
+  // If there's an identifier after the typename, parse that as part of the
+  // declarator - or something.
+  if(Tok.is(tok::identifier)) {
+    ConsumeToken();
+  }
+
+  // Is there a default value? Parsing this can be fairly annoying because
+  // we have to stop on the first non-nested (paren'd) '>' as the closure
+  // for the template parameter list. Or a ','.
+  if(Tok.is(tok::equal)) {
+    // TODO: Implement default non-type values.
+    SkipUntil(tok::comma, tok::greater, true, true);
+  }
+  
+  // FIXME: Add an action for non-type template parameters.
+  return 0;
+}

Modified: cfe/trunk/lib/Parse/Parser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/Parser.cpp?rev=60384&r1=60383&r2=60384&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/Parser.cpp (original)
+++ cfe/trunk/lib/Parse/Parser.cpp Mon Dec  1 17:54:00 2008
@@ -348,6 +348,8 @@
     return 0;
   case tok::kw_namespace:
   case tok::kw_typedef:
+  case tok::kw_template:
+  case tok::kw_export:    // As in 'export template'
     // A function definition cannot start with a these keywords.
     return ParseDeclaration(Declarator::FileContext);
   default:

Added: cfe/trunk/test/Parser/cxx-template-decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-template-decl.cpp?rev=60384&view=auto

==============================================================================
--- cfe/trunk/test/Parser/cxx-template-decl.cpp (added)
+++ cfe/trunk/test/Parser/cxx-template-decl.cpp Mon Dec  1 17:54:00 2008
@@ -0,0 +1,50 @@
+// RUN: clang -fsyntax-only -verify %s
+
+// Errors
+export class foo { };   // expected-error {{expected template}}
+template  x;            // expected-error {{expected '<' after 'template'}}
+export template x;      // expected-error {{expected '<' after 'template'}}
+template < ;            // expected-error {{parse error}}
+template <template X> ; // expected-error {{expected '<' after 'template'}}
+template <template <typename> > ;       // expected-error {{expected 'class' before '>'}}
+template <template <typename> Foo> ;    // expected-error {{expected 'class' before 'Foo'}}
+
+// Template function declarations
+template <typename T> void foo();
+template <typename T, typename U> void foo();
+
+// TODO Implement ParseDeclarationOrFunction()
+// Template function definitions. Not done yet.
+// template <typename T> void foo() { }
+
+// Template class (forward) declarations
+template <typename T> struct A;
+template <typename T, typename U> struct b;
+template <typename> struct C;
+template <typename, typename> struct D;
+
+// Forward declarations with default parameters?
+template <typename T = int> X1;
+template <typename = int> X2;
+
+// Forward declarations w/template template parameters
+template <template <typename> class T> class TTP1;
+template <template <typename> class> class TTP2;
+template <template <typename> class T = foo> TTP3;
+template <template <typename> class = foo> TTP3;
+template <template <typename X, typename Y> class T> TTP5;
+
+// Forward declararations with non-type params
+template <int> class NTP0;
+template <int N> class NTP1;
+template <int N = 5> class NTP2;
+template <int = 10> class NTP3;
+template <unsigned int N = 12u> NTP4;;
+template <unsigned int = 12u> NTP5;
+template <unsigned = 15u> NTP6;
+template <typename T, T Obj> NTP7;      // expected-error {{parse error}}
+
+// Template class declarations
+template <typename T> struct A { };
+template <typename T, typename U> struct B { };
+





More information about the cfe-commits mailing list