[ 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.) */