Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
Here we are wrongly parsing

  int y(auto(42));

which uses the C++23 cast-to-prvalue feature, and initializes y to 42.
However, we were treating the auto as an implicit template parameter.

Fixing the auto{42} case is easy, but when auto is followed by a (,
I found the fix to be much more involved.  For instance, we cannot
use cp_parser_expression, because that can give hard errors.  It's
also necessary to disambiguate 'auto(i)' as 'auto i', not a cast.
auto(), auto(int), auto(f)(int), auto(*), auto(i[]), auto(...), etc.
are all function declarations.  We have to look at more than one
token to decide.

In this fix, I'm (ab)using cp_parser_declarator, with member_p=false
so that it doesn't commit.  But it handles even more complicated
cases as

  int fn (auto (*const **&f)(int) -> char);

        PR c++/112410

gcc/cp/ChangeLog:

        * parser.cc (cp_parser_simple_type_specifier): Disambiguate
        between a variable and function declaration with auto.
        (cp_parser_constructor_declarator_p): Use cp_parser_starts_param_decl_p.
        (cp_parser_starts_param_decl_p): New, factored out of
        cp_parser_constructor_declarator_p.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp23/auto-fncast13.C: New test.
---
 gcc/cp/parser.cc                           | 79 ++++++++++++++++++----
 gcc/testsuite/g++.dg/cpp23/auto-fncast13.C | 61 +++++++++++++++++
 2 files changed, 125 insertions(+), 15 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast13.C

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 5116bcb78f6..3edee092e56 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -2887,6 +2887,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_starts_param_decl_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
@@ -19991,6 +19993,8 @@ cp_parser_simple_type_specifier (cp_parser* parser,
          /* The 'auto' might be the placeholder return type for a function decl
             with trailing return type.  */
          bool have_trailing_return_fn_decl = false;
+         /* Or it might be auto(x) or auto {x}.  */
+         bool decay_copy = false;
 
          cp_parser_parse_tentatively (parser);
          cp_lexer_consume_token (parser->lexer);
@@ -20002,12 +20006,43 @@ cp_parser_simple_type_specifier (cp_parser* parser,
              if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
                {
                  cp_lexer_consume_token (parser->lexer);
+                 /* An auto specifier that appears in a parameter declaration
+                    might be the placeholder for a late return type, or it
+                    can be an implicit template parameter.  But it can also
+                    be a prvalue cast, rendering the current construct not
+                    a function declaration at all.  Check if it decidedly
+                    cannot be a valid function-style cast first...  */
+                 if (!cp_parser_starts_param_decl_p (parser))
+                   {
+                     /* Ug, we couldn't tell.  Try to parse whatever follows
+                        as a declarator; this should detect cases like
+                        auto(i), auto(*), auto(f[]), auto(f)(int).  */
+                     cp_parser_declarator (parser, CP_PARSER_DECLARATOR_EITHER,
+                                           CP_PARSER_FLAGS_NONE,
+                                           /*ctor_dtor_or_conv_p=*/nullptr,
+                                           /*parenthesized_p=*/NULL,
+                                           /*member_p=*/true,
+                                           /*friend_p=*/false,
+                                           /*static_p=*/true);
+                     /* OK, if we now see a ')', it looks like a valid
+                        function declaration.  Otherwise, let's go with
+                        auto(x).  */
+                     decay_copy
+                       = cp_lexer_next_token_is_not (parser->lexer,
+                                                     CPP_CLOSE_PAREN);
+                   }
                  cp_parser_skip_to_closing_parenthesis (parser,
                                                         /*recovering*/false,
                                                         /*or_comma*/false,
                                                         /*consume_paren*/true);
                  continue;
                }
+             /* The easy case: it has got to be C++23 auto(x).  */
+             else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+               {
+                 decay_copy = true;
+                 break;
+               }
 
              if (cp_lexer_next_token_is (parser->lexer, CPP_DEREF))
                {
@@ -20019,6 +20054,12 @@ cp_parser_simple_type_specifier (cp_parser* parser,
            }
          cp_parser_abort_tentative_parse (parser);
 
+         if (decay_copy)
+           {
+             type = error_mark_node;
+             break;
+           }
+
          if (have_trailing_return_fn_decl)
            {
              type = make_auto ();
@@ -32066,21 +32107,7 @@ cp_parser_constructor_declarator_p (cp_parser *parser, 
cp_parser_flags flags,
          && !cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
        constructor_p = false;
 
-      if (constructor_p
-         && cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN)
-         && cp_lexer_next_token_is_not (parser->lexer, CPP_ELLIPSIS)
-         /* A parameter declaration begins with a decl-specifier,
-            which is either the "attribute" keyword, a storage class
-            specifier, or (usually) a type-specifier.  */
-         && !cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer)
-         /* GNU attributes can actually appear both at the start of
-            a parameter and parenthesized declarator.
-            S (__attribute__((unused)) int);
-            is a constructor, but
-            S (__attribute__((unused)) foo) (int);
-            is a function declaration. [[attribute]] can appear in the
-            first form too, but not in the second form.  */
-         && !cp_next_tokens_can_be_std_attribute_p (parser))
+      if (constructor_p && !cp_parser_starts_param_decl_p (parser))
        {
          tree type;
          tree pushed_scope = NULL_TREE;
@@ -34334,6 +34361,28 @@ cp_parser_nth_token_starts_template_argument_list_p 
(cp_parser * parser,
   return false;
 }
 
+/* We've consumed a '(' and now we're asking if what follows starts
+   a parameter declaration.  Return true if it does.  */
+
+static bool
+cp_parser_starts_param_decl_p (cp_parser *parser)
+{
+  return (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)
+         || cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
+         /* A parameter declaration begins with a decl-specifier,
+            which is either the "attribute" keyword, a storage class
+            specifier, or (usually) a type-specifier.  */
+         || cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer)
+         /* GNU attributes can actually appear both at the start of
+            a parameter and parenthesized declarator.
+            S (__attribute__((unused)) int);
+            is a constructor, but
+            S (__attribute__((unused)) foo) (int);
+            is a function declaration.  [[attribute]] can appear in the
+            first form too, but not in the second form.  */
+         || cp_next_tokens_can_be_std_attribute_p (parser));
+}
+
 /* 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/cpp23/auto-fncast13.C 
b/gcc/testsuite/g++.dg/cpp23/auto-fncast13.C
new file mode 100644
index 00000000000..1bceffb70cf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast13.C
@@ -0,0 +1,61 @@
+// PR c++/112410
+// { dg-do compile { target c++23 } }
+
+int f1 (auto(int) -> char);
+int f2 (auto x);
+int f3 (auto);
+int f4 (auto(i));
+
+int v1 (auto(42));
+int v2 (auto{42});
+int e1 (auto{i}); // { dg-error "not declared" }
+int i;
+int v3 (auto{i});
+int v4 (auto(i + 1));
+int v5 (auto(+i));
+int v6 (auto(i = 4));
+
+int f5 (auto(i));
+int f6 (auto());
+int f7 (auto(int));
+int f8 (auto(f)(int));
+int f9 (auto(...) -> char);
+// FIXME: ICEs (PR c++/89867)
+//int f10 (auto(__attribute__((unused)) i));
+int f11 (auto((i)));
+int f12 (auto(i[]));
+int f13 (auto(*i));
+int f14 (auto(*));
+
+int e2 (auto{}); // { dg-error "invalid use of .auto." }
+int e3 (auto(i, i)); // { dg-error "invalid use of .auto." }
+
+char bar (int);
+char baz ();
+char qux (...);
+
+void
+g (int i)
+{
+  f1 (bar);
+  f2 (42);
+  f3 (42);
+  f4 (42);
+  f5 (42);
+  f6 (baz);
+  f7 (bar);
+  f8 (bar);
+  f9 (qux);
+//  f10 (42);
+  f11 (42);
+  f12 (&i);
+  f13 (&i);
+  f14 (&i);
+
+  v1 = 1;
+  v2 = 2;
+  v3 = 3;
+  v4 = 4;
+  v5 = 5;
+  v6 = 6;
+}

base-commit: 016b3002e13acefb5773da78659c050187d3a96f
-- 
2.41.0

Reply via email to