diff mbox series

c++: fix parsing with auto(x) at block scope [PR112482]

Message ID 20231214210227.1190177-1-polacek@redhat.com
State New
Headers show
Series c++: fix parsing with auto(x) at block scope [PR112482] | expand

Commit Message

Marek Polacek Dec. 14, 2023, 9:02 p.m. UTC
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
This is sort of like r14-5514, but at block scope.  Consider

  struct A { A(int, int); };
  void
  g (int a)
  {
    A bar(auto(a), 42); // not a fn decl
  }

where we emit error: 'auto' parameter not permitted in this context
which is bogus -- bar doesn't declare a function, so the auto is OK,
but we don't know it till we've seen the second argument.  The error
comes from grokdeclarator invoked just after we've parsed the auto(a).

A possible approach seems to be to delay the auto parameter checking
and only check once we know we are indeed dealing with a function
declaration.  For tparms, we should still emit the error right away.

	PR c++/112482

gcc/cp/ChangeLog:

	* decl.cc (grokdeclarator): Do not issue the auto parameter error while
	tentatively parsing a function parameter.
	* parser.cc (cp_parser_parameter_declaration_clause): Check it here.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp23/auto-fncast15.C: New test.
---
 gcc/cp/decl.cc                             | 13 +++++++++++--
 gcc/cp/parser.cc                           | 17 +++++++++++++++--
 gcc/testsuite/g++.dg/cpp23/auto-fncast15.C | 21 +++++++++++++++++++++
 3 files changed, 47 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast15.C


base-commit: 767e2674875139ac8f354ceee655c1a9561b9779

Comments

Jason Merrill Dec. 14, 2023, 9:07 p.m. UTC | #1
On 12/14/23 16:02, Marek Polacek wrote:
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

OK.

> -- >8 --
> This is sort of like r14-5514, but at block scope.  Consider
> 
>    struct A { A(int, int); };
>    void
>    g (int a)
>    {
>      A bar(auto(a), 42); // not a fn decl
>    }
> 
> where we emit error: 'auto' parameter not permitted in this context
> which is bogus -- bar doesn't declare a function, so the auto is OK,
> but we don't know it till we've seen the second argument.  The error
> comes from grokdeclarator invoked just after we've parsed the auto(a).
> 
> A possible approach seems to be to delay the auto parameter checking
> and only check once we know we are indeed dealing with a function
> declaration.  For tparms, we should still emit the error right away.
> 
> 	PR c++/112482
> 
> gcc/cp/ChangeLog:
> 
> 	* decl.cc (grokdeclarator): Do not issue the auto parameter error while
> 	tentatively parsing a function parameter.
> 	* parser.cc (cp_parser_parameter_declaration_clause): Check it here.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp23/auto-fncast15.C: New test.
> ---
>   gcc/cp/decl.cc                             | 13 +++++++++++--
>   gcc/cp/parser.cc                           | 17 +++++++++++++++--
>   gcc/testsuite/g++.dg/cpp23/auto-fncast15.C | 21 +++++++++++++++++++++
>   3 files changed, 47 insertions(+), 4 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast15.C
> 
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 4d17ead123a..1ffe4c82748 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -14203,6 +14203,7 @@ grokdeclarator (const cp_declarator *declarator,
>         tree auto_node = type_uses_auto (type);
>         if (auto_node && !(cxx_dialect >= cxx17 && template_parm_flag))
>   	{
> +	  bool err_p = true;
>   	  if (cxx_dialect >= cxx14)
>   	    {
>   	      if (decl_context == PARM && AUTO_IS_DECLTYPE (auto_node))
> @@ -14221,13 +14222,21 @@ grokdeclarator (const cp_declarator *declarator,
>   			    "abbreviated function template");
>   		  inform (DECL_SOURCE_LOCATION (c), "%qD declared here", c);
>   		}
> -	      else
> +	      else if (decl_context == CATCHPARM || template_parm_flag)
>   		error_at (typespec_loc,
>   			  "%<auto%> parameter not permitted in this context");
> +	      else
> +		/* Do not issue an error while tentatively parsing a function
> +		   parameter: for T t(auto(a), 42);, when we just saw the 1st
> +		   parameter, we don't know yet that this construct won't be
> +		   a function declaration.  Defer the checking to
> +		   cp_parser_parameter_declaration_clause.  */
> +		err_p = false;
>   	    }
>   	  else
>   	    error_at (typespec_loc, "parameter declared %<auto%>");
> -	  type = error_mark_node;
> +	  if (err_p)
> +	    type = error_mark_node;
>   	}
>   
>         /* A parameter declared as an array of T is really a pointer to T.
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 58e910d64af..e4fbab1bab5 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -25102,8 +25102,21 @@ cp_parser_parameter_declaration_clause (cp_parser* parser,
>        committed yet, nor should we.  Pushing here will detect the error
>        of redefining a parameter.  */
>     if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
> -    for (tree p : pending_decls)
> -      pushdecl (p);
> +    {
> +      for (tree p : pending_decls)
> +	pushdecl (p);
> +
> +      /* Delayed checking of auto parameters.  */
> +      if (!parser->auto_is_implicit_function_template_parm_p
> +	  && cxx_dialect >= cxx14)
> +	for (tree p = parameters; p; p = TREE_CHAIN (p))
> +	  if (type_uses_auto (TREE_TYPE (TREE_VALUE (p))))
> +	    {
> +	      error_at (location_of (TREE_VALUE (p)),
> +			"%<auto%> parameter not permitted in this context");
> +	      TREE_TYPE (TREE_VALUE (p)) = error_mark_node;
> +	    }
> +    }
>   
>     /* Finish the parameter list.  */
>     if (!ellipsis_p)
> diff --git a/gcc/testsuite/g++.dg/cpp23/auto-fncast15.C b/gcc/testsuite/g++.dg/cpp23/auto-fncast15.C
> new file mode 100644
> index 00000000000..deb1efcc46c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast15.C
> @@ -0,0 +1,21 @@
> +// PR c++/112482
> +// { dg-do compile { target c++23 } }
> +// { dg-options "-Wno-vexing-parse" }
> +
> +void foo (auto i, auto j);
> +
> +struct A {
> +   A(int,int);
> +};
> +
> +void
> +g (int a)
> +{
> +  A b1(auto(42), auto(42));
> +  A b2(auto(a), auto(42));
> +  A b3(auto(42), auto(a));
> +  A b4(auto(a), // { dg-error "13:'auto' parameter" }
> +       auto(a2)); // { dg-error "13:'auto' parameter" }
> +  int v1(auto(42));
> +  int fn1(auto(a)); // { dg-error "16:'auto' parameter" }
> +}
> 
> base-commit: 767e2674875139ac8f354ceee655c1a9561b9779
diff mbox series

Patch

diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4d17ead123a..1ffe4c82748 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -14203,6 +14203,7 @@  grokdeclarator (const cp_declarator *declarator,
       tree auto_node = type_uses_auto (type);
       if (auto_node && !(cxx_dialect >= cxx17 && template_parm_flag))
 	{
+	  bool err_p = true;
 	  if (cxx_dialect >= cxx14)
 	    {
 	      if (decl_context == PARM && AUTO_IS_DECLTYPE (auto_node))
@@ -14221,13 +14222,21 @@  grokdeclarator (const cp_declarator *declarator,
 			    "abbreviated function template");
 		  inform (DECL_SOURCE_LOCATION (c), "%qD declared here", c);
 		}
-	      else
+	      else if (decl_context == CATCHPARM || template_parm_flag)
 		error_at (typespec_loc,
 			  "%<auto%> parameter not permitted in this context");
+	      else
+		/* Do not issue an error while tentatively parsing a function
+		   parameter: for T t(auto(a), 42);, when we just saw the 1st
+		   parameter, we don't know yet that this construct won't be
+		   a function declaration.  Defer the checking to
+		   cp_parser_parameter_declaration_clause.  */
+		err_p = false;
 	    }
 	  else
 	    error_at (typespec_loc, "parameter declared %<auto%>");
-	  type = error_mark_node;
+	  if (err_p)
+	    type = error_mark_node;
 	}
 
       /* A parameter declared as an array of T is really a pointer to T.
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 58e910d64af..e4fbab1bab5 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -25102,8 +25102,21 @@  cp_parser_parameter_declaration_clause (cp_parser* parser,
      committed yet, nor should we.  Pushing here will detect the error
      of redefining a parameter.  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
-    for (tree p : pending_decls)
-      pushdecl (p);
+    {
+      for (tree p : pending_decls)
+	pushdecl (p);
+
+      /* Delayed checking of auto parameters.  */
+      if (!parser->auto_is_implicit_function_template_parm_p
+	  && cxx_dialect >= cxx14)
+	for (tree p = parameters; p; p = TREE_CHAIN (p))
+	  if (type_uses_auto (TREE_TYPE (TREE_VALUE (p))))
+	    {
+	      error_at (location_of (TREE_VALUE (p)),
+			"%<auto%> parameter not permitted in this context");
+	      TREE_TYPE (TREE_VALUE (p)) = error_mark_node;
+	    }
+    }
 
   /* Finish the parameter list.  */
   if (!ellipsis_p)
diff --git a/gcc/testsuite/g++.dg/cpp23/auto-fncast15.C b/gcc/testsuite/g++.dg/cpp23/auto-fncast15.C
new file mode 100644
index 00000000000..deb1efcc46c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast15.C
@@ -0,0 +1,21 @@ 
+// PR c++/112482
+// { dg-do compile { target c++23 } }
+// { dg-options "-Wno-vexing-parse" }
+
+void foo (auto i, auto j);
+
+struct A {
+   A(int,int);
+};
+
+void
+g (int a)
+{
+  A b1(auto(42), auto(42));
+  A b2(auto(a), auto(42));
+  A b3(auto(42), auto(a));
+  A b4(auto(a), // { dg-error "13:'auto' parameter" }
+       auto(a2)); // { dg-error "13:'auto' parameter" }
+  int v1(auto(42));
+  int fn1(auto(a)); // { dg-error "16:'auto' parameter" }
+}