From: Richard Smith Date: Wed, 16 May 2012 23:40:17 +0000 (+0000) Subject: Recover better from a missing 'typename' in a function template definition. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=25582fc0aeac053cdc115ee95cbed53f28bf592e;p=clang Recover better from a missing 'typename' in a function template definition. Disambiguate past such a potential problem, and use the absence of 'typename' to break ties in favor of a parenthesized thingy being an initializer, if nothing else in the declaration disambiguates it as declaring a function. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@156963 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index bd5592cd33..814b45afc7 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1753,7 +1753,8 @@ private: /// BracedCastResult. /// Doesn't consume tokens. TPResult - isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False()); + isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False(), + bool *HasMissingTypename = 0); // "Tentative parsing" functions, used for disambiguation. If a parsing error // is encountered they will return TPResult::Error(). @@ -1762,13 +1763,13 @@ private: // that more tentative parsing is necessary for disambiguation. // They all consume tokens, so backtracking should be used after calling them. - TPResult TryParseDeclarationSpecifier(); + TPResult TryParseDeclarationSpecifier(bool *HasMissingTypename = 0); TPResult TryParseSimpleDeclaration(bool AllowForRangeDecl); TPResult TryParseTypeofSpecifier(); TPResult TryParseProtocolQualifiers(); TPResult TryParseInitDeclaratorList(); TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier=true); - TPResult TryParseParameterDeclarationClause(); + TPResult TryParseParameterDeclarationClause(bool *InvalidAsDeclaration = 0); TPResult TryParseFunctionDeclarator(); TPResult TryParseBracketDeclarator(); diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index e9685d25fe..c2e539ca61 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -827,6 +827,10 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { /// be either a decl-specifier or a function-style cast, and TPResult::Error() /// if a parsing error was found and reported. /// +/// If HasMissingTypename is provided, a name with a dependent scope specifier +/// will be treated as ambiguous if the 'typename' keyword is missing. If this +/// happens, *HasMissingTypename will be set to 'true'. +/// /// decl-specifier: /// storage-class-specifier /// type-specifier @@ -918,7 +922,8 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { /// [GNU] restrict /// Parser::TPResult -Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) { +Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, + bool *HasMissingTypename) { switch (Tok.getKind()) { case tok::identifier: // foo::bar // Check for need to substitute AltiVec __vector keyword @@ -936,7 +941,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) { return (!getLangOpts().ObjC1 && Next.is(tok::identifier)) ? TPResult::True() : TPResult::False(); } - return isCXXDeclarationSpecifier(BracedCastResult); + return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename); case tok::coloncolon: { // ::foo::bar const Token &Next = NextToken(); @@ -950,7 +955,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) { // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) return TPResult::Error(); - return isCXXDeclarationSpecifier(BracedCastResult); + return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename); // decl-specifier: // storage-class-specifier @@ -1052,12 +1057,20 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) { bool isIdentifier = Tok.is(tok::identifier); TPResult TPR = TPResult::False(); if (!isIdentifier) - TPR = isCXXDeclarationSpecifier(BracedCastResult); + TPR = isCXXDeclarationSpecifier(BracedCastResult, + HasMissingTypename); PA.Revert(); if (isIdentifier || TPR == TPResult::True() || TPR == TPResult::Error()) return TPResult::Error(); + + if (HasMissingTypename) { + // We can't tell whether this is a missing 'typename' or a valid + // expression. + *HasMissingTypename = true; + return TPResult::Ambiguous(); + } } } return TPResult::False(); @@ -1221,21 +1234,24 @@ Parser::TPResult Parser::TryParseProtocolQualifiers() { return TPResult::Error(); } -Parser::TPResult Parser::TryParseDeclarationSpecifier() { - TPResult TPR = isCXXDeclarationSpecifier(); +Parser::TPResult +Parser::TryParseDeclarationSpecifier(bool *HasMissingTypename) { + TPResult TPR = isCXXDeclarationSpecifier(TPResult::False(), + HasMissingTypename); if (TPR != TPResult::Ambiguous()) return TPR; if (Tok.is(tok::kw_typeof)) TryParseTypeofSpecifier(); else { + if (Tok.is(tok::annot_cxxscope)) + ConsumeToken(); ConsumeToken(); if (getLangOpts().ObjC1 && Tok.is(tok::less)) TryParseProtocolQualifiers(); } - assert(Tok.is(tok::l_paren) && "Expected '('!"); return TPResult::Ambiguous(); } @@ -1263,9 +1279,28 @@ bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) { TentativeParsingAction PA(*this); ConsumeParen(); - TPResult TPR = TryParseParameterDeclarationClause(); - if (TPR == TPResult::Ambiguous() && Tok.isNot(tok::r_paren)) - TPR = TPResult::False(); + bool InvalidAsDeclaration = false; + TPResult TPR = TryParseParameterDeclarationClause(&InvalidAsDeclaration); + if (TPR == TPResult::Ambiguous()) { + if (Tok.isNot(tok::r_paren)) + TPR = TPResult::False(); + else { + const Token &Next = NextToken(); + if (Next.is(tok::amp) || Next.is(tok::ampamp) || + Next.is(tok::kw_const) || Next.is(tok::kw_volatile) || + Next.is(tok::kw_throw) || Next.is(tok::kw_noexcept) || + Next.is(tok::l_square) || isCXX0XVirtSpecifier(Next) || + Next.is(tok::l_brace) || Next.is(tok::kw_try) || + Next.is(tok::equal)) + // The next token cannot appear after a constructor-style initializer, + // and can appear next in a function definition. This must be a function + // declarator. + TPR = TPResult::True(); + else if (InvalidAsDeclaration) + // Use the absence of 'typename' as a tie-breaker. + TPR = TPResult::False(); + } + } SourceLocation TPLoc = Tok.getLocation(); PA.Revert(); @@ -1303,7 +1338,8 @@ bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) { /// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt] /// attributes[opt] '=' assignment-expression /// -Parser::TPResult Parser::TryParseParameterDeclarationClause() { +Parser::TPResult +Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration) { if (Tok.is(tok::r_paren)) return TPResult::True(); @@ -1336,7 +1372,7 @@ Parser::TPResult Parser::TryParseParameterDeclarationClause() { // decl-specifier-seq // A parameter-declaration's initializer must be preceded by an '=', so // decl-specifier-seq '{' is not a parameter in C++11. - TPResult TPR = TryParseDeclarationSpecifier(); + TPResult TPR = TryParseDeclarationSpecifier(InvalidAsDeclaration); if (TPR != TPResult::Ambiguous()) return TPR; diff --git a/test/CXX/except/except.spec/p5-pointers.cpp b/test/CXX/except/except.spec/p5-pointers.cpp index dd3c0600ce..f855520aa2 100644 --- a/test/CXX/except/except.spec/p5-pointers.cpp +++ b/test/CXX/except/except.spec/p5-pointers.cpp @@ -65,9 +65,9 @@ void fnptrs() void (*(*t7)())() throw(B1) = &s8; // valid void (*(*t8)())() throw(A) = &s8; // expected-error {{return types differ}} void (*(*t9)())() throw(D) = &s8; // expected-error {{return types differ}} - void (*t10)(void (*)() throw(B1)) = &s9; // valid expected-warning{{disambiguated}} - void (*t11)(void (*)() throw(A)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}} - void (*t12)(void (*)() throw(D)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}} + void (*t10)(void (*)() throw(B1)) = &s9; // valid + void (*t11)(void (*)() throw(A)) = &s9; // expected-error {{argument types differ}} + void (*t12)(void (*)() throw(D)) = &s9; // expected-error {{argument types differ}} } // Member function stuff diff --git a/test/SemaCXX/unknown-type-name.cpp b/test/SemaCXX/unknown-type-name.cpp index 7912fbec3f..893e0cc5dc 100644 --- a/test/SemaCXX/unknown-type-name.cpp +++ b/test/SemaCXX/unknown-type-name.cpp @@ -22,6 +22,11 @@ struct A { type f(); type g(); + + static int n; + static type m; + static int h(T::type, int); // expected-error{{missing 'typename'}} + static int h(T::type x, char); // expected-error{{missing 'typename'}} }; template @@ -30,12 +35,50 @@ A::type g(T t) { return t; } // expected-error{{missing 'typename'}} template A::type A::f() { return type(); } // expected-error{{missing 'typename'}} +template +void f(T::type) { } // expected-error{{missing 'typename'}} + +template +void g(T::type x) { } // expected-error{{missing 'typename'}} + +template +void f(T::type, int) { } // expected-error{{missing 'typename'}} + +template +void f(T::type x, char) { } // expected-error{{missing 'typename'}} + template void f(int, T::type) { } // expected-error{{missing 'typename'}} +template +void f(char, T::type x) { } // expected-error{{missing 'typename'}} + template void f(int, T::type, int) { } // expected-error{{missing 'typename'}} +template +void f(int, T::type x, char) { } // expected-error{{missing 'typename'}} + +template int A::n(T::value); // ok +template +A::type // expected-error{{missing 'typename'}} +A::m(T::value, 0); // ok + +template int A::h(T::type, int) {} // expected-error{{missing 'typename'}} +template int A::h(T::type x, char) {} // expected-error{{missing 'typename'}} + +template int h(T::type, int); // expected-error{{missing 'typename'}} +template int h(T::type x, char); // expected-error{{missing 'typename'}} + +template int junk1(T::junk); // expected-error{{declared as a template}} +template int junk2(T::junk) throw(); // expected-error{{missing 'typename'}} +template int junk3(T::junk) = delete; // expected-error{{missing 'typename'}} expected-warning{{C++11}} +template int junk4(T::junk j); // expected-error{{missing 'typename'}} + +// FIXME: We can tell this was intended to be a function because it does not +// have a dependent nested name specifier. +template int i(T::type, int()); // expected-error{{variable 'i' declared as a template}} + // FIXME: We know which type specifier should have been specified here. Provide // a fix-it to add 'typename A::type' template