[ Entering the contest to fix the oldest PR in this cycle. ]

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
This 18-year-old PR reports that we parse certain comma expressions
as a declaration rather than statement when the statement begins with
a functional-style cast expression.  Consider

  int(x), 0;

which does not declare x--it only casts x to int--, whereas

  int(x), (y);

declares x and y.  We need some kind of look-ahead to decide how we
should disambiguate the construct, because cp_parser_init_declarator
commits eagerly once it has seen "int(x)", and then it's too late to
recover.

This patch makes us try to parse the code as a sequence of declarators;
if that fails, we are likely looking at a statement.  That's a simple
idea, but it's complicated by code like

  void (*p)(void *)(fun);

which initializes a pointer-to-function, or

  int(x), (x) + 1;

which is an expression statement, but the second (x) is parsed as
a valid declarator, only the + after reveals that the whole thing
is an expression.  You can have things like

  int(**p)

which by itself doesn't tell you much.  You can have

  int(*q)(void*)

which looks like it starts with a functional-style cast, but it is not
a cast.  The simple

  int(x) = 42;

has an initializer so it declares x; it is not an assignment.  But then,

    int(d) __attribute__(());

does not have an initializer, but the attribute makes it a declaration.

        PR c++/29834
        PR c++/54905

gcc/cp/ChangeLog:

        * parser.cc (cp_parser_lambda_introducer): Use
        cp_parser_next_token_starts_initializer_p.
        (cp_parser_simple_declaration): Add look-ahead to decide if we're
        looking at a declaration or statement.
        (cp_parser_next_token_starts_initializer_p): New.

gcc/testsuite/ChangeLog:

        * g++.dg/parse/ambig15.C: New test.
        * g++.dg/parse/ambig16.C: New test.
---
 gcc/cp/parser.cc                     | 73 ++++++++++++++++++++++--
 gcc/testsuite/g++.dg/parse/ambig15.C | 83 ++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/parse/ambig16.C | 18 ++++++
 3 files changed, 168 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/parse/ambig15.C
 create mode 100644 gcc/testsuite/g++.dg/parse/ambig16.C

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 1fa0780944b..797cfc3204e 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -2947,6 +2947,8 @@ static bool cp_parser_next_token_ends_template_argument_p
   (cp_parser *);
 static bool cp_parser_nth_token_starts_template_argument_list_p
   (cp_parser *, size_t);
+static bool cp_parser_next_token_starts_initializer_p
+  (cp_parser *);
 static enum tag_types cp_parser_token_is_class_key
   (cp_token *);
 static enum tag_types cp_parser_token_is_type_parameter_key
@@ -11663,9 +11665,7 @@ cp_parser_lambda_introducer (cp_parser* parser, tree 
lambda_expr)
        }
 
       /* Find the initializer for this capture.  */
-      if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)
-         || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
-         || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+      if (cp_parser_next_token_starts_initializer_p (parser))
        {
          /* An explicit initializer exists.  */
          if (cxx_dialect < cxx14)
@@ -11747,9 +11747,7 @@ cp_parser_lambda_introducer (cp_parser* parser, tree 
lambda_expr)
                  /* If what follows is an initializer, the second '...' is
                     invalid.  But for cases like [...xs...], the first one
                     is invalid.  */
-                 if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)
-                     || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
-                     || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+                 if (cp_parser_next_token_starts_initializer_p (parser))
                    ellipsis_loc = loc;
                  error_at (ellipsis_loc, "too many %<...%> in lambda capture");
                  continue;
