aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/semantics.cc
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2024-04-10 10:08:12 +0200
committerJakub Jelinek <jakub@redhat.com>2024-04-10 10:19:04 +0200
commit4be1cc5f50578fafcdcbd09160235066d76a3f86 (patch)
treea763e327dd8ec081d05b7299de58c38efd149b00 /gcc/cp/semantics.cc
parent4923ed49b93352bcf9e43cafac38345e4a54c3f8 (diff)
downloadgcc-4be1cc5f50578fafcdcbd09160235066d76a3f86.zip
gcc-4be1cc5f50578fafcdcbd09160235066d76a3f86.tar.gz
gcc-4be1cc5f50578fafcdcbd09160235066d76a3f86.tar.bz2
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.
Diffstat (limited to 'gcc/cp/semantics.cc')
-rw-r--r--gcc/cp/semantics.cc75
1 files changed, 72 insertions, 3 deletions
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 329c524..abaa4a3 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