See <https://wg21.link/P2809R3>. DR.
Apart from marking via -ffinite-loops GCC considers loops without an exit as not required to make "forward progress". That's more than just a constant controlling expression but should allow optimizing most relevant cases.
I know, but I think this will need changes in the C++ FE. I was asking recently Jason what the "when interpreted as a constant-expression" in the paper actually means, whether such expression should be evaluated as manifestly constant evaluation or not. If yes, it seems like an incompatible change in a DR, because say #include <type_traits> void foo () { while (std::is_constant_evaluated ()) ; } changing behavior from previous doing nothing to an infinite loop. If it is not manifestly constant evaluation, still, the FE would need to (at least for the cases listed in the paper) try to silently constant evaluate the condition (with mce_false such that std::is_constant_evaluated () is folded to false in there; but perhaps only during cp_fold_function?) and if that evaluates to non-zero replace the condition with true, such that the middle-end would then really consider it as infinite loop regardless of -ffinite-loops. Because if one has constexpr bool bar () { return true; } void baz () { while (bar ()) ; } we want middle-end to see while (true) ; rather than while (bar ()) ; because the latter would be under -ffinite-loops decision.
And another case to watch for is: void qux () { while (const bool b = bar ()) ; }
Ah, if there is a declaration in the condition, then it is not a valid trivial empty iteration statement. Anyway, I'd say cp_fold should for WHILE_STMT, DO_STMT and FOR_STMT if the body is a STATEMENT_LIST with no statements at all or an empty statement (do we have predicate for NOP_EXPR to void_type_node of integer_zero_node?) and in case of FOR_STMT empty increment expression argument try to evaluate the condition as mce_false constant expression and if it evaluates to constant non-zero, replace the condition with boolean_true_node.
constexpr bool foo () { return true; } volatile int v; void bar (int x) { switch (x) { case 0: while (foo ()) ; break; case 1: while (foo ()) {} break; case 2: do ; while (foo ()); break; case 3: do {} while (foo ()); break; case 4: do {} while (foo ()); break; case 5: for (v = 42; foo (); ) ; break; case 6: for (v = 42; foo (); ) {} break; case 7: for (int w = 42; foo (); ) ; break; case 8: for (int w = 42; foo (); ) {} break; default: break; } }
.
Created attachment 57847 [details] gcc14-pr114462.patch Untested fix.
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>: https://gcc.gnu.org/g:4be1cc5f50578fafcdcbd09160235066d76a3f86 commit r14-9887-g4be1cc5f50578fafcdcbd09160235066d76a3f86 Author: Jakub Jelinek <jakub@redhat.com> Date: Wed Apr 10 10:08:12 2024 +0200 c++: Implement C++26 P2809R3 - Trivial infinite loops are not Undefined Behavior The following patch attempts to implement P2809R3, which has been voted in as a DR. The middle-end has its behavior documented: '-ffinite-loops' Assume that a loop with an exit will eventually take the exit and not loop indefinitely. This allows the compiler to remove loops that otherwise have no side-effects, not considering eventual endless looping as such. This option is enabled by default at '-O2' for C++ with -std=c++11 or higher. So, the following patch attempts to detect trivial infinite loops by detecting trivially empty loops, if their condition is not INTEGER_CST (that case is handled by the middle-end right already) trying to constant evaluate with mce=true their condition and if it evaluates to true (and -ffinite-loops and not processing_template_decl) wraps the condition into an ANNOTATE_EXPR which tells the middle-end that the loop shouldn't be loop->finite_p despite -ffinite-loops). Furthermore, the patch adds -Wtautological-compare warnings for loop conditions containing std::is_constant_evaluated(), either if those always evaluate to true, or always evaluate to false, or will evaluate to true just when checking if it is trivial infinite loop (and if in non-constexpr function also say that it will evaluate to false otherwise). The user is doing something weird in all those cases. 2024-04-10 Jakub Jelinek <jakub@redhat.com> PR c++/114462 gcc/ * tree-core.h (enum annot_expr_kind): Add annot_expr_maybe_infinite_kind enumerator. * gimplify.cc (gimple_boolify): Handle annot_expr_maybe_infinite_kind. * tree-cfg.cc (replace_loop_annotate_in_block): Likewise. (replace_loop_annotate): Likewise. Move loop->finite_p initialization before the replace_loop_annotate_in_block calls. * tree-pretty-print.cc (dump_generic_node): Handle annot_expr_maybe_infinite_kind. gcc/cp/ * semantics.cc: Implement C++26 P2809R3 - Trivial infinite loops are not Undefined Behavior. (maybe_warn_for_constant_evaluated): Add trivial_infinite argument and emit special diagnostics for that case. (finish_if_stmt_cond): Adjust caller. (finish_loop_cond): New function. (finish_while_stmt): Use it. (finish_do_stmt): Likewise. (finish_for_stmt): Likewise. gcc/testsuite/ * g++.dg/cpp26/trivial-infinite-loop1.C: New test. * g++.dg/cpp26/trivial-infinite-loop2.C: New test. * g++.dg/cpp26/trivial-infinite-loop3.C: New test.
Implemented for GCC 14.1.