[ 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