From 2399628cea7b5e1aa9836a9077ce00ee2f76187a Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 12 May 2009 21:31:51 +0000 Subject: [PATCH] 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 --- .../clang/Basic/DiagnosticParseKinds.td | 3 + clang/include/clang/Parse/Parser.h | 10 ++ clang/lib/Parse/ParseDecl.cpp | 157 ++++++++++-------- clang/lib/Parse/ParseTemplate.cpp | 116 ++++++++++++- clang/lib/Parse/Parser.cpp | 41 +++-- clang/test/Parser/cxx-template-decl.cpp | 3 +- clang/test/SemaTemplate/temp.cpp | 2 + 7 files changed, 239 insertions(+), 93 deletions(-) create mode 100644 clang/test/SemaTemplate/temp.cpp diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 9481ce531710..adfbb3540b48 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -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'">; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 084996d61c19..49554422bbeb 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -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] diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index b3c2d8c55561..9d7d9d400f7a 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -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. diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index d38df93ace1b..22d11779ab32 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -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 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); +} diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 94036dbd86e6..e886bd43ca99 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -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); diff --git a/clang/test/Parser/cxx-template-decl.cpp b/clang/test/Parser/cxx-template-decl.cpp index ec333de8b4f1..6a795eeabe49 100644 --- a/clang/test/Parser/cxx-template-decl.cpp +++ b/clang/test/Parser/cxx-template-decl.cpp @@ -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}} diff --git a/clang/test/SemaTemplate/temp.cpp b/clang/test/SemaTemplate/temp.cpp new file mode 100644 index 000000000000..bd37cc668b1c --- /dev/null +++ b/clang/test/SemaTemplate/temp.cpp @@ -0,0 +1,2 @@ +// RUN: clang-cc -fsyntax-only -verify %s +template int foo(T), bar(T, T); // expected-error{{single entity}}