@@ -16047,6 +16045,58 @@ cp_parser_simple_declaration (cp_parser* parser,
     else
       break;
 
+  /* If we are still uncommitted, we're probably looking at something like
+     T(x), which can be a declaration but does not have to be, depending
+     on what comes after.  Consider
+       int(x), 0;
+     which is _not_ a declaration of x, it's a functional cast, and
+       int(x), (y);
+     which declares x and y.  We need some kind of look-ahead to decide,
+     cp_parser_init_declarator below will commit eagerly once it has seen
+     "int(x)".  So we try to parse this as a sequence of declarators; if
+     that fails, we are likely looking at a statement.  (We could avoid
+     all of this if there is no non-nested comma.)  */
+  if (cp_parser_uncommitted_to_tentative_parse_p (parser)
+      && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+    {
+      bool all_ok = true;
+      cp_lexer_save_tokens (parser->lexer);
+      /* Avoid committing to outer tentative parse.  This is here to parse
+        "void (*p)(void *);" correctly.  */
+      tentative_firewall firewall (parser);
+      while (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
+       {
+         /* Try to parse what follows as a declarator.  */
+         cp_declarator *d
+           = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
+                                   CP_PARSER_FLAGS_NONE,
+                                   /*ctor_dtor_or_conv_p=*/nullptr,
+                                   /*parenthesized_p=*/nullptr,
+                                   /*member_p=*/false,
+                                   /*friend_p=*/false,
+                                   /*static_p=*/false);
+         if (cp_parser_error_occurred (parser) || d == cp_error_declarator)
+           {
+             all_ok = false;
+             break;
+           }
+         /* If this was not a function-style cast, go ahead with a
+            declaration.  */
+         if (d->kind == cdk_function
+             /* A declarator followed by an initializer makes this an
+                init-declarator.  */
+             || cp_parser_next_token_starts_initializer_p (parser)
+             || cp_next_tokens_can_be_attribute_p (parser))
+           break;
+         if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+           cp_lexer_consume_token (parser->lexer);
+       }
+      cp_lexer_rollback_tokens (parser->lexer);
+      if (!all_ok)
+       /* Not a declaration.  Bail and parse as a statement instead.  */
+       goto done;
+    }
+
   tree last_type;
   bool auto_specifier_p;
   /* NULL_TREE if both variable and function declaration are allowed,
@@ -34936,6 +34986,17 @@ cp_parser_nth_token_starts_template_argument_list_p 
(cp_parser * parser,
   return false;
 }
 
+/* Returns true if the next token can start an initializer; that is, it is
+   '=', '(', or '{'.  */
+
+static bool
+cp_parser_next_token_starts_initializer_p (cp_parser *parser)
+{
+  return (cp_lexer_next_token_is (parser->lexer, CPP_EQ)
+         || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
+         || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE));
+}
+
 /* Returns the kind of tag indicated by TOKEN, if it is a class-key,
    or none_type otherwise.  */
 
diff --git a/gcc/testsuite/g++.dg/parse/ambig15.C 
b/gcc/testsuite/g++.dg/parse/ambig15.C
new file mode 100644
index 00000000000..d086b2b6bac
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/ambig15.C
@@ -0,0 +1,83 @@
+// PR c++/29834
+// { dg-do compile { target c++11 } }
+
+void fun (void *);
+using T = void(void*);
+
+struct A {
+  int foo ();
+};
+
+void
+f1 ()
+{
+  int(x), a, b, c, d, e, f, g, h, etc;
+  int(x), a, b, c, d, e, f, g, h, etc, (new int);
+}
+
+int
+f2 (int x, int *p, int **pp)
+{
+  // Statements.
+  int(x), 0;
+  (int(x), 0);
+  (int(x)), 0;
+  int(*p), 0;
+  int(**pp), !**pp;
+  int(x), x + 1;
+  int(x), (x) + 1;
+  int(x), x++;
+  int(x), --x;
+  int(p[1]), 0;
+
+  // Declarations.
+  int(a), (b), (c);
+  a = b = c = 42;
+  int(g) = 0, (h) = 0;
+  int(k), l __attribute__((unused));
+  int(&r) = x;
+  void (*p1)(void*) = fun;
+  void (*p2)(void*);
+  void (*p3)(void*) __attribute__((unused)) = fun;
+  void (**p4)(void*) __attribute__((unused));
+  void (*p5)(void*) __attribute__((unused));
+  void (p6)(void*) __attribute__((unused));
+  void (&p7)(void*) __attribute__((unused)) = fun;
+  void (*p8)(void*)(fun);
+  void (*p9)(void*){fun};
+  int (p10)(int), m;
+  int(d) __attribute__(());
+  int(e) __attribute__(()), f;
+  int (A::*foo)() = &A::foo;
+  int (A::*foo2)();
+  int (*A::*foo3)();
+  int(j[1]);
+  T(fun2);
+
+  return a + b + c + r;
+}
+
+struct Doh {
+  Doh(int) {}
+};
+
+
+int
+f3 (int x)
+{
+  Doh(x), ++x;
+  return Doh(x), x;
+}
+
+void
+bad (int x, int y, int z)
+{
+  int(x) = 1; // { dg-error "shadows a parameter" }
+  int(y)(1); // { dg-error "shadows a parameter" }
+  int(z){1}; // { dg-error "shadows a parameter" }
+  void (*p)(void*), 0; // { dg-error "" }
+  int(a),;  // { dg-error "expected" }
+  int(x) __attribute__(()), 0; // { dg-error "" }
+  int(i), i; // { dg-error "redeclaration" }
+  T(fun), ++x; // { dg-error "invalid cast" }
+}
diff --git a/gcc/testsuite/g++.dg/parse/ambig16.C 
b/gcc/testsuite/g++.dg/parse/ambig16.C
new file mode 100644
index 00000000000..51bc16dffcf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/ambig16.C
@@ -0,0 +1,18 @@
+// PR c++/54905
+// { dg-do compile }
+
+struct F
+{
+};
+
+F f;
+struct A
+{
+  A(F& s);
+};
+
+void
+foo ()
+{
+  A(f), 1;
+}

base-commit: 493c55578fe00f5f4a7534b8f5cb5213f86f4d01
-- 
2.45.2

Reply via email to