[v2] c++: P2360R0: Extend init-stmt to allow alias-decl [PR102617]

Message ID YXHpRnYi9/cJtMX3@redhat.com
State Committed
Commit 5469d58d6620195c6275d11b474d686f5921c3ba
Headers
Series [v2] c++: P2360R0: Extend init-stmt to allow alias-decl [PR102617] |

Commit Message

Marek Polacek Oct. 21, 2021, 10:27 p.m. UTC
  On Thu, Oct 21, 2021 at 04:56:57PM -0400, Jason Merrill wrote:
> On 10/21/21 16:26, Marek Polacek wrote:
> > The following patch implements C++23 P2360R0.  This proposal merely
> > extends init-statement to contain alias-declaration.  init-statement
> > is used in if/for/switch.  The unsightly duplication of the new code
> > seems to be necessary to handle
> > 
> >    for ( init-statement condition[opt] ; expression[opt] ) statement
> > 
> > as well as
> > 
> >    for ( init-statement[opt] for-range-declaration : for-range-initializer ) statement
> 
> It seems like the duplication of the new code is a consequence of the
> duplication of the old code.  I'd think we could remove the duplication by
> remembering the result of cp_parser_range_based_for_with_init_p and then
> recursing at the end if it was true.  Or check it in cp_parser_for and call
> cp_parser_init_statement twice.

That works well, just had to move the pedwarn too.  dg.exp passes, full testing
running, OK if it passes?

-- >8 --
The following patch implements C++23 P2360R0.  This proposal merely
extends init-statement to contain alias-declaration.  init-statement
is used in if/for/switch.  It also removes the unsightly duplication
of code by calling cp_parser_init_statement twice.

	PR c++/102617

gcc/cp/ChangeLog:

	* parser.c (cp_parser_for): Maybe call cp_parser_init_statement
	twice.  Warn about range-based for loops with initializer here.
	(cp_parser_init_statement): Don't duplicate code.  Allow
	alias-declaration in init-statement.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp23/init-stmt1.C: New test.
	* g++.dg/cpp23/init-stmt2.C: New test.
---
 gcc/cp/parser.c                         | 70 ++++++++++++++-----------
 gcc/testsuite/g++.dg/cpp23/init-stmt1.C | 31 +++++++++++
 gcc/testsuite/g++.dg/cpp23/init-stmt2.C | 25 +++++++++
 3 files changed, 95 insertions(+), 31 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/init-stmt1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/init-stmt2.C


base-commit: 1373066a46d8d47abd97e46a005aef3b3dbfe94a
  

Comments

Jason Merrill Oct. 26, 2021, 2:23 p.m. UTC | #1
On 10/21/21 18:27, Marek Polacek wrote:
> On Thu, Oct 21, 2021 at 04:56:57PM -0400, Jason Merrill wrote:
>> On 10/21/21 16:26, Marek Polacek wrote:
>>> The following patch implements C++23 P2360R0.  This proposal merely
>>> extends init-statement to contain alias-declaration.  init-statement
>>> is used in if/for/switch.  The unsightly duplication of the new code
>>> seems to be necessary to handle
>>>
>>>     for ( init-statement condition[opt] ; expression[opt] ) statement
>>>
>>> as well as
>>>
>>>     for ( init-statement[opt] for-range-declaration : for-range-initializer ) statement
>>
>> It seems like the duplication of the new code is a consequence of the
>> duplication of the old code.  I'd think we could remove the duplication by
>> remembering the result of cp_parser_range_based_for_with_init_p and then
>> recursing at the end if it was true.  Or check it in cp_parser_for and call
>> cp_parser_init_statement twice.
> 
> That works well, just had to move the pedwarn too.  dg.exp passes, full testing
> running, OK if it passes?

OK.

