Refactor the parsing of declarations so that template declarations can

parse just a single declaration and provide a reasonable diagnostic
when the "only one declarator per template declaration" rule is
violated. This eliminates some ugly, ugly hackery where we used to
require thatn the layout of a DeclGroup of a single element be the
same as the layout of a single declaration.

llvm-svn: 71596
This commit is contained in:
Douglas Gregor 2009-05-12 21:31:51 +00:00
parent e541ea3a61
commit 2399628cea
7 changed files with 239 additions and 93 deletions

View File

@ -240,6 +240,9 @@ def err_two_right_angle_brackets_need_space : Error<
def warn_cxx0x_right_shift_in_template_arg : Warning<
"use of right-shift operator ('>>') in template argument will require "
"parentheses in C++0x">;
def err_multiple_template_declarators : Error<
"%select{a template declaration|an explicit template instantiation}0 can "
"only %select{declare|instante}0 a single entity">;
def err_expected_qualified_after_typename : Error<
"expected a qualified name after 'typename'">;

View File

@ -550,6 +550,8 @@ private:
//===--------------------------------------------------------------------===//
// C99 6.9: External Definitions.
DeclGroupPtrTy ParseExternalDeclaration();
bool isDeclarationAfterDeclarator();
bool isStartOfFunctionDefinition();
DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(
TemplateParameterLists *TemplateParams = 0,
AccessSpecifier AS = AS_none);
@ -816,6 +818,7 @@ private:
DeclGroupPtrTy ParseSimpleDeclaration(unsigned Context,
SourceLocation &DeclEnd,
bool RequireSemi = true);
DeclPtrTy ParseDeclarationAfterDeclarator(Declarator &D);
DeclGroupPtrTy ParseInitDeclaratorListAfterFirstDeclarator(Declarator &D);
DeclPtrTy ParseFunctionStatementBody(DeclPtrTy Decl);
DeclPtrTy ParseFunctionTryBlock(DeclPtrTy Decl);
@ -1052,6 +1055,12 @@ private:
DeclPtrTy ParseTemplateDeclarationOrSpecialization(unsigned Context,
SourceLocation &DeclEnd,
AccessSpecifier AS=AS_none);
DeclPtrTy ParseSingleDeclarationAfterTemplate(
unsigned Context,
TemplateParameterLists *TemplateParams,
SourceLocation TemplateLoc,
SourceLocation &DeclEnd,
AccessSpecifier AS=AS_none);
bool ParseTemplateParameters(unsigned Depth,
TemplateParameterList &TemplateParams,
SourceLocation &LAngleLoc,
@ -1086,6 +1095,7 @@ private:
TemplateArgIsTypeList &TemplateArgIsType,
TemplateArgLocationList &TemplateArgLocations);
void *ParseTemplateArgument(bool &ArgIsType);
DeclPtrTy ParseExplicitInstantiation(SourceLocation &DeclEnd);
//===--------------------------------------------------------------------===//
// GNU G++: Type Traits [Type-Traits.html in the GCC manual]

View File

