From e6e40cb7459c9b21b291fe28e46cd4ebcd924dff Mon Sep 17 00:00:00 2001 From: Marek Polacek <polacek@redhat.com> Date: Wed, 29 Jan 2025 15:58:38 -0500 Subject: [PATCH] c++: auto in trailing-return-type in parameter [PR117778] 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. Reviewed-by: Jason Merrill <jason@redhat.com> --- 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 1da881e295b4..9b8be084452e 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -25518,6 +25518,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); } @@ -26283,15 +26303,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)) @@ -26305,6 +26316,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 000000000000..f377e3acc912 --- /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 000000000000..902382651b89 --- /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 000000000000..865fc5cd10d0 --- /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" } +} -- GitLab