public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
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
> 


      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).