[v2] c++: auto in trailing-return-type in parameter [PR117778]
Checks
Context |
Check |
Description |
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 |
success
|
Test passed
|
linaro-tcwg-bot/tcwg_gcc_build--master-arm |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gcc_check--master-arm |
success
|
Test passed
|
Commit Message
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
base-commit: d6418fe22684f9335474d1fd405ade45954c069d
Comments
On 1/31/25 4:23 PM, Marek Polacek wrote:
> 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?
OK.
> -- >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
@@ -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);
new file mode 100644
@@ -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" }
new file mode 100644
@@ -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 ();
+}
new file mode 100644
@@ -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" }
+}