https://gcc.gnu.org/g:4be1cc5f50578fafcdcbd09160235066d76a3f86

commit r14-9887-g4be1cc5f50578fafcdcbd09160235066d76a3f86
Author: Jakub Jelinek <ja...@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  <ja...@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.

Diff:
---
 gcc/cp/semantics.cc                                |  75 ++++++++++-
 gcc/gimplify.cc                                    |   1 +
 .../g++.dg/cpp26/trivial-infinite-loop1.C          | 148 +++++++++++++++++++++
 .../g++.dg/cpp26/trivial-infinite-loop2.C          | 147 ++++++++++++++++++++
 .../g++.dg/cpp26/trivial-infinite-loop3.C          | 148 +++++++++++++++++++++
 gcc/tree-cfg.cc                                    |  10 +-
 gcc/tree-core.h                                    |   1 +
 gcc/tree-pretty-print.cc                           |   3 +
 8 files changed, 527 insertions(+), 6 deletions(-)

diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 329c524a509..abaa4a3ca53 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -1090,7 +1090,8 @@ find_std_constant_evaluated_r (tree *tp, int 
*walk_subtrees, void *)
    (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,18 @@ maybe_warn_for_constant_evaluated (tree cond, bool 
constexpr_if)
        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")
+             && !maybe_constexpr_fn (current_function_decl))
+           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 "
@@ -1126,7 +1139,8 @@ tree
 finish_if_stmt_cond (tree orig_cond, tree if_stmt)
 {
   tree cond = maybe_convert_cond (orig_cond);
-  maybe_warn_for_constant_evaluated (cond, IF_STMT_CONSTEXPR_P (if_stmt));
+  maybe_warn_for_constant_evaluated (cond, 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)
@@ -1205,6 +1219,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
+         && !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
+       maybe_warn_for_constant_evaluated (cond, /*constexpr_if=*/false,
+                                          /*trivial_infinite=*/true);
+      else if (!trivially_empty
+              || !processing_template_decl
+              || 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.  */
 
@@ -1260,6 +1316,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
@@ -1317,6 +1374,12 @@ finish_do_stmt (tree cond, tree do_stmt, bool ivdep, 
tree unroll,
                   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
@@ -1487,7 +1550,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
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 7b972c093ba..3b731525f15 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -4584,6 +4584,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;
diff --git a/gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop1.C 
b/gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop1.C
new file mode 100644
index 00000000000..288a736e4de
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop1.C
@@ -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;
+    }
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop2.C 
b/gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop2.C
new file mode 100644
index 00000000000..fd8305e7f8a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop2.C
@@ -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;
+    }
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop3.C 
b/gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop3.C
new file mode 100644
index 00000000000..e88d55317be
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop3.C
@@ -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;
+    }
+}
diff --git a/gcc/tree-cfg.cc b/gcc/tree-cfg.cc
index bdffc3b4ed2..96686db8ed3 100644
--- a/gcc/tree-cfg.cc
+++ b/gcc/tree-cfg.cc
@@ -297,6 +297,9 @@ replace_loop_annotate_in_block (basic_block bb, class loop 
*loop)
          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 ();
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index a8439f8acbb..9fa74342919 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -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
 };
 
diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc
index 926f7e006a7..c935a7da7d1 100644
--- a/gcc/tree-pretty-print.cc
+++ b/gcc/tree-pretty-print.cc
@@ -3479,6 +3479,9 @@ dump_generic_node (pretty_printer *pp, tree node, int 
spc, dump_flags_t flags,
        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 ();
        }

Reply via email to