[v2] c++: P2360R0: Extend init-stmt to allow alias-decl [PR102617]
Commit Message
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
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
>
@@ -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);
new file mode 100644
@@ -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;
+ }
+}
new file mode 100644
@@ -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" }
+ {
+ }
+}