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

--- Comment #7 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Jakub Jelinek <[email protected]>:

https://gcc.gnu.org/g:94843e8b8a47e9a4972b73637578dac12fe1ade7

commit r17-3017-g94843e8b8a47e9a4972b73637578dac12fe1ade7
Author: Jakub Jelinek <[email protected]>
Date:   Thu Aug 6 11:26:42 2026 +0200

    c++: Fix up constexpr handling of break in expansion statements [PR125601]

    As the following testcase shows, we mishandle break statements in expansion
    statements during constant evaluation.
    finish_expansion_stmt changes the BREAK_STMT/CONTINUE_STMTs to GOTO_EXPRs
    to corresponding labels and marks those labels with
    LABEL_DECL_BREAK/LABEL_DECL_CONTINUE so that constexpr.cc is happy about
    those.  The continue label (if any is needed) is right after each
    iteration's instantiated body, the break label (if any is needed) is after
    the last body.
    Now, continue seems to work properly, when we encounter it, we set
    *jump_target to it and continues predicate is true on it, but
    cxx_eval_statement_list has
          /* We've found a continue, so skip everything until we reach
             the label its jumping to.  */
          if (continues (jump_target))
            {
              if (label_matches (ctx, jump_target, stmt))
                /* Found it.  */
                *jump_target = NULL_TREE;
              else
                continue;
            }
    ...
          if (returns (jump_target)
              || breaks (jump_target)
              || throws (jump_target))
            break;
    and so it properly iterates through statement lists until it finds
    the label decl.
    But unfortunately it doesn't work for break, we set *jump_target on
    the GOTO_EXPR, breaks predicate is true, but then break out of any
    STATEMENT_LISTs and the only way to resume processing of statements
    in that case is when cxx_eval_loop_expr does
              if (breaks (jump_target))
                {
                  *jump_target = NULL_TREE;
                  break;
                }
    or similarly switch handling.  But for expansion stmt there is
    nothing like that in the IL, so either we break some outer loop
    (foo in the testcase) instead, or fail because we think there was no return
    in the function.

    The following patch fixes this by wrapping the series of instantiated
    expansion stmt bodies (for all iterations) in an artificial
    do ... while (0); statement, but does that only if break; was actually
    needed (i.e. when we are emitting a break_label).

    2026-08-06  Jakub Jelinek  <[email protected]>

            PR c++/125601
            * pt.cc (finish_expansion_stmt): If break; was seen in any of the
            expansion stmt bodies, wrap all the bodies in an artificial
            do ... while (0); stmt.

            * g++.dg/cpp26/expansion-stmt45.C: New test.

    Reviewed-by: Jason Merrill <[email protected]>

Reply via email to