diff mbox series

c++, v2: Implement C++26 P2809R3 - Trivial infinite loops are not Undefined Behavior

Message ID Zg+uz0I1M3FQRuZ+@tucnak
State New
Headers show
Series c++, v2: Implement C++26 P2809R3 - Trivial infinite loops are not Undefined Behavior | expand

Commit Message

Jakub Jelinek April 5, 2024, 7:57 a.m. UTC
Hi!

Here is a new version of the PR114462 P2809R3 patch.
As clarified on core, for trivially empty iteration statements
(where the condition isn't empty or INTEGER_CST, because those
loops can't contain std::is_constant_evaluated() in the condition
and the middle-end handles them right even with -ffinite-loops)
it attempts to fold_non_dependent_expr with manifestly_const_eval=true
the expression and if that returns integer_nonzerop, treats it
as trivial infinite loops, but keeps the condition as is.
Instead for -ffinite-loops it adds ANNOTATE_EXPR that the loop is
maybe infinite to override -ffinite-loops behavior for the particular loop.

And, it also emits maybe_warn_for_constant_evaluated warnings for loop
conditions.  For non-trivially empty loops or in immediate functions
or if !processing_template_decl and the condition of trivially empty loops
is not a constant expression or doesn't evaluate to integer non-zero
warns like for condition of non-constexpr if, and in non-constexpr functions
when the condition constant evaluates to true warns that the code has
weird behavior, that it evaluates to true when checking if the loop
is trivially infinite and to false at runtime.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2024-04-05  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.


	Jakub

Comments

