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.
This commit is contained in:
parent
4923ed49b9
commit
4be1cc5f50
8 changed files with 527 additions and 6 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
148
gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop1.C
Normal file
148
gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop1.C
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
147
gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop2.C
Normal file
147
gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop2.C
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
148
gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop3.C
Normal file
148
gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop3.C
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 ();
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue