From: Jason Merrill <jason@redhat.com>
To: Jakub Jelinek <jakub@redhat.com>
Cc: gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++, v3: Implement C++26 P2809R3 - Trivial infinite loops are not Undefined Behavior
Date: Tue, 9 Apr 2024 10:41:05 -0400 [thread overview]
Message-ID: <23a2a4df-f958-4d62-bb33-8bf40d11c698@redhat.com> (raw)
In-Reply-To: <ZhUQ/MW8clDoNEif@tucnak>
On 4/9/24 05:57, Jakub Jelinek wrote:
> On Mon, Apr 08, 2024 at 06:53:29PM -0400, Jason Merrill wrote:
>>> + 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.
>
> If we leave in the maybe_constexpr_fn (current_function_decl) case the
> "and evaluates to false when actually evaluating"
> "the condition in non-%<constexpr%> function"
> note, yes (as implemented in the updated patch below).
>
>>> + 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.
>
> The intent of the above is to warn only if we have something relevant and
> final (i.e. not something that will be different e.g. during instantiation)
> to warn about.
>
> So, IMHO:
> 1) DECL_IMMEDIATE_FUNCTION_P (current_function_decl), regardless of
> trivial_infinite or not, is always true, doesn't depend on anything else;
> 2) otherwise, trivial_infinite, warn that during trivial infinite checking
> it evaluates true (and for non-constexpr functions otherwise evaluates
> to false);
> 3) otherwise, !trivially_empty in non-constexpr functions, always
> evaluates to false
> 4) otherwise, !processing_template_decl we know !trivial_infinite is final,
> so in non-constexpr functions always evaluates to false as well
> Now, the processing_template_decl case, we don't really know if it will
> evaluate to true or false when manifestly-constant evaluated to check for
> trivial_infinite, so I think we should defer until instantiation.
> Otherwise, we could warn that std::is_constant_evaluated() always
> evaluates false in non-constexpr functions even when it will actually
> evaluate to true if it will be trivial infinite loop.
> Because of the earlier changes I've changed the
> || (!processing_template_decl && !trivial_infinite)
> condition just to
> || !processing_template_decl
> because in the trivial_infinite case it will fall into this condition
> only for the DECL_IMMEDIATE_FUNCTION_P (current_function_decl) case.
OK.
> 2024-04-09 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-04-05 09:19:48.319044584 +0200
> +++ gcc/tree-core.h 2024-04-09 11:27:24.535821914 +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-04-05 09:19:48.216046013 +0200
> +++ gcc/gimplify.cc 2024-04-09 11:27:24.538821874 +0200
> @@ -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;
> --- gcc/tree-cfg.cc.jj 2024-04-05 09:19:12.915535710 +0200
> +++ gcc/tree-cfg.cc 2024-04-09 11:27:24.539821860 +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-04-05 09:19:13.035534046 +0200
> +++ gcc/tree-pretty-print.cc 2024-04-09 11:27:24.540821847 +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-09 09:31:26.343560220 +0200
> +++ gcc/cp/semantics.cc 2024-04-09 11:30:28.608332303 +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,18 @@ 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")
> + && !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,
> 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
> --- gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop1.C.jj 2024-04-09 11:27:24.542821820 +0200
> +++ gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop1.C 2024-04-09 11:27:24.542821820 +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-09 11:27:24.543821806 +0200
> +++ gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop2.C 2024-04-09 11:27:24.542821820 +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-09 11:27:24.543821806 +0200
> +++ gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop3.C 2024-04-09 11:27:24.543821806 +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;
> + }
> +}
>
>
> Jakub
>
prev parent reply other threads:[~2024-04-09 14:41 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-04-05 7:57 [PATCH] c++, v2: " Jakub Jelinek
2024-04-08 22:53 ` Jason Merrill
2024-04-09 9:57 ` [PATCH] c++, v3: " Jakub Jelinek
2024-04-09 14:41 ` Jason Merrill [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=23a2a4df-f958-4d62-bb33-8bf40d11c698@redhat.com \
--to=jason@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jakub@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).