> -- >8 --
> The following patch implements C++23 P2360R0.  This proposal merely
> extends init-statement to contain alias-declaration.  init-statement
> is used in if/for/switch.  It also removes the unsightly duplication
> of code by calling cp_parser_init_statement twice.
> 
> 	PR c++/102617
> 
> gcc/cp/ChangeLog:
> 
> 	* parser.c (cp_parser_for): Maybe call cp_parser_init_statement
> 	twice.  Warn about range-based for loops with initializer here.
> 	(cp_parser_init_statement): Don't duplicate code.  Allow
> 	alias-declaration in init-statement.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp23/init-stmt1.C: New test.
> 	* g++.dg/cpp23/init-stmt2.C: New test.
> ---
>   gcc/cp/parser.c                         | 70 ++++++++++++++-----------
>   gcc/testsuite/g++.dg/cpp23/init-stmt1.C | 31 +++++++++++
>   gcc/testsuite/g++.dg/cpp23/init-stmt2.C | 25 +++++++++
>   3 files changed, 95 insertions(+), 31 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp23/init-stmt1.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp23/init-stmt2.C
> 
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 49d951cfb19..93335c817d7 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -12040,6 +12040,7 @@ cp_parser_handle_directive_omp_attributes (cp_parser *parser, tree *pattrs,
>     init-statement:
>       expression-statement
>       simple-declaration
> +    alias-declaration
>   
>     TM Extension:
>   
> @@ -13327,6 +13328,23 @@ cp_parser_for (cp_parser *parser, bool ivdep, unsigned short unroll)
>     /* Begin the for-statement.  */
>     scope = begin_for_scope (&init);
>   
> +  /* Maybe parse the optional init-statement in a range-based for loop.  */
> +  if (cp_parser_range_based_for_with_init_p (parser)
> +      /* Checked for diagnostic purposes only.  */
> +      && cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
> +    {
> +      tree dummy;
> +      cp_parser_init_statement (parser, &dummy);
> +      if (cxx_dialect < cxx20)
> +	{
> +	  pedwarn (cp_lexer_peek_token (parser->lexer)->location,
> +		   OPT_Wc__20_extensions,
> +		   "range-based %<for%> loops with initializer only "
> +		   "available with %<-std=c++20%> or %<-std=gnu++20%>");
> +	  decl = error_mark_node;
> +	}
> +    }
> +
>     /* Parse the initialization.  */
>     is_range_for = cp_parser_init_statement (parser, &decl);
>   
> @@ -13987,12 +14005,13 @@ cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep,
>     return statement;
>   }
>   
> -/* Parse a init-statement or the declarator of a range-based-for.
> +/* Parse an init-statement or the declarator of a range-based-for.
>      Returns true if a range-based-for declaration is seen.
>   
>      init-statement:
>        expression-statement
> -     simple-declaration  */
> +     simple-declaration
> +     alias-declaration  */
>   
>   static bool
>   cp_parser_init_statement (cp_parser *parser, tree *decl)
> @@ -14008,40 +14027,29 @@ cp_parser_init_statement (cp_parser *parser, tree *decl)
>         bool is_range_for = false;
>         bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
>   
> -      /* Try to parse the init-statement.  */
> -      if (cp_parser_range_based_for_with_init_p (parser))
> -	{
> -	  tree dummy;
> -	  cp_parser_parse_tentatively (parser);
> -	  /* Parse the declaration.  */
> -	  cp_parser_simple_declaration (parser,
> -					/*function_definition_allowed_p=*/false,
> -					&dummy);
> -	  cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
> -	  if (!cp_parser_parse_definitely (parser))
> -	    /* That didn't work, try to parse it as an expression-statement.  */
> -	    cp_parser_expression_statement (parser, NULL_TREE);
> -
> -	  if (cxx_dialect < cxx20)
> -	    {
> -	      pedwarn (cp_lexer_peek_token (parser->lexer)->location,
> -		       OPT_Wc__20_extensions,
> -		       "range-based %<for%> loops with initializer only "
> -		       "available with %<-std=c++20%> or %<-std=gnu++20%>");
> -	      *decl = error_mark_node;
> -	    }
> -	}
> -
>         /* A colon is used in range-based for.  */
>         parser->colon_corrects_to_scope_p = false;
>   
>         /* We're going to speculatively look for a declaration, falling back
>   	 to an expression, if necessary.  */
>         cp_parser_parse_tentatively (parser);
> -      /* Parse the declaration.  */
> -      cp_parser_simple_declaration (parser,
> -				    /*function_definition_allowed_p=*/false,
> -				    decl);
> +      bool expect_semicolon_p = true;
> +      if (cp_lexer_next_token_is_keyword (parser->lexer, RID_USING))
> +	{
> +	  cp_parser_alias_declaration (parser);
> +	  expect_semicolon_p = false;
> +	  if (cxx_dialect < cxx23
> +	      && !cp_parser_uncommitted_to_tentative_parse_p (parser))
> +	    pedwarn (cp_lexer_peek_token (parser->lexer)->location,
> +		     OPT_Wc__23_extensions,
> +		     "alias-declaration in init-statement only "
> +		     "available with %<-std=c++23%> or %<-std=gnu++23%>");
> +	}
> +      else
> +	/* Parse the declaration.  */
> +	cp_parser_simple_declaration (parser,
> +				      /*function_definition_allowed_p=*/false,
> +				      decl);
>         parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
>         if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
>   	{
> @@ -14054,7 +14062,7 @@ cp_parser_init_statement (cp_parser *parser, tree *decl)
>   		     "range-based %<for%> loops only available with "
>   		     "%<-std=c++11%> or %<-std=gnu++11%>");
>   	}
> -      else
> +      else if (expect_semicolon_p)
>   	/* The ';' is not consumed yet because we told
>   	   cp_parser_simple_declaration not to.  */
>   	cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
> diff --git a/gcc/testsuite/g++.dg/cpp23/init-stmt1.C b/gcc/testsuite/g++.dg/cpp23/init-stmt1.C
> new file mode 100644
> index 00000000000..29e3256aae6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/init-stmt1.C
> @@ -0,0 +1,31 @@
> +// PR c++/102617
> +// P2360R0: Extend init-statement to allow alias-declaration
> +// { dg-do compile { target c++20 } }
> +// Test valid use.
> +
> +int v[10];
> +
> +void
> +g ()
> +{
> +  for (using T = int; (T) false;) // { dg-error "only available with" "" { target c++20_only } }
> +    ;
> +  for (using T = int; T e : v) // { dg-error "only available with" "" { target c++20_only } }
> +    (void) e;
> +  if (using T = int; true) // { dg-error "only available with" "" { target c++20_only } }
> +    {
> +      T x = 0;
> +      (void) x;
> +    }
> +  if constexpr (using T = int; true) // { dg-error "only available with" "" { target c++20_only } }
> +    {
> +      T x = 0;
> +      (void) x;
> +    }
> +  switch (using T = int; 42) // { dg-error "only available with" "" { target c++20_only } }
> +    case 42:
> +      {
> +	T x = 0;
> +	(void) x;
> +      }
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp23/init-stmt2.C b/gcc/testsuite/g++.dg/cpp23/init-stmt2.C
> new file mode 100644
> index 00000000000..ca6201bc340
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/init-stmt2.C
> @@ -0,0 +1,25 @@
> +// PR c++/102617
> +// P2360R0: Extend init-statement to allow alias-declaration
> +// { dg-do compile { target c++23 } }
> +// Test invalid use.
> +
> +int v[10];
> +namespace N { using X = int; }
> +
> +void
> +g ()
> +{
> +  for (using N::X; false;) // { dg-error "expected" }
> +    ;
> +  for (using N::X; int e : v) // { dg-error "expected" }
> +    (void) e;
> +  for (using T = int; using U = int; int e : v) // { dg-error "" }
> +    ;
> +  if (using N::X; false) // { dg-error "expected" }
> +    {}
> +  switch (using N::X; 0) // { dg-error "expected" }
> +    ;
> + if (using T = int;) // { dg-error "expected" }
> +  {
> +  }
> +}
> 
> base-commit: 1373066a46d8d47abd97e46a005aef3b3dbfe94a
>
  

Patch

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 49d951cfb19..93335c817d7 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -12040,6 +12040,7 @@  cp_parser_handle_directive_omp_attributes (cp_parser *parser, tree *pattrs,
   init-statement:
     expression-statement
     simple-declaration
+    alias-declaration
 
   TM Extension:
 
@@ -13327,6 +13328,23 @@  cp_parser_for (cp_parser *parser, bool ivdep, unsigned short unroll)
   /* Begin the for-statement.  */
   scope = begin_for_scope (&init);
 
+  /* Maybe parse the optional init-statement in a range-based for loop.  */
+  if (cp_parser_range_based_for_with_init_p (parser)
+      /* Checked for diagnostic purposes only.  */
+      && cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
+    {
+      tree dummy;
+      cp_parser_init_statement (parser, &dummy);
+      if (cxx_dialect < cxx20)
+	{
+	  pedwarn (cp_lexer_peek_token (parser->lexer)->location,
+		   OPT_Wc__20_extensions,
+		   "range-based %<for%> loops with initializer only "
+		   "available with %<-std=c++20%> or %<-std=gnu++20%>");
+	  decl = error_mark_node;
+	}
+    }
+
   /* Parse the initialization.  */
   is_range_for = cp_parser_init_statement (parser, &decl);
 
@@ -13987,12 +14005,13 @@  cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep,
   return statement;
 }
 
-/* Parse a init-statement or the declarator of a range-based-for.
+/* Parse an init-statement or the declarator of a range-based-for.
    Returns true if a range-based-for declaration is seen.
 
    init-statement:
      expression-statement
-     simple-declaration  */
+     simple-declaration
+     alias-declaration  */
 
 static bool
 cp_parser_init_statement (cp_parser *parser, tree *decl)
@@ -14008,40 +14027,29 @@  cp_parser_init_statement (cp_parser *parser, tree *decl)
       bool is_range_for = false;
       bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
 
-      /* Try to parse the init-statement.  */
-      if (cp_parser_range_based_for_with_init_p (parser))
-	{
-	  tree dummy;
-	  cp_parser_parse_tentatively (parser);
-	  /* Parse the declaration.  */
-	  cp_parser_simple_declaration (parser,
-					/*function_definition_allowed_p=*/false,
-					&dummy);
-	  cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
-	  if (!cp_parser_parse_definitely (parser))
-	    /* That didn't work, try to parse it as an expression-statement.  */
-	    cp_parser_expression_statement (parser, NULL_TREE);
-
-	  if (cxx_dialect < cxx20)
-	    {
-	      pedwarn (cp_lexer_peek_token (parser->lexer)->location,
-		       OPT_Wc__20_extensions,
-		       "range-based %<for%> loops with initializer only "
-		       "available with %<-std=c++20%> or %<-std=gnu++20%>");
-	      *decl = error_mark_node;
-	    }
-	}
-
       /* A colon is used in range-based for.  */
       parser->colon_corrects_to_scope_p = false;
 
       /* We're going to speculatively look for a declaration, falling back
 	 to an expression, if necessary.  */
       cp_parser_parse_tentatively (parser);
-      /* Parse the declaration.  */
-      cp_parser_simple_declaration (parser,
-				    /*function_definition_allowed_p=*/false,
-				    decl);
+      bool expect_semicolon_p = true;
+      if (cp_lexer_next_token_is_keyword (parser->lexer, RID_USING))
+	{
+	  cp_parser_alias_declaration (parser);
+	  expect_semicolon_p = false;
+	  if (cxx_dialect < cxx23
+	      && !cp_parser_uncommitted_to_tentative_parse_p (parser))
+	    pedwarn (cp_lexer_peek_token (parser->lexer)->location,
+		     OPT_Wc__23_extensions,
+		     "alias-declaration in init-statement only "
+		     "available with %<-std=c++23%> or %<-std=gnu++23%>");
+	}
+      else
+	/* Parse the declaration.  */
+	cp_parser_simple_declaration (parser,
+				      /*function_definition_allowed_p=*/false,
+				      decl);
       parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
       if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
 	{
@@ -14054,7 +14062,7 @@  cp_parser_init_statement (cp_parser *parser, tree *decl)
 		     "range-based %<for%> loops only available with "
 		     "%<-std=c++11%> or %<-std=gnu++11%>");
 	}
-      else
+      else if (expect_semicolon_p)
 	/* The ';' is not consumed yet because we told
 	   cp_parser_simple_declaration not to.  */
 	cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
diff --git a/gcc/testsuite/g++.dg/cpp23/init-stmt1.C b/gcc/testsuite/g++.dg/cpp23/init-stmt1.C
new file mode 100644
index 00000000000..29e3256aae6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/init-stmt1.C
@@ -0,0 +1,31 @@ 
+// PR c++/102617
+// P2360R0: Extend init-statement to allow alias-declaration
+// { dg-do compile { target c++20 } }
+// Test valid use.
+
+int v[10];
+
+void
+g ()
+{
+  for (using T = int; (T) false;) // { dg-error "only available with" "" { target c++20_only } }
+    ;
+  for (using T = int; T e : v) // { dg-error "only available with" "" { target c++20_only } }
+    (void) e;
+  if (using T = int; true) // { dg-error "only available with" "" { target c++20_only } }
+    {
+      T x = 0;
+      (void) x;
+    }
+  if constexpr (using T = int; true) // { dg-error "only available with" "" { target c++20_only } }
+    {
+      T x = 0;
+      (void) x;
+    }
+  switch (using T = int; 42) // { dg-error "only available with" "" { target c++20_only } }
+    case 42:
+      {
+	T x = 0;
+	(void) x;
+      }
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/init-stmt2.C b/gcc/testsuite/g++.dg/cpp23/init-stmt2.C
new file mode 100644
index 00000000000..ca6201bc340
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/init-stmt2.C
@@ -0,0 +1,25 @@ 
+// PR c++/102617
+// P2360R0: Extend init-statement to allow alias-declaration
+// { dg-do compile { target c++23 } }
+// Test invalid use.
+
+int v[10];
+namespace N { using X = int; }
+
+void
+g ()
+{
+  for (using N::X; false;) // { dg-error "expected" }
+    ;
+  for (using N::X; int e : v) // { dg-error "expected" }
+    (void) e;
+  for (using T = int; using U = int; int e : v) // { dg-error "" }
+    ;
+  if (using N::X; false) // { dg-error "expected" }
+    {}
+  switch (using N::X; 0) // { dg-error "expected" }
+    ;
+ if (using T = int;) // { dg-error "expected" }
+  {
+  }
+}