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