https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118822
--- Comment #11 from GCC Commits <cvs-commit at gcc dot gnu.org> --- The releases/gcc-14 branch has been updated by Jakub Jelinek <ja...@gcc.gnu.org>: https://gcc.gnu.org/g:db73411a72cf2052dddcf9fa0523588599a73537 commit r14-11788-gdb73411a72cf2052dddcf9fa0523588599a73537 Author: Jakub Jelinek <ja...@redhat.com> Date: Thu Feb 13 10:21:29 2025 +0100 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 <ja...@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)