@ -233,8 +233,14 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(unsigned Context,
SourceLocation &DeclEnd) {
DeclPtrTy SingleDecl;
switch (Tok.getKind()) {
case tok::kw_export:
case tok::kw_template:
if (NextToken().isNot(tok::less)) {
SingleDecl = ParseExplicitInstantiation(DeclEnd);
break;
}
// Fall through for template declarations and specializations
case tok::kw_export:
SingleDecl = ParseTemplateDeclarationOrSpecialization(Context, DeclEnd);
break;
case tok::kw_namespace:
@ -302,15 +308,11 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(unsigned Context,
return DG;
}
/// ParseInitDeclaratorListAfterFirstDeclarator - Parse 'declaration' after
/// parsing 'declaration-specifiers declarator'. This method is split out this
/// way to handle the ambiguity between top-level function-definitions and
/// declarations.
/// \brief Parse 'declaration' after parsing 'declaration-specifiers
/// declarator'. This method parses the remainder of the declaration
/// (including any attributes or initializer, among other things) and
/// finalizes the declaration.
///
/// init-declarator-list: [C99 6.7]
/// init-declarator
/// init-declarator-list ',' init-declarator
/// init-declarator: [C99 6.7]
/// declarator
/// declarator '=' initializer
@ -327,6 +329,81 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(unsigned Context,
/// According to the standard grammar, =default and =delete are function
/// definitions, but that definitely doesn't fit with the parser here.
///
Parser::DeclPtrTy Parser::ParseDeclarationAfterDeclarator(Declarator &D) {
// If a simple-asm-expr is present, parse it.
if (Tok.is(tok::kw_asm)) {
SourceLocation Loc;
OwningExprResult AsmLabel(ParseSimpleAsm(&Loc));
if (AsmLabel.isInvalid()) {
SkipUntil(tok::semi, true, true);
return DeclPtrTy();
}
D.setAsmLabel(AsmLabel.release());
D.SetRangeEnd(Loc);
}
// If attributes are present, parse them.
if (Tok.is(tok::kw___attribute)) {
SourceLocation Loc;
AttributeList *AttrList = ParseAttributes(&Loc);
D.AddAttributes(AttrList, Loc);
}
// Inform the current actions module that we just parsed this declarator.
DeclPtrTy ThisDecl = Actions.ActOnDeclarator(CurScope, D);
// Parse declarator '=' initializer.
if (Tok.is(tok::equal)) {
ConsumeToken();
if (getLang().CPlusPlus0x && Tok.is(tok::kw_delete)) {
SourceLocation DelLoc = ConsumeToken();
Actions.SetDeclDeleted(ThisDecl, DelLoc);
} else {
OwningExprResult Init(ParseInitializer());
if (Init.isInvalid()) {
SkipUntil(tok::semi, true, true);
return DeclPtrTy();
}
Actions.AddInitializerToDecl(ThisDecl, move(Init));
}
} else if (Tok.is(tok::l_paren)) {
// Parse C++ direct initializer: '(' expression-list ')'
SourceLocation LParenLoc = ConsumeParen();
ExprVector Exprs(Actions);
CommaLocsTy CommaLocs;
if (ParseExpressionList(Exprs, CommaLocs)) {
SkipUntil(tok::r_paren);
} else {
// Match the ')'.
SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
assert(!Exprs.empty() && Exprs.size()-1 == CommaLocs.size() &&
"Unexpected number of commas!");
Actions.AddCXXDirectInitializerToDecl(ThisDecl, LParenLoc,
move_arg(Exprs),
&CommaLocs[0], RParenLoc);
}
} else {
Actions.ActOnUninitializedDecl(ThisDecl);
}
return ThisDecl;
}
/// ParseInitDeclaratorListAfterFirstDeclarator - Parse 'declaration' after
/// parsing 'declaration-specifiers declarator'. This method is split out this
/// way to handle the ambiguity between top-level function-definitions and
/// declarations.
///
/// init-declarator-list: [C99 6.7]
/// init-declarator
/// init-declarator-list ',' init-declarator
///
/// According to the standard grammar, =default and =delete are function
/// definitions, but that definitely doesn't fit with the parser here.
///
Parser::DeclGroupPtrTy Parser::
ParseInitDeclaratorListAfterFirstDeclarator(Declarator &D) {
// Declarators may be grouped together ("int X, *Y, Z();"). Remember the decls
@ -336,65 +413,9 @@ ParseInitDeclaratorListAfterFirstDeclarator(Declarator &D) {
// At this point, we know that it is not a function definition. Parse the
// rest of the init-declarator-list.
while (1) {
// If a simple-asm-expr is present, parse it.
if (Tok.is(tok::kw_asm)) {
SourceLocation Loc;
OwningExprResult AsmLabel(ParseSimpleAsm(&Loc));
if (AsmLabel.isInvalid()) {
SkipUntil(tok::semi, true, true);
return DeclGroupPtrTy();
}
D.setAsmLabel(AsmLabel.release());
D.SetRangeEnd(Loc);
}
// If attributes are present, parse them.
if (Tok.is(tok::kw___attribute)) {
SourceLocation Loc;
AttributeList *AttrList = ParseAttributes(&Loc);
D.AddAttributes(AttrList, Loc);
}
// Inform the current actions module that we just parsed this declarator.
DeclPtrTy ThisDecl = Actions.ActOnDeclarator(CurScope, D);
DeclsInGroup.push_back(ThisDecl);
// Parse declarator '=' initializer.
if (Tok.is(tok::equal)) {
ConsumeToken();
if (getLang().CPlusPlus0x && Tok.is(tok::kw_delete)) {
SourceLocation DelLoc = ConsumeToken();
Actions.SetDeclDeleted(ThisDecl, DelLoc);
} else {
OwningExprResult Init(ParseInitializer());
if (Init.isInvalid()) {
SkipUntil(tok::semi, true, true);
return DeclGroupPtrTy();
}
Actions.AddInitializerToDecl(ThisDecl, move(Init));
}
} else if (Tok.is(tok::l_paren)) {
// Parse C++ direct initializer: '(' expression-list ')'
SourceLocation LParenLoc = ConsumeParen();
ExprVector Exprs(Actions);
CommaLocsTy CommaLocs;
if (ParseExpressionList(Exprs, CommaLocs)) {
SkipUntil(tok::r_paren);
} else {
// Match the ')'.
SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
assert(!Exprs.empty() && Exprs.size()-1 == CommaLocs.size() &&
"Unexpected number of commas!");
Actions.AddCXXDirectInitializerToDecl(ThisDecl, LParenLoc,
move_arg(Exprs),
&CommaLocs[0], RParenLoc);
}
} else {
Actions.ActOnUninitializedDecl(ThisDecl);
}
DeclPtrTy ThisDecl = ParseDeclarationAfterDeclarator(D);
if (ThisDecl.get())
DeclsInGroup.push_back(ThisDecl);
// If we don't have a comma, it is either the end of the list (a ';') or an
// error, bail out.

View File

@ -95,15 +95,102 @@ Parser::ParseTemplateDeclarationOrSpecialization(unsigned Context,
} while (Tok.is(tok::kw_export) || Tok.is(tok::kw_template));
// Parse the actual template declaration.
return ParseSingleDeclarationAfterTemplate(Context, &ParamLists,
SourceLocation(),
DeclEnd, AS);
}
// FIXME: This accepts template<typename x> int y;
// FIXME: Converting DeclGroupPtr to DeclPtr like this is an insanely gruesome
// hack, will bring up on cfe-dev.
DeclGroupPtrTy DG = ParseDeclarationOrFunctionDefinition(&ParamLists, AS);
// FIXME: Should be ';' location not the token after it. Resolve with above
// fixmes.
DeclEnd = Tok.getLocation();
return DeclPtrTy::make(DG.get());
/// \brief Parse a single declaration that declares a template,
/// template specialization, or explicit instantiation of a template.
///
/// \param TemplateParams if non-NULL, the template parameter lists
/// that preceded this declaration. In this case, the declaration is a
/// template declaration, out-of-line definition of a template, or an
/// explicit template specialization. When NULL, the declaration is an
/// explicit template instantiation.
///
/// \param TemplateLoc when TemplateParams is NULL, the location of
/// the 'template' keyword that indicates that we have an explicit
/// template instantiation.
///
/// \param DeclEnd will receive the source location of the last token
/// within this declaration.
///
/// \param AS the access specifier associated with this
/// declaration. Will be AS_none for namespace-scope declarations.
///
/// \returns the new declaration.
Parser::DeclPtrTy
Parser::ParseSingleDeclarationAfterTemplate(
unsigned Context,
TemplateParameterLists *TemplateParams,
SourceLocation TemplateLoc,
SourceLocation &DeclEnd,
AccessSpecifier AS) {
// Parse the declaration specifiers.
DeclSpec DS;
// FIXME: Pass TemplateLoc through for explicit template instantiations
ParseDeclarationSpecifiers(DS, TemplateParams, AS);
if (Tok.is(tok::semi)) {
DeclEnd = ConsumeToken();
return Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
}
// Parse the declarator.
Declarator DeclaratorInfo(DS, (Declarator::TheContext)Context);
ParseDeclarator(DeclaratorInfo);
// Error parsing the declarator?
if (!DeclaratorInfo.hasName()) {
// If so, skip until the semi-colon or a }.
SkipUntil(tok::r_brace, true, true);
if (Tok.is(tok::semi))
ConsumeToken();
return DeclPtrTy();
}
// If we have a declaration or declarator list, handle it.
if (isDeclarationAfterDeclarator()) {
// Parse this declaration.
DeclPtrTy ThisDecl = ParseDeclarationAfterDeclarator(DeclaratorInfo);
if (Tok.is(tok::comma)) {
Diag(Tok, diag::err_multiple_template_declarators)
<< (TemplateParams == 0);
SkipUntil(tok::semi, true, false);
return ThisDecl;
}
// Eat the semi colon after the declaration.
ExpectAndConsume(tok::semi, diag::err_expected_semi_declation);
return ThisDecl;
}
if (DeclaratorInfo.isFunctionDeclarator() &&
isStartOfFunctionDefinition()) {
if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
Diag(Tok, diag::err_function_declared_typedef);
if (Tok.is(tok::l_brace)) {
// This recovery skips the entire function body. It would be nice
// to simply call ParseFunctionDefinition() below, however Sema
// assumes the declarator represents a function, not a typedef.
ConsumeBrace();
SkipUntil(tok::r_brace, true);
} else {
SkipUntil(tok::semi);
}
return DeclPtrTy();
}
return ParseFunctionDefinition(DeclaratorInfo);
}
if (DeclaratorInfo.isFunctionDeclarator())
Diag(Tok, diag::err_expected_fn_body);
else
Diag(Tok, diag::err_invalid_token_after_toplevel_declarator);
SkipUntil(tok::semi);
return DeclPtrTy();
}
/// ParseTemplateParameters - Parses a template-parameter-list enclosed in
@ -693,3 +780,16 @@ Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
return Tok.isNot(tok::greater) && Tok.isNot(tok::greatergreater);
}
/// \brief Parse a C++ explicit template instantiation
/// (C++ [temp.explicit]).
///
/// explicit-instantiation:
/// 'template' declaration
Parser::DeclPtrTy Parser::ParseExplicitInstantiation(SourceLocation &DeclEnd) {
assert(Tok.is(tok::kw_template) && NextToken().isNot(tok::less) &&
"Token does not start an explicit instantiation.");
SourceLocation TemplateLoc = ConsumeToken();
return ParseSingleDeclarationAfterTemplate(Declarator::FileContext, 0,
TemplateLoc, DeclEnd, AS_none);
}

View File

@ -437,6 +437,29 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration() {
return Actions.ConvertDeclToDeclGroup(SingleDecl);
}
/// \brief Determine whether the current token, if it occurs after a
/// declarator, continues a declaration or declaration list.
bool Parser::isDeclarationAfterDeclarator() {
return Tok.is(tok::equal) || // int X()= -> not a function def
Tok.is(tok::comma) || // int X(), -> not a function def
Tok.is(tok::semi) || // int X(); -> not a function def
Tok.is(tok::kw_asm) || // int X() __asm__ -> not a function def
Tok.is(tok::kw___attribute) || // int X() __attr__ -> not a function def
(getLang().CPlusPlus &&
Tok.is(tok::l_paren)); // int X(0) -> not a function def [C++]
}
/// \brief Determine whether the current token, if it occurs after a
/// declarator, indicates the start of a function definition.
bool Parser::isStartOfFunctionDefinition() {
return Tok.is(tok::l_brace) || // int X() {}
(!getLang().CPlusPlus &&
isDeclarationSpecifier()) || // int X(f) int f; {}
(getLang().CPlusPlus &&
(Tok.is(tok::colon) || // X() : Base() {} (used for ctors)
Tok.is(tok::kw_try))); // X() try { ... }
}
/// ParseDeclarationOrFunctionDefinition - Parse either a function-definition or
/// a declaration. We can't tell which we have until we read up to the
/// compound-statement in function-definition. TemplateParams, if
@ -514,14 +537,8 @@ Parser::ParseDeclarationOrFunctionDefinition(
return DeclGroupPtrTy();
}
// If the declarator is the start of a function definition, handle it.
if (Tok.is(tok::equal) || // int X()= -> not a function def
Tok.is(tok::comma) || // int X(), -> not a function def
Tok.is(tok::semi) || // int X(); -> not a function def
Tok.is(tok::kw_asm) || // int X() __asm__ -> not a function def
Tok.is(tok::kw___attribute) || // int X() __attr__ -> not a function def
(getLang().CPlusPlus &&
Tok.is(tok::l_paren))) { // int X(0) -> not a function def [C++]
// If we have a declaration or declarator list, handle it.
if (isDeclarationAfterDeclarator()) {
// Parse the init-declarator-list for a normal declaration.
DeclGroupPtrTy DG =
ParseInitDeclaratorListAfterFirstDeclarator(DeclaratorInfo);
@ -530,14 +547,8 @@ Parser::ParseDeclarationOrFunctionDefinition(
return DG;
}
if (DeclaratorInfo.isFunctionDeclarator() &&
(Tok.is(tok::l_brace) || // int X() {}
(!getLang().CPlusPlus &&
isDeclarationSpecifier()) || // int X(f) int f; {}
(getLang().CPlusPlus &&
(Tok.is(tok::colon) || // X() : Base() {} (used for ctors)
Tok.is(tok::kw_try))))) { // X() try { ... }
isStartOfFunctionDefinition()) {
if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
Diag(Tok, diag::err_function_declared_typedef);

View File

@ -2,8 +2,7 @@
// Errors
export class foo { }; // expected-error {{expected template}}
template x; // expected-error {{expected '<' after 'template'}} \
// expected-error {{C++ requires a type specifier for all declarations}}
template x; // expected-error {{C++ requires a type specifier for all declarations}}
export template x; // expected-error {{expected '<' after 'template'}} \
// expected-note {{exported templates are unsupported}} \
// expected-error {{C++ requires a type specifier for all declarations}}

View File

@ -0,0 +1,2 @@
// RUN: clang-cc -fsyntax-only -verify %s
template<typename T> int foo(T), bar(T, T); // expected-error{{single entity}}