]> git.ipfire.org Git - thirdparty/gcc.git/commit
c++: Fix up regressions caused by for/while loops with declarations [PR118822]
authorJakub Jelinek <jakub@redhat.com>
Thu, 13 Feb 2025 09:21:29 +0000 (10:21 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 15 May 2025 11:26:51 +0000 (13:26 +0200)
commitdb73411a72cf2052dddcf9fa0523588599a73537
tree8c379ad86b839244b7ff0c60f402a5e9f323f2d2
parent24cd4753e470ec122d232c9da825274bce0bae15
c++: Fix up regressions caused by for/while loops with declarations [PR118822]

The recent PR86769 r15-7426 changes regressed the following two testcases,
the first one is more important as it is derived from real-world code.

The first problem is that the chosen
prep = do_pushlevel (sk_block);
// emit something
body = push_stmt_list ();
// emit further stuff
body = pop_stmt_list (body);
prep = do_poplevel (prep);
way of constructing the {FOR,WHILE}_COND_PREP and {FOR,WHILE}_BODY
isn't reliable.  If during parsing a label is seen in the body and then
some decl with destructors, sk_cleanup transparent scope is added, but
the correspondiong result from push_stmt_list is saved in
*current_binding_level and pop_stmt_list then pops even that statement list
but only do_poplevel actually attempts to pop the sk_cleanup scope and so we
ICE.
The reason for not doing do_pushlevel (sk_block); do_pushlevel (sk_block);
is that variables should be in the same scope (otherwise various e.g.
redeclaration*.C tests FAIL) and doing do_pushlevel (sk_block); do_pushlevel
(sk_cleanup); wouldn't work either as do_poplevel would silently unwind even
the cleanup one.

So, the following patch changes the earlier approach.  Nothing is removed
from the {FOR,WHILE}_COND_PREP subtrees while doing adjust_loop_decl_cond,
push_stmt_list isn't called either; all it does is remember as an integer
the number of cleanups (CLEANUP_STMT at the end of the STATEMENT_LISTs)
from querying stmt_list_stack and finding the initial *body_p in there
(that integer is stored into {FOR,WHILE}_COND_CLEANUP), and temporarily
{FOR,WHILE}_BODY is set to the last statement (if any) in the innermost
STATEMENT_LIST at the adjust_loop_decl_cond time; then at
finish_{for,while}_stmt a new finish_loop_cond_prep routine takes care of
do_poplevel for the scope (which is in {FOR,WHILE}_COND_PREP) and finds
given {FOR,WHILE}_COND_CLEANUP number and {FOR,WHILE}_BODY tree the right
spot where body statements start and moves that into {FOR,WHILE}_BODY.
Finally genericize_c_loop then inserts the cond, body, continue label, expr
into the right subtree of {FOR,WHILE}_COND_PREP.
The constexpr evaluation unfortunately had to be changed as well, because
we don't want to evaluate everything in BIND_EXPR_BODY (*_COND_PREP ())
right away, we want to evaluate it with the exception of the CLEANUP_STMT
cleanups at the end (given {FOR,WHILE}_COND_CLEANUP levels), and defer
the evaluation of the cleanups until after cond, body, expr are evaluated.

2025-02-13  Jakub Jelinek  <jakub@redhat.com>

PR c++/118822
gcc/
* tree-iterator.h (tsi_split_stmt_list): Declare.
* tree-iterator.cc (tsi_split_stmt_list): New function.
gcc/c-family/
* c-common.h (WHILE_COND_CLEANUP): Change description in comment.
(FOR_COND_CLEANUP): Likewise.
* c-gimplify.cc (genericize_c_loop): Adjust for COND_CLEANUP
being CLEANUP_STMT/TRY_FINALLY_EXPR trailing nesting depth
instead of actual cleanup.
gcc/cp/
* semantics.cc (adjust_loop_decl_cond): Allow multiple trailing
CLEANUP_STMT levels in *BODY_P.  Set *CLEANUP_P to the number
of levels rather than one particular cleanup, keep the cleanups
in *PREP_P.  Set *BODY_P to the last stmt in the cur_stmt_list
or NULL if *CLEANUP_P and the innermost cur_stmt_list is empty.
(finish_loop_cond_prep): New function.
(finish_while_stmt, finish_for_stmt): Use it.  Don't call
set_one_cleanup_loc.
* constexpr.cc (cxx_eval_loop_expr): Adjust handling of
{FOR,WHILE}_COND_{PREP,CLEANUP}.
gcc/testsuite/
* g++.dg/expr/for9.C: New test.

(cherry picked from commit 26baa2c09b39abf037afad349a318dc5734eae25)
gcc/c-family/c-common.h
gcc/c-family/c-gimplify.cc
gcc/cp/constexpr.cc
gcc/cp/semantics.cc
gcc/testsuite/g++.dg/expr/for9.C [new file with mode: 0644]
gcc/tree-iterator.cc
gcc/tree-iterator.h