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++, c, v3: Implement C++23 P1774R8 - Portable assumptions [PR106654]
Date: Wed, 5 Oct 2022 08:33:02 -0400	[thread overview]
Message-ID: <f297fee8-e0d7-2d4e-af28-187ea58052be@redhat.com> (raw)
In-Reply-To: <Yz1UiUPXZGIGXRJV@tucnak>

On 10/5/22 05:55, Jakub Jelinek wrote:
> On Tue, Oct 04, 2022 at 04:42:04PM -0400, Jason Merrill wrote:
>> It could choose which function to call based on whether the constexpr_ctx
>> parameter is null?
> 
> Done, though it needs to be in constexpr.cc then because struct
> constexpr_ctx and cxx_eval_constant_expression is local to constexpr.cc.
> 
>>> 	* attribs.h (remove_attribute): Declare overload with additional
>>> 	attr_ns argument.
>>> 	(private_lookup_attribute): Declare overload with additional
>>> 	attr_ns and attr_ns_len arguments.
>>> 	(lookup_attribute): New overload with additional attr_ns argument.
>>> 	* attribs.cc (remove_attribute): New overload with additional
>>> 	attr_ns argument.
>>> 	(private_lookup_attribute): New overload with additional
>>> 	attr_ns and attr_ns_len arguments.
>>
>> I think go ahead and commit the attribs.{h,cc} changes separately.
> 
> Done last night.
> 
>>> @@ -8519,10 +8520,11 @@ cp_parser_parenthesized_expression_list
>>>    	  }
>>>    	else
>>>    	  {
>>> -	    expr
>>> -	      = cp_parser_parenthesized_expression_list_elt (parser, cast_p,
>>> -							     allow_expansion_p,
>>> -							     non_constant_p);
>>> +	    if (is_attribute_list == assume_attr)
>>> +	      expr = cp_parser_conditional_expression (parser);
>>> +	    else
>>> +	      expr = cp_parser_parenthesized_expression_list_elt
>>> +			(parser, cast_p, allow_expansion_p, non_constant_p);
>>
>> This needs more parens to preserve this indentation.
> 
> I've changed the code some more and now the cp_parser_parenthesized_expression_list_elt
> call doesn't need reindentation and thus formatting uglification.
> For some strange reason, the id_attr identifier case was setting
> identifier variable and not pushing anything into the vector right away,
> then at the very end it memmoved all the other elements one element to the
> right and inserted the identifier there.  That doesn't make any sense to me,
> nothing is really looking at the expression_list vector until it is
> returned, so I've just assigned the identifier to expr and handled it
> like all other expressions.
>>> +	      else
>>> +		{
>>
>> Maybe
>>
>> 	tree &arg = (*call_args)[0];
> 
> Good idea, changed.
> 
> Here is an updated, so far lightly tested, patch, ok if it passes
> full bootstrap/regtest?

OK, thanks.

> 2022-10-05  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/106654
> gcc/
> 	* internal-fn.def (ASSUME): New internal function.
> 	* internal-fn.h (expand_ASSUME): Declare.
> 	* internal-fn.cc (expand_ASSUME): Define.
> 	* gimplify.cc (gimplify_call_expr): Gimplify IFN_ASSUME.
> 	* fold-const.h (simple_condition_p): Declare.
> 	* fold-const.cc (simple_operand_p_2): Rename to ...
> 	(simple_condition_p): ... this.  Remove forward declaration.
> 	No longer static.  Adjust function comment and fix a typo in it.
> 	Adjust recursive call.
> 	(simple_operand_p): Adjust function comment.
> 	(fold_truth_andor): Adjust simple_operand_p_2 callers to call
> 	simple_condition_p.
> 	* doc/extend.texi: Document assume attribute.  Move fallthrough
> 	attribute example to its section.
> gcc/c-family/
> 	* c-attribs.cc (handle_assume_attribute): New function.
> 	(c_common_attribute_table): Add entry for assume attribute.
> 	* c-lex.cc (c_common_has_attribute): Handle
> 	__have_cpp_attribute (assume).
> gcc/c/
> 	* c-parser.cc (handle_assume_attribute): New function.
> 	(c_parser_declaration_or_fndef): Handle assume attribute.
> 	(c_parser_attribute_arguments): Add assume_attr argument,
> 	if true, parse first argument as conditional expression.
> 	(c_parser_gnu_attribute, c_parser_std_attribute): Adjust
> 	c_parser_attribute_arguments callers.
> 	(c_parser_statement_after_labels) <case RID_ATTRIBUTE>: Handle
> 	assume attribute.
> gcc/cp/
> 	* cp-tree.h (process_stmt_assume_attribute): Implement C++23
> 	P1774R8 - Portable assumptions.  Declare.
> 	(diagnose_failing_condition): Declare.
> 	(find_failing_clause): Likewise.
> 	* parser.cc (assume_attr): New enumerator.
> 	(cp_parser_parenthesized_expression_list): Handle assume_attr.
> 	Remove identifier variable, for id_attr push the identifier into
> 	expression_list right away instead of inserting it before all the
> 	others at the end.
> 	(cp_parser_conditional_expression): New function.
> 	(cp_parser_constant_expression): Use it.
> 	(cp_parser_statement): Handle assume attribute.
> 	(cp_parser_expression_statement): Likewise.
> 	(cp_parser_gnu_attribute_list): Use assume_attr for assume
> 	attribute.
> 	(cp_parser_std_attribute): Likewise.  Handle standard assume
> 	attribute like gnu::assume.
> 	* cp-gimplify.cc (process_stmt_assume_attribute): New function.
> 	* constexpr.cc: Include fold-const.h.
> 	(find_failing_clause_r, find_failing_clause): New functions,
> 	moved from semantics.cc with ctx argument added and if non-NULL,
> 	call cxx_eval_constant_expression rather than fold_non_dependent_expr.
> 	(cxx_eval_internal_function): Handle IFN_ASSUME.
> 	(potential_constant_expression_1): Likewise.
> 	* pt.cc (tsubst_copy_and_build): Likewise.
> 	* semantics.cc (diagnose_failing_condition): New function.
> 	(find_failing_clause_r, find_failing_clause): Moved to constexpr.cc.
> 	(finish_static_assert): Use it.  Add auto_diagnostic_group.
> gcc/testsuite/
> 	* gcc.dg/attr-assume-1.c: New test.
> 	* gcc.dg/attr-assume-2.c: New test.
> 	* gcc.dg/attr-assume-3.c: New test.
> 	* g++.dg/cpp2a/feat-cxx2a.C: Add colon to C++20 features
> 	comment, add C++20 attributes comment and move C++20
> 	new features after the attributes before them.
> 	* g++.dg/cpp23/feat-cxx2b.C: Likewise.  Test
> 	__has_cpp_attribute(assume).
> 	* g++.dg/cpp23/attr-assume1.C: New test.
> 	* g++.dg/cpp23/attr-assume2.C: New test.
> 	* g++.dg/cpp23/attr-assume3.C: New test.
> 	* g++.dg/cpp23/attr-assume4.C: New test.
> 
> --- gcc/internal-fn.def.jj	2022-10-04 10:34:33.081216171 +0200
> +++ gcc/internal-fn.def	2022-10-05 11:19:18.638580467 +0200
> @@ -462,6 +462,10 @@ DEF_INTERNAL_FN (TRAP, ECF_CONST | ECF_L
>   		       | ECF_NOTHROW | ECF_COLD | ECF_LOOPING_CONST_OR_PURE,
>   		 NULL)
>   
> +/* [[assume (cond)]].  */
> +DEF_INTERNAL_FN (ASSUME, ECF_CONST | ECF_LEAF | ECF_NOTHROW
> +			 | ECF_LOOPING_CONST_OR_PURE, NULL)
> +
>   #undef DEF_INTERNAL_INT_FN
>   #undef DEF_INTERNAL_FLT_FN
>   #undef DEF_INTERNAL_FLT_FLOATN_FN
> --- gcc/internal-fn.h.jj	2022-10-04 10:34:33.092216023 +0200
> +++ gcc/internal-fn.h	2022-10-05 11:19:18.638580467 +0200
> @@ -243,6 +243,7 @@ extern void expand_PHI (internal_fn, gca
>   extern void expand_SHUFFLEVECTOR (internal_fn, gcall *);
>   extern void expand_SPACESHIP (internal_fn, gcall *);
>   extern void expand_TRAP (internal_fn, gcall *);
> +extern void expand_ASSUME (internal_fn, gcall *);
>   
>   extern bool vectorized_internal_fn_supported_p (internal_fn, tree);
>   
> --- gcc/internal-fn.cc.jj	2022-10-04 10:34:33.047216630 +0200
> +++ gcc/internal-fn.cc	2022-10-05 11:19:19.055574822 +0200
> @@ -4522,3 +4522,9 @@ expand_TRAP (internal_fn, gcall *)
>   {
>     expand_builtin_trap ();
>   }
> +
> +void
> +expand_ASSUME (internal_fn, gcall *)
> +{
> +  gcc_unreachable ();
> +}
> --- gcc/gimplify.cc.jj	2022-10-04 10:34:33.010217130 +0200
> +++ gcc/gimplify.cc	2022-10-05 11:19:19.092574321 +0200
> @@ -3554,6 +3554,25 @@ gimplify_call_expr (tree *expr_p, gimple
>         enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
>         auto_vec<tree> vargs (nargs);
>   
> +      if (ifn == IFN_ASSUME)
> +	{
> +	  if (simple_condition_p (CALL_EXPR_ARG (*expr_p, 0)))
> +	    {
> +	      /* If the [[assume (cond)]]; condition is simple
> +		 enough and can be evaluated unconditionally
> +		 without side-effects, expand it as
> +		 if (!cond) __builtin_unreachable ();  */
> +	      tree fndecl = builtin_decl_explicit (BUILT_IN_UNREACHABLE);
> +	      *expr_p = build3 (COND_EXPR, void_type_node,
> +				CALL_EXPR_ARG (*expr_p, 0), void_node,
> +				build_call_expr_loc (EXPR_LOCATION (*expr_p),
> +						     fndecl, 0));
> +	      return GS_OK;
> +	    }
> +	  /* FIXME: Otherwise expand it specially.  */
> +	  return GS_ALL_DONE;
> +	}
> +
>         for (i = 0; i < nargs; i++)
>   	{
>   	  gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
> --- gcc/fold-const.h.jj	2022-10-04 10:34:32.967217710 +0200
> +++ gcc/fold-const.h	2022-10-05 11:19:19.093574308 +0200
> @@ -215,6 +215,7 @@ extern tree build_range_check (location_
>   extern bool merge_ranges (int *, tree *, tree *, int, tree, tree, int,
>   			  tree, tree);
>   extern tree sign_bit_p (tree, const_tree);
> +extern bool simple_condition_p (tree);
>   extern tree exact_inverse (tree, tree);
>   extern bool expr_not_equal_to (tree t, const wide_int &);
>   extern tree const_unop (enum tree_code, tree, tree);
> --- gcc/fold-const.cc.jj	2022-10-04 10:34:32.948217967 +0200
> +++ gcc/fold-const.cc	2022-10-05 11:19:19.125573875 +0200
> @@ -130,7 +130,6 @@ static tree eval_subst (location_t, tree
>   static tree optimize_bit_field_compare (location_t, enum tree_code,
>   					tree, tree, tree);
>   static bool simple_operand_p (const_tree);
> -static bool simple_operand_p_2 (tree);
>   static tree range_binop (enum tree_code, tree, tree, int, tree, int);
>   static tree range_predecessor (tree);
>   static tree range_successor (tree);
> @@ -4868,8 +4867,8 @@ sign_bit_p (tree exp, const_tree val)
>     return NULL_TREE;
>   }
>   
> -/* Subroutine for fold_truth_andor_1: determine if an operand is simple enough
> -   to be evaluated unconditionally.  */
> +/* Subroutine for fold_truth_andor_1 and simple_condition_p: determine if an
> +   operand is simple enough to be evaluated unconditionally.  */
>   
>   static bool
>   simple_operand_p (const_tree exp)
> @@ -4897,13 +4896,12 @@ simple_operand_p (const_tree exp)
>   	      && (! TREE_STATIC (exp) || DECL_REGISTER (exp))));
>   }
>   
> -/* Subroutine for fold_truth_andor: determine if an operand is simple enough
> -   to be evaluated unconditionally.
> -   I addition to simple_operand_p, we assume that comparisons, conversions,
> +/* Determine if an operand is simple enough to be evaluated unconditionally.
> +   In addition to simple_operand_p, we assume that comparisons, conversions,
>      and logic-not operations are simple, if their operands are simple, too.  */
>   
> -static bool
> -simple_operand_p_2 (tree exp)
> +bool
> +simple_condition_p (tree exp)
>   {
>     enum tree_code code;
>   
> @@ -4920,7 +4918,7 @@ simple_operand_p_2 (tree exp)
>   	    && simple_operand_p (TREE_OPERAND (exp, 1)));
>   
>     if (code == TRUTH_NOT_EXPR)
> -      return simple_operand_p_2 (TREE_OPERAND (exp, 0));
> +    return simple_condition_p (TREE_OPERAND (exp, 0));
>   
>     return simple_operand_p (exp);
>   }
> @@ -9787,10 +9785,10 @@ fold_truth_andor (location_t loc, enum t
>   	 side-effects, or isn't simple, then we can't add to it,
>   	 as otherwise we might destroy if-sequence.  */
>         if (TREE_CODE (arg0) == icode
> -	  && simple_operand_p_2 (arg1)
> +	  && simple_condition_p (arg1)
>   	  /* Needed for sequence points to handle trappings, and
>   	     side-effects.  */
> -	  && simple_operand_p_2 (TREE_OPERAND (arg0, 1)))
> +	  && simple_condition_p (TREE_OPERAND (arg0, 1)))
>   	{
>   	  tem = fold_build2_loc (loc, ncode, type, TREE_OPERAND (arg0, 1),
>   				 arg1);
> @@ -9800,10 +9798,10 @@ fold_truth_andor (location_t loc, enum t
>   	/* Same as above but for (A AND[-IF] (B AND-IF C)) -> ((A AND B) AND-IF C),
>   	   or (A OR[-IF] (B OR-IF C) -> ((A OR B) OR-IF C).  */
>         else if (TREE_CODE (arg1) == icode
> -	  && simple_operand_p_2 (arg0)
> +	  && simple_condition_p (arg0)
>   	  /* Needed for sequence points to handle trappings, and
>   	     side-effects.  */
> -	  && simple_operand_p_2 (TREE_OPERAND (arg1, 0)))
> +	  && simple_condition_p (TREE_OPERAND (arg1, 0)))
>   	{
>   	  tem = fold_build2_loc (loc, ncode, type,
>   				 arg0, TREE_OPERAND (arg1, 0));
> @@ -9814,8 +9812,8 @@ fold_truth_andor (location_t loc, enum t
>   	 into (A OR B).
>   	 For sequence point consistancy, we need to check for trapping,
>   	 and side-effects.  */
> -      else if (code == icode && simple_operand_p_2 (arg0)
> -               && simple_operand_p_2 (arg1))
> +      else if (code == icode && simple_condition_p (arg0)
> +	       && simple_condition_p (arg1))
>   	return fold_build2_loc (loc, ncode, type, arg0, arg1);
>       }
>   
> --- gcc/doc/extend.texi.jj	2022-10-04 10:34:32.894218696 +0200
> +++ gcc/doc/extend.texi	2022-10-05 11:19:19.132573780 +0200
> @@ -9187,6 +9187,20 @@ available for functions (@pxref{Function
>   (@pxref{Variable Attributes}), labels (@pxref{Label Attributes}), enumerators
>   (@pxref{Enumerator Attributes}), and for types (@pxref{Type Attributes}).
>   
> +@table @code
> +@item fallthrough
> +@cindex @code{fallthrough} statement attribute
> +The @code{fallthrough} attribute with a null statement serves as a
> +fallthrough statement.  It hints to the compiler that a statement
> +that falls through to another case label, or user-defined label
> +in a switch statement is intentional and thus the
> +@option{-Wimplicit-fallthrough} warning must not trigger.  The
> +fallthrough attribute may appear at most once in each attribute
> +list, and may not be mixed with other attributes.  It can only
> +be used in a switch statement (the compiler will issue an error
> +otherwise), after a preceding statement and before a logically
> +succeeding case label, or user-defined label.
> +
>   This example uses the @code{fallthrough} statement attribute to indicate that
>   the @option{-Wimplicit-fallthrough} warning should not be emitted:
>   
> @@ -9201,19 +9215,28 @@ switch (cond)
>     @}
>   @end smallexample
>   
> -@table @code
> -@item fallthrough
> -@cindex @code{fallthrough} statement attribute
> -The @code{fallthrough} attribute with a null statement serves as a
> -fallthrough statement.  It hints to the compiler that a statement
> -that falls through to another case label, or user-defined label
> -in a switch statement is intentional and thus the
> -@option{-Wimplicit-fallthrough} warning must not trigger.  The
> -fallthrough attribute may appear at most once in each attribute
> -list, and may not be mixed with other attributes.  It can only
> -be used in a switch statement (the compiler will issue an error
> -otherwise), after a preceding statement and before a logically
> -succeeding case label, or user-defined label.
> +@item assume
> +@cindex @code{assume} statement attribute
> +The @code{assume} attribute with a null statement serves as portable
> +assumption.  It should have a single argument, a conditional expression,
> +which is not evaluated.  If the argument would evaluate to true
> +at the point where it appears, it has no effect, otherwise there
> +is undefined behavior.  This is a GNU variant of the ISO C++23
> +standard @code{assume} attribute, but it can be used in any version of
> +both C and C++.
> +
> +@smallexample
> +int
> +foo (int x, int y)
> +@{
> +  __attribute__((assume(x == 42)));
> +  __attribute__((assume(++y == 43)));
> +  return x + y;
> +@}
> +@end smallexample
> +
> +@code{y} is not actually incremented and the compiler can but does not
> +have to optimize it to just @code{return 42 + 42;}.
>   
>   @end table
>   
> --- gcc/c-family/c-attribs.cc.jj	2022-10-04 10:34:32.298226744 +0200
> +++ gcc/c-family/c-attribs.cc	2022-10-05 11:19:19.138573699 +0200
> @@ -144,6 +144,7 @@ static tree handle_type_generic_attribut
>   static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
>   static tree handle_alloc_align_attribute (tree *, tree, tree, int, bool *);
>   static tree handle_assume_aligned_attribute (tree *, tree, tree, int, bool *);
> +static tree handle_assume_attribute (tree *, tree, tree, int, bool *);
>   static tree handle_target_attribute (tree *, tree, tree, int, bool *);
>   static tree handle_target_clones_attribute (tree *, tree, tree, int, bool *);
>   static tree handle_optimize_attribute (tree *, tree, tree, int, bool *);
> @@ -530,6 +531,8 @@ const struct attribute_spec c_common_att
>   			      handle_designated_init_attribute, NULL },
>     { "fallthrough",	      0, 0, false, false, false, false,
>   			      handle_fallthrough_attribute, NULL },
> +  { "assume",		      1, 1, false, false, false, false,
> +			      handle_assume_attribute, NULL },
>     { "patchable_function_entry",	1, 2, true, false, false, false,
>   			      handle_patchable_function_entry_attribute,
>   			      NULL },
> @@ -5738,6 +5741,18 @@ handle_fallthrough_attribute (tree *, tr
>   {
>     pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored", name);
>     *no_add_attrs = true;
> +  return NULL_TREE;
> +}
> +
> +/* Handle a "assume" attribute; arguments as in struct
> +   attribute_spec.handler.  */
> +
> +tree
> +handle_assume_attribute (tree *, tree name, tree, int,
> +			 bool *no_add_attrs)
> +{
> +  pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored", name);
> +  *no_add_attrs = true;
>     return NULL_TREE;
>   }
>   
> --- gcc/c-family/c-lex.cc.jj	2022-10-04 20:53:25.193829422 +0200
> +++ gcc/c-family/c-lex.cc	2022-10-05 11:19:19.138573699 +0200
> @@ -378,6 +378,8 @@ c_common_has_attribute (cpp_reader *pfil
>   		result = 201803;
>   	      else if (is_attribute_p ("nodiscard", attr_name))
>   		result = 201907;
> +	      else if (is_attribute_p ("assume", attr_name))
> +		result = 202207;
>   	    }
>   	  else
>   	    {
> --- gcc/c/c-parser.cc.jj	2022-10-04 10:36:46.536414202 +0200
> +++ gcc/c/c-parser.cc	2022-10-05 11:19:19.144573617 +0200
> @@ -1823,6 +1823,46 @@ add_debug_begin_stmt (location_t loc)
>     add_stmt (stmt);
>   }
>   
> +/* Helper function for c_parser_declaration_or_fndef and
> +   Handle assume attribute(s).  */
> +
> +static tree
> +handle_assume_attribute (location_t here, tree attrs, bool nested)
> +{
> +  if (nested)
> +    for (tree attr = lookup_attribute ("gnu", "assume", attrs); attr;
> +	 attr = lookup_attribute ("gnu", "assume", TREE_CHAIN (attr)))
> +      {
> +	tree args = TREE_VALUE (attr);
> +	int nargs = list_length (args);
> +	if (nargs != 1)
> +	  {
> +	    error_at (here, "wrong number of arguments specified "
> +			    "for %qE attribute",
> +		      get_attribute_name (attr));
> +	    inform (here, "expected %i, found %i", 1, nargs);
> +	  }
> +	else
> +	  {
> +	    tree arg = TREE_VALUE (args);
> +	    arg = c_objc_common_truthvalue_conversion (here, arg);
> +	    arg = c_fully_fold (arg, false, NULL);
> +	    if (arg != error_mark_node)
> +	      {
> +		tree fn = build_call_expr_internal_loc (here, IFN_ASSUME,
> +							void_type_node, 1,
> +							arg);
> +		add_stmt (fn);
> +	      }
> +	  }
> +      }
> +  else
> +    pedwarn (here, OPT_Wattributes,
> +	     "%<assume%> attribute at top level");
> +
> +  return remove_attribute ("gnu", "assume", attrs);
> +}
> +
>   /* Parse a declaration or function definition (C90 6.5, 6.7.1, C99
>      6.7, 6.9.1, C11 6.7, 6.9.1).  If FNDEF_OK is true, a function definition
>      is accepted; otherwise (old-style parameter declarations) only other
> @@ -2037,6 +2077,14 @@ c_parser_declaration_or_fndef (c_parser
>     bool auto_type_p = specs->typespec_word == cts_auto_type;
>     if (c_parser_next_token_is (parser, CPP_SEMICOLON))
>       {
> +      bool handled_assume = false;
> +      if (specs->typespec_kind == ctsk_none
> +	  && lookup_attribute ("gnu", "assume", specs->attrs))
> +	{
> +	  handled_assume = true;
> +	  specs->attrs
> +	    = handle_assume_attribute (here, specs->attrs, nested);
> +	}
>         if (auto_type_p)
>   	error_at (here, "%<__auto_type%> in empty declaration");
>         else if (specs->typespec_kind == ctsk_none
> @@ -2054,13 +2102,15 @@ c_parser_declaration_or_fndef (c_parser
>   	    pedwarn (here, OPT_Wattributes,
>   		     "%<fallthrough%> attribute at top level");
>   	}
> -      else if (empty_ok && !(have_attrs
> -			     && specs->non_std_attrs_seen_p))
> +      else if (empty_ok
> +	       && !(have_attrs && specs->non_std_attrs_seen_p)
> +	       && !handled_assume)
>   	shadow_tag (specs);
>         else
>   	{
>   	  shadow_tag_warned (specs, 1);
> -	  pedwarn (here, 0, "empty declaration");
> +	  if (!handled_assume)
> +	    pedwarn (here, 0, "empty declaration");
>   	}
>         c_parser_consume_token (parser);
>         if (oacc_routine_data)
> @@ -2160,6 +2210,9 @@ c_parser_declaration_or_fndef (c_parser
>     else if (attribute_fallthrough_p (specs->attrs))
>       warning_at (here, OPT_Wattributes,
>   		"%<fallthrough%> attribute not followed by %<;%>");
> +  else if (lookup_attribute ("gnu", "assume", specs->attrs))
> +    warning_at (here, OPT_Wattributes,
> +		"%<assume%> attribute not followed by %<;%>");
>   
>     pending_xref_error ();
>     prefix_attrs = specs->attrs;
> @@ -4598,7 +4651,8 @@ c_parser_gnu_attribute_any_word (c_parse
>   
>   static tree
>   c_parser_attribute_arguments (c_parser *parser, bool takes_identifier,
> -			      bool require_string, bool allow_empty_args)
> +			      bool require_string, bool assume_attr,
> +			      bool allow_empty_args)
>   {
>     vec<tree, va_gc> *expr_list;
>     tree attr_args;
> @@ -4617,6 +4671,7 @@ c_parser_attribute_arguments (c_parser *
>   	      == CPP_CLOSE_PAREN))
>         && (takes_identifier
>   	  || (c_dialect_objc ()
> +	      && !assume_attr
>   	      && c_parser_peek_token (parser)->id_kind
>   	      == C_ID_CLASSNAME)))
>       {
> @@ -4653,6 +4708,23 @@ c_parser_attribute_arguments (c_parser *
>   	  tree string = c_parser_string_literal (parser, false, true).value;
>   	  attr_args = build_tree_list (NULL_TREE, string);
>   	}
> +      else if (assume_attr)
> +	{
> +	  tree cond
> +	    = c_parser_conditional_expression (parser, NULL, NULL_TREE).value;
> +	  if (!c_parser_next_token_is (parser, CPP_COMMA))
> +	    attr_args = build_tree_list (NULL_TREE, cond);
> +	  else
> +	    {
> +	      tree tree_list;
> +	      c_parser_consume_token (parser);
> +	      expr_list = c_parser_expr_list (parser, false, true,
> +					      NULL, NULL, NULL, NULL);
> +	      tree_list = build_tree_list_vec (expr_list);
> +	      attr_args = tree_cons (NULL_TREE, cond, tree_list);
> +	      release_tree_vector (expr_list);
> +	    }
> +	}
>         else
>   	{
>   	  expr_list = c_parser_expr_list (parser, false, true,
> @@ -4736,7 +4808,9 @@ c_parser_gnu_attribute (c_parser *parser
>     tree attr_args
>       = c_parser_attribute_arguments (parser,
>   				    attribute_takes_identifier_p (attr_name),
> -				    false, true);
> +				    false,
> +				    is_attribute_p ("assume", attr_name),
> +				    true);
>   
>     attr = build_tree_list (attr_name, attr_args);
>     if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
> @@ -4982,9 +5056,13 @@ c_parser_std_attribute (c_parser *parser
>   	  = (ns == NULL_TREE
>   	     && (strcmp (IDENTIFIER_POINTER (name), "deprecated") == 0
>   		 || strcmp (IDENTIFIER_POINTER (name), "nodiscard") == 0));
> +	bool assume_attr
> +	  = (ns != NULL_TREE
> +	     && strcmp (IDENTIFIER_POINTER (ns), "gnu") == 0
> +	     && strcmp (IDENTIFIER_POINTER (name), "assume") == 0);
>   	TREE_VALUE (attribute)
>   	  = c_parser_attribute_arguments (parser, takes_identifier,
> -					  require_string, false);
> +					  require_string, assume_attr, false);
>         }
>       else
>         c_parser_balanced_token_sequence (parser);
> @@ -6264,8 +6342,21 @@ c_parser_statement_after_labels (c_parse
>   	  break;
>   	case RID_ATTRIBUTE:
>   	  {
> -	    /* Allow '__attribute__((fallthrough));'.  */
> +	    /* Allow '__attribute__((fallthrough));' or
> +	       '__attribute__((assume(cond)));'.  */
>   	    tree attrs = c_parser_gnu_attributes (parser);
> +	    bool has_assume = lookup_attribute ("assume", attrs);
> +	    if (has_assume)
> +	      {
> +		if (c_parser_next_token_is (parser, CPP_SEMICOLON))
> +		  attrs = handle_assume_attribute (loc, attrs, true);
> +		else
> +		  {
> +		    warning_at (loc, OPT_Wattributes,
> +				"%<assume%> attribute not followed by %<;%>");
> +		    has_assume = false;
> +		  }
> +	      }
>   	    if (attribute_fallthrough_p (attrs))
>   	      {
>   		if (c_parser_next_token_is (parser, CPP_SEMICOLON))
> @@ -6282,9 +6373,13 @@ c_parser_statement_after_labels (c_parse
>   			      "%<fallthrough%> attribute not followed "
>   			      "by %<;%>");
>   	      }
> +	    else if (has_assume)
> +	      /* Eat the ';'.  */
> +	      c_parser_consume_token (parser);
>   	    else if (attrs != NULL_TREE)
> -	      warning_at (loc, OPT_Wattributes, "only attribute %<fallthrough%>"
> -			  " can be applied to a null statement");
> +	      warning_at (loc, OPT_Wattributes,
> +			  "only attribute %<fallthrough%> or %<assume%> can "
> +			  "be applied to a null statement");
>   	    break;
>   	  }
>   	default:
> --- gcc/cp/cp-tree.h.jj	2022-10-04 20:53:25.226828978 +0200
> +++ gcc/cp/cp-tree.h	2022-10-05 11:24:06.447685943 +0200
> @@ -7715,6 +7715,7 @@ extern tree build_transaction_expr		(loc
>   extern bool cxx_omp_create_clause_info		(tree, tree, bool, bool,
>   						 bool, bool);
>   extern tree baselink_for_fns                    (tree);
> +extern void diagnose_failing_condition		(tree, location_t, bool);
>   extern void finish_static_assert                (tree, tree, location_t,
>   						 bool, bool);
>   extern tree finish_decltype_type                (tree, bool, tsubst_flags_t);
> @@ -8242,6 +8243,7 @@ extern tree predeclare_vla			(tree);
>   extern void clear_fold_cache			(void);
>   extern tree lookup_hotness_attribute		(tree);
>   extern tree process_stmt_hotness_attribute	(tree, location_t);
> +extern tree process_stmt_assume_attribute	(tree, tree, location_t);
>   extern bool simple_empty_class_p		(tree, tree, tree_code);
>   extern tree fold_builtin_source_location	(location_t);
>   
> @@ -8447,6 +8449,8 @@ extern tree fold_sizeof_expr			(tree);
>   extern void clear_cv_and_fold_caches		(void);
>   extern tree unshare_constructor			(tree CXX_MEM_STAT_INFO);
>   extern bool decl_implicit_constexpr_p		(tree);
> +struct constexpr_ctx;
> +extern tree find_failing_clause			(constexpr_ctx *ctx, tree);
>   extern bool replace_decl			(tree *, tree, tree);
>   
>   /* An RAII sentinel used to restrict constexpr evaluation so that it
> --- gcc/cp/parser.cc.jj	2022-10-04 10:36:46.589413486 +0200
> +++ gcc/cp/parser.cc	2022-10-05 11:28:28.292141301 +0200
> @@ -2258,7 +2258,7 @@ static vec<tree, va_gc> *cp_parser_paren
>     (cp_parser *, int, bool, bool, bool *, location_t * = NULL,
>      bool = false);
>   /* Values for the second parameter of cp_parser_parenthesized_expression_list.  */
> -enum { non_attr = 0, normal_attr = 1, id_attr = 2 };
> +enum { non_attr = 0, normal_attr = 1, id_attr = 2, assume_attr = 3 };
>   static void cp_parser_pseudo_destructor_name
>     (cp_parser *, tree, tree *, tree *);
>   static cp_expr cp_parser_unary_expression
> @@ -2287,6 +2287,7 @@ static cp_expr cp_parser_binary_expressi
>     (cp_parser *, bool, bool, enum cp_parser_prec, cp_id_kind *);
>   static tree cp_parser_question_colon_clause
>     (cp_parser *, cp_expr);
> +static cp_expr cp_parser_conditional_expression (cp_parser *);
>   static cp_expr cp_parser_assignment_expression
>     (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false);
>   static enum tree_code cp_parser_assignment_operator_opt
> @@ -8480,7 +8481,6 @@ cp_parser_parenthesized_expression_list
>   					 bool wrap_locations_p)
>   {
>     vec<tree, va_gc> *expression_list;
> -  tree identifier = NULL_TREE;
>     bool saved_greater_than_is_operator_p;
>   
>     /* Assume all the expressions will be constant.  */
> @@ -8509,33 +8509,26 @@ cp_parser_parenthesized_expression_list
>   	   next token is an identifier.  */
>   	if (is_attribute_list == id_attr
>   	    && cp_lexer_peek_token (parser->lexer)->type == CPP_NAME)
> -	  {
> -	    cp_token *token;
> -
> -	    /* Consume the identifier.  */
> -	    token = cp_lexer_consume_token (parser->lexer);
> -	    /* Save the identifier.  */
> -	    identifier = token->u.value;
> -	  }
> +	  expr = cp_lexer_consume_token (parser->lexer)->u.value;
> +	else if (is_attribute_list == assume_attr)
> +	  expr = cp_parser_conditional_expression (parser);
>   	else
> -	  {
> -	    expr
> -	      = cp_parser_parenthesized_expression_list_elt (parser, cast_p,
> -							     allow_expansion_p,
> -							     non_constant_p);
> -
> -	    if (wrap_locations_p)
> -	      expr.maybe_add_location_wrapper ();
> -
> -	     /* Add it to the list.  We add error_mark_node
> -		expressions to the list, so that we can still tell if
> -		the correct form for a parenthesized expression-list
> -		is found. That gives better errors.  */
> -	    vec_safe_push (expression_list, expr.get_value ());
> +	  expr
> +	    = cp_parser_parenthesized_expression_list_elt (parser, cast_p,
> +							   allow_expansion_p,
> +							   non_constant_p);
> +
> +	if (wrap_locations_p)
> +	  expr.maybe_add_location_wrapper ();
> +
> +	/* Add it to the list.  We add error_mark_node
> +	   expressions to the list, so that we can still tell if
> +	   the correct form for a parenthesized expression-list
> +	   is found. That gives better errors.  */
> +	vec_safe_push (expression_list, expr.get_value ());
>   
> -	    if (expr == error_mark_node)
> -	      goto skip_comma;
> -	  }
> +	if (expr == error_mark_node)
> +	  goto skip_comma;
>   
>   	/* After the first item, attribute lists look the same as
>   	   expression lists.  */
> @@ -8577,9 +8570,6 @@ cp_parser_parenthesized_expression_list
>     parser->greater_than_is_operator_p
>       = saved_greater_than_is_operator_p;
>   
> -  if (identifier)
> -    vec_safe_insert (expression_list, 0, identifier);
> -
>     return expression_list;
>   }
>   
> @@ -10310,7 +10300,8 @@ cp_parser_binary_expression (cp_parser*
>      logical-or-expression that started the conditional-expression.
>      Returns a representation of the entire conditional-expression.
>   
> -   This routine is used by cp_parser_assignment_expression.
> +   This routine is used by cp_parser_assignment_expression
> +   and cp_parser_conditional_expression.
>   
>        ? expression : assignment-expression
>   
> @@ -10377,6 +10368,28 @@ cp_parser_question_colon_clause (cp_pars
>                                      tf_warning_or_error);
>   }
>   
> +/* Parse a conditional-expression.
> +
> +   conditional-expression:
> +     logical-or-expression
> +     logical-or-expression ? expression : assignment-expression
> +
> +   GNU Extensions:
> +
> +     logical-or-expression ? : assignment-expression  */
> +
> +static cp_expr
> +cp_parser_conditional_expression (cp_parser *parser)
> +{
> +  cp_expr expr = cp_parser_binary_expression (parser, false, false, false,
> +					      PREC_NOT_OPERATOR, NULL);
> +  /* If the next token is a `?' then we're actually looking at
> +     a conditional-expression; otherwise we're done.  */
> +  if (cp_lexer_next_token_is (parser->lexer, CPP_QUERY))
> +    return cp_parser_question_colon_clause (parser, expr);
> +  return expr;
> +}
> +
>   /* Parse an assignment-expression.
>   
>      assignment-expression:
> @@ -10702,15 +10715,7 @@ cp_parser_constant_expression (cp_parser
>        determine whether a particular assignment-expression is in fact
>        constant.  */
>     if (strict_p)
> -    {
> -      /* Parse the binary expressions (logical-or-expression).  */
> -      expression = cp_parser_binary_expression (parser, false, false, false,
> -						PREC_NOT_OPERATOR, NULL);
> -      /* If the next token is a `?' then we're actually looking at
> -	 a conditional-expression; otherwise we're done.  */
> -      if (cp_lexer_next_token_is (parser->lexer, CPP_QUERY))
> -	expression = cp_parser_question_colon_clause (parser, expression);
> -    }
> +    expression = cp_parser_conditional_expression (parser);
>     else
>       expression = cp_parser_assignment_expression (parser);
>     /* Restore the old settings.  */
> @@ -12503,6 +12508,9 @@ cp_parser_statement (cp_parser* parser,
>         /* Look for an expression-statement instead.  */
>         statement = cp_parser_expression_statement (parser, in_statement_expr);
>   
> +      std_attrs = process_stmt_assume_attribute (std_attrs, statement,
> +						 attrs_loc);
> +
>         /* Handle [[fallthrough]];.  */
>         if (attribute_fallthrough_p (std_attrs))
>   	{
> @@ -12526,7 +12534,7 @@ cp_parser_statement (cp_parser* parser,
>     if (statement && STATEMENT_CODE_P (TREE_CODE (statement)))
>       SET_EXPR_LOCATION (statement, statement_location);
>   
> -  /* Allow "[[fallthrough]];", but warn otherwise.  */
> +  /* Allow "[[fallthrough]];" or "[[assume(cond)]];", but warn otherwise.  */
>     if (std_attrs != NULL_TREE)
>       warning_at (attrs_loc,
>   		OPT_Wattributes,
> @@ -12718,6 +12726,8 @@ cp_parser_expression_statement (cp_parse
>   	}
>       }
>   
> +  attr = process_stmt_assume_attribute (attr, statement, loc);
> +
>     /* Handle [[fallthrough]];.  */
>     if (attribute_fallthrough_p (attr))
>       {
> @@ -28876,6 +28886,8 @@ cp_parser_gnu_attribute_list (cp_parser*
>   	      vec<tree, va_gc> *vec;
>   	      int attr_flag = (attribute_takes_identifier_p (identifier)
>   			       ? id_attr : normal_attr);
> +	      if (is_attribute_p ("assume", identifier))
> +		attr_flag = assume_attr;
>   	      vec = cp_parser_parenthesized_expression_list
>   		    (parser, attr_flag, /*cast_p=*/false,
>   		    /*allow_expansion_p=*/false,
> @@ -29127,6 +29139,9 @@ cp_parser_std_attribute (cp_parser *pars
>         /* C++17 fallthrough attribute is equivalent to GNU's.  */
>         else if (is_attribute_p ("fallthrough", attr_id))
>   	TREE_PURPOSE (TREE_PURPOSE (attribute)) = gnu_identifier;
> +      /* C++23 assume attribute is equivalent to GNU's.  */
> +      else if (is_attribute_p ("assume", attr_id))
> +	TREE_PURPOSE (TREE_PURPOSE (attribute)) = gnu_identifier;
>         /* Transactional Memory TS optimize_for_synchronized attribute is
>   	 equivalent to GNU transaction_callable.  */
>         else if (is_attribute_p ("optimize_for_synchronized", attr_id))
> @@ -29171,8 +29186,12 @@ cp_parser_std_attribute (cp_parser *pars
>   	return error_mark_node;
>         }
>   
> -    if (attr_ns == gnu_identifier
> -	&& attribute_takes_identifier_p (attr_id))
> +    if (is_attribute_p ("assume", attr_id)
> +	&& (attr_ns == NULL_TREE || attr_ns == gnu_identifier))
> +      /* The assume attribute needs special handling of the argument.  */
> +      attr_flag = assume_attr;
> +    else if (attr_ns == gnu_identifier
> +	     && attribute_takes_identifier_p (attr_id))
>         /* A GNU attribute that takes an identifier in parameter.  */
>         attr_flag = id_attr;
>   
> --- gcc/cp/cp-gimplify.cc.jj	2022-10-05 11:18:59.705836655 +0200
> +++ gcc/cp/cp-gimplify.cc	2022-10-05 11:19:19.160573401 +0200
> @@ -3081,6 +3081,50 @@ process_stmt_hotness_attribute (tree std
>     return std_attrs;
>   }
>   
> +/* If [[assume (cond)]] appears on this statement, handle it.  */
> +
> +tree
> +process_stmt_assume_attribute (tree std_attrs, tree statement,
> +			       location_t attrs_loc)
> +{
> +  if (std_attrs == error_mark_node)
> +    return std_attrs;
> +  tree attr = lookup_attribute ("gnu", "assume", std_attrs);
> +  if (!attr)
> +    return std_attrs;
> +  /* The next token after the assume attribute is not ';'.  */
> +  if (statement)
> +    {
> +      warning_at (attrs_loc, OPT_Wattributes,
> +		  "%<assume%> attribute not followed by %<;%>");
> +      attr = NULL_TREE;
> +    }
> +  for (; attr; attr = lookup_attribute ("gnu", "assume", TREE_CHAIN (attr)))
> +    {
> +      tree args = TREE_VALUE (attr);
> +      int nargs = list_length (args);
> +      if (nargs != 1)
> +	{
> +	  auto_diagnostic_group d;
> +	  error_at (attrs_loc, "wrong number of arguments specified for "
> +			       "%qE attribute", get_attribute_name (attr));
> +	  inform (attrs_loc, "expected %i, found %i", 1, nargs);
> +	}
> +      else
> +	{
> +	  tree arg = TREE_VALUE (args);
> +	  if (!type_dependent_expression_p (arg))
> +	    arg = contextual_conv_bool (arg, tf_warning_or_error);
> +	  if (error_operand_p (arg))
> +	    continue;
> +	  statement = build_call_expr_internal_loc (attrs_loc, IFN_ASSUME,
> +						    void_type_node, 1, arg);
> +	  finish_expr_stmt (statement);
> +	}
> +    }
> +  return remove_attribute ("gnu", "assume", std_attrs);
> +}
> +
>   /* Helper of fold_builtin_source_location, return the
>      std::source_location::__impl type after performing verification
>      on it.  LOC is used for reporting any errors.  */
> --- gcc/cp/constexpr.cc.jj	2022-10-04 20:53:25.224829005 +0200
> +++ gcc/cp/constexpr.cc	2022-10-05 11:25:38.213444206 +0200
> @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.
>   #include "opts.h"
>   #include "stringpool.h"
>   #include "attribs.h"
> +#include "fold-const.h"
>   
>   static bool verify_constant (tree, bool, bool *, bool *);
>   #define VERIFY_CONSTANT(X)						\
> @@ -1818,6 +1819,52 @@ cx_error_context (void)
>     return r;
>   }
>   
> +/* If we have a condition in conjunctive normal form (CNF), find the first
> +   failing clause.  In other words, given an expression like
> +
> +     true && true && false && true && false
> +
> +   return the first 'false'.  EXPR is the expression.  */
> +
> +static tree
> +find_failing_clause_r (constexpr_ctx *ctx, tree expr)
> +{
> +  if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR)
> +    {
> +      /* First check the left side...  */
> +      tree e = find_failing_clause_r (ctx, TREE_OPERAND (expr, 0));
> +      if (e == NULL_TREE)
> +	/* ...if we didn't find a false clause, check the right side.  */
> +	e = find_failing_clause_r (ctx, TREE_OPERAND (expr, 1));
> +      return e;
> +    }
> +  tree e = contextual_conv_bool (expr, tf_none);
> +  if (ctx)
> +    {
> +      bool new_non_constant_p = false, new_overflow_p = false;
> +      e = cxx_eval_constant_expression (ctx, e, vc_prvalue,
> +					&new_non_constant_p,
> +					&new_overflow_p);
> +    }
> +  else
> +    e = fold_non_dependent_expr (e, tf_none, /*manifestly_const_eval=*/true);
> +  if (integer_zerop (e))
> +    /* This is the failing clause.  */
> +    return expr;
> +  return NULL_TREE;
> +}
> +
> +/* Wrapper for find_failing_clause_r.  */
> +
> +tree
> +find_failing_clause (constexpr_ctx *ctx, tree expr)
> +{
> +  if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR)
> +    if (tree e = find_failing_clause_r (ctx, expr))
> +      expr = e;
> +  return expr;
> +}
> +
>   /* Evaluate a call T to a GCC internal function when possible and return
>      the evaluated result or, under the control of CTX, give an error, set
>      NON_CONSTANT_P, and return the unevaluated call T otherwise.  */
> @@ -1837,6 +1884,48 @@ cxx_eval_internal_function (const conste
>       case IFN_FALLTHROUGH:
>         return void_node;
>   
> +    case IFN_ASSUME:
> +      /* For now, restrict constexpr evaluation of [[assume (cond)]]
> +	 only to the cases which don't have side-effects.  Evaluating
> +	 it even when it does would mean we'd need to somehow undo
> +	 all the side-effects e.g. in ctx->global->values.  */
> +      if (!TREE_SIDE_EFFECTS (CALL_EXPR_ARG (t, 0))
> +	  /* And it needs to be a potential constant expression.  */
> +	  && potential_rvalue_constant_expression (CALL_EXPR_ARG (t, 0)))
> +	{
> +	  constexpr_ctx new_ctx = *ctx;
> +	  new_ctx.quiet = true;
> +	  tree arg = CALL_EXPR_ARG (t, 0);
> +	  bool new_non_constant_p = false, new_overflow_p = false;
> +	  arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
> +					      &new_non_constant_p,
> +					      &new_overflow_p);
> +	  if (!new_non_constant_p && !new_overflow_p && integer_zerop (arg))
> +	    {
> +	      if (!*non_constant_p && !ctx->quiet)
> +		{
> +		  /* See if we can find which clause was failing
> +		     (for logical AND).  */
> +		  tree bad = find_failing_clause (&new_ctx,
> +						  CALL_EXPR_ARG (t, 0));
> +		  /* If not, or its location is unusable, fall back to the
> +		     previous location.  */
> +		  location_t cloc = cp_expr_loc_or_loc (bad, EXPR_LOCATION (t));
> +
> +		  auto_diagnostic_group d;
> +
> +		  /* Report the error. */
> +		  error_at (cloc,
> +			    "failed %<assume%> attribute assumption");
> +		  diagnose_failing_condition (bad, cloc, false);
> +		}
> +
> +	      *non_constant_p = true;
> +	      return t;
> +	    }
> +	}
> +      return void_node;
> +
>       case IFN_ADD_OVERFLOW:
>         opcode = PLUS_EXPR;
>         break;
> @@ -8706,6 +8795,7 @@ potential_constant_expression_1 (tree t,
>   		case IFN_UBSAN_BOUNDS:
>   		case IFN_UBSAN_VPTR:
>   		case IFN_FALLTHROUGH:
> +		case IFN_ASSUME:
>   		  return true;
>   
>   		case IFN_ADD_OVERFLOW:
> --- gcc/cp/pt.cc.jj	2022-10-04 20:53:25.254828601 +0200
> +++ gcc/cp/pt.cc	2022-10-05 11:30:53.077177370 +0200
> @@ -21163,6 +21163,33 @@ tsubst_copy_and_build (tree t,
>   		break;
>   	      }
>   
> +	    case IFN_ASSUME:
> +	      gcc_assert (nargs == 1);
> +	      if (vec_safe_length (call_args) != 1)
> +		{
> +		  error_at (cp_expr_loc_or_input_loc (t),
> +			    "wrong number of arguments to "
> +			    "%<assume%> attribute");
> +		  ret = error_mark_node;
> +		}
> +	      else
> +		{
> +		  tree &arg = (*call_args)[0];
> +		  if (!type_dependent_expression_p (arg))
> +		    arg = contextual_conv_bool (arg, tf_warning_or_error);
> +		  if (error_operand_p (arg))
> +		    {
> +		      ret = error_mark_node;
> +		      break;
> +		    }
> +		  ret = build_call_expr_internal_loc (EXPR_LOCATION (t),
> +						      IFN_ASSUME,
> +						      void_type_node, 1,
> +						      arg);
> +		  RETURN (ret);
> +		}
> +	      break;
> +
>   	    default:
>   	      /* Unsupported internal function with arguments.  */
>   	      gcc_unreachable ();
> --- gcc/cp/semantics.cc.jj	2022-10-04 10:36:46.591413459 +0200
> +++ gcc/cp/semantics.cc	2022-10-05 11:25:53.090242897 +0200
> @@ -11172,42 +11172,31 @@ init_cp_semantics (void)
>   }
>   \f
>   
> -/* If we have a condition in conjunctive normal form (CNF), find the first
> -   failing clause.  In other words, given an expression like
> +/* Emit additional diagnostics for failing condition BAD.
> +   Used by finish_static_assert and IFN_ASSUME constexpr diagnostics.
> +   If SHOW_EXPR_P is true, print the condition (because it was
> +   instantiation-dependent).  */
>   
> -     true && true && false && true && false
> -
> -   return the first 'false'.  EXPR is the expression.  */
> -
> -static tree
> -find_failing_clause_r (tree expr)
> +void
> +diagnose_failing_condition (tree bad, location_t cloc, bool show_expr_p)
>   {
> -  if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR)
> +  /* Nobody wants to see the artificial (bool) cast.  */
> +  bad = tree_strip_nop_conversions (bad);
> +
> +  /* Actually explain the failure if this is a concept check or a
> +     requires-expression.  */
> +  if (concept_check_p (bad) || TREE_CODE (bad) == REQUIRES_EXPR)
> +    diagnose_constraints (cloc, bad, NULL_TREE);
> +  else if (COMPARISON_CLASS_P (bad)
> +	   && ARITHMETIC_TYPE_P (TREE_TYPE (TREE_OPERAND (bad, 0))))
>       {
> -      /* First check the left side...  */
> -      tree e = find_failing_clause_r (TREE_OPERAND (expr, 0));
> -      if (e == NULL_TREE)
> -	/* ...if we didn't find a false clause, check the right side.  */
> -	e = find_failing_clause_r (TREE_OPERAND (expr, 1));
> -      return e;
> +      tree op0 = fold_non_dependent_expr (TREE_OPERAND (bad, 0));
> +      tree op1 = fold_non_dependent_expr (TREE_OPERAND (bad, 1));
> +      tree cond = build2 (TREE_CODE (bad), boolean_type_node, op0, op1);
> +      inform (cloc, "the comparison reduces to %qE", cond);
>       }
> -  tree e = contextual_conv_bool (expr, tf_none);
> -  e = fold_non_dependent_expr (e, tf_none, /*manifestly_const_eval=*/true);
> -  if (integer_zerop (e))
> -    /* This is the failing clause.  */
> -    return expr;
> -  return NULL_TREE;
> -}
> -
> -/* Wrapper for find_failing_clause_r.  */
> -
> -static tree
> -find_failing_clause (tree expr)
> -{
> -  if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR)
> -    if (tree e = find_failing_clause_r (expr))
> -      expr = e;
> -  return expr;
> +  else if (show_expr_p)
> +    inform (cloc, "%qE evaluates to false", bad);
>   }
>   
>   /* Build a STATIC_ASSERT for a static assertion with the condition
> @@ -11274,12 +11263,12 @@ finish_static_assert (tree condition, tr
>   	  int len = TREE_STRING_LENGTH (message) / sz - 1;
>   
>   	  /* See if we can find which clause was failing (for logical AND).  */
> -	  tree bad = find_failing_clause (orig_condition);
> +	  tree bad = find_failing_clause (NULL, orig_condition);
>   	  /* If not, or its location is unusable, fall back to the previous
>   	     location.  */
>   	  location_t cloc = cp_expr_loc_or_loc (bad, location);
> -	  /* Nobody wants to see the artificial (bool) cast.  */
> -	  bad = tree_strip_nop_conversions (bad);
> +
> +	  auto_diagnostic_group d;
>   
>             /* Report the error. */
>   	  if (len == 0)
> @@ -11288,21 +11277,7 @@ finish_static_assert (tree condition, tr
>   	    error_at (cloc, "static assertion failed: %s",
>   		      TREE_STRING_POINTER (message));
>   
> -	  /* Actually explain the failure if this is a concept check or a
> -	     requires-expression.  */
> -	  if (concept_check_p (bad)
> -	      || TREE_CODE (bad) == REQUIRES_EXPR)
> -	    diagnose_constraints (location, bad, NULL_TREE);
> -	  else if (COMPARISON_CLASS_P (bad)
> -		   && ARITHMETIC_TYPE_P (TREE_TYPE (TREE_OPERAND (bad, 0))))
> -	    {
> -	      tree op0 = fold_non_dependent_expr (TREE_OPERAND (bad, 0));
> -	      tree op1 = fold_non_dependent_expr (TREE_OPERAND (bad, 1));
> -	      tree cond = build2 (TREE_CODE (bad), boolean_type_node, op0, op1);
> -	      inform (cloc, "the comparison reduces to %qE", cond);
> -	    }
> -	  else if (show_expr_p)
> -	    inform (cloc, "%qE evaluates to false", bad);
> +	  diagnose_failing_condition (bad, cloc, show_expr_p);
>   	}
>         else if (condition && condition != error_mark_node)
>   	{
> --- gcc/testsuite/gcc.dg/attr-assume-1.c.jj	2022-10-05 11:19:19.173573225 +0200
> +++ gcc/testsuite/gcc.dg/attr-assume-1.c	2022-10-05 11:19:19.173573225 +0200
> @@ -0,0 +1,69 @@
> +/* Portable assumptions */
> +/* { dg-do run } */
> +/* { dg-options "-std=c2x" } */
> +
> +int
> +f1 (int i)
> +{
> +  [[gnu::assume (i == 42)]];
> +  return i;
> +}
> +
> +int
> +f2 (int i)
> +{
> +  __attribute__ ((assume (++i == 44)));
> +  return i;
> +}
> +
> +int a;
> +int *volatile c;
> +
> +int
> +f3 ()
> +{
> +  ++a;
> +  return 1;
> +}
> +
> +int
> +f4 (double x)
> +{
> +  [[gnu::assume (__builtin_isfinite (x) && x >= 0.0)]];
> +  return __builtin_isfinite (__builtin_sqrt (x));
> +}
> +
> +double
> +f5 (double x)
> +{
> +  __attribute__((assume (__builtin_isfinite (__builtin_sqrt (x)))));
> +  return __builtin_sqrt (x);
> +}
> +
> +int
> +f6 (int x)
> +{
> +  [[gnu::assume (x == 93 ? 1 : 0)]];
> +  return x;
> +}
> +
> +int
> +main ()
> +{
> +  int b = 42;
> +  double d = 42.0, e = 43.0;
> +  c = &b;
> +  [[__gnu__::__assume__ (f3 ())]];
> +  if (a)
> +    __builtin_abort ();
> +  [[gnu::assume (++b == 43)]];
> +  if (b != 42 || *c != 42)
> +    __builtin_abort ();
> +  __attribute__((assume (d < e)));
> +  int i = 90, j = 91, k = 92;
> +  [[gnu::__assume__ (i == 90), gnu::assume (j <= 91)]] [[gnu::assume (k >= 92)]]
> +  ;
> +  __attribute__((__assume__ (i == 90), assume (j <= 91))) __attribute__((assume (k >= 92)));
> +  if (f6 (93) != 93)
> +    __builtin_abort ();
> +}
> --- gcc/testsuite/gcc.dg/attr-assume-2.c.jj	2022-10-05 11:19:19.173573225 +0200
> +++ gcc/testsuite/gcc.dg/attr-assume-2.c	2022-10-05 11:19:19.173573225 +0200
> @@ -0,0 +1,66 @@
> +/* Portable assumptions */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x" } */
> +
> +[[gnu::__assume__ (1)]] void f1 (void);	/* { dg-warning "'assume' attribute not followed by ';'" } */
> +					/* { dg-warning "'assume' attribute ignored" "" { target *-*-* } .-1 } */
> +typedef int intx [[gnu::assume (1)]];	/* { dg-warning "'assume' attribute ignored" } */
> +[[__gnu__::assume (1)]];		/* { dg-warning "'assume' attribute at top level" } */
> +__attribute__((assume (1))) void f1b ();/* { dg-warning "'assume' attribute not followed by ';'" } */
> +					/* { dg-warning "'assume' attribute ignored" "" { target *-*-* } .-1 } */
> +typedef int inty __attribute__((assume (1)));	/* { dg-warning "'assume' attribute ignored" } */
> +
> +void
> +foo ()
> +{
> +  int i;
> +  [[gnu::assume]];			/* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
> +					/* { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } */
> +  [[gnu::__assume__ ()]];		/* { dg-error "parentheses must be omitted if attribute argument list is empty" } */
> +					/* { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 } */
> +					/* { dg-message "expected 1, found 0" "" { target *-*-* } .-2 } */
> +  [[gnu::assume (1, 1)]];		/* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
> +					/* { dg-message "expected 1, found 2" "" { target *-*-* } .-1 } */
> +  [[gnu::assume (1)]] i = 1;		/* { dg-warning "'assume' attribute ignored" } */
> +  [[gnu::assume (i = 1)]];		/* { dg-error "expected" } */
> +					/* { dg-warning "'assume' attribute ignored" "" { target *-*-* } .-1 } */
> +  __attribute__((assume));		/* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
> +					/* { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } */
> +  __attribute__((assume ()));		/* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
> +					/* { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } */
> +  __attribute__((assume (1, 1)));	/* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
> +					/* { dg-message "expected 1, found 2" "" { target *-*-* } .-1 } */
> +  __attribute__((assume (i = 1)));	/* { dg-error "expected" } */
> +}
> +
> +int
> +f2 (int x)
> +{
> +  __asm ("" : "+r" (x));
> +  return x;
> +}
> +
> +int
> +f3 (int x)
> +{
> +  [[gnu::assume (f2 (42) == 42)]];
> +  return x;
> +}
> +
> +int
> +f3a (int x)
> +{
> +  __attribute__((assume (f2 (42) == 42)));
> +  return x;
> +}
> +
> +struct S {};
> +
> +int
> +f4 ()
> +{
> +  struct S s;
> +  [[gnu::assume (s)]];			/* { dg-error "used struct type value where scalar is required" } */
> +  __attribute__((assume (s)));		/* { dg-error "used struct type value where scalar is required" } */
> +  return 0;
> +}
> --- gcc/testsuite/gcc.dg/attr-assume-3.c.jj	2022-10-05 11:19:19.174573212 +0200
> +++ gcc/testsuite/gcc.dg/attr-assume-3.c	2022-10-05 11:19:19.174573212 +0200
> @@ -0,0 +1,35 @@
> +/* Portable assumptions */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x" } */
> +
> +void
> +foo (int x)
> +{
> +  if (x == 1)
> +    goto l1;						/* { dg-error "jump into statement expression" } */
> +  else if (x == 2)
> +    goto l2;						/* { dg-error "jump into statement expression" } */
> +  else if (x == 3)
> +    goto l3;						/* { dg-error "jump into statement expression" } */
> +  [[gnu::assume (({ l0:; if (x == 0) goto l0; 1; }))]];
> +  [[gnu::assume (({ if (x == 0) __builtin_abort (); 1; }))]];
> +  [[gnu::assume (({ l1:; 1; }))]];			/* { dg-message "label 'l1' defined here" } */
> +  [[gnu::assume (({ l2:; 1; }))]];			/* { dg-message "label 'l2' defined here" } */
> +  __attribute__((assume (({ l3:; 1; }))));		/* { dg-message "label 'l3' defined here" } */
> +  [[gnu::assume (({ l4:; 1; }))]];			/* { dg-message "label 'l4' defined here" } */
> +  [[gnu::assume (({ l5:; 1; }))]];			/* { dg-message "label 'l5' defined here" } */
> +  __attribute__((assume (({ l6:; 1; }))));		/* { dg-message "label 'l6' defined here" } */
> +  switch (x)						/* { dg-message "switch starts here" } */
> +    {
> +    case 7:
> +      [[gnu::assume (({ case 8:; 1; }))]];		/* { dg-error "switch jumps into statement expression" } */
> +      __attribute__((assume (({ default:; 1; }))));	/* { dg-error "switch jumps into statement expression" } */
> +      break;
> +    }
> +  if (x == 4)
> +    goto l4;						/* { dg-error "jump into statement expression" } */
> +  else if (x == 5)
> +    goto l5;						/* { dg-error "jump into statement expression" } */
> +  else if (x == 6)
> +    goto l6;						/* { dg-error "jump into statement expression" } */
> +}
> --- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C.jj	2022-10-04 10:34:33.237214065 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C	2022-10-05 11:19:19.190572995 +0200
> @@ -422,7 +422,7 @@
>   #  error "__cpp_nontype_template_parameter_auto != 201606"
>   #endif
>   
> -// C++20 features
> +// C++20 features:
>   
>   #ifndef __cpp_conditional_explicit
>   #  error "__cpp_conditional_explicit"
> @@ -460,6 +460,44 @@
>   #  error "__cpp_aggregate_paren_init != 201902"
>   #endif
>   
> +#ifndef __cpp_char8_t
> +#  error "__cpp_char8_t"
> +#elif __cpp_char8_t != 202207
> +#  error "__cpp_char8_t != 202207"
> +#endif
> +
> +#ifndef __cpp_designated_initializers
> +#  error "__cpp_designated_initializers"
> +#elif __cpp_designated_initializers != 201707
> +#  error "__cpp_designated_initializers != 201707"
> +#endif
> +
> +#ifndef __cpp_constexpr_in_decltype
> +#  error "__cpp_constexpr_in_decltype"
> +#elif __cpp_constexpr_in_decltype != 201711
> +#  error "__cpp_constexpr_in_decltype != 201711"
> +#endif
> +
> +#ifndef __cpp_consteval
> +#  error "__cpp_consteval"
> +#elif __cpp_consteval != 201811
> +#  error "__cpp_consteval != 201811"
> +#endif
> +
> +#ifndef __cpp_concepts
> +#  error "__cpp_concepts"
> +#elif __cpp_concepts != 202002
> +#  error "__cpp_concepts != 202002"
> +#endif
> +
> +#ifndef __cpp_using_enum
> +#  error "__cpp_using_enum"
> +#elif __cpp_using_enum != 201907
> +#  error "__cpp_using_enum != 201907"
> +#endif
> +
> +// C++20 attributes:
> +
>   #ifdef __has_cpp_attribute
>   
>   #  if ! __has_cpp_attribute(maybe_unused)
> @@ -501,39 +539,3 @@
>   #else
>   #  error "__has_cpp_attribute"
>   #endif
> -
> -#ifndef __cpp_char8_t
> -#  error "__cpp_char8_t"
> -#elif __cpp_char8_t != 202207
> -#  error "__cpp_char8_t != 202207"
> -#endif
> -
> -#ifndef __cpp_designated_initializers
> -#  error "__cpp_designated_initializers"
> -#elif __cpp_designated_initializers != 201707
> -#  error "__cpp_designated_initializers != 201707"
> -#endif
> -
> -#ifndef __cpp_constexpr_in_decltype
> -#  error "__cpp_constexpr_in_decltype"
> -#elif __cpp_constexpr_in_decltype != 201711
> -#  error "__cpp_constexpr_in_decltype != 201711"
> -#endif
> -
> -#ifndef __cpp_consteval
> -#  error "__cpp_consteval"
> -#elif __cpp_consteval != 201811
> -#  error "__cpp_consteval != 201811"
> -#endif
> -
> -#ifndef __cpp_concepts
> -#  error "__cpp_concepts"
> -#elif __cpp_concepts != 202002
> -#  error "__cpp_concepts != 202002"
> -#endif
> -
> -#ifndef __cpp_using_enum
> -#  error "__cpp_using_enum"
> -#elif __cpp_using_enum != 201907
> -#  error "__cpp_using_enum != 201907"
> -#endif
> --- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj	2022-10-04 10:34:33.189214713 +0200
> +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2022-10-05 11:19:19.207572765 +0200
> @@ -422,7 +422,7 @@
>   #  error "__cpp_nontype_template_parameter_auto != 201606"
>   #endif
>   
> -// C++20 features
> +// C++20 features:
>   
>   #ifndef __cpp_conditional_explicit
>   #  error "__cpp_conditional_explicit"
> @@ -460,6 +460,44 @@
>   #  error "__cpp_aggregate_paren_init != 201902"
>   #endif
>   
> +#ifndef __cpp_char8_t
> +#  error "__cpp_char8_t"
> +#elif __cpp_char8_t != 202207
> +#  error "__cpp_char8_t != 202207"
> +#endif
> +
> +#ifndef __cpp_designated_initializers
> +#  error "__cpp_designated_initializers"
> +#elif __cpp_designated_initializers != 201707
> +#  error "__cpp_designated_initializers != 201707"
> +#endif
> +
> +#ifndef __cpp_constexpr_in_decltype
> +#  error "__cpp_constexpr_in_decltype"
> +#elif __cpp_constexpr_in_decltype != 201711
> +#  error "__cpp_constexpr_in_decltype != 201711"
> +#endif
> +
> +#ifndef __cpp_consteval
> +#  error "__cpp_consteval"
> +#elif __cpp_consteval != 201811
> +#  error "__cpp_consteval != 201811"
> +#endif
> +
> +#ifndef __cpp_concepts
> +#  error "__cpp_concepts"
> +#elif __cpp_concepts != 202002
> +#  error "__cpp_concepts != 202002"
> +#endif
> +
> +#ifndef __cpp_using_enum
> +#  error "__cpp_using_enum"
> +#elif __cpp_using_enum != 201907
> +#  error "__cpp_using_enum != 201907"
> +#endif
> +
> +// C++20 attributes:
> +
>   #ifdef __has_cpp_attribute
>   
>   #  if ! __has_cpp_attribute(maybe_unused)
> @@ -502,42 +540,6 @@
>   #  error "__has_cpp_attribute"
>   #endif
>   
> -#ifndef __cpp_char8_t
> -#  error "__cpp_char8_t"
> -#elif __cpp_char8_t != 202207
> -#  error "__cpp_char8_t != 202207"
> -#endif
> -
> -#ifndef __cpp_designated_initializers
> -#  error "__cpp_designated_initializers"
> -#elif __cpp_designated_initializers != 201707
> -#  error "__cpp_designated_initializers != 201707"
> -#endif
> -
> -#ifndef __cpp_constexpr_in_decltype
> -#  error "__cpp_constexpr_in_decltype"
> -#elif __cpp_constexpr_in_decltype != 201711
> -#  error "__cpp_constexpr_in_decltype != 201711"
> -#endif
> -
> -#ifndef __cpp_consteval
> -#  error "__cpp_consteval"
> -#elif __cpp_consteval != 201811
> -#  error "__cpp_consteval != 201811"
> -#endif
> -
> -#ifndef __cpp_concepts
> -#  error "__cpp_concepts"
> -#elif __cpp_concepts != 202002
> -#  error "__cpp_concepts != 202002"
> -#endif
> -
> -#ifndef __cpp_using_enum
> -#  error "__cpp_using_enum"
> -#elif __cpp_using_enum != 201907
> -#  error "__cpp_using_enum != 201907"
> -#endif
> -
>   // C++23 features:
>   
>   #ifndef __cpp_size_t_suffix
> @@ -575,3 +577,15 @@
>   #elif __cpp_implicit_move != 202207
>   #  error "__cpp_implicit_move != 202207"
>   #endif
> +
> +//  C++23 attributes:
> +
> +#ifdef __has_cpp_attribute
> +#  if ! __has_cpp_attribute(assume)
> +#    error "__has_cpp_attribute(assume)"
> +#  elif __has_cpp_attribute(assume) != 202207
> +#    error "__has_cpp_attribute(assume) != 202207"
> +#  endif
> +#else
> +#  error "__has_cpp_attribute"
> +#endif
> --- gcc/testsuite/g++.dg/cpp23/attr-assume1.C.jj	2022-10-05 11:19:19.208572752 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume1.C	2022-10-05 11:19:19.208572752 +0200
> @@ -0,0 +1,191 @@
> +// P1774R8 - Portable assumptions
> +// { dg-do run { target c++11 } }
> +
> +namespace std
> +{
> +  constexpr bool
> +  isfinite (float x)
> +  { return __builtin_isfinite (x); }
> +
> +  constexpr bool
> +  isfinite (double x)
> +  { return __builtin_isfinite (x); }
> +
> +  constexpr bool
> +  isfinite (long double x)
> +  { return __builtin_isfinite (x); }
> +
> +  constexpr float
> +  sqrt (float x)
> +  { return __builtin_sqrtf (x); }
> +
> +  constexpr double
> +  sqrt (double x)
> +  { return __builtin_sqrt (x); }
> +
> +  constexpr long double
> +  sqrt (long double x)
> +  { return __builtin_sqrtl (x); }
> +
> +  extern "C" void
> +  abort ();
> +}
> +
> +constexpr int
> +f1 (int i)
> +{
> +#if __cpp_constexpr >= 201603L
> +  auto f = [=] { [[assume (i == 0)]]; };
> +  return sizeof (f);
> +#else
> +  return sizeof (int);
> +#endif
> +}
> +
> +void
> +f2 ()
> +{
> +  static_assert (f1 (0) >= sizeof (int), "");
> +}
> +
> +int
> +f3 (int i)
> +{
> +  [[assume (i == 42)]];
> +  return i;
> +}
> +
> +int
> +f4 (int i)
> +{
> +  [[assume (++i == 44)]];
> +  return i;
> +}
> +
> +int a;
> +int *volatile c;
> +
> +bool
> +f5 ()
> +{
> +  ++a;
> +  return true;
> +}
> +
> +constexpr int
> +f6 ()
> +{
> +#if __cpp_constexpr >= 201304L
> +  [[assume (f5 ())]];
> +#endif
> +  return 1;
> +}
> +
> +template <int ...args>
> +bool
> +f7 ()
> +{
> +#if __cpp_fold_expressions >= 201411L
> +  [[assume (((args >= 0) && ...))]];
> +  return ((args >= 0) && ...);
> +#else
> +  return true;
> +#endif
> +}
> +
> +bool
> +f8 (double x)
> +{
> +  [[assume (std::isfinite (x) && x >= 0.0)]];
> +  return std::isfinite (std::sqrt (x));
> +}
> +
> +double
> +f9 (double x)
> +{
> +  [[assume (std::isfinite (std::sqrt (x)))]];
> +  return std::sqrt (x);
> +}
> +
> +template <typename T, T N>
> +T
> +f10 (T x)
> +{
> +  [[assume (x == N)]];
> +  return x;
> +}
> +
> +int
> +f11 (int x)
> +{
> +  [[assume (x == 93 ? true : throw 1)]];
> +  return x;
> +}
> +
> +constexpr int
> +f12 (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> +  [[assume (++x == 43)]];
> +#endif
> +  return x;
> +}
> +
> +static_assert (f12 (42) == 42, "");
> +
> +struct S
> +{
> +  operator bool () { return true; }
> +};
> +
> +int
> +f13 ()
> +{
> +  S s;
> +  [[assume (s)]];
> +  return 0;
> +}
> +
> +template <typename T>
> +int
> +f14 ()
> +{
> +  T t;
> +  [[assume (t)]];
> +  return 0;
> +}
> +
> +int
> +main ()
> +{
> +  int b = 42;
> +  double d = 42.0, e = 43.0;
> +  c = &b;
> +  [[assume (f5 ())]];
> +  if (a)
> +    std::abort ();
> +  [[assume (++b == 43)]];
> +  if (b != 42 || *c != 42)
> +    std::abort ();
> +  static_assert (f6 () == 1, "");
> +  if (f6 () != 1)
> +    std::abort ();
> +  if (a)
> +    std::abort ();
> +  if (!f7 <0> () || !f7 <1, 2, 3, 4> ())
> +    std::abort ();
> +  [[assume (d < e)]];
> +  if (f10 <int, 45> (45) != 45
> +      || f10 <long long, 128LL> (128LL) != 128LL
> +#if __cpp_nontype_template_args >= 201911L
> +      || f10 <long double, -42.0L> (-42.0L) != -42.0L
> +#endif
> +      || false)
> +    std::abort ();
> +  int i = 90, j = 91, k = 92;
> +  [[assume (i == 90), assume (j <= 91)]] [[assume (k >= 92)]];
> +  if (f11 (93) != 93)
> +    std::abort ();
> +  if (f14 <S> () != 0)
> +    std::abort ();
> +}
> --- gcc/testsuite/g++.dg/cpp23/attr-assume2.C.jj	2022-10-05 11:19:19.208572752 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume2.C	2022-10-05 11:19:19.208572752 +0200
> @@ -0,0 +1,83 @@
> +// P1774R8 - Portable assumptions
> +// { dg-do compile { target c++11 } }
> +
> +[[assume (true)]] void f1 ();		// { dg-error "'assume' attribute ignored" }
> +typedef int intx [[assume (true)]];	// { dg-error "'assume' attribute ignored" }
> +[[assume (true)]];			// { dg-warning "attribute ignored" }
> +
> +void
> +foo ()
> +{
> +  int i;
> +  [[assume]];				// { dg-error "wrong number of arguments specified for 'assume' attribute" }
> +					// { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
> +  [[assume ()]];			// { dg-error "parentheses must be omitted if 'assume' attribute argument list is empty" }
> +					// { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 }
> +					// { dg-message "expected 1, found 0" "" { target *-*-* } .-2 }
> +  [[assume (true, true)]];		// { dg-error "wrong number of arguments specified for 'assume' attribute" }
> +					// { dg-message "expected 1, found 2" "" { target *-*-* } .-1 }
> +  [[assume (true)]] i = 1;		// { dg-warning "'assume' attribute not followed by ';'" }
> +  [[assume (throw 1)]];			// { dg-error "expected primary-expression before 'throw'" }
> +  [[assume (i = 1)]];			// { dg-error "expected '\\\)' before '=' token" }
> +}
> +
> +constexpr int
> +f2 (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> +  [[assume (x == 42)]];			// { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
> +#endif					// { dg-message "the comparison reduces to '\\\(x == 42\\\)'" "" { target c++14 } .-1 }
> +  return x;
> +}
> +
> +constexpr int a = f2 (44);
> +
> +int
> +f3 (int x)
> +{
> +  __asm ("" : "+r" (x));
> +  return x;
> +}
> +
> +constexpr int
> +f4 (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> +  [[assume (f3 (42) == 42)]];
> +#endif
> +  return x;
> +}
> +
> +static_assert (f4 (42) == 42, "");
> +
> +struct S {};
> +
> +int
> +f5 ()
> +{
> +  S s;
> +  [[assume (s)]];			// { dg-error "could not convert 's' from 'S' to 'bool'" }
> +  return 0;
> +}
> +
> +template <typename T>
> +int
> +f6 ()
> +{
> +  T t;
> +  [[assume (t)]];			// { dg-error "could not convert 't' from 'S' to 'bool'" }
> +  return 0;
> +}
> +
> +int z = f6 <S> ();
> +
> +constexpr int
> +f7 (int x, int y, int z, int w)
> +{
> +#if __cpp_constexpr >= 201304L
> +  [[assume (x == 42 && y == 43 && z == 44 && w == 45)]];	// { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
> +#endif					// { dg-message "the comparison reduces to '\\\(z == 44\\\)'" "" { target c++14 } .-1 }
> +  return x;
> +}
> +
> +constexpr int w = f7 (42, 43, 45, 44);
> --- gcc/testsuite/g++.dg/cpp23/attr-assume3.C.jj	2022-10-05 11:19:19.208572752 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume3.C	2022-10-05 11:19:19.208572752 +0200
> @@ -0,0 +1,198 @@
> +// P1774R8 - Portable assumptions
> +// { dg-do run { target c++11 } }
> +
> +namespace std
> +{
> +  constexpr bool
> +  isfinite (float x)
> +  { return __builtin_isfinite (x); }
> +
> +  constexpr bool
> +  isfinite (double x)
> +  { return __builtin_isfinite (x); }
> +
> +  constexpr bool
> +  isfinite (long double x)
> +  { return __builtin_isfinite (x); }
> +
> +  constexpr float
> +  sqrt (float x)
> +  { return __builtin_sqrtf (x); }
> +
> +  constexpr double
> +  sqrt (double x)
> +  { return __builtin_sqrt (x); }
> +
> +  constexpr long double
> +  sqrt (long double x)
> +  { return __builtin_sqrtl (x); }
> +
> +  extern "C" void
> +  abort ();
> +}
> +
> +constexpr int
> +f1 (int i)
> +{
> +#if __cpp_constexpr >= 201603L
> +  auto f = [=] { [[__assume__ (i == 0)]]; };
> +  return sizeof (f);
> +#else
> +  return sizeof (int);
> +#endif
> +}
> +
> +void
> +f2 ()
> +{
> +  static_assert (f1 (0) >= sizeof (int), "");
> +}
> +
> +int
> +f3 (int i)
> +{
> +  [[gnu::assume (i == 42)]];
> +  return i;
> +}
> +
> +int
> +f4 (int i)
> +{
> +  __attribute__ ((assume (++i == 44)));
> +  return i;
> +}
> +
> +int a;
> +int *volatile c;
> +
> +bool
> +f5 ()
> +{
> +  ++a;
> +  return true;
> +}
> +
> +constexpr int
> +f6 ()
> +{
> +#if __cpp_constexpr >= 201304L
> +  [[__assume__ (f5 ())]];
> +#endif
> +  return 1;
> +}
> +
> +template <int ...args>
> +bool
> +f7 ()
> +{
> +#if __cpp_fold_expressions >= 201411L
> +  [[__gnu__::__assume__ (((args >= 0) && ...))]];
> +  return ((args >= 0) && ...);
> +#else
> +  return true;
> +#endif
> +}
> +
> +bool
> +f8 (double x)
> +{
> +  [[gnu::assume (std::isfinite (x) && x >= 0.0)]];
> +  return std::isfinite (std::sqrt (x));
> +}
> +
> +double
> +f9 (double x)
> +{
> +  __attribute__((assume (std::isfinite (std::sqrt (x)))));
> +  return std::sqrt (x);
> +}
> +
> +template <typename T, T N>
> +T
> +f10 (T x)
> +{
> +  [[__assume__ (x == N)]];
> +  return x;
> +}
> +
> +int
> +f11 (int x)
> +{
> +  [[gnu::assume (x == 93 ? true : throw 1)]];
> +  return x;
> +}
> +
> +constexpr int
> +f12 (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> +  __attribute__((assume (++x == 43)));
> +#endif
> +  return x;
> +}
> +
> +static_assert (f12 (42) == 42, "");
> +
> +struct S
> +{
> +  operator bool () { return true; }
> +};
> +
> +int
> +f13 ()
> +{
> +  S s;
> +  [[__gnu__::__assume__ (s)]];
> +  return 0;
> +}
> +
> +template <typename T>
> +int
> +f14 ()
> +{
> +  T t;
> +  __attribute__((assume (t)));
> +  return 0;
> +}
> +
> +int
> +main ()
> +{
> +  int b = 42;
> +  double d = 42.0, e = 43.0;
> +  c = &b;
> +  [[__assume__ (f5 ())]];
> +  if (a)
> +    std::abort ();
> +  [[gnu::assume (++b == 43)]];
> +  if (b != 42 || *c != 42)
> +    std::abort ();
> +  static_assert (f6 () == 1, "");
> +  if (f6 () != 1)
> +    std::abort ();
> +  if (a)
> +    std::abort ();
> +  if (!f7 <0> () || !f7 <1, 2, 3, 4> ())
> +    std::abort ();
> +  __attribute__((assume (d < e)));
> +  if (f10 <int, 45> (45) != 45
> +      || f10 <long long, 128LL> (128LL) != 128LL
> +#if __cpp_nontype_template_args >= 201911L
> +      || f10 <long double, -42.0L> (-42.0L) != -42.0L
> +#endif
> +      || false)
> +    std::abort ();
> +  int i = 90, j = 91, k = 92;
> +  [[__assume__ (i == 90), gnu::assume (j <= 91)]]
> +#if __cplusplus >= 201703L
> +  [[using gnu:assume (k >= 92)]]
> +#else
> +  [[gnu::assume (k >= 92)]]
> +#endif
> +  ;
> +  __attribute__((__assume__ (i == 90), assume (j <= 91))) __attribute__((assume (k >= 92)));
> +  if (f11 (93) != 93)
> +    std::abort ();
> +  if (f14 <S> () != 0)
> +    std::abort ();
> +}
> --- gcc/testsuite/g++.dg/cpp23/attr-assume4.C.jj	2022-10-05 11:19:19.209572738 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume4.C	2022-10-05 11:19:19.209572738 +0200
> @@ -0,0 +1,136 @@
> +// P1774R8 - Portable assumptions
> +// { dg-do compile { target c++11 } }
> +
> +[[__assume__ (true)]] void f1 ();		// { dg-error "'assume' attribute ignored" }
> +typedef int intx [[__assume__ (true)]];		// { dg-error "'assume' attribute ignored" }
> +[[__assume__ (true)]];				// { dg-warning "attribute ignored" }
> +[[gnu::assume (true)]] void f1a ();		// { dg-error "'assume' attribute ignored" }
> +typedef int inty [[gnu::__assume__ (true)]];	// { dg-error "'assume' attribute ignored" }
> +[[__gnu__::assume (true)]];			// { dg-warning "attribute ignored" }
> +__attribute__((assume (true))) void f1b ();	// { dg-error "'assume' attribute ignored" }
> +typedef int intz __attribute__((assume (true)));// { dg-error "'assume' attribute ignored" }
> +
> +void
> +foo ()
> +{
> +  int i;
> +  [[__assume__]];			// { dg-error "wrong number of arguments specified for 'assume' attribute" }
> +					// { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
> +  [[__assume__ ()]];			// { dg-error "parentheses must be omitted if 'assume' attribute argument list is empty" }
> +					// { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 }
> +					// { dg-message "expected 1, found 0" "" { target *-*-* } .-2 }
> +  [[__assume__ (true, true)]];		// { dg-error "wrong number of arguments specified for 'assume' attribute" }
> +					// { dg-message "expected 1, found 2" "" { target *-*-* } .-1 }
> +  [[__assume__ (true)]] i = 1;		// { dg-warning "'assume' attribute not followed by ';'" }
> +  [[__assume__ (throw 1)]];		// { dg-error "expected primary-expression before 'throw'" }
> +  [[__assume__ (i = 1)]];		// { dg-error "expected '\\\)' before '=' token" }
> +  [[gnu::assume]];			// { dg-error "wrong number of arguments specified for 'assume' attribute" }
> +					// { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
> +  [[gnu::assume ()]];			// { dg-error "parentheses must be omitted if 'assume' attribute argument list is empty" }
> +					// { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 }
> +					// { dg-message "expected 1, found 0" "" { target *-*-* } .-2 }
> +  [[gnu::assume (true, true)]];		// { dg-error "wrong number of arguments specified for 'assume' attribute" }
> +					// { dg-message "expected 1, found 2" "" { target *-*-* } .-1 }
> +  [[gnu::assume (true)]] i = 1;		// { dg-warning "'assume' attribute not followed by ';'" }
> +  [[gnu::assume (throw 1)]];		// { dg-error "expected primary-expression before 'throw'" }
> +  [[gnu::assume (i = 1)]];		// { dg-error "expected '\\\)' before '=' token" }
> +  __attribute__((assume));		// { dg-error "wrong number of arguments specified for 'assume' attribute" }
> +					// { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
> +  __attribute__((assume ()));		// { dg-error "wrong number of arguments specified for 'assume' attribute" }
> +					// { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
> +  __attribute__((assume (true, true)));	// { dg-error "wrong number of arguments specified for 'assume' attribute" }
> +					// { dg-message "expected 1, found 2" "" { target *-*-* } .-1 }
> +  __attribute__((assume (true))) i = 1;	// { dg-warning "'assume' attribute not followed by ';'" }
> +  __attribute__((assume (throw 1)));	// { dg-error "expected primary-expression before 'throw'" }
> +  __attribute__((assume (i = 1)));	// { dg-error "expected '\\\)' before '=' token" }
> +}
> +
> +constexpr int
> +f2 (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> +  [[__assume__ (x == 42)]];		// { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
> +#endif
> +  return x;
> +}
> +
> +constexpr int
> +f2a (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> +  [[gnu::__assume__ (x == 42)]];	// { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
> +#endif
> +  return x;
> +}
> +
> +constexpr int
> +f2b (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> +  __attribute__((__assume__ (x == 42)));// { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
> +#endif
> +  return x;
> +}
> +
> +constexpr int a = f2 (44);
> +constexpr int aa = f2a (44);
> +constexpr int ab = f2b (44);
> +
> +int
> +f3 (int x)
> +{
> +  __asm ("" : "+r" (x));
> +  return x;
> +}
> +
> +constexpr int
> +f4 (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> +  [[__assume__ (f3 (42) == 42)]];
> +#endif
> +  return x;
> +}
> +
> +constexpr int
> +f4a (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> +  [[gnu::assume (f3 (42) == 42)]];
> +#endif
> +  return x;
> +}
> +
> +constexpr int
> +f4b (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> +  __attribute__((assume (f3 (42) == 42)));
> +#endif
> +  return x;
> +}
> +
> +static_assert (f4 (42) == 42, "");
> +static_assert (f4a (42) == 42, "");
> +static_assert (f4b (42) == 42, "");
> +
> +struct S {};
> +
> +int
> +f5 ()
> +{
> +  S s;
> +  [[gnu::assume (s)]];			// { dg-error "could not convert 's' from 'S' to 'bool'" }
> +  return 0;
> +}
> +
> +template <typename T>
> +int
> +f6 ()
> +{
> +  T t;
> +  __attribute__((assume (t)));		// { dg-error "could not convert 't' from 'S' to 'bool'" }
> +  return 0;
> +}
> +
> +int z = f6 <S> ();
> 
> 
> 	Jakub
> 


      reply	other threads:[~2022-10-05 12:33 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-09-22  9:55 [PATCH] c++, c: " Jakub Jelinek
2022-09-30 20:39 ` Jason Merrill
2022-10-02 11:35   ` [PATCH] c++: Disallow jumps into statement expressions Jakub Jelinek
2022-10-03 15:02     ` Jason Merrill
2022-10-03 19:22   ` [PATCH] c++, c, v2: Implement C++23 P1774R8 - Portable assumptions [PR106654] Jakub Jelinek
2022-10-04 10:20     ` Jakub Jelinek
2022-10-04 20:42     ` Jason Merrill
2022-10-05  9:55       ` [PATCH] c++, c, v3: " Jakub Jelinek
2022-10-05 12:33         ` 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=f297fee8-e0d7-2d4e-af28-187ea58052be@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).