https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118822

--- Comment #9 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Jakub Jelinek <ja...@gcc.gnu.org>:

https://gcc.gnu.org/g:26baa2c09b39abf037afad349a318dc5734eae25

commit r15-7502-g26baa2c09b39abf037afad349a318dc5734eae25
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.

    The second problem is that my assumption that the declaration in the
    condition will have zero or one cleanup is just wrong, at least for
    structured bindings used as condition, there can be as many cleanups as
    there are names in the binding + 1.

    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
            PR c++/118833
    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.
            * g++.dg/cpp26/decomp12.C: New test.

Reply via email to