Jason Merrill April 8, 2024, 10:53 p.m. UTC | #1
On 4/5/24 03:57, Jakub Jelinek wrote:
> Hi!
> 
> Here is a new version of the PR114462 P2809R3 patch.
> As clarified on core, for trivially empty iteration statements
> (where the condition isn't empty or INTEGER_CST, because those
> loops can't contain std::is_constant_evaluated() in the condition
> and the middle-end handles them right even with -ffinite-loops)
> it attempts to fold_non_dependent_expr with manifestly_const_eval=true
> the expression and if that returns integer_nonzerop, treats it
> as trivial infinite loops, but keeps the condition as is.
> Instead for -ffinite-loops it adds ANNOTATE_EXPR that the loop is
> maybe infinite to override -ffinite-loops behavior for the particular loop.
> 
> And, it also emits maybe_warn_for_constant_evaluated warnings for loop
> conditions.  For non-trivially empty loops or in immediate functions
> or if !processing_template_decl and the condition of trivially empty loops
> is not a constant expression or doesn't evaluate to integer non-zero
> warns like for condition of non-constexpr if, and in non-constexpr functions
> when the condition constant evaluates to true warns that the code has
> weird behavior, that it evaluates to true when checking if the loop
> is trivially infinite and to false at runtime.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2024-04-05  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.
> 
> --- gcc/tree-core.h.jj	2024-03-15 09:13:35.333541416 +0100
> +++ gcc/tree-core.h	2024-04-04 12:39:42.707652592 +0200
> @@ -983,6 +983,7 @@ enum annot_expr_kind {
>     annot_expr_no_vector_kind,
>     annot_expr_vector_kind,
>     annot_expr_parallel_kind,
> +  annot_expr_maybe_infinite_kind,
>     annot_expr_kind_last
>   };
>   
> --- gcc/gimplify.cc.jj	2024-03-11 12:36:18.494005912 +0100
> +++ gcc/gimplify.cc	2024-04-04 12:40:44.619799465 +0200
> @@ -4516,6 +4516,7 @@ gimple_boolify (tree expr)
>   	case annot_expr_no_vector_kind:
>   	case annot_expr_vector_kind:
>   	case annot_expr_parallel_kind:
> +	case annot_expr_maybe_infinite_kind:
>   	  TREE_OPERAND (expr, 0) = gimple_boolify (TREE_OPERAND (expr, 0));
>   	  if (TREE_CODE (type) != BOOLEAN_TYPE)
>   	    TREE_TYPE (expr) = boolean_type_node;
> --- gcc/tree-cfg.cc.jj	2024-02-15 09:51:34.608063945 +0100
> +++ gcc/tree-cfg.cc	2024-04-04 12:42:38.271233376 +0200
> @@ -297,6 +297,9 @@ replace_loop_annotate_in_block (basic_bl
>   	  loop->can_be_parallel = true;
>   	  loop->safelen = INT_MAX;
>   	  break;
> +	case annot_expr_maybe_infinite_kind:
> +	  loop->finite_p = false;
> +	  break;
>   	default:
>   	  gcc_unreachable ();
>   	}
> @@ -320,12 +323,12 @@ replace_loop_annotate (void)
>   
>     for (auto loop : loops_list (cfun, 0))
>       {
> +      /* Push the global flag_finite_loops state down to individual loops.  */
> +      loop->finite_p = flag_finite_loops;
> +
>         /* Check all exit source blocks for annotations.  */
>         for (auto e : get_loop_exit_edges (loop))
>   	replace_loop_annotate_in_block (e->src, loop);
> -
> -      /* Push the global flag_finite_loops state down to individual loops.  */
> -      loop->finite_p = flag_finite_loops;
>       }
>   
>     /* Remove IFN_ANNOTATE.  Safeguard for the case loop->latch == NULL.  */
> @@ -347,6 +350,7 @@ replace_loop_annotate (void)
>   	    case annot_expr_no_vector_kind:
>   	    case annot_expr_vector_kind:
>   	    case annot_expr_parallel_kind:
> +	    case annot_expr_maybe_infinite_kind:
>   	      break;
>   	    default:
>   	      gcc_unreachable ();
> --- gcc/tree-pretty-print.cc.jj	2024-03-14 14:07:34.303423928 +0100
> +++ gcc/tree-pretty-print.cc	2024-04-04 12:39:35.369753709 +0200
> @@ -3479,6 +3479,9 @@ dump_generic_node (pretty_printer *pp, t
>   	case annot_expr_parallel_kind:
>   	  pp_string (pp, ", parallel");
>   	  break;
> +	case annot_expr_maybe_infinite_kind:
> +	  pp_string (pp, ", maybe-infinite");
> +	  break;
>   	default:
>   	  gcc_unreachable ();
>   	}
> --- gcc/cp/semantics.cc.jj	2024-04-04 12:11:36.203886572 +0200
> +++ gcc/cp/semantics.cc	2024-04-04 14:45:36.641830281 +0200
> @@ -1090,7 +1090,8 @@ find_std_constant_evaluated_r (tree *tp,
>      (e.g., in a non-constexpr non-consteval function) so give the user a clue.  */
>   
>   static void
> -maybe_warn_for_constant_evaluated (tree cond, bool constexpr_if)
> +maybe_warn_for_constant_evaluated (tree cond, bool constexpr_if,
> +				   bool trivial_infinite)
>   {
>     if (!warn_tautological_compare)
>       return;
> @@ -1108,6 +1109,17 @@ maybe_warn_for_constant_evaluated (tree
>   	warning_at (EXPR_LOCATION (cond), OPT_Wtautological_compare,
>   		    "%<std::is_constant_evaluated%> always evaluates to "
>   		    "true in %<if constexpr%>");
> +      else if (trivial_infinite)
> +	{
> +	  auto_diagnostic_group d;
> +	  if (warning_at (EXPR_LOCATION (cond), OPT_Wtautological_compare,
> +			  "%<std::is_constant_evaluated%> evaluates to "
> +			  "true when checking if trivially empty iteration "
> +			  "statement is trivial infinite loop"))
> +	    inform (EXPR_LOCATION (cond),
> +		    "and evaluates to false when actually evaluating "
> +		    "the condition in non-%<constexpr%> function");
> +	}
>         else if (!maybe_constexpr_fn (current_function_decl))
>   	warning_at (EXPR_LOCATION (cond), OPT_Wtautological_compare,
>   		    "%<std::is_constant_evaluated%> always evaluates to "
> @@ -1128,7 +1140,8 @@ finish_if_stmt_cond (tree orig_cond, tre
>     tree cond = maybe_convert_cond (orig_cond);
>     maybe_warn_for_constant_evaluated (cond,
>   				     /*constexpr_if=*/
> -				     IF_STMT_CONSTEXPR_P (if_stmt));
> +				     IF_STMT_CONSTEXPR_P (if_stmt),
> +				     /*trivial_infinite=*/false);
>     if (IF_STMT_CONSTEXPR_P (if_stmt)
>         && !type_dependent_expression_p (cond)
>         && require_constant_expression (cond)
> @@ -1207,6 +1220,48 @@ finish_if_stmt (tree if_stmt)
>     add_stmt (do_poplevel (scope));
>   }
>   
> +/* Determine if iteration statement with *CONDP condition and
> +   loop BODY is trivially empty iteration statement or even
> +   trivial infinite loop.  In the latter case for -ffinite-loops
> +   add ANNOTATE_EXPR to mark the loop as maybe validly infinite.
> +   Also, emit -Wtautological-compare warning for std::is_constant_evaluated ()
> +   calls in the condition when needed.  */
> +
> +static void
> +finish_loop_cond (tree *condp, tree body)
> +{
> +  if (TREE_CODE (*condp) == INTEGER_CST)
> +    return;
> +  bool trivially_empty = expr_first (body) == NULL_TREE;
> +  bool trivial_infinite = false;
> +  if (trivially_empty)
> +    {
> +      tree c = fold_non_dependent_expr (*condp, tf_none,
> +					/*manifestly_const_eval=*/true);
> +      trivial_infinite = c && integer_nonzerop (c);
> +    }
> +  if (warn_tautological_compare)
> +    {
> +      tree cond = *condp;
> +      while (TREE_CODE (cond) == ANNOTATE_EXPR)
> +	cond = TREE_OPERAND (cond, 0);
> +      if (trivial_infinite
> +	  && !maybe_constexpr_fn (current_function_decl))

I think we also want this warning for constexpr functions, since they 
can also be evaluated at runtime.  Just not for consteval.

> +	maybe_warn_for_constant_evaluated (cond, /*constexpr_if=*/false,
> +					   /*trivial_infinite=*/true);
> +      else if (!trivially_empty
> +	       || (!processing_template_decl && !trivial_infinite)
> +	       || DECL_IMMEDIATE_FUNCTION_P (current_function_decl))

Do we need these conditions on the else?

Well, I suppose the existing !maybe_constexpr_fn case in maybe_warn_.... 
would be misleading for a trivially empty loop.

So I suppose the earlier condition should be trivially_empty rather than 
trivial_infinite.

> +	maybe_warn_for_constant_evaluated (cond, /*constexpr_if=*/false,
> +					   /*trivial_infinite=*/false);
> +    }
diff mbox series

Patch

--- gcc/tree-core.h.jj	2024-03-15 09:13:35.333541416 +0100
+++ gcc/tree-core.h	2024-04-04 12:39:42.707652592 +0200
@@ -983,6 +983,7 @@  enum annot_expr_kind {
   annot_expr_no_vector_kind,
   annot_expr_vector_kind,
   annot_expr_parallel_kind,
+  annot_expr_maybe_infinite_kind,
   annot_expr_kind_last
 };
 
--- gcc/gimplify.cc.jj	2024-03-11 12:36:18.494005912 +0100
+++ gcc/gimplify.cc	2024-04-04 12:40:44.619799465 +0200
@@ -4516,6 +4516,7 @@  gimple_boolify (tree expr)
 	case annot_expr_no_vector_kind:
 	case annot_expr_vector_kind:
 	case annot_expr_parallel_kind:
+	case annot_expr_maybe_infinite_kind:
 	  TREE_OPERAND (expr, 0) = gimple_boolify (TREE_OPERAND (expr, 0));
 	  if (TREE_CODE (type) != BOOLEAN_TYPE)
 	    TREE_TYPE (expr) = boolean_type_node;
--- gcc/tree-cfg.cc.jj	2024-02-15 09:51:34.608063945 +0100
+++ gcc/tree-cfg.cc	2024-04-04 12:42:38.271233376 +0200
@@ -297,6 +297,9 @@  replace_loop_annotate_in_block (basic_bl
 	  loop->can_be_parallel = true;
 	  loop->safelen = INT_MAX;
 	  break;
+	case annot_expr_maybe_infinite_kind:
+	  loop->finite_p = false;
+	  break;
 	default:
 	  gcc_unreachable ();
 	}
@@ -320,12 +323,12 @@  replace_loop_annotate (void)
 
   for (auto loop : loops_list (cfun, 0))
     {
+      /* Push the global flag_finite_loops state down to individual loops.  */
+      loop->finite_p = flag_finite_loops;
+
       /* Check all exit source blocks for annotations.  */
       for (auto e : get_loop_exit_edges (loop))
 	replace_loop_annotate_in_block (e->src, loop);
-
-      /* Push the global flag_finite_loops state down to individual loops.  */
-      loop->finite_p = flag_finite_loops;
     }
 
   /* Remove IFN_ANNOTATE.  Safeguard for the case loop->latch == NULL.  */
@@ -347,6 +350,7 @@  replace_loop_annotate (void)
 	    case annot_expr_no_vector_kind:
 	    case annot_expr_vector_kind:
 	    case annot_expr_parallel_kind:
+	    case annot_expr_maybe_infinite_kind:
 	      break;
 	    default:
 	      gcc_unreachable ();
--- gcc/tree-pretty-print.cc.jj	2024-03-14 14:07:34.303423928 +0100
+++ gcc/tree-pretty-print.cc	2024-04-04 12:39:35.369753709 +0200
@@ -3479,6 +3479,9 @@  dump_generic_node (pretty_printer *pp, t
 	case annot_expr_parallel_kind:
 	  pp_string (pp, ", parallel");
 	  break;
+	case annot_expr_maybe_infinite_kind:
+	  pp_string (pp, ", maybe-infinite");
+	  break;
 	default:
 	  gcc_unreachable ();
 	}
--- gcc/cp/semantics.cc.jj	2024-04-04 12:11:36.203886572 +0200
+++ gcc/cp/semantics.cc	2024-04-04 14:45:36.641830281 +0200
@@ -1090,7 +1090,8 @@  find_std_constant_evaluated_r (tree *tp,
    (e.g., in a non-constexpr non-consteval function) so give the user a clue.  */
 
 static void
-maybe_warn_for_constant_evaluated (tree cond, bool constexpr_if)
+maybe_warn_for_constant_evaluated (tree cond, bool constexpr_if,
+				   bool trivial_infinite)
 {
   if (!warn_tautological_compare)
     return;
@@ -1108,6 +1109,17 @@  maybe_warn_for_constant_evaluated (tree
 	warning_at (EXPR_LOCATION (cond), OPT_Wtautological_compare,
 		    "%<std::is_constant_evaluated%> always evaluates to "
 		    "true in %<if constexpr%>");
+      else if (trivial_infinite)
+	{
+	  auto_diagnostic_group d;
+	  if (warning_at (EXPR_LOCATION (cond), OPT_Wtautological_compare,
+			  "%<std::is_constant_evaluated%> evaluates to "
+			  "true when checking if trivially empty iteration "
+			  "statement is trivial infinite loop"))
+	    inform (EXPR_LOCATION (cond),
+		    "and evaluates to false when actually evaluating "
+		    "the condition in non-%<constexpr%> function");
+	}
       else if (!maybe_constexpr_fn (current_function_decl))
 	warning_at (EXPR_LOCATION (cond), OPT_Wtautological_compare,
 		    "%<std::is_constant_evaluated%> always evaluates to "
@@ -1128,7 +1140,8 @@  finish_if_stmt_cond (tree orig_cond, tre
   tree cond = maybe_convert_cond (orig_cond);
   maybe_warn_for_constant_evaluated (cond,
 				     /*constexpr_if=*/
-				     IF_STMT_CONSTEXPR_P (if_stmt));
+				     IF_STMT_CONSTEXPR_P (if_stmt),
+				     /*trivial_infinite=*/false);
   if (IF_STMT_CONSTEXPR_P (if_stmt)
       && !type_dependent_expression_p (cond)
       && require_constant_expression (cond)
@@ -1207,6 +1220,48 @@  finish_if_stmt (tree if_stmt)
   add_stmt (do_poplevel (scope));
 }
 
+/* Determine if iteration statement with *CONDP condition and
+   loop BODY is trivially empty iteration statement or even
+   trivial infinite loop.  In the latter case for -ffinite-loops
+   add ANNOTATE_EXPR to mark the loop as maybe validly infinite.
+   Also, emit -Wtautological-compare warning for std::is_constant_evaluated ()
+   calls in the condition when needed.  */
+
+static void
+finish_loop_cond (tree *condp, tree body)
+{
+  if (TREE_CODE (*condp) == INTEGER_CST)
+    return;
+  bool trivially_empty = expr_first (body) == NULL_TREE;
+  bool trivial_infinite = false;
+  if (trivially_empty)
+    {
+      tree c = fold_non_dependent_expr (*condp, tf_none,
+					/*manifestly_const_eval=*/true);
+      trivial_infinite = c && integer_nonzerop (c);
+    }
+  if (warn_tautological_compare)
+    {
+      tree cond = *condp;
+      while (TREE_CODE (cond) == ANNOTATE_EXPR)
+	cond = TREE_OPERAND (cond, 0);
+      if (trivial_infinite
+	  && !maybe_constexpr_fn (current_function_decl))
+	maybe_warn_for_constant_evaluated (cond, /*constexpr_if=*/false,
+					   /*trivial_infinite=*/true);
+      else if (!trivially_empty
+	       || (!processing_template_decl && !trivial_infinite)
+	       || DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
+	maybe_warn_for_constant_evaluated (cond, /*constexpr_if=*/false,
+					   /*trivial_infinite=*/false);
+    }
+  if (trivial_infinite && flag_finite_loops && !processing_template_decl)
+    *condp = build3 (ANNOTATE_EXPR, TREE_TYPE (*condp), *condp,
+		     build_int_cst (integer_type_node,
+				    annot_expr_maybe_infinite_kind),
+		     integer_zero_node);
+}
+
 /* Begin a while-statement.  Returns a newly created WHILE_STMT if
    appropriate.  */
 
@@ -1262,6 +1317,7 @@  finish_while_stmt (tree while_stmt)
 {
   end_maybe_infinite_loop (boolean_true_node);
   WHILE_BODY (while_stmt) = do_poplevel (WHILE_BODY (while_stmt));
+  finish_loop_cond (&WHILE_COND (while_stmt), WHILE_BODY (while_stmt));
 }
 
 /* Begin a do-statement.  Returns a newly created DO_STMT if
@@ -1319,6 +1375,12 @@  finish_do_stmt (tree cond, tree do_stmt,
 		   build_int_cst (integer_type_node, annot_expr_no_vector_kind),
 		   integer_zero_node);
   DO_COND (do_stmt) = cond;
+  tree do_body = DO_BODY (do_stmt);
+  if (CONVERT_EXPR_P (do_body)
+      && integer_zerop (TREE_OPERAND (do_body, 0))
+      && VOID_TYPE_P (TREE_TYPE (do_body)))
+    do_body = NULL_TREE;
+  finish_loop_cond (&DO_COND (do_stmt), do_body);
 }
 
 /* Finish a return-statement.  The EXPRESSION returned, if any, is as
@@ -1489,7 +1551,13 @@  finish_for_stmt (tree for_stmt)
   if (TREE_CODE (for_stmt) == RANGE_FOR_STMT)
     RANGE_FOR_BODY (for_stmt) = do_poplevel (RANGE_FOR_BODY (for_stmt));
   else
-    FOR_BODY (for_stmt) = do_poplevel (FOR_BODY (for_stmt));
+    {
+      FOR_BODY (for_stmt) = do_poplevel (FOR_BODY (for_stmt));
+      if (FOR_COND (for_stmt))
+	finish_loop_cond (&FOR_COND (for_stmt),
+			  FOR_EXPR (for_stmt) ? integer_one_node
+					      : FOR_BODY (for_stmt));
+    }
 
   /* Pop the scope for the body of the loop.  */
   tree *scope_ptr = (TREE_CODE (for_stmt) == RANGE_FOR_STMT
--- gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop1.C.jj	2024-04-04 14:09:17.620753147 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop1.C	2024-04-04 14:35:54.951824609 +0200
@@ -0,0 +1,148 @@ 
+// P2809R3 - Trivial infinite loops are not Undefined Behavior
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-fdump-tree-gimple -fno-inline -Wtautological-compare -O2" }
+// { dg-final { scan-tree-dump-times ".ANNOTATE \\\(\[^\n\r]*, 5, 0\\\)" 32 "gimple" { target c++20 } } }
+// { dg-final { scan-tree-dump-times ".ANNOTATE \\\(\[^\n\r]*, 5, 0\\\)" 16 "gimple" { target c++17_down } } }
+
+volatile int v;
+
+constexpr bool
+foo ()
+{
+  return true;
+}
+
+struct S
+{
+  constexpr S () : s (true) {}
+  constexpr operator bool () const { return s; }
+  bool s;
+};
+
+#if __cplusplus >= 202002L
+namespace std {
+  constexpr inline bool
+  is_constant_evaluated () noexcept
+  {
+#if __cpp_if_consteval >= 202106L
+    if consteval { return true; } else { return false; }
+#else
+    return __builtin_is_constant_evaluated ();
+#endif
+  }
+}
+
+constexpr bool
+baz ()
+{
+  return std::is_constant_evaluated ();
+}
+#endif
+
+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:
+      for (v = 42; foo (); ) ;
+      break;
+    case 5:
+      for (v = 42; foo (); ) {}
+      break;
+    case 6:
+      for (int w = 42; foo (); ) ;
+      break;
+    case 7:
+      for (int w = 42; foo (); ) {}
+      break;
+    case 10:
+      while (S {}) ;
+      break;
+    case 11:
+      while (S {}) {}
+      break;
+    case 12:
+      do ; while (S {});
+      break;
+    case 13:
+      do {} while (S {});
+      break;
+    case 14:
+      for (v = 42; S {}; ) ;
+      break;
+    case 15:
+      for (v = 42; S {}; ) {}
+      break;
+    case 16:
+      for (int w = 42; S {}; ) ;
+      break;
+    case 17:
+      for (int w = 42; S {}; ) {}
+      break;
+#if __cplusplus >= 202002L
+    case 20:
+      while (baz ()) ;
+      break;
+    case 21:
+      while (baz ()) {}
+      break;
+    case 22:
+      do ; while (baz ());
+      break;
+    case 23:
+      do {} while (baz ());
+      break;
+    case 24:
+      for (v = 42; baz (); ) ;
+      break;
+    case 25:
+      for (v = 42; baz (); ) {}
+      break;
+    case 26:
+      for (int w = 42; baz (); ) ;
+      break;
+    case 27:
+      for (int w = 42; baz (); ) {}
+      break;
+    case 30:
+      while (std::is_constant_evaluated ()) ;			// { dg-warning "'std::is_constant_evaluated' evaluates to true when checking if trivially empty iteration statement is trivial infinite loop" "" { target c++20 } }
+      break;							// { dg-message "and evaluates to false when actually evaluating the condition in non-'constexpr' function" "" { target c++20 } .-1 }
+    case 31:
+      while (std::is_constant_evaluated ()) {}			// { dg-warning "'std::is_constant_evaluated' evaluates to true when checking if trivially empty iteration statement is trivial infinite loop" "" { target c++20 } }
+      break;							// { dg-message "and evaluates to false when actually evaluating the condition in non-'constexpr' function" "" { target c++20 } .-1 }
+    case 32:
+      do ; while (std::is_constant_evaluated ());		// { dg-warning "'std::is_constant_evaluated' evaluates to true when checking if trivially empty iteration statement is trivial infinite loop" "" { target c++20 } }
+      break;							// { dg-message "and evaluates to false when actually evaluating the condition in non-'constexpr' function" "" { target c++20 } .-1 }
+    case 33:
+      do {} while (std::is_constant_evaluated ());		// { dg-warning "'std::is_constant_evaluated' evaluates to true when checking if trivially empty iteration statement is trivial infinite loop" "" { target c++20 } }
+      break;							// { dg-message "and evaluates to false when actually evaluating the condition in non-'constexpr' function" "" { target c++20 } .-1 }
+    case 34:
+      for (v = 42; std::is_constant_evaluated (); ) ;		// { dg-warning "'std::is_constant_evaluated' evaluates to true when checking if trivially empty iteration statement is trivial infinite loop" "" { target c++20 } }
+      break;							// { dg-message "and evaluates to false when actually evaluating the condition in non-'constexpr' function" "" { target c++20 } .-1 }
+    case 35:
+      for (v = 42; std::is_constant_evaluated (); ) {}		// { dg-warning "'std::is_constant_evaluated' evaluates to true when checking if trivially empty iteration statement is trivial infinite loop" "" { target c++20 } }
+      break;							// { dg-message "and evaluates to false when actually evaluating the condition in non-'constexpr' function" "" { target c++20 } .-1 }
+    case 36:
+      for (int w = 42; std::is_constant_evaluated (); ) ;	// { dg-warning "'std::is_constant_evaluated' evaluates to true when checking if trivially empty iteration statement is trivial infinite loop" "" { target c++20 } }
+      break;							// { dg-message "and evaluates to false when actually evaluating the condition in non-'constexpr' function" "" { target c++20 } .-1 }
+    case 37:
+      for (int w = 42; std::is_constant_evaluated (); ) {}	// { dg-warning "'std::is_constant_evaluated' evaluates to true when checking if trivially empty iteration statement is trivial infinite loop" "" { target c++20 } }
+      break;							// { dg-message "and evaluates to false when actually evaluating the condition in non-'constexpr' function" "" { target c++20 } .-1 }
+#endif
+    default:
+      break;
+    }
+}
--- gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop2.C.jj	2024-04-04 14:09:17.620753147 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop2.C	2024-04-04 14:36:38.936220130 +0200
@@ -0,0 +1,147 @@ 
+// P2809R3 - Trivial infinite loops are not Undefined Behavior
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-fdump-tree-gimple -fno-inline -Wtautological-compare -O2" }
+// { dg-final { scan-tree-dump-not ".ANNOTATE \\\(\[^\n\r]*, 5, 0\\\)" "gimple" } }
+
+volatile int v;
+
+constexpr bool
+foo ()
+{
+  return false;
+}
+
+struct S
+{
+  constexpr S () : s (false) {}
+  constexpr operator bool () const { return s; }
+  bool s;
+};
+
+#if __cplusplus >= 202002L
+namespace std {
+  constexpr inline bool
+  is_constant_evaluated () noexcept
+  {
+#if __cpp_if_consteval >= 202106L
+    if consteval { return true; } else { return false; }
+#else
+    return __builtin_is_constant_evaluated ();
+#endif
+  }
+}
+
+constexpr bool
+baz ()
+{
+  return !std::is_constant_evaluated ();
+}
+#endif
+
+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:
+      for (v = 42; foo (); ) ;
+      break;
+    case 5:
+      for (v = 42; foo (); ) {}
+      break;
+    case 6:
+      for (int w = 42; foo (); ) ;
+      break;
+    case 7:
+      for (int w = 42; foo (); ) {}
+      break;
+    case 10:
+      while (S {}) ;
+      break;
+    case 11:
+      while (S {}) {}
+      break;
+    case 12:
+      do ; while (S {});
+      break;
+    case 13:
+      do {} while (S {});
+      break;
+    case 14:
+      for (v = 42; S {}; ) ;
+      break;
+    case 15:
+      for (v = 42; S {}; ) {}
+      break;
+    case 16:
+      for (int w = 42; S {}; ) ;
+      break;
+    case 17:
+      for (int w = 42; S {}; ) {}
+      break;
+#if __cplusplus >= 202002L
+    case 20:
+      while (baz ()) ;
+      break;
+    case 21:
+      while (baz ()) {}
+      break;
+    case 22:
+      do ; while (baz ());
+      break;
+    case 23:
+      do {} while (baz ());
+      break;
+    case 24:
+      for (v = 42; baz (); ) ;
+      break;
+    case 25:
+      for (v = 42; baz (); ) {}
+      break;
+    case 26:
+      for (int w = 42; baz (); ) ;
+      break;
+    case 27:
+      for (int w = 42; baz (); ) {}
+      break;
+    case 30:
+      while (!std::is_constant_evaluated ()) ;			// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 31:
+      while (!std::is_constant_evaluated ()) {}			// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 32:
+      do ; while (!std::is_constant_evaluated ());		// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 33:
+      do {} while (!std::is_constant_evaluated ());		// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 34:
+      for (v = 42; !std::is_constant_evaluated (); ) ;		// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 35:
+      for (v = 42; !std::is_constant_evaluated (); ) {}		// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 36:
+      for (int w = 42; !std::is_constant_evaluated (); ) ;	// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 37:
+      for (int w = 42; !std::is_constant_evaluated (); ) {}	// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+#endif
+    default:
+      break;
+    }
+}
--- gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop3.C.jj	2024-04-04 14:09:17.620753147 +0200
+++ gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop3.C	2024-04-04 14:37:37.870410190 +0200
@@ -0,0 +1,148 @@ 
+// P2809R3 - Trivial infinite loops are not Undefined Behavior
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-fdump-tree-gimple -fno-inline -Wtautological-compare" }
+// { dg-final { scan-tree-dump-not ".ANNOTATE \\\(\[^\n\r]*, 5, 0\\\)" "gimple" } }
+
+volatile int v;
+int y;
+
+constexpr bool
+foo ()
+{
+  return true;
+}
+
+struct S
+{
+  constexpr S () : s (true) {}
+  constexpr operator bool () const { return s; }
+  bool s;
+};
+
+#if __cplusplus >= 202002L
+namespace std {
+  constexpr inline bool
+  is_constant_evaluated () noexcept
+  {
+#if __cpp_if_consteval >= 202106L
+    if consteval { return true; } else { return false; }
+#else
+    return __builtin_is_constant_evaluated ();
+#endif
+  }
+}
+
+constexpr bool
+baz ()
+{
+  return std::is_constant_evaluated ();
+}
+#endif
+
+void
+bar (int x)
+{
+  switch (x)
+    {
+    case 0:
+      while (foo ()) ++y;
+      break;
+    case 1:
+      while (foo ()) { ++y; }
+      break;
+    case 2:
+      do ++y; while (foo ());
+      break;
+    case 3:
+      do { ++y; } while (foo ());
+      break;
+    case 4:
+      for (v = 42; foo (); ) ++y;
+      break;
+    case 5:
+      for (v = 42; foo (); ) { ++y; }
+      break;
+    case 6:
+      for (int w = 42; foo (); ) ++y;
+      break;
+    case 7:
+      for (int w = 42; foo (); ) { ++y; }
+      break;
+    case 10:
+      while (S {}) ++y;
+      break;
+    case 11:
+      while (S {}) { ++y; }
+      break;
+    case 12:
+      do ++y; while (S {});
+      break;
+    case 13:
+      do { ++y; } while (S {});
+      break;
+    case 14:
+      for (v = 42; S {}; ) ++y;
+      break;
+    case 15:
+      for (v = 42; S {}; ) { ++y; }
+      break;
+    case 16:
+      for (int w = 42; S {}; ) ++y;
+      break;
+    case 17:
+      for (int w = 42; S {}; ) { ++y; }
+      break;
+#if __cplusplus >= 202002L
+    case 20:
+      while (baz ()) ++y;
+      break;
+    case 21:
+      while (baz ()) { ++y; }
+      break;
+    case 22:
+      do ++y; while (baz ());
+      break;
+    case 23:
+      do { ++y; } while (baz ());
+      break;
+    case 24:
+      for (v = 42; baz (); ) ++y;
+      break;
+    case 25:
+      for (v = 42; baz (); ) { ++y; }
+      break;
+    case 26:
+      for (int w = 42; baz (); ) ++y;
+      break;
+    case 27:
+      for (int w = 42; baz (); ) { ++y; }
+      break;
+    case 30:
+      while (std::is_constant_evaluated ()) ++y;			// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 31:
+      while (std::is_constant_evaluated ()) { ++y; }			// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 32:
+      do ++y; while (std::is_constant_evaluated ());			// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 33:
+      do { ++y; } while (std::is_constant_evaluated ());		// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 34:
+      for (v = 42; std::is_constant_evaluated (); ) ++y;		// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 35:
+      for (v = 42; std::is_constant_evaluated (); ) { ++y; }		// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 36:
+      for (int w = 42; std::is_constant_evaluated (); ) ++y;		// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+    case 37:
+      for (int w = 42; std::is_constant_evaluated (); ) { ++y; }	// { dg-warning "'std::is_constant_evaluated' always evaluates to false in a non-'constexpr' function" "" { target c++20 } }
+      break;
+#endif
+    default:
+      break;
+    }
+}