c++: Implement P2324R2, labels at the end of compound-stmts [PR103539]

This patch implements C++23 <https://wg21.link/p2324r2>, which allows
labels at the end of a compound statement.   Its C FE counterpart was
already implemented in r11-4813.

In cp_parser_statement I rely on in_compound to determine whether we're
in a compound-statement, so that the patch doesn't accidentally allow

  void fn(int c) {
    if (c)
  label:
  }

Strangely, in_compound was reset after seeing a label (this is tested in
c-c++-common/gomp/pr63326.c), so I've made a modifiable copy specific
for OpenMP #pragma purposes.

	PR c++/103539

gcc/cp/ChangeLog:

	* parser.cc (cp_parser_statement): Constify the in_compound parameter.
	Create a modifiable copy.  Allow labels at the end of compound
	statements.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp23/label1.C: New test.
	* g++.dg/cpp23/label2.C: New test.
This commit is contained in:
Marek Polacek 2022-05-08 17:36:34 -04:00
parent 8c97f7fd23
commit 4b2a662864
3 changed files with 175 additions and 9 deletions

View file

@ -12174,7 +12174,7 @@ cp_parser_handle_directive_omp_attributes (cp_parser *parser, tree *pattrs,
atomic-statement
IN_COMPOUND is true when the statement is nested inside a
cp_parser_compound_statement; this matters for certain pragmas.
cp_parser_compound_statement.
If IF_P is not NULL, *IF_P is set to indicate whether the statement
is a (possibly labeled) if statement which is not enclosed in braces
@ -12184,7 +12184,7 @@ cp_parser_handle_directive_omp_attributes (cp_parser *parser, tree *pattrs,
static void
cp_parser_statement (cp_parser* parser, tree in_statement_expr,
bool in_compound, bool *if_p, vec<tree> *chain,
const bool in_compound, bool *if_p, vec<tree> *chain,
location_t *loc_after_labels)
{
tree statement, std_attrs = NULL_TREE;
@ -12192,6 +12192,9 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
location_t statement_location, attrs_loc;
bool in_omp_attribute_pragma = parser->lexer->in_omp_attribute_pragma;
bool has_std_attrs;
/* A copy of IN_COMPOUND which is set to false after seeing a label.
This matters for certain pragmas. */
bool in_compound_for_pragma = in_compound;
restart:
if (if_p != NULL)
@ -12286,7 +12289,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
Parse the label, and then use tail recursion to parse
the statement. */
cp_parser_label_for_labeled_statement (parser, std_attrs);
in_compound = false;
in_compound_for_pragma = false;
in_omp_attribute_pragma = parser->lexer->in_omp_attribute_pragma;
goto restart;
@ -12370,7 +12373,21 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
the statement. */
cp_parser_label_for_labeled_statement (parser, std_attrs);
in_compound = false;
/* If there's no statement, it's not a labeled-statement, just
a label. That's allowed in C++23, but only if we're at the
end of a compound-statement. */
if (in_compound
&& cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
{
location_t loc = cp_lexer_peek_token (parser->lexer)->location;
if (cxx_dialect < cxx23)
pedwarn (loc, OPT_Wc__23_extensions,
"label at end of compound statement only available "
"with %<-std=c++2b%> or %<-std=gnu++2b%>");
return;
}
in_compound_for_pragma = false;
in_omp_attribute_pragma = parser->lexer->in_omp_attribute_pragma;
goto restart;
}
@ -12393,7 +12410,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
the context of a compound, accept the pragma as a "statement" and
return so that we can check for a close brace. Otherwise we
require a real statement and must go back and read one. */
if (in_compound)
if (in_compound_for_pragma)
cp_parser_pragma (parser, pragma_compound, if_p);
else if (!cp_parser_pragma (parser, pragma_stmt, if_p))
do_restart = true;
@ -12544,9 +12561,13 @@ attr_chainon (tree attrs, tree attr)
/* Parse the label for a labeled-statement, i.e.
identifier :
case constant-expression :
default :
label:
attribute-specifier-seq[opt] identifier :
attribute-specifier-seq[opt] case constant-expression :
attribute-specifier-seq[opt] default :
labeled-statement:
label statement
GNU Extension:
case constant-expression ... constant-expression : statement
@ -12766,7 +12787,11 @@ cp_parser_expression_statement (cp_parser* parser, tree in_statement_expr)
/* Parse a compound-statement.
compound-statement:
{ statement-seq [opt] }
{ statement-seq [opt] label-seq [opt] }
label-seq:
label
label-seq label
GNU extension:

View file

@ -0,0 +1,89 @@
// P2324R2 - Labels at the end of compound statements
// PR c++/103539
// { dg-do compile }
// Test good cases.
void
p2324 ()
{
first:
int x;
second:
x = 1;
last:
} // { dg-error "label at end of compound statement only available with" "" { target c++20_down } }
void
fn1 ()
{
l1:
} // { dg-error "label at end of compound statement only available with" "" { target c++20_down } }
void
fn2 ()
{
if (1)
{
l1:
} // { dg-error "label at end of compound statement only available with" "" { target c++20_down } }
}
void
fn3 ()
{
{
{
label:
} // { dg-error "label at end of compound statement only available with" "" { target c++20_down } }
}
}
void
fn4 ()
{
switch (1)
{
lab:
} // { dg-error "label at end of compound statement only available with" "" { target c++20_down } }
}
void
fn5 ()
{
l1:
l2:
l3:
} // { dg-error "label at end of compound statement only available with" "" { target c++20_down } }
void
fn6 ()
{
;
l1:
l2:
l3:
} // { dg-error "label at end of compound statement only available with" "" { target c++20_down } }
#if __cplusplus >= 201103L
void
fn7 ()
{
auto l = [](){
lab:
}; // { dg-error "label at end of compound statement only available with" "" { target { c++20_down && c++11 } } }
}
#endif
void
fn8 ()
{
try
{
lab1:
} // { dg-error "label at end of compound statement only available with" "" { target c++20_down } }
catch (int)
{
lab2:
} // { dg-error "label at end of compound statement only available with" "" { target c++20_down } }
}

View file

@ -0,0 +1,52 @@
// P2324R2 - Labels at the end of compound statements
// PR c++/103539
// { dg-do compile { target c++23 } }
// Test bad cases.
void
fn1 ()
{
/* A selection-statement wants a statement, but a mere label isn't a statement. */
if (1)
lab:
} // { dg-error "expected" }
void
fn2 ()
{
if (0)
{
}
else
lab:
} // { dg-error "expected" }
void
fn3 ()
{
do
lab:
while (0); // { dg-error "expected" }
} // { dg-error "expected" }
void
fn4 ()
{
for (;;)
lab:
} // { dg-error "expected" }
void
fn5 ()
{
switch (1)
lab:
} // { dg-error "expected" }
void
fn6 ()
{
if (1)
lab1:
lab2:
} // { dg-error "expected" }