Basic support for parsing templates, from Andrew Sutton
llvm-svn: 60384
This commit is contained in:
parent
351b6659ad
commit
eb31f39558
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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:
|
||||
|
|
|
@ -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 { };
|
||||
|
Loading…
Reference in New Issue