@@ -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
};
@@ -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;
@@ -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 ();
@@ -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 ();
}
@@ -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
@@ -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;
+ }
+}
@@ -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;
+ }
+}
@@ -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;
+ }
+}