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

Reply via email to