https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125601
--- Comment #9 from GCC Commits <cvs-commit at gcc dot gnu.org> --- The releases/gcc-16 branch has been updated by Jakub Jelinek <[email protected]>: https://gcc.gnu.org/g:31c151da46e71a35e3ee6e0ad6abd21a45f06022 commit r16-9510-g31c151da46e71a35e3ee6e0ad6abd21a45f06022 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]> (cherry picked from commit 94843e8b8a47e9a4972b73637578dac12fe1ade7)
