From: Douglas Gregor Date: Mon, 1 Dec 2008 23:54:00 +0000 (+0000) Subject: Basic support for parsing templates, from Andrew Sutton X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=adcac8824a9cff13f1ef61a69e38c1041cba12ee;p=clang Basic support for parsing templates, from Andrew Sutton git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@60384 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index d049b6c655..57408da6a2 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -590,6 +590,16 @@ DIAG(err_expected_member_or_base_name, ERROR, 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 diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index f4695af279..feb479c4eb 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -802,6 +802,17 @@ private: // 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 diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 4280c6050c..192c70a192 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -202,11 +202,15 @@ AttributeList *Parser::ParseAttributes() { /// 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 @@ void Parser::ParseSpecifierQualifierList(DeclSpec &DS) { /// [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 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS) { 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 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS) { 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++ diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp new file mode 100644 index 0000000000..9a7f6cbd73 --- /dev/null +++ b/lib/Parse/ParseTemplate.cpp @@ -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 >. + // 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 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; +} diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 999349cfbe..f23fedf2b1 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -348,6 +348,8 @@ Parser::DeclTy *Parser::ParseExternalDeclaration() { 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: diff --git a/test/Parser/cxx-template-decl.cpp b/test/Parser/cxx-template-decl.cpp new file mode 100644 index 0000000000..abc7bfc649 --- /dev/null +++ b/test/Parser/cxx-template-decl.cpp @@ -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