On Fri, Jan 31, 2025 at 09:34:52AM -0500, Jason Merrill wrote: > On 1/30/25 5:24 PM, Marek Polacek wrote: > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/14? > > > > -- >8 -- > > This PR describes a few issues, both ICE and rejects-valid, but > > ultimately the problem is that we don't properly synthesize the > > second auto in: > > > > int > > g (auto fp() -> auto) > > { > > return fp (); > > } > > > > since r12-5860, which disabled auto_is_implicit_function_template_parm_p > > in cp_parser_parameter_declaration after parsing the decl-specifier-seq. > > > > If there is no trailing auto, there is no problem. > > > > So we have to make sure auto_is_implicit_function_template_parm_p is > > properly set when parsing the trailing auto. A complication is that > > one can write: > > > > auto f (auto fp(auto fp2() -> auto) -> auto) -> auto; > > ~~~~~~~ > > > > where only the underlined auto should be synthesized. So when we > > parse a parameter-declaration-clause inside another > > parameter-declaration-clause, we should not enable the flag. We > > have no flags to keep track of such nesting, but I think I can walk > > current_binding_level to see if we find ourselves in such an unlikely > > scenario. > > > > PR c++/117778 > > > > gcc/cp/ChangeLog: > > > > * parser.cc (cp_parser_late_return_type_opt): Maybe override > > auto_is_implicit_function_template_parm_p. > > (cp_parser_parameter_declaration): Update commentary. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/cpp1y/lambda-generic-117778.C: New test. > > * g++.dg/cpp2a/abbrev-fn2.C: New test. > > * g++.dg/cpp2a/abbrev-fn3.C: New test. > > --- > > gcc/cp/parser.cc | 24 ++++++++- > > .../g++.dg/cpp1y/lambda-generic-117778.C | 12 +++++ > > gcc/testsuite/g++.dg/cpp2a/abbrev-fn2.C | 49 +++++++++++++++++++ > > gcc/testsuite/g++.dg/cpp2a/abbrev-fn3.C | 7 +++ > > 4 files changed, 90 insertions(+), 2 deletions(-) > > create mode 100644 gcc/testsuite/g++.dg/cpp1y/lambda-generic-117778.C > > create mode 100644 gcc/testsuite/g++.dg/cpp2a/abbrev-fn2.C > > create mode 100644 gcc/testsuite/g++.dg/cpp2a/abbrev-fn3.C > > > > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > > index 44515bb9074..89c5c2721a7 100644 > > --- a/gcc/cp/parser.cc > > +++ b/gcc/cp/parser.cc > > @@ -25514,6 +25514,25 @@ cp_parser_late_return_type_opt (cp_parser *parser, > > cp_declarator *declarator, > > /* Consume the ->. */ > > cp_lexer_consume_token (parser->lexer); > > + /* We may be in the context of parsing a parameter declaration, > > + namely, its declarator. auto_is_implicit_function_template_parm_p > > + will be disabled in that case. But for code like > > + > > + int g (auto fp() -> auto); > > + > > + we have to re-enable the flag for the trailing auto. However, that > > + only applies for the outermost trailing auto in a parameter clause; in > > + > > + int f2 (auto fp(auto fp2() -> auto) -> auto); > > + > > + the inner -> auto should not be synthesized. */ > > + int i = 0; > > + for (cp_binding_level *b = current_binding_level; > > + b->kind == sk_function_parms; b = b->level_chain) > > + ++i; > > + auto cleanup = make_temp_override > > + (parser->auto_is_implicit_function_template_parm_p, i == 2); > > This looks like it will wrongly allow declaring an implicit template within > a function; you need a testcase with local extern declarations.
Ah right, I didn't check that so it was broken. We should check !current_function_decl. > Incidentally, it seems odd that the override in > cp_parser_parameter_declaration is before an error early exit a few lines > below, moving it after that would avoid needing to clean it up on that path. Good point, adjusted. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- This PR describes a few issues, both ICE and rejects-valid, but ultimately the problem is that we don't properly synthesize the second auto in: int g (auto fp() -> auto) { return fp (); } since r12-5860, which disabled auto_is_implicit_function_template_parm_p in cp_parser_parameter_declaration after parsing the decl-specifier-seq. If there is no trailing auto, there is no problem. So we have to make sure auto_is_implicit_function_template_parm_p is properly set when parsing the trailing auto. A complication is that one can write: auto f (auto fp(auto fp2() -> auto) -> auto) -> auto; ~~~~~~~ where only the underlined auto should be synthesized. So when we parse a parameter-declaration-clause inside another parameter-declaration-clause, we should not enable the flag. We have no flags to keep track of such nesting, but I think I can walk current_binding_level to see if we find ourselves in such an unlikely scenario. PR c++/117778 gcc/cp/ChangeLog: * parser.cc (cp_parser_late_return_type_opt): Maybe override auto_is_implicit_function_template_parm_p. (cp_parser_parameter_declaration): Move a make_temp_override below. gcc/testsuite/ChangeLog: * g++.dg/cpp1y/lambda-generic-117778.C: New test. * g++.dg/cpp2a/abbrev-fn2.C: New test. * g++.dg/cpp2a/abbrev-fn3.C: New test. --- gcc/cp/parser.cc | 39 +++++++++++---- .../g++.dg/cpp1y/lambda-generic-117778.C | 12 +++++ gcc/testsuite/g++.dg/cpp2a/abbrev-fn2.C | 49 +++++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/abbrev-fn3.C | 15 ++++++ 4 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1y/lambda-generic-117778.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/abbrev-fn2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/abbrev-fn3.C diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 44515bb9074..7b5bbb171e8 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -25514,6 +25514,26 @@ cp_parser_late_return_type_opt (cp_parser *parser, cp_declarator *declarator, /* Consume the ->. */ cp_lexer_consume_token (parser->lexer); + /* We may be in the context of parsing a parameter declaration, + namely, its declarator. auto_is_implicit_function_template_parm_p + will be disabled in that case. But for code like + + int g (auto fp() -> auto); + + we have to re-enable the flag for the trailing auto. However, that + only applies for the outermost trailing auto in a parameter clause; in + + int f2 (auto fp(auto fp2() -> auto) -> auto); + + the inner -> auto should not be synthesized. */ + int i = 0; + for (cp_binding_level *b = current_binding_level; + b->kind == sk_function_parms; b = b->level_chain) + ++i; + auto cleanup = make_temp_override + (parser->auto_is_implicit_function_template_parm_p, + (i == 2 && !current_function_decl)); + type = cp_parser_trailing_type_id (parser); } @@ -26279,15 +26299,6 @@ cp_parser_parameter_declaration (cp_parser *parser, &decl_specifiers, &declares_class_or_enum); - /* [dcl.spec.auto.general]: "A placeholder-type-specifier of the form - type-constraint opt auto can be used as a decl-specifier of the - decl-specifier-seq of a parameter-declaration of a function declaration - or lambda-expression..." but we must not synthesize an implicit template - type parameter in its declarator. That is, in "void f(auto[auto{10}]);" - we want to synthesize only the first auto. */ - auto cleanup = make_temp_override - (parser->auto_is_implicit_function_template_parm_p, false); - /* Complain about missing 'typename' or other invalid type names. */ if (!decl_specifiers.any_type_specifiers_p && cp_parser_parse_and_diagnose_invalid_type_name (parser)) @@ -26301,6 +26312,16 @@ cp_parser_parameter_declaration (cp_parser *parser, return NULL; } + /* [dcl.spec.auto.general]: "A placeholder-type-specifier of the form + type-constraint opt auto can be used as a decl-specifier of the + decl-specifier-seq of a parameter-declaration of a function declaration + or lambda-expression..." but we must not synthesize an implicit template + type parameter in its declarator (except the trailing-return-type). + That is, in "void f(auto[auto{10}]);" we want to synthesize only the + first auto. */ + auto cleanup = make_temp_override + (parser->auto_is_implicit_function_template_parm_p, false); + /* Peek at the next token. */ token = cp_lexer_peek_token (parser->lexer); diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-117778.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-117778.C new file mode 100644 index 00000000000..f377e3acc91 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-117778.C @@ -0,0 +1,12 @@ +// PR c++/117778 +// { dg-do compile { target c++14 } } + +auto l1 = [](auto (*fp)() -> auto) { return fp; }; +auto l2 = [](auto fp() -> auto) { return fp; }; +auto l3 = [](auto fp()) { return fp; }; +auto l4 = [](auto (*fp)()) { return fp; }; +auto l5 = [](auto fp() -> auto) -> auto { return fp; }; +auto l6 = [](auto fp(auto fp2()) -> auto) -> auto { return fp; }; // { dg-error ".auto. parameter not permitted" } +auto l7 = [](auto fp(auto fp2() -> auto) -> auto) -> auto { return fp; }; // { dg-error ".auto. parameter not permitted" } +auto l8 = [](int fp(auto fp2())) { return fp; }; // { dg-error ".auto. parameter not permitted" } +auto l9 = [](auto fp(auto fp2() -> auto) -> auto) { return fp; }; // { dg-error ".auto. parameter not permitted" } diff --git a/gcc/testsuite/g++.dg/cpp2a/abbrev-fn2.C b/gcc/testsuite/g++.dg/cpp2a/abbrev-fn2.C new file mode 100644 index 00000000000..902382651b8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/abbrev-fn2.C @@ -0,0 +1,49 @@ +// PR c++/117778 +// { dg-do run { target c++20 } } + +int +f (auto fp()) +{ + return fp (); +} + +int +g (auto fp() -> auto) +{ + return fp (); +} + +int +h (auto (*fp)() -> auto) +{ + return fp (); +} + +auto +fa (auto fp()) -> auto +{ + return fp (); +} + +auto +ga (auto fp() -> auto) -> auto +{ + return fp (); +} + +auto +ha (auto (*fp)() -> auto) -> auto +{ + return fp (); +} + +int bar() { return 42; } + +int +main () +{ + if (f (bar) != 42 || g (bar) != 42 || h (bar) != 42) + __builtin_abort (); + if (fa (bar) != 42 || ga (bar) != 42 || ha (bar) != 42) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/abbrev-fn3.C b/gcc/testsuite/g++.dg/cpp2a/abbrev-fn3.C new file mode 100644 index 00000000000..865fc5cd10d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/abbrev-fn3.C @@ -0,0 +1,15 @@ +// PR c++/117778 +// { dg-do compile { target c++20 } } + +int f1 (auto fp(auto fp2())); // { dg-error ".auto. parameter not permitted" } +int f2 (auto fp(auto fp2() -> auto)); // { dg-error ".auto. parameter not permitted" } +auto f3 (auto fp() -> auto) -> auto; +auto f3 (auto fp(auto fp2() -> auto) -> auto) -> auto; // { dg-error ".auto. parameter not permitted" } + +void +g () +{ + extern int e1 (auto fp()); // { dg-error ".auto. parameter not permitted" } + extern int e2 (auto fp() -> auto); // { dg-error ".auto. parameter not permitted" } + extern int e3 (auto fp(auto fp2() -> auto) -> auto); // { dg-error ".auto. parameter not permitted" } +} base-commit: d6418fe22684f9335474d1fd405ade45954c069d -- 2.48.1