r185598 - PR16480: Reimplement token-caching for constructor initializer lists. This
Richard Smith
richard-llvm at metafoo.co.uk
Wed Jul 3 17:13:48 PDT 2013
Author: rsmith
Date: Wed Jul 3 19:13:48 2013
New Revision: 185598
URL: http://llvm.org/viewvc/llvm-project?rev=185598&view=rev
Log:
PR16480: Reimplement token-caching for constructor initializer lists. This
previously didn't work if a mem-initializer-id had a template argument which
contained parentheses or braces.
We now implement a simple rule: just look for a ') {' or '} {' that is not
nested. The '{' is assumed to start the function-body. There are still two
cases which we misparse, where the ') {' comes from a compound literal or
from a lambda. The former case is not valid C++, and the latter will probably
not be valid C++ once DR1607 is resolved, so these seem to be of low value,
and we do not regress on them with this change. EDG and g++ also misparse
both of these cases.
Modified:
cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp
cfe/trunk/test/Parser/cxx-member-initializers.cpp
Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=185598&r1=185597&r2=185598&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Wed Jul 3 19:13:48 2013
@@ -152,7 +152,7 @@ def err_expected_ident_lparen : Error<"e
def err_expected_ident_lbrace : Error<"expected identifier or '{'">;
def err_expected_lbrace : Error<"expected '{'">;
def err_expected_lparen : Error<"expected '('">;
-def err_expected_lparen_or_lbrace : Error<"expected '('or '{'">;
+def err_expected_lparen_or_lbrace : Error<"expected '(' or '{'">;
def err_expected_rparen : Error<"expected ')'">;
def err_expected_lsquare : Error<"expected '['">;
def err_expected_rsquare : Error<"expected ']'">;
Modified: cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp?rev=185598&r1=185597&r2=185598&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp (original)
+++ cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp Wed Jul 3 19:13:48 2013
@@ -151,9 +151,9 @@ NamedDecl *Parser::ParseCXXInlineMethodD
// We didn't find the left-brace we expected after the
// constructor initializer; we already printed an error, and it's likely
// impossible to recover, so don't try to parse this method later.
- // If we stopped at a semicolon, consume it to avoid an extra warning.
- if (Tok.is(tok::semi))
- ConsumeToken();
+ // Skip over the rest of the decl and back to somewhere that looks
+ // reasonable.
+ SkipMalformedDecl();
delete getCurrentClass().LateParsedDeclarations.back();
getCurrentClass().LateParsedDeclarations.pop_back();
return FnD;
@@ -652,83 +652,171 @@ bool Parser::ConsumeAndStoreUntil(tok::T
/// the opening brace of the function body. The opening brace will be consumed
/// if and only if there was no error.
///
-/// \return True on error.
+/// \return True on error.
bool Parser::ConsumeAndStoreFunctionPrologue(CachedTokens &Toks) {
if (Tok.is(tok::kw_try)) {
Toks.push_back(Tok);
ConsumeToken();
}
- bool ReadInitializer = false;
- if (Tok.is(tok::colon)) {
- // Initializers can contain braces too.
- Toks.push_back(Tok);
- ConsumeToken();
- while (Tok.is(tok::identifier) || Tok.is(tok::coloncolon)) {
- if (Tok.is(tok::eof) || Tok.is(tok::semi))
- return Diag(Tok.getLocation(), diag::err_expected_lbrace);
+ if (Tok.isNot(tok::colon)) {
+ // Easy case, just a function body.
- // Grab the identifier.
- if (!ConsumeAndStoreUntil(tok::l_paren, tok::l_brace, Toks,
- /*StopAtSemi=*/true,
- /*ConsumeFinalToken=*/false))
- return Diag(Tok.getLocation(), diag::err_expected_lparen);
+ // Grab any remaining garbage to be diagnosed later. We stop when we reach a
+ // brace: an opening one is the function body, while a closing one probably
+ // means we've reached the end of the class.
+ ConsumeAndStoreUntil(tok::l_brace, tok::r_brace, Toks,
+ /*StopAtSemi=*/true,
+ /*ConsumeFinalToken=*/false);
+ if (Tok.isNot(tok::l_brace))
+ return Diag(Tok.getLocation(), diag::err_expected_lbrace);
- tok::TokenKind kind = Tok.getKind();
- Toks.push_back(Tok);
- bool IsLParen = (kind == tok::l_paren);
- SourceLocation LOpen = Tok.getLocation();
+ Toks.push_back(Tok);
+ ConsumeBrace();
+ return false;
+ }
- if (IsLParen) {
- ConsumeParen();
- } else {
- assert(kind == tok::l_brace && "Must be left paren or brace here.");
- ConsumeBrace();
- // In C++03, this has to be the start of the function body, which
- // means the initializer is malformed; we'll diagnose it later.
- if (!getLangOpts().CPlusPlus11)
- return false;
- }
+ Toks.push_back(Tok);
+ ConsumeToken();
- // Grab the initializer
- if (!ConsumeAndStoreUntil(IsLParen ? tok::r_paren : tok::r_brace,
- Toks, /*StopAtSemi=*/true)) {
- Diag(Tok, IsLParen ? diag::err_expected_rparen :
- diag::err_expected_rbrace);
- Diag(LOpen, diag::note_matching) << (IsLParen ? "(" : "{");
+ // We can't reliably skip over a mem-initializer-id, because it could be
+ // a template-id involving not-yet-declared names. Given:
+ //
+ // S ( ) : a < b < c > ( e )
+ //
+ // 'e' might be an initializer or part of a template argument, depending
+ // on whether 'b' is a template.
+
+ // Track whether we might be inside a template argument. We can give
+ // significantly better diagnostics if we know that we're not.
+ bool MightBeTemplateArgument = false;
+
+ while (true) {
+ // Skip over the mem-initializer-id, if possible.
+ if (Tok.is(tok::kw_decltype)) {
+ Toks.push_back(Tok);
+ SourceLocation OpenLoc = ConsumeToken();
+ if (Tok.isNot(tok::l_paren))
+ return Diag(Tok.getLocation(), diag::err_expected_lparen_after)
+ << "decltype";
+ Toks.push_back(Tok);
+ ConsumeParen();
+ if (!ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/true)) {
+ Diag(Tok.getLocation(), diag::err_expected_rparen);
+ Diag(OpenLoc, diag::note_matching) << "(";
return true;
}
-
- // Grab pack ellipsis, if present
- if (Tok.is(tok::ellipsis)) {
+ }
+ do {
+ // Walk over a component of a nested-name-specifier.
+ if (Tok.is(tok::coloncolon)) {
Toks.push_back(Tok);
ConsumeToken();
+
+ if (Tok.is(tok::kw_template)) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+ }
}
- // Grab the separating comma, if any.
- if (Tok.is(tok::comma)) {
+ if (Tok.is(tok::identifier) || Tok.is(tok::kw_template)) {
Toks.push_back(Tok);
ConsumeToken();
- } else if (Tok.isNot(tok::l_brace)) {
- ReadInitializer = true;
+ } else if (Tok.is(tok::code_completion)) {
+ Toks.push_back(Tok);
+ ConsumeCodeCompletionToken();
+ // Consume the rest of the initializers permissively.
+ // FIXME: We should be able to perform code-completion here even if
+ // there isn't a subsequent '{' token.
+ MightBeTemplateArgument = true;
+ break;
+ } else {
break;
}
+ } while (Tok.is(tok::coloncolon));
+
+ if (Tok.is(tok::less))
+ MightBeTemplateArgument = true;
+
+ if (MightBeTemplateArgument) {
+ // We may be inside a template argument list. Grab up to the start of the
+ // next parenthesized initializer or braced-init-list. This *might* be the
+ // initializer, or it might be a subexpression in the template argument
+ // list.
+ // FIXME: Count angle brackets, and clear MightBeTemplateArgument
+ // if all angles are closed.
+ if (!ConsumeAndStoreUntil(tok::l_paren, tok::l_brace, Toks,
+ /*StopAtSemi=*/true,
+ /*ConsumeFinalToken=*/false)) {
+ // We're not just missing the initializer, we're also missing the
+ // function body!
+ return Diag(Tok.getLocation(), diag::err_expected_lbrace);
+ }
+ } else if (Tok.isNot(tok::l_paren) && Tok.isNot(tok::l_brace)) {
+ // We found something weird in a mem-initializer-id.
+ return Diag(Tok.getLocation(), getLangOpts().CPlusPlus11
+ ? diag::err_expected_lparen_or_lbrace
+ : diag::err_expected_lparen);
}
- }
- // Grab any remaining garbage to be diagnosed later. We stop when we reach a
- // brace: an opening one is the function body, while a closing one probably
- // means we've reached the end of the class.
- ConsumeAndStoreUntil(tok::l_brace, tok::r_brace, Toks,
- /*StopAtSemi=*/true,
- /*ConsumeFinalToken=*/false);
- if (Tok.isNot(tok::l_brace)) {
- if (ReadInitializer)
+ tok::TokenKind kind = Tok.getKind();
+ Toks.push_back(Tok);
+ bool IsLParen = (kind == tok::l_paren);
+ SourceLocation OpenLoc = Tok.getLocation();
+
+ if (IsLParen) {
+ ConsumeParen();
+ } else {
+ assert(kind == tok::l_brace && "Must be left paren or brace here.");
+ ConsumeBrace();
+ // In C++03, this has to be the start of the function body, which
+ // means the initializer is malformed; we'll diagnose it later.
+ if (!getLangOpts().CPlusPlus11)
+ return false;
+ }
+
+ // Grab the initializer (or the subexpression of the template argument).
+ // FIXME: If we support lambdas here, we'll need to set StopAtSemi to false
+ // if we might be inside the braces of a lambda-expression.
+ if (!ConsumeAndStoreUntil(IsLParen ? tok::r_paren : tok::r_brace,
+ Toks, /*StopAtSemi=*/true)) {
+ Diag(Tok, IsLParen ? diag::err_expected_rparen :
+ diag::err_expected_rbrace);
+ Diag(OpenLoc, diag::note_matching) << (IsLParen ? "(" : "{");
+ return true;
+ }
+
+ // Grab pack ellipsis, if present.
+ if (Tok.is(tok::ellipsis)) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+ }
+
+ // If we know we just consumed a mem-initializer, we must have ',' or '{'
+ // next.
+ if (Tok.is(tok::comma)) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+ } else if (Tok.is(tok::l_brace)) {
+ // This is the function body if the ')' or '}' is immediately followed by
+ // a '{'. That cannot happen within a template argument, apart from the
+ // case where a template argument contains a compound literal:
+ //
+ // S ( ) : a < b < c > ( d ) { }
+ // // End of declaration, or still inside the template argument?
+ //
+ // ... and the case where the template argument contains a lambda:
+ //
+ // S ( ) : a < 0 && b < c > ( d ) + [ ] ( ) { return 0; }
+ // ( ) > ( ) { }
+ //
+ // FIXME: Disambiguate these cases. Note that the latter case is probably
+ // going to be made ill-formed by core issue 1607.
+ Toks.push_back(Tok);
+ ConsumeBrace();
+ return false;
+ } else if (!MightBeTemplateArgument) {
return Diag(Tok.getLocation(), diag::err_expected_lbrace_or_comma);
- return Diag(Tok.getLocation(), diag::err_expected_lbrace);
+ }
}
-
- Toks.push_back(Tok);
- ConsumeBrace();
- return false;
}
Modified: cfe/trunk/test/Parser/cxx-member-initializers.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-member-initializers.cpp?rev=185598&r1=185597&r2=185598&view=diff
==============================================================================
--- cfe/trunk/test/Parser/cxx-member-initializers.cpp (original)
+++ cfe/trunk/test/Parser/cxx-member-initializers.cpp Wed Jul 3 19:13:48 2013
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
struct x {
x() : a(4) ; // expected-error {{expected '{'}}
@@ -11,5 +11,71 @@ struct y {
struct z {
int a;
- z() : a {} // expected-error {{expected '('}}
-};
+ z() : a {}
+}; // expected-error {{expected '{'}}
+
+namespace PR16480 {
+ template<int n> struct X {
+ X();
+ X(int);
+ };
+
+ struct A : X<0> {
+ A() : X<a<b>{0}.n>() {}
+
+ template<int> struct a {
+ int n;
+ };
+
+ static const int b = 1;
+ };
+
+ struct B : X<0> {
+ B() : X<a<b>{0} {}
+
+ static const int a = 0, b = 0;
+ };
+
+ template<int> struct a {
+ constexpr a(int) {}
+ constexpr operator int() const { return 0; }
+ };
+
+ struct C : X<0> {
+ C() : X<a<b>(0)>() {}
+
+ static const int b = 0;
+ };
+
+ struct D : X<0> {
+ D() : X<a<b>(0) {}
+
+ static const int a = 0, b = 0;
+ };
+
+ template<typename T> struct E : X<0> {
+ E(X<0>) : X<(0)>{} {}
+ E(X<1>) : X<int{}>{} {}
+ E(X<2>) : X<(0)>() {}
+ E(X<3>) : X<int{}>() {}
+ };
+
+ // FIXME: This should be valid in the union of C99 and C++11.
+ struct F : X<0> {
+ F() : X<A<T>().n + (T){}.n>{} {} // expected-error +{{}}
+
+ struct T { int n; };
+ template<typename> struct A { int n; };
+ }; // expected-error +{{}}
+
+ // FIXME: This is valid now, but may be made ill-formed by DR1607.
+ struct G : X<0> {
+ G() : X<0 && [](){return 0;}()>{} // expected-error +{{}}
+ }; // expected-error +{{}}
+
+ struct Errs : X<0> {
+ Errs(X<0>) : decltype X<0>() {} // expected-error {{expected '(' after 'decltype'}}
+ Errs(X<1>) : what is this () {} // expected-error {{expected '(' or '{'}}
+ Errs(X<2>) : decltype(X<0> // expected-note {{to match this '('}}
+ }; // expected-error {{expected ')'}}
+}
More information about the cfe-commits
mailing list