Message ID | YXHpRnYi9/cJtMX3@redhat.com |
---|---|
State | New |
Headers | show |
Series | [v2] c++: P2360R0: Extend init-stmt to allow alias-decl [PR102617] | expand |
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 >
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" } + { + } +}