From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 365DF3858C24 for ; Fri, 5 Apr 2024 07:57:12 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 365DF3858C24 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 365DF3858C24 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1712303836; cv=none; b=lhKwk+kCOp29t9sxyXeUWf+fSMYm/9PSNsA5WUw5wluduu1dHiIqi3ybxOUweW03Y0KW8LbvE3wAXM2EZxqVE9Q/yvXX2usM+GCnVKhEUZVn/AucvQITmpH+MHBOgU/TIaSSFsErW1pKhS6W6hphcORAAOdnSDZnL5tVvPsDExw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1712303836; c=relaxed/simple; bh=N+dKAD0s6CJM/y/H6hHagD+39y4L7zxm9MWBesZMg/w=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=l9Qqqfv+L9EiZKF5w/cfuIyUYrJgedALZYn9FMfLj9Ffh19gxaUezUxMc8A8o/oMPp6EGw/KOusgA8YrW4FgWCqBOfb5Eu4W8v/9eVArlE6f58VD/Qw4IjWT1Zz/J30RkFjTdsFc1ZGal4d/EWvQjDqx7ZIg6+aE9Je1tMXO3S4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1712303831; h=from:from:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type; bh=1wDFXjF//pCr8GYUM7+yUQGRtHt3lY4bUY4cD+u3vLw=; b=aXfOuneJwQEAZfTpjtypXsAAOZMsHkeBdlkyO9cmJgYUJBqJsGnTwuorJ13OgVuSOtuULm EsHBfYz4sPMrWoBCsb46jjrZ9XWlDMqZWR51i6BeW4a1gNA2/fKsmSBDcKoXT7MMW+Adka 0tSxMIvm4uK7+S/ddZ4jfnoKlMX9Pkg= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-587-d3_h_bfVOEGXM4BxRityvw-1; Fri, 05 Apr 2024 03:57:09 -0400 X-MC-Unique: d3_h_bfVOEGXM4BxRityvw-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 8FF69381D4C9 for ; Fri, 5 Apr 2024 07:57:09 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.45.224.14]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 203361121313; Fri, 5 Apr 2024 07:57:09 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.17.1/8.17.1) with ESMTPS id 4357v3MS2322656 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Fri, 5 Apr 2024 09:57:03 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 4357v3e02322655; Fri, 5 Apr 2024 09:57:03 +0200 Date: Fri, 5 Apr 2024 09:57:03 +0200 From: Jakub Jelinek To: Jason Merrill Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] c++, v2: Implement C++26 P2809R3 - Trivial infinite loops are not Undefined Behavior Message-ID: Reply-To: Jakub Jelinek MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.3 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=us-ascii Content-Disposition: inline X-Spam-Status: No, score=-3.6 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Hi! Here is a new version of the PR114462 P2809R3 patch. As clarified on core, for trivially empty iteration statements (where the condition isn't empty or INTEGER_CST, because those loops can't contain std::is_constant_evaluated() in the condition and the middle-end handles them right even with -ffinite-loops) it attempts to fold_non_dependent_expr with manifestly_const_eval=true the expression and if that returns integer_nonzerop, treats it as trivial infinite loops, but keeps the condition as is. Instead for -ffinite-loops it adds ANNOTATE_EXPR that the loop is maybe infinite to override -ffinite-loops behavior for the particular loop. And, it also emits maybe_warn_for_constant_evaluated warnings for loop conditions. For non-trivially empty loops or in immediate functions or if !processing_template_decl and the condition of trivially empty loops is not a constant expression or doesn't evaluate to integer non-zero warns like for condition of non-constexpr if, and in non-constexpr functions when the condition constant evaluates to true warns that the code has weird behavior, that it evaluates to true when checking if the loop is trivially infinite and to false at runtime. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2024-04-05 Jakub Jelinek 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-03-15 09:13:35.333541416 +0100 +++ gcc/tree-core.h 2024-04-04 12:39:42.707652592 +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-03-11 12:36:18.494005912 +0100 +++ gcc/gimplify.cc 2024-04-04 12:40:44.619799465 +0200 @@ -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; --- gcc/tree-cfg.cc.jj 2024-02-15 09:51:34.608063945 +0100 +++ gcc/tree-cfg.cc 2024-04-04 12:42:38.271233376 +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-03-14 14:07:34.303423928 +0100 +++ gcc/tree-pretty-print.cc 2024-04-04 12:39:35.369753709 +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-04 12:11:36.203886572 +0200 +++ gcc/cp/semantics.cc 2024-04-04 14:45:36.641830281 +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,17 @@ maybe_warn_for_constant_evaluated (tree warning_at (EXPR_LOCATION (cond), OPT_Wtautological_compare, "% always evaluates to " "true in %"); + else if (trivial_infinite) + { + auto_diagnostic_group d; + if (warning_at (EXPR_LOCATION (cond), OPT_Wtautological_compare, + "% 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-% function"); + } else if (!maybe_constexpr_fn (current_function_decl)) warning_at (EXPR_LOCATION (cond), OPT_Wtautological_compare, "% 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 --- gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop1.C.jj 2024-04-04 14:09:17.620753147 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop1.C 2024-04-04 14:35:54.951824609 +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-04 14:09:17.620753147 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop2.C 2024-04-04 14:36:38.936220130 +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-04 14:09:17.620753147 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivial-infinite-loop3.C 2024-04-04 14:37:37.870410190 +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