2025-02-06 Jakub Jelinek <ja...@redhat.com>
Jason Merrill <ja...@redhat.com>
PR c++/86769
gcc/c-family/
* c-common.def (FOR_STMT): Add 2 operands and document them.
(WHILE_STMT): Likewise.
* c-common.h (WHILE_COND_PREP, WHILE_COND_CLEANUP): Define.
(FOR_COND_PREP, FOR_COND_CLEANUP): Define.
* c-gimplify.cc (genericize_c_loop): Add COND_PREP and COND_CLEANUP
arguments, handle them if they are non-NULL.
(genericize_for_stmt, genericize_while_stmt, genericize_do_stmt):
Adjust callers.
gcc/c/
* c-parser.cc (c_parser_while_statement): Add 2 further NULL_TREE
operands to build_stmt.
(c_parser_for_statement): Likewise.
gcc/cp/
* semantics.cc (set_one_cleanup_loc): New function.
(set_cleanup_locs): Use it.
(simplify_loop_decl_cond): Remove.
(adjust_loop_decl_cond): New function.
(begin_while_stmt): Add 2 further NULL_TREE operands to build_stmt.
(finish_while_stmt_cond): Call adjust_loop_decl_cond instead of
simplify_loop_decl_cond.
(finish_while_stmt): Call do_poplevel also on WHILE_COND_PREP if
non-NULL and also use pop_stmt_list rather than do_poplevel for
WHILE_BODY in that case. Call set_one_cleanup_loc.
(begin_for_stmt): Add 2 further NULL_TREE operands to build_stmt.
(finish_for_cond): Call adjust_loop_decl_cond instead of
simplify_loop_decl_cond.
(finish_for_stmt): Call do_poplevel also on FOR_COND_PREP if non-NULL
and also use pop_stmt_list rather than do_poplevel for FOR_BODY in
that case. Call set_one_cleanup_loc.
* constexpr.cc (cxx_eval_loop_expr): Handle
{WHILE,FOR}_COND_{PREP,CLEANUP}.
(check_for_return_continue): Handle {WHILE,FOR}_COND_PREP.
(potential_constant_expression_1): RECUR on
{WHILE,FOR}_COND_{PREP,CLEANUP}.
gcc/testsuite/
* g++.dg/diagnostic/redeclaration-7.C: New test.
* g++.dg/expr/for3.C: New test.
* g++.dg/expr/for4.C: New test.
* g++.dg/expr/for5.C: New test.
* g++.dg/expr/for6.C: New test.
* g++.dg/expr/for7.C: New test.
* g++.dg/expr/for8.C: New test.
* g++.dg/ext/stmtexpr27.C: New test.
* g++.dg/cpp2a/constexpr-86769.C: New test.
* g++.dg/cpp26/name-independent-decl7.C: New test.
* g++.dg/cpp26/name-independent-decl8.C: New test.
--- gcc/c-family/c-common.def.jj 2025-02-05 13:14:34.464202519 +0100
+++ gcc/c-family/c-common.def 2025-02-06 18:50:18.191080462 +0100
@@ -58,13 +58,14 @@ DEFTREECODE (SIZEOF_EXPR, "sizeof_expr",
DEFTREECODE (PAREN_SIZEOF_EXPR, "paren_sizeof_expr", tcc_expression, 1)
/* Used to represent a `for' statement. The operands are
- FOR_INIT_STMT, FOR_COND, FOR_EXPR, FOR_BODY, FOR_SCOPE, and FOR_NAME
- respectively. */
-DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 6)
+ FOR_INIT_STMT, FOR_COND, FOR_EXPR, FOR_BODY, FOR_SCOPE, FOR_NAME,
+ FOR_COND_PREP and FOR_COND_CLEANUP, respectively. */
+DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 8)
/* Used to represent a 'while' statement. The operands are WHILE_COND,
- WHILE_BODY, and WHILE_NAME, respectively. */
-DEFTREECODE (WHILE_STMT, "while_stmt", tcc_statement, 3)
+ WHILE_BODY, WHILE_NAME, WHILE_COND_PREP and WHILE_COND_CLEANUP,
+ respectively. */
+DEFTREECODE (WHILE_STMT, "while_stmt", tcc_statement, 5)
/* Used to represent a 'do' statement. The operands are DO_COND, DO_BODY,
and DO_NAME, respectively. */
--- gcc/c-family/c-common.h.jj 2025-02-05 13:14:34.505201941 +0100
+++ gcc/c-family/c-common.h 2025-02-06 18:50:18.198080368 +0100
@@ -1517,10 +1517,13 @@ extern tree build_userdef_literal (tree
/* WHILE_STMT accessors. These give access to the condition of the
- while statement, the body and name of the while statement, respectively. */
+ while statement, the body, and name of the while statement, and
+ condition preparation statements and its cleanup, respectively. */
#define WHILE_COND(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 0)
#define WHILE_BODY(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 1)
#define WHILE_NAME(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 2)
+#define WHILE_COND_PREP(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 3)
+#define WHILE_COND_CLEANUP(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 4)
/* DO_STMT accessors. These give access to the condition of the do
statement, the body and name of the do statement, respectively. */
@@ -1530,13 +1533,15 @@ extern tree build_userdef_literal (tree
/* FOR_STMT accessors. These give access to the init statement,
condition, update expression, body and name of the for statement,
- respectively. */
+ and condition preparation statements and its cleanup, respectively. */
#define FOR_INIT_STMT(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 0)
#define FOR_COND(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 1)
#define FOR_EXPR(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 2)
#define FOR_BODY(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 3)
#define FOR_SCOPE(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 4)
#define FOR_NAME(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 5)
+#define FOR_COND_PREP(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 6)
+#define FOR_COND_CLEANUP(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 7)
/* BREAK_STMT accessors. */
#define BREAK_NAME(NODE) TREE_OPERAND (BREAK_STMT_CHECK (NODE), 0)
--- gcc/c-family/c-gimplify.cc.jj 2025-02-05 13:14:34.666199667 +0100
+++ gcc/c-family/c-gimplify.cc 2025-02-06 18:54:47.436460396 +0100
@@ -254,24 +254,31 @@ expr_loc_or_loc (const_tree expr, locati
controlled by the loop. INCR is the increment expression of a for-loop,
or NULL_TREE. COND_IS_FIRST indicates whether the condition is
evaluated before the loop body as in while and for loops, or after the
- loop body as in do-while loops. */
+ loop body as in do-while loops. COND_PREP and COND_CLEANUP are used
+ for C++ for/while loops with variable declaration as condition. COND_PREP
+ is a BIND_EXPR with the declaration and initialization of the condition
+ variable, into which COND, BODY, continue label if needed and INCR if
+ non-NULL should be appended, and COND_CLEANUP are statements which should
+ be evaluated after that or if anything in COND, BODY or INCR throws. */
static void
genericize_c_loop (tree *stmt_p, location_t start_locus, tree cond, tree body,
- tree incr, tree name, bool cond_is_first,
- int *walk_subtrees, void *data, walk_tree_fn func,
- walk_tree_lh lh)
+ tree incr, tree name, tree cond_prep, tree cond_cleanup,
+ bool cond_is_first, int *walk_subtrees, void *data,
+ walk_tree_fn func, walk_tree_lh lh)
{
tree blab, clab;
tree entry = NULL, exit = NULL, t;
- tree stmt_list = NULL;
+ tree stmt_list = NULL, outer_stmt_list = NULL_TREE, *stmt_list_p = NULL;
location_t cond_locus = expr_loc_or_loc (cond, start_locus);
location_t incr_locus = expr_loc_or_loc (incr, start_locus);
protected_set_expr_location_if_unset (incr, start_locus);
+ walk_tree_1 (&cond_prep, func, data, NULL, lh);
walk_tree_1 (&cond, func, data, NULL, lh);
walk_tree_1 (&incr, func, data, NULL, lh);
+ walk_tree_1 (&cond_cleanup, func, data, NULL, lh);
blab = begin_bc_block (bc_break, start_locus);
clab = begin_bc_block (bc_continue, start_locus);
@@ -284,9 +291,66 @@ genericize_c_loop (tree *stmt_p, locatio
if (name)
release_named_bc (name);
- /* If condition is zero don't generate a loop construct. */
- if (cond && integer_zerop (cond))
+ if (cond_prep)
{
+ /* The C++ cases of
+ while (A x = 42) body;
+ for (; A x = 42; expr) body;
+ This should be expanded into:
+
+ top:
+ COND_PREP
+
+ with either
+
+ if (COND); else break;
+ BODY;
+ cont:
+ EXPR;
+ goto top;
+
+ or
+
+ try {
+ if (COND); else break;
+ BODY;
+ cont:
+ EXPR;
+ } finally {
+ COND_CLEANUP
+ }
+
+ appended into COND_PREP body. */
+ gcc_assert (cond_is_first && TREE_CODE (cond_prep) == BIND_EXPR);
+ tree top = build1 (LABEL_EXPR, void_type_node,
+ create_artificial_label (start_locus));
+ exit = build1 (GOTO_EXPR, void_type_node, LABEL_EXPR_LABEL (top));
+ append_to_statement_list (top, &outer_stmt_list);
+ append_to_statement_list (cond_prep, &outer_stmt_list);
+ stmt_list = BIND_EXPR_BODY (cond_prep);
+ BIND_EXPR_BODY (cond_prep) = NULL_TREE;
+ stmt_list_p = &BIND_EXPR_BODY (cond_prep);
+ if (cond_cleanup && TREE_SIDE_EFFECTS (cond_cleanup))
+ {
+ t = build2_loc (EXPR_LOCATION (cond_cleanup), TRY_FINALLY_EXPR,
+ void_type_node, NULL_TREE, cond_cleanup);
+ append_to_statement_list (t, &stmt_list);
+ *stmt_list_p = stmt_list;
+ stmt_list_p = &TREE_OPERAND (t, 0);
+ stmt_list = NULL_TREE;
+ }
+ tree after_cond = create_artificial_label (cond_locus);
+ tree goto_after_cond = build1 (GOTO_EXPR, void_type_node, after_cond);
+ t = build1 (GOTO_EXPR, void_type_node, get_bc_label (bc_break));
+ t = fold_build3_loc (cond_locus, COND_EXPR, void_type_node, cond,
+ goto_after_cond, t);
+ append_to_statement_list (t, &stmt_list);
+ t = build1 (LABEL_EXPR, void_type_node, after_cond);
+ append_to_statement_list (t, &stmt_list);
+ }
+ else if (cond && integer_zerop (cond))
+ {
+ /* If condition is zero don't generate a loop construct. */
if (cond_is_first)
{
t = build1_loc (start_locus, GOTO_EXPR, void_type_node,
@@ -383,6 +447,11 @@ genericize_c_loop (tree *stmt_p, locatio
append_to_statement_list (d, &stmt_list);
}
append_to_statement_list (exit, &stmt_list);
+ if (stmt_list_p)
+ {
+ *stmt_list_p = stmt_list;
+ stmt_list = outer_stmt_list;
+ }
finish_bc_block (&stmt_list, bc_break, blab);
if (!stmt_list)
stmt_list = build_empty_stmt (start_locus);
@@ -408,7 +477,8 @@ genericize_for_stmt (tree *stmt_p, int *
}
genericize_c_loop (&loop, EXPR_LOCATION (stmt), FOR_COND (stmt),
- FOR_BODY (stmt), FOR_EXPR (stmt), FOR_NAME (stmt), 1,
+ FOR_BODY (stmt), FOR_EXPR (stmt), FOR_NAME (stmt),
+ FOR_COND_PREP (stmt), FOR_COND_CLEANUP (stmt), 1,
walk_subtrees, data, func, lh);
append_to_statement_list (loop, &expr);
if (expr == NULL_TREE)
@@ -424,7 +494,8 @@ genericize_while_stmt (tree *stmt_p, int
{
tree stmt = *stmt_p;
genericize_c_loop (stmt_p, EXPR_LOCATION (stmt), WHILE_COND (stmt),
- WHILE_BODY (stmt), NULL_TREE, WHILE_NAME (stmt), 1,
+ WHILE_BODY (stmt), NULL_TREE, WHILE_NAME (stmt),
+ WHILE_COND_PREP (stmt), WHILE_COND_CLEANUP (stmt), 1,
walk_subtrees, data, func, lh);
}
@@ -436,8 +507,8 @@ genericize_do_stmt (tree *stmt_p, int *w
{
tree stmt = *stmt_p;
genericize_c_loop (stmt_p, EXPR_LOCATION (stmt), DO_COND (stmt),
- DO_BODY (stmt), NULL_TREE, DO_NAME (stmt), 0,
- walk_subtrees, data, func, lh);
+ DO_BODY (stmt), NULL_TREE, DO_NAME (stmt),
+ NULL_TREE, NULL_TREE, 0, walk_subtrees, data, func, lh);
}
/* Genericize a SWITCH_STMT node *STMT_P by turning it into a SWITCH_EXPR. */
--- gcc/c/c-parser.cc.jj 2025-02-05 13:14:34.742198595 +0100
+++ gcc/c/c-parser.cc 2025-02-06 18:50:18.214080153 +0100
@@ -8802,7 +8802,8 @@ c_parser_while_statement (c_parser *pars
body = c_parser_c99_block_statement (parser, if_p, &loc_after_labels);
if (loop_name && !C_DECL_LOOP_SWITCH_NAME_USED (loop_name))
loop_name = NULL_TREE;
- add_stmt (build_stmt (loc, WHILE_STMT, cond, body, loop_name));
+ add_stmt (build_stmt (loc, WHILE_STMT, cond, body, loop_name, NULL_TREE,
+ NULL_TREE));
add_stmt (c_end_compound_stmt (loc, block, flag_isoc99));
c_parser_maybe_reclassify_token (parser);
if (num_names)
@@ -9207,7 +9208,7 @@ c_parser_for_statement (c_parser *parser
add_stmt (build_stmt (for_loc, FOR_STMT, NULL_TREE, cond, incr,
body, NULL_TREE,
loop_name && C_DECL_LOOP_SWITCH_NAME_USED (loop_name)
- ? loop_name : NULL_TREE));
+ ? loop_name : NULL_TREE, NULL_TREE, NULL_TREE));
add_stmt (c_end_compound_stmt (for_loc, block,
flag_isoc99 || c_dialect_objc ()));
c_parser_maybe_reclassify_token (parser);
--- gcc/cp/semantics.cc.jj 2025-02-05 13:14:34.823197451 +0100
+++ gcc/cp/semantics.cc 2025-02-06 18:59:19.089807954 +0100
@@ -601,6 +601,25 @@ add_decl_expr (tree decl)
add_stmt (r);
}
+/* Set EXPR_LOCATION on one cleanup T to LOC. */
+
+static void
+set_one_cleanup_loc (tree t, location_t loc)
+{
+ if (!t)
+ return;
+ if (TREE_CODE (t) != POSTCONDITION_STMT)
+ protected_set_expr_location (t, loc);
+ /* Avoid locus differences for C++ cdtor calls depending on whether
+ cdtor_returns_this: a conversion to void is added to discard the return
+ value, and this conversion ends up carrying the location, and when it
+ gets discarded, the location is lost. So hold it in the call as well. */
+ if (TREE_CODE (t) == NOP_EXPR
+ && TREE_TYPE (t) == void_type_node
+ && TREE_CODE (TREE_OPERAND (t, 0)) == CALL_EXPR)
+ protected_set_expr_location (TREE_OPERAND (t, 0), loc);
+}
+
/* Set EXPR_LOCATION of the cleanups of any CLEANUP_STMT in STMTS to LOC. */
static void
@@ -608,18 +627,7 @@ set_cleanup_locs (tree stmts, location_t
{
if (TREE_CODE (stmts) == CLEANUP_STMT)
{
- tree t = CLEANUP_EXPR (stmts);
- if (t && TREE_CODE (t) != POSTCONDITION_STMT)
- protected_set_expr_location (t, loc);
- /* Avoid locus differences for C++ cdtor calls depending on whether
- cdtor_returns_this: a conversion to void is added to discard the return
- value, and this conversion ends up carrying the location, and when it
- gets discarded, the location is lost. So hold it in the call as
- well. */
- if (TREE_CODE (t) == NOP_EXPR
- && TREE_TYPE (t) == void_type_node
- && TREE_CODE (TREE_OPERAND (t, 0)) == CALL_EXPR)
- protected_set_expr_location (TREE_OPERAND (t, 0), loc);
+ set_one_cleanup_loc (CLEANUP_EXPR (stmts), loc);
set_cleanup_locs (CLEANUP_BODY (stmts), loc);
}
else if (TREE_CODE (stmts) == STATEMENT_LIST)
@@ -777,37 +785,48 @@ finish_cond (tree *cond_p, tree expr)
*cond_p = expr;
}
-/* If *COND_P specifies a conditional with a declaration, transform the
- loop such that
+/* If loop condition specifies a conditional with a declaration,
+ such as
while (A x = 42) { }
for (; A x = 42;) { }
- becomes
- while (true) { A x = 42; if (!x) break; }
- for (;;) { A x = 42; if (!x) break; }
- The statement list for BODY will be empty if the conditional did
+ move the *BODY_P statements as a BIND_EXPR into {FOR,WHILE}_COND_PREP
+ and if there is any CLEANUP_STMT at the end, remove that and
+ put the cleanup into {FOR,WHILE}_COND_CLEANUP.
+ genericize_c_loop will then handle it appropriately. In particular,
+ the {FOR,WHILE}_COND, {FOR,WHILE}_BODY, if used continue label and
+ FOR_EXPR will be appended into the {FOR,WHILE}_COND_PREP BIND_EXPR,
+ but it can't be done too early because only the actual body should
+ bind BREAK_STMT and CONTINUE_STMT to the inner loop.
+ The statement list for *BODY will be empty if the conditional did
not declare anything. */
static void
-simplify_loop_decl_cond (tree *cond_p, tree body)
+adjust_loop_decl_cond (tree *body_p, tree *prep_p, tree *cleanup_p)
{
- tree cond, if_stmt;
-
- if (!TREE_SIDE_EFFECTS (body))
+ if (!TREE_SIDE_EFFECTS (*body_p))
return;
- cond = *cond_p;
- *cond_p = boolean_true_node;
-
- if_stmt = begin_if_stmt ();
- cond_p = &cond;
- while (TREE_CODE (*cond_p) == ANNOTATE_EXPR)
- cond_p = &TREE_OPERAND (*cond_p, 0);
- *cond_p = cp_build_unary_op (TRUTH_NOT_EXPR, *cond_p, false,
- tf_warning_or_error);
- finish_if_stmt_cond (cond, if_stmt);
- finish_break_stmt ();
- finish_then_clause (if_stmt);
- finish_if_stmt (if_stmt);
+ gcc_assert (!processing_template_decl);
+ if (*body_p != cur_stmt_list)
+ {
+ /* There can be either no cleanup at all, if the condition
+ declaration doesn't have non-trivial destructor, or a single
+ one if it does. In that case extract it into *CLEANUP_P. */
+ gcc_assert (stmt_list_stack->length () > 1
+ && (*stmt_list_stack)[stmt_list_stack->length ()
+ - 2] == *body_p);
+ tree_stmt_iterator last = tsi_last (*body_p);
+ gcc_assert (tsi_one_before_end_p (last)
+ && TREE_CODE (tsi_stmt (last)) == CLEANUP_STMT
+ && CLEANUP_BODY (tsi_stmt (last)) == cur_stmt_list
+ && tsi_end_p (tsi_last (cur_stmt_list))
+ && !CLEANUP_EH_ONLY (tsi_stmt (last)));
+ *cleanup_p = CLEANUP_EXPR (tsi_stmt (last));
+ tsi_delink (&last);
+ }
+ current_binding_level->keep = true;
+ *prep_p = *body_p;
+ *body_p = push_stmt_list ();
}
/* Finish a goto-statement. */
@@ -1368,7 +1387,8 @@ tree
begin_while_stmt (void)
{
tree r;
- r = build_stmt (input_location, WHILE_STMT, NULL_TREE, NULL_TREE, NULL_TREE);
+ r = build_stmt (input_location, WHILE_STMT, NULL_TREE, NULL_TREE, NULL_TREE,
+ NULL_TREE, NULL_TREE);
add_stmt (r);
WHILE_BODY (r) = do_pushlevel (sk_block);
begin_cond (&WHILE_COND (r));
@@ -1406,7 +1426,9 @@ finish_while_stmt_cond (tree cond, tree
build_int_cst (integer_type_node,
annot_expr_no_vector_kind),
integer_zero_node);
- simplify_loop_decl_cond (&WHILE_COND (while_stmt), WHILE_BODY (while_stmt));
+ adjust_loop_decl_cond (&WHILE_BODY (while_stmt),
+ &WHILE_COND_PREP (while_stmt),
+ &WHILE_COND_CLEANUP (while_stmt));
}
/* Finish a while-statement, which may be given by WHILE_STMT. */
@@ -1415,8 +1437,14 @@ void
finish_while_stmt (tree while_stmt)
{
end_maybe_infinite_loop (boolean_true_node);
- WHILE_BODY (while_stmt) = do_poplevel (WHILE_BODY (while_stmt));
+ WHILE_BODY (while_stmt)
+ = (WHILE_COND_PREP (while_stmt)
+ ? pop_stmt_list (WHILE_BODY (while_stmt))
+ : do_poplevel (WHILE_BODY (while_stmt)));
finish_loop_cond (&WHILE_COND (while_stmt), WHILE_BODY (while_stmt));
+ if (WHILE_COND_PREP (while_stmt))
+ WHILE_COND_PREP (while_stmt) = do_poplevel (WHILE_COND_PREP (while_stmt));
+ set_one_cleanup_loc (WHILE_COND_CLEANUP (while_stmt), input_location);
}
/* Begin a do-statement. Returns a newly created DO_STMT if
@@ -1547,7 +1575,8 @@ begin_for_stmt (tree scope, tree init)
tree r;
r = build_stmt (input_location, FOR_STMT, NULL_TREE, NULL_TREE,
- NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
+ NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE,
+ NULL_TREE, NULL_TREE);
if (scope == NULL_TREE)
{
@@ -1605,7 +1634,8 @@ finish_for_cond (tree cond, tree for_stm
build_int_cst (integer_type_node,
annot_expr_no_vector_kind),
integer_zero_node);
- simplify_loop_decl_cond (&FOR_COND (for_stmt), FOR_BODY (for_stmt));
+ adjust_loop_decl_cond (&FOR_BODY (for_stmt), &FOR_COND_PREP (for_stmt),
+ &FOR_COND_CLEANUP (for_stmt));
}
/* Finish the increment-EXPRESSION in a for-statement, which may be
@@ -1679,11 +1709,17 @@ finish_for_stmt (tree for_stmt)
RANGE_FOR_BODY (for_stmt) = do_poplevel (RANGE_FOR_BODY (for_stmt));
else
{
- FOR_BODY (for_stmt) = do_poplevel (FOR_BODY (for_stmt));
+ FOR_BODY (for_stmt)
+ = (FOR_COND_PREP (for_stmt)
+ ? pop_stmt_list (FOR_BODY (for_stmt))
+ : do_poplevel (FOR_BODY (for_stmt)));
if (FOR_COND (for_stmt))
finish_loop_cond (&FOR_COND (for_stmt),
FOR_EXPR (for_stmt) ? integer_one_node
: FOR_BODY (for_stmt));
+ if (FOR_COND_PREP (for_stmt))
+ FOR_COND_PREP (for_stmt) = do_poplevel (FOR_COND_PREP (for_stmt));
+ set_one_cleanup_loc (FOR_COND_CLEANUP (for_stmt), input_location);
}
/* Pop the scope for the body of the loop. */
--- gcc/cp/constexpr.cc.jj 2025-02-06 15:38:41.679410238 +0100
+++ gcc/cp/constexpr.cc 2025-02-06 19:01:40.642908996 +0100
@@ -7152,6 +7152,7 @@ cxx_eval_loop_expr (const constexpr_ctx
}
tree body, cond = NULL_TREE, expr = NULL_TREE;
+ tree cond_prep = NULL_TREE, cond_cleanup = NULL_TREE;
int count = 0;
switch (TREE_CODE (t))
{
@@ -7165,6 +7166,8 @@ cxx_eval_loop_expr (const constexpr_ctx
case WHILE_STMT:
body = WHILE_BODY (t);
cond = WHILE_COND (t);
+ cond_prep = WHILE_COND_PREP (t);
+ cond_cleanup = WHILE_COND_CLEANUP (t);
count = -1;
break;
case FOR_STMT:
@@ -7176,11 +7179,25 @@ cxx_eval_loop_expr (const constexpr_ctx
body = FOR_BODY (t);
cond = FOR_COND (t);
expr = FOR_EXPR (t);
+ cond_prep = FOR_COND_PREP (t);
+ cond_cleanup = FOR_COND_CLEANUP (t);
count = -1;
break;
default:
gcc_unreachable ();
}
+ if (cond_prep)
+ gcc_assert (TREE_CODE (cond_prep) == BIND_EXPR);
+ auto cleanup_cond = [=] {
+ /* Clean up the condition variable after each iteration. */
+ if (cond_cleanup && !*non_constant_p)
+ cxx_eval_constant_expression (ctx, cond_cleanup, vc_discard,
+ non_constant_p, overflow_p);
+ if (cond_prep)
+ for (tree decl = BIND_EXPR_VARS (cond_prep);
+ decl; decl = DECL_CHAIN (decl))
+ destroy_value_checked (ctx, decl, non_constant_p);
+ };
do
{
if (count != -1)
@@ -7202,6 +7219,17 @@ cxx_eval_loop_expr (const constexpr_ctx
cxx_eval_constant_expression (ctx, expr, vc_prvalue,
non_constant_p, overflow_p,
jump_target);
+ cleanup_cond ();
+ }
+
+ if (cond_prep)
+ {
+ for (tree decl = BIND_EXPR_VARS (cond_prep);
+ decl; decl = DECL_CHAIN (decl))
+ ctx->global->clear_value (decl);
+ cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (cond_prep),
+ vc_discard, non_constant_p,
+ overflow_p, jump_target);
}
if (cond)
@@ -7239,6 +7267,8 @@ cxx_eval_loop_expr (const constexpr_ctx
&& (!switches (jump_target) || count == 0)
&& !*non_constant_p);
+ cleanup_cond ();
+
return NULL_TREE;
}
@@ -9618,6 +9648,7 @@ check_for_return_continue (tree *tp, int
case WHILE_STMT:
*walk_subtrees = 0;
+ RECUR (WHILE_COND_PREP (t));
RECUR (WHILE_COND (t));
s = d->continue_stmt;
b = d->break_stmt;
@@ -9629,6 +9660,7 @@ check_for_return_continue (tree *tp, int
case FOR_STMT:
*walk_subtrees = 0;
RECUR (FOR_INIT_STMT (t));
+ RECUR (FOR_COND_PREP (t));
RECUR (FOR_COND (t));
RECUR (FOR_EXPR (t));
s = d->continue_stmt;
@@ -10132,6 +10164,8 @@ potential_constant_expression_1 (tree t,
case FOR_STMT:
if (!RECUR (FOR_INIT_STMT (t), any))
return false;
+ if (!RECUR (FOR_COND_PREP (t), any))
+ return false;
tmp = FOR_COND (t);
if (!RECUR (tmp, rval))
return false;
@@ -10159,6 +10193,8 @@ potential_constant_expression_1 (tree t,
return false;
if (!RECUR (FOR_BODY (t), any))
return false;
+ if (!RECUR (FOR_COND_CLEANUP (t), any))
+ return false;
if (breaks (jump_target) || continues (jump_target))
*jump_target = NULL_TREE;
return true;
@@ -10175,6 +10211,8 @@ potential_constant_expression_1 (tree t,
return true;
case WHILE_STMT:
+ if (!RECUR (WHILE_COND_PREP (t), any))
+ return false;
tmp = WHILE_COND (t);
if (!RECUR (tmp, rval))
return false;
@@ -10196,6 +10234,8 @@ potential_constant_expression_1 (tree t,
}
if (!RECUR (WHILE_BODY (t), any))
return false;
+ if (!RECUR (WHILE_COND_CLEANUP (t), any))
+ return false;
if (breaks (jump_target) || continues (jump_target))
*jump_target = NULL_TREE;
return true;
--- gcc/testsuite/g++.dg/diagnostic/redeclaration-7.C.jj 2025-02-06
18:50:18.217080113 +0100
+++ gcc/testsuite/g++.dg/diagnostic/redeclaration-7.C 2025-02-06
18:50:18.217080113 +0100
@@ -0,0 +1,23 @@
+// PR c++/52953
+// { dg-do compile }
+// { dg-options "-pedantic-errors" }
+
+volatile int v;
+
+void
+baz ()
+{
+ for (int x = v; // { dg-message "'int x' previously declared
here" }
+ int x = v; ++x) // { dg-error "redeclaration of 'int
x'" }
+ ;
+ for (int x = v; // { dg-message "'int x' previously declared
here" }
+ int x = v; ++x) // { dg-error "redeclaration of 'int
x'" }
+ { // { dg-message "'int x' previously declared
here" "" { target *-*-* } .-1 }
+ int x; // { dg-error "redeclaration of 'int
x'" }
+ }
+ for (int x = v; // { dg-message "'int x' previously declared
here" }
+ int x = v; ++x) // { dg-error "redeclaration of 'int
x'" }
+ { // { dg-message "previous declaration 'int x'"
"" { target *-*-* } .-1 }
+ extern int x; // { dg-error "'int x' conflicts with a
previous declaration" }
+ }
+}
--- gcc/testsuite/g++.dg/expr/for3.C.jj 2025-02-06 18:50:18.217080113 +0100
+++ gcc/testsuite/g++.dg/expr/for3.C 2025-02-06 18:50:18.217080113 +0100
@@ -0,0 +1,112 @@
+// PR c++/86769
+// { dg-do run { target c++11 } }
+
+int d, e, f, g, h;
+struct A {
+ int a;
+ A (int x) : a(x) { ++d; ++h; }
+ ~A () { --d; ++h; }
+ A (const A &x) : a(x.a) { ++d; ++h; }
+ operator bool () { return a != 0; }
+};
+struct B {
+ B () { ++e; ++h; }
+ ~B () { --e; ++h; }
+ B (const B &) { ++e; ++h; }
+};
+
+int
+foo (B, int x)
+{
+ if (e != 1)
+ __builtin_abort ();
+ if (f ? x - 1 != (f - 1) % 3 : x)
+ __builtin_abort ();
+ ++f;
+ if (x == 1)
+ return ++g < 3;
+ return 0;
+}
+
+int
+bar (int n)
+{
+ if (e != 0 || d != n)
+ __builtin_abort ();
+ return 0;
+}
+
+int
+main ()
+{
+ for (A a = (bar (0), foo (B {}, 0));
+ A b = (bar (1), foo (B {}, 1));
+ bar (2), foo (B {}, 3))
+ A c = (bar (2), foo (B {}, 2));
+ if (f != 8 || h != 28 || d || e)
+ __builtin_abort ();
+ f = 0; g = -2; h = 0;
+ for (A a = (bar (0), foo (B {}, 0));
+ A b = (bar (1), foo (B {}, 1));
+ bar (2), foo (B {}, 3))
+ {
+ A c = (bar (2), foo (B {}, 2));
+ bar (3);
+ }
+ if (f != 14 || h != 48 || d || e)
+ __builtin_abort ();
+ f = 0; g = 0; h = 0;
+ {
+ A a = (bar (0), foo (B {}, 0));
+ while (A b = (bar (1), foo (B {}, 1)))
+ {
+ A c = (bar (2), foo (B {}, 2));
+ bar (3);
+ foo (B {}, 3);
+ }
+ }
+ if (f != 8 || h != 28 || d || e)
+ __builtin_abort ();
+ f = 0; g = -5; h = 0;
+ for (A a = (bar (0), foo (B {}, 0));
+ A b = (bar (1), foo (B {}, 1));
+ bar (2), foo (B {}, 3))
+ {
+ if (f == 5)
+ {
+ bar (2);
+ foo (B {}, 2);
+ bar (2);
+ continue;
+ }
+ if (f == 11)
+ break;
+ A c = (bar (2), foo (B {}, 2));
+ bar (3);
+ }
+ if (f != 11 || h != 36 || d || e)
+ __builtin_abort ();
+ f = 0; g = -5; h = 0;
+ {
+ A a = (bar (0), foo (B {}, 0));
+ while (A b = (bar (1), foo (B {}, 1)))
+ {
+ if (f == 5)
+ {
+ bar (2);
+ foo (B {}, 2);
+ bar (2);
+ foo (B {}, 3);
+ bar (2);
+ continue;
+ }
+ else if (f == 11)
+ break;
+ A c = (bar (2), foo (B {}, 2));
+ bar (3);
+ foo (B {}, 3);
+ }
+ }
+ if (f != 11 || h != 36 || d || e)
+ __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/expr/for4.C.jj 2025-02-06 18:50:18.217080113 +0100
+++ gcc/testsuite/g++.dg/expr/for4.C 2025-02-06 18:50:18.217080113 +0100
@@ -0,0 +1,116 @@
+// PR c++/86769
+// { dg-do run }
+
+int d, e, f, g, h;
+struct A {
+ int a;
+ A (int x) : a(x) { ++d; ++h; }
+ ~A () { --d; ++h; }
+ A (const A &x) : a(x.a) { ++d; ++h; }
+ operator bool () { return a != 0; }
+};
+struct B {
+ B () { ++e; ++h; }
+ ~B () { --e; ++h; }
+ B (const B &) { ++e; ++h; }
+};
+B k;
+
+int
+foo (B, int x)
+{
+ if (e != 1)
+ __builtin_abort ();
+ if (f ? x - 1 != (f - 1) % 3 : x)
+ __builtin_abort ();
+ ++f;
+ if (x == 1)
+ return ++g < 3;
+ return 0;
+}
+
+int
+bar (int n)
+{
+ if (e != 0 || d != n)
+ __builtin_abort ();
+ return 0;
+}
+
+int
+main ()
+{
+ if (e != 1 || h != 1)
+ __builtin_abort ();
+ e = 0; h = 0;
+ for (A a = (bar (0), foo (k, 0));
+ A b = (bar (1), foo (k, 1));
+ bar (2), foo (k, 3))
+ A c = (bar (2), foo (k, 2));
+ if (f != 8 || h != 28 || d || e)
+ __builtin_abort ();
+ f = 0; g = -2; h = 0;
+ for (A a = (bar (0), foo (k, 0));
+ A b = (bar (1), foo (k, 1));
+ bar (2), foo (k, 3))
+ {
+ A c = (bar (2), foo (k, 2));
+ bar (3);
+ }
+ if (f != 14 || h != 48 || d || e)
+ __builtin_abort ();
+ f = 0; g = 0; h = 0;
+ {
+ A a = (bar (0), foo (k, 0));
+ while (A b = (bar (1), foo (k, 1)))
+ {
+ A c = (bar (2), foo (k, 2));
+ bar (3);
+ foo (k, 3);
+ }
+ }
+ if (f != 8 || h != 28 || d || e)
+ __builtin_abort ();
+ f = 0; g = -5; h = 0;
+ for (A a = (bar (0), foo (k, 0));
+ A b = (bar (1), foo (k, 1));
+ bar (2), foo (k, 3))
+ {
+ if (f == 5)
+ {
+ bar (2);
+ foo (k, 2);
+ bar (2);
+ continue;
+ }
+ if (f == 11)
+ break;
+ A c = (bar (2), foo (k, 2));
+ bar (3);
+ }
+ if (f != 11 || h != 36 || d || e)
+ __builtin_abort ();
+ f = 0; g = -5; h = 0;
+ {
+ A a = (bar (0), foo (k, 0));
+ while (A b = (bar (1), foo (k, 1)))
+ {
+ if (f == 5)
+ {
+ bar (2);
+ foo (k, 2);
+ bar (2);
+ foo (k, 3);
+ bar (2);
+ continue;
+ }
+ else if (f == 11)
+ break;
+ A c = (bar (2), foo (k, 2));
+ bar (3);
+ foo (k, 3);
+ }
+ }
+ if (f != 11 || h != 36 || d || e)
+ __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/expr/for5.C.jj 2025-02-06 18:50:18.218080099 +0100
+++ gcc/testsuite/g++.dg/expr/for5.C 2025-02-06 18:50:18.218080099 +0100
@@ -0,0 +1,34 @@
+// PR c++/86769
+// { dg-do run }
+
+int g;
+
+struct X {
+ X () { g++; }
+ ~X () { g--; }
+ operator bool () { return g == 0; }
+};
+
+void
+foo ()
+{
+ if (g <= 0)
+ __builtin_abort ();
+}
+
+void
+bar ()
+{
+ if (g)
+ __builtin_abort ();
+}
+
+int
+main ()
+{
+ for (int i = 0; i < 1; ++i, bar ())
+ {
+ X x = X ();
+ foo ();
+ }
+}
--- gcc/testsuite/g++.dg/expr/for6.C.jj 2025-02-06 18:50:18.218080099 +0100
+++ gcc/testsuite/g++.dg/expr/for6.C 2025-02-06 18:50:18.218080099 +0100
@@ -0,0 +1,39 @@
+// PR c++/86769
+// { dg-do run { target c++11 } }
+
+int v;
+
+struct S {
+ int s;
+ S (int x) : s(x)
+ {
+ if ((v != 0 || s != 0) && (v != 3 || s != 1))
+ __builtin_abort ();
+ ++v;
+ }
+ ~S ()
+ {
+ if ((v != 2 || s != 0) && (v != 4 || s != 1))
+ __builtin_abort ();
+ ++v;
+ }
+ operator bool () const { return true; }
+};
+
+void
+foo (const S &s)
+{
+ if (v != 1 || s.s != 0)
+ __builtin_abort ();
+ ++v;
+}
+
+int
+main ()
+{
+ for (int i = 0; S j{i}; foo (j))
+ {
+ if (++i == 2)
+ break;
+ }
+}
--- gcc/testsuite/g++.dg/expr/for7.C.jj 2025-02-06 18:50:18.218080099 +0100
+++ gcc/testsuite/g++.dg/expr/for7.C 2025-02-06 18:50:18.218080099 +0100
@@ -0,0 +1,20 @@
+// PR c++/86769
+// { dg-do run }
+
+int v;
+
+struct S
+{
+ S (int x) : s(x) { v++; }
+ ~S () { v--; }
+ int s;
+ operator int () { if (!v) __builtin_abort (); return s; }
+};
+
+int
+main ()
+{
+ int x = 10;
+ for (int l = 1; S d = x - l; l = d + 1)
+ ;
+}
--- gcc/testsuite/g++.dg/expr/for8.C.jj 2025-02-06 18:50:18.218080099 +0100
+++ gcc/testsuite/g++.dg/expr/for8.C 2025-02-06 18:50:18.218080099 +0100
@@ -0,0 +1,22 @@
+// PR c++/86769
+// { dg-do run }
+// { dg-options "-O1" }
+
+__attribute__((noipa)) void
+foo (int)
+{
+ static int a = 0;
+ if (++a == 3)
+ __builtin_abort ();
+}
+
+int
+main ()
+{
+ volatile int x = 10;
+ for (int l = 1; int d = x - l; l = d + 1)
+ {
+ int &z = d;
+ foo (z);
+ }
+}
--- gcc/testsuite/g++.dg/ext/stmtexpr27.C.jj 2025-02-06 18:50:18.218080099
+0100
+++ gcc/testsuite/g++.dg/ext/stmtexpr27.C 2025-02-06 18:50:18.218080099
+0100
@@ -0,0 +1,64 @@
+// PR c++/86769
+// { dg-do run }
+// { dg-options "" }
+
+struct A {
+ int a;
+ A (int x) : a(x) {}
+ ~A () {}
+ A (const A &x) : a(x.a) {}
+ operator bool () { return a != 0; }
+};
+
+int
+main ()
+{
+ int v = 0;
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; j < ({ if (i == 1 && j == 1) continue; 4; }); ++j)
+ ++v;
+ if (v != 9)
+ __builtin_abort ();
+ v = 0;
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; j < ({ if (i == 1 && j == 1) break; 4; }); ++j)
+ ++v;
+ if (v != 5)
+ __builtin_abort ();
+ v = 0;
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; j < 4; ({ if (i == 1 && j == 1) continue; 1; }), ++j)
+ ++v;
+ if (v != 10)
+ __builtin_abort ();
+ v = 0;
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; j < 4; ({ if (i == 1 && j == 1) break; 1; }), ++j)
+ ++v;
+ if (v != 6)
+ __builtin_abort ();
+ v = 0;
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; A c = j < ({ if (i == 1 && j == 1) continue; 4; }); ++j)
+ ++v;
+ if (v != 9)
+ __builtin_abort ();
+ v = 0;
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; A c = j < ({ if (i == 1 && j == 1) break; 4; }); ++j)
+ ++v;
+ if (v != 5)
+ __builtin_abort ();
+ v = 0;
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; A c = j < 4; ({ if (i == 1 && j == 1) continue; 1; }), ++j)
+ ++v;
+ if (v != 10)
+ __builtin_abort ();
+ v = 0;
+ for (int i = 0; i < 3; ++i)
+ for (int j = 0; A c = j < 4; ({ if (i == 1 && j == 1) break; 1; }), ++j)
+ ++v;
+ if (v != 6)
+ __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp2a/constexpr-86769.C.jj 2025-02-06
18:50:18.218080099 +0100
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-86769.C 2025-02-06
18:50:18.218080099 +0100
@@ -0,0 +1,36 @@
+// PR c++/86769
+// { dg-do compile { target c++20 } }
+
+struct A {
+ int *a;
+ constexpr A (int x) : a (new int (x)) {}
+ constexpr A (const A &x) : a (new int (x.a[0])) {}
+ constexpr ~A () { delete a; }
+ constexpr operator bool () { return *a != 0; }
+};
+
+constexpr int
+foo ()
+{
+ int i = 0;
+ for (A a = 0; A b = a.a[0] < 16; a.a[0] += b.a[0])
+ i += a.a[0] + b.a[0];
+ return i;
+}
+
+static_assert (foo () == 136);
+
+constexpr int
+bar ()
+{
+ int i = 0;
+ A a = 0;
+ while (A b = a.a[0] < 15)
+ {
+ i += a.a[0] + b.a[0];
+ a.a[0] += b.a[0];
+ }
+ return i;
+}
+
+static_assert (bar () == 120);
--- gcc/testsuite/g++.dg/cpp26/name-independent-decl7.C.jj 2025-02-06
18:50:18.230079938 +0100
+++ gcc/testsuite/g++.dg/cpp26/name-independent-decl7.C 2025-02-06
18:50:18.230079938 +0100
@@ -0,0 +1,27 @@
+// P2169R4 - A nice placeholder with no name
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wunused-variable -Wunused-but-set-variable -Wunused-parameter
-Wshadow" }
+
+int bar ();
+
+void
+baz ()
+{
+ for (; int _ = bar (); ++_)
+ int _ = 1; // { dg-warning "name-independent declarations only available
with" "" { target c++23_down } }
+ for (int _ = bar ();
+ int _ = bar (); ) // { dg-warning "name-independent declarations only available
with" "" { target c++23_down } }
+ int _ = 2; // { dg-warning "name-independent declarations only available
with" "" { target c++23_down } }
+ for (int _ = bar ();
+ int _ = bar (); ) // { dg-warning "name-independent declarations only available
with" "" { target c++23_down } }
+ ;
+ for (; int _ = bar (); ++_)
+ {
+ int _ = 3; // { dg-warning "name-independent declarations only available
with" "" { target c++23_down } }
+ }
+ for (int _ = bar ();
+ int _ = bar (); ) // { dg-warning "name-independent declarations only available
with" "" { target c++23_down } }
+ {
+ int _ = 4; // { dg-warning "name-independent declarations only available
with" "" { target c++23_down } }
+ }
+}
--- gcc/testsuite/g++.dg/cpp26/name-independent-decl8.C.jj 2025-02-06
18:50:18.230079938 +0100
+++ gcc/testsuite/g++.dg/cpp26/name-independent-decl8.C 2025-02-06
18:50:18.230079938 +0100
@@ -0,0 +1,24 @@
+// P2169R4 - A nice placeholder with no name
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+int bar ();
+
+void
+baz ()
+{
+ for (int _ = bar ();
+ int _ = bar (); ++_) // { dg-warning "name-independent declarations only available
with" "" { target c++23_down } }
+ ; // { dg-error "reference to '_' is ambiguous"
"" { target *-*-* } .-1 }
+ for (; int _ = bar (); ++_)
+ {
+ int _ = 3; // { dg-warning "name-independent declarations only available
with" "" { target c++23_down } }
+ ++_; // { dg-error "reference to '_' is ambiguous" }
+ }
+ for (int _ = bar ();
+ int _ = bar (); ) // { dg-warning "name-independent declarations only available
with" "" { target c++23_down } }
+ {
+ int _ = 4; // { dg-warning "name-independent declarations only available
with" "" { target c++23_down } }
+ ++_; // { dg-error "reference to '_' is ambiguous" }
+ }
+}
Jakub