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.133.124]) by sourceware.org (Postfix) with ESMTPS id DF5F838432FF for ; Tue, 9 Apr 2024 14:41:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DF5F838432FF 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 DF5F838432FF Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1712673673; cv=none; b=RB8EZow66M26V+hMaUQNMOESpP1Qu4t4D4FYV2Ll3QeOIs7VaenmGrU9W5QWbXGF6DYlg5BWNglEPUVG07T+P0Ii8I9ga+8pmyZUuUZaz60PATpZMsy+GT/VzCKDgbL9IidKF9vWl9c0P/emBp6oohpxxTErfC9XQW7ZiycJntg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1712673673; c=relaxed/simple; bh=ui7rq2gTY4xRwH3A3keecG6jmhZ9GRjtseFqBuMr1zY=; h=DKIM-Signature:Message-ID:Date:MIME-Version:Subject:To:From; b=UXXsGcoLjPkRgS7qTYtAjgX8Mdx4c3a9btIuGdprEpiFtVvVzXT6Qcq73NOkdgcYUSXSeMAOYU5p4hXbI++w4l+RLk3dmtZvCaIHiu8cEbzjaQjrINfVcOsG78iH7yUAMqdwioSGfPUcIZc5lEpPok9vgAlynKxgnwv+DqaK28c= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1712673669; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=GeEdXNcD8qPTHsL5OQ94S0o19hWOXbD/dWFV6QVwKUQ=; b=T+Gi8zFc0WoIXuQlu2pkdkq/7rTquZey58R82f1MsaKG5URPnIKBZbzBDmtJnN+X7bz1d9 vz+JecLhtF6rxPV6WSYA9O7pH/WpAQlSCzC9Jz6ephhkBxDtSPMgxZo+7mO+hilVKSnDaE pmu3QBjmilnUZLksfZrQTqKA1/stqvE= Received: from mail-yb1-f200.google.com (mail-yb1-f200.google.com [209.85.219.200]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-86-tMPIbvX2OhaojmCMALJtLw-1; Tue, 09 Apr 2024 10:41:07 -0400 X-MC-Unique: tMPIbvX2OhaojmCMALJtLw-1 Received: by mail-yb1-f200.google.com with SMTP id 3f1490d57ef6-ddaf2f115f2so7517614276.3 for ; Tue, 09 Apr 2024 07:41:07 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712673667; x=1713278467; h=content-transfer-encoding:in-reply-to:from:content-language :references:cc:to:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=GeEdXNcD8qPTHsL5OQ94S0o19hWOXbD/dWFV6QVwKUQ=; b=m9Wg/na4kPt0QuwNmwywqGNqcjzGLzkfPsVr7EVMQ19+FdLpBU9wHqt50MY2ndnWgc iIk53o4riMPjtYOLwpaYEUKmOoN9lpgkoq87mzEQ9hcye8Yn+L+FZouPSaAlpqyD4HTf WhGCpUPEAoJaRtOe43GlFoy54zkQMFLDsAclJafV1kIJ59Chw2aGXpZHLML7+n7LFYDn 1q924b7TzMPzmT9H+r0ol73V1Ussg/OwxMQRAmPZyY2XdbUw35Fr7B3QAkGyWhfg8HR+ 95LE+dl9JQw3ezZfQGM1amiuZpOyNJgjLOQIcOvkDfi2C8wXVexAEBrcMA7ElVYskIwE Kc0A== X-Gm-Message-State: AOJu0Yxjf6++96ALGqfz/c5TQtfde8bLQRRHTs3SMEoiAGwj6xgSWpfq 8uVJqg5sKF5D73nexLZTyyvIdEUVErk6MbNAVOvSmZU4vj9WwnGI7hQYk/eEW4LxcTNNLKDyLDA fSglnLjmap8WB3iUU8/rCQCTXC/TJyWgqQ0I8cFW4+D+lnYFbDWmb9hY= X-Received: by 2002:a25:8241:0:b0:dd1:40cf:942b with SMTP id d1-20020a258241000000b00dd140cf942bmr9812554ybn.48.1712673666836; Tue, 09 Apr 2024 07:41:06 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHutjR+W0Fs27G7XpEhG4PoHS36NO3oLrNduHK3DJkMlxgB7U9SeNaLbWzXLiBLM4ov+BzzeA== X-Received: by 2002:a25:8241:0:b0:dd1:40cf:942b with SMTP id d1-20020a258241000000b00dd140cf942bmr9812523ybn.48.1712673666307; Tue, 09 Apr 2024 07:41:06 -0700 (PDT) Received: from [192.168.1.130] (130-44-146-16.s12558.c3-0.arl-cbr1.sbo-arl.ma.cable.rcncustomer.com. [130.44.146.16]) by smtp.gmail.com with ESMTPSA id t6-20020a0cf986000000b006994394017dsm3665474qvn.27.2024.04.09.07.41.05 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 09 Apr 2024 07:41:05 -0700 (PDT) Message-ID: <23a2a4df-f958-4d62-bb33-8bf40d11c698@redhat.com> Date: Tue, 9 Apr 2024 10:41:05 -0400 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH] c++, v3: Implement C++26 P2809R3 - Trivial infinite loops are not Undefined Behavior To: Jakub Jelinek Cc: gcc-patches@gcc.gnu.org References: From: Jason Merrill In-Reply-To: X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-7.1 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: 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-% 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 > > 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, > "% 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; > + } > +} > > > Jakub >