From patchwork Tue Apr 9 09:57:16 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 1921317 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=YuRL07WS; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VDLxB6VMcz1yYd for ; Tue, 9 Apr 2024 19:57:50 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 154F33857C4F for ; Tue, 9 Apr 2024 09:57:49 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org 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 EC4403858CDB for ; Tue, 9 Apr 2024 09:57:24 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org EC4403858CDB 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 EC4403858CDB 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=1712656648; cv=none; b=elvhDJdWJUnjdDx8xmxSM0gLmSQFIV7RI/E8Wc906yP01ihNogG9cVucqgVM6NR6jwtFn3zDkurQVkTbFzw1pgMvLgce6V/YTTAeSQj3RI7pknjUA34NdtDLGq8rXDzefDYkOiL6GHHJ2FCYlfz0Gb/m09t+K5hEgBUQDQ0wtCM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1712656648; c=relaxed/simple; bh=phW6xtFxZ7jSuSOKkNnvf8VCY45ZVrR9yIlkrjt43yw=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=xldxtjHoZD+maAoJ62nQZndYdLQEmwaxY41AlPbDgP/1YWozEzjzsUh72Y6zQB/u61el/D+c1SZP2Fvhv2li1bIFUaebe80aFa4+QwEXCLXgeA2OBJTKoTOzF90NWiHzMRTzp2iQtl4GzYvQRMhKK29rEygkR0J1Ku0cxxJ5pIk= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1712656644; 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:in-reply-to:in-reply-to: references:references; bh=acNSW26DFKGSxuFiwcCbt43fnBHUJy8QMedEgJFvuLI=; b=YuRL07WSa/HDoTqbCiNi8Liq0lP/9NjtLlXnTHjmtRRrpMBXswmMlBRQUgrNEfiTlivF4O 64SSog4JE4cAGchpM+2QH9Rjb0L7QR30uJecEixqe690GyVZ8JepruZ/O7poCbUc6CN+H6 5g9KbDG9SpnBIKEWmz3r8+n2wCzB4Fw= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-483-4jr4kRUkNlmN5xCcWlvyww-1; Tue, 09 Apr 2024 05:57:23 -0400 X-MC-Unique: 4jr4kRUkNlmN5xCcWlvyww-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (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 B8C40808785 for ; Tue, 9 Apr 2024 09:57:22 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.45.224.14]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 48CDB17AB1; Tue, 9 Apr 2024 09:57:22 +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 4399vGAM1313463 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Tue, 9 Apr 2024 11:57:16 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 4399vGZJ1313462; Tue, 9 Apr 2024 11:57:16 +0200 Date: Tue, 9 Apr 2024 11:57:16 +0200 From: Jakub Jelinek To: Jason Merrill Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] c++, v3: Implement C++26 P2809R3 - Trivial infinite loops are not Undefined Behavior Message-ID: References: MIME-Version: 1.0 In-Reply-To: X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.5 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-4.3 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 X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Jakub Jelinek Errors-To: gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org 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-% 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. 2024-04-09 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. Jakub --- 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, "% 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") + && !maybe_constexpr_fn (current_function_decl)) + 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 " @@ -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; + } +}