[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