public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++, c: Implement C++23 P1774R8 - Portable assumptions [PR106654]
@ 2022-09-22  9:55 Jakub Jelinek
  2022-09-30 20:39 ` Jason Merrill
  0 siblings, 1 reply; 9+ messages in thread
From: Jakub Jelinek @ 2022-09-22  9:55 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

Hi!

The following patch implements C++23 P1774R8 - Portable assumptions
paper, by introducing support for [[assume (cond)]]; attribute for C++.
In addition to that the patch adds [[gnu::assume (cond)]]; and
__attribute__((assume (cond))); support to both C and C++.
As described in C++23, the attribute argument is conditional-expression
rather than the usual assignment-expression for attribute arguments,
the condition is contextually converted to bool (for C truthvalue conversion
is done on it) and is never evaluated at runtime.
For C++ constant expression evaluation, I only check the simplest conditions
for undefined behavior, because otherwise I'd need to undo changes to
*ctx->global which happened during the evaluation (but I believe the spec
allows that and we can further improve later).
The patch uses a new internal function, .ASSUME, to hold the condition
in the FEs.  At gimplification time, if the condition is simple/without
side-effects, it is gimplified as if (cond) ; else __builtin_unreachable ();
and otherwise for now dropped on the floor.  The intent is to incrementally
outline the conditions into separate artificial functions and use
.ASSUME further to tell the ranger and perhaps other optimization passes
about the assumptions, as detailed in the PR.

When implementing it, I found that assume entry hasn't been added to
https://eel.is/c++draft/cpp.cond#6
Jonathan said he'll file a NB comment about it, this patch assumes it
has been added into the table as 202207L when the paper has been voted in.

With the attributes for both C/C++, I'd say we don't need to add
__builtin_assume with similar purpose, especially when __builtin_assume
in LLVM is just weird.  It is strange for side-effects in function call's
argument not to be evaluated, and LLVM in that case (annoyingly) warns
and ignores the side-effects (but doesn't do then anything with it),
if there are no side-effects, it will work like our
if (!cond) __builtin_unreachable ();

During bootstrap/regtest, I've discovered a problem with the way we
handle scoped attributes.  For declaration or type attributes for attributes
we don't know anything about we just don't add them to the declarations or
types, so later in the FEs and middle-end it is fine to use lookup_attribute
etc. which just check the attribute name and not namespace because
non-standard non-GNU attributes just won't show there.  But in the case of
attributes on statements, nothing has filtered out the unknown attributes,
so with my earlier patch e.g. c-c++-common/Wno-attributes-6.c test failed
because it uses:
[[vendor::assume(1 + 1 == 2)]];
with -Wno-attributes=vendor::assume and lookup_attribute ("assume", )
finds such attribute and handled it that way.
So, for those cases, this patch introduces lookup_attribute and
remove_attribute overloads which specify also the namespace.
I think the fallthrough, hot, cold, likely, unlikely attribute handling
will need to use the new APIs too, so that we don't handle
msft::fallthrough attribute as something we'd know.

Earlier version (without the attribs.{h,cc} changes and the 3 argument
lookup_attribute/remove_attribute uses instead of 2) has been successfully
bootstrapped/regtested on x86_64-linux and i686-linux with the
FAIL: c-c++-common/Wno-attributes-6.c  -Wc++-compat  (test for excess errors)
regression, ok for trunk if this passes full bootstrap/regtest again
(note, I've of course checked it already with
GXX_TESTSUITE_STDS=98,11,14,17,20,2b make check-gcc check-g++ \
  RUNTESTFLAGS="dg.exp='feat* attr-assume* Wno-attrib*'"
)?

2022-09-22  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_operand_p_2): Declare.
	* fold-const.cc (simple_operand_p_2): Remove forward declaration.
	No longer static.  Adjust function comment and fix a typo in it.
	(simple_operand_p): Adjust function comment.
	* 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.
	* 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.
	* name-lookup.h (enum scope_kind): Add sk_assume.
	* parser.cc (assume_attr): New enumerator.
	(cp_parser_parenthesized_expression_list): Handle assume_attr.
	(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.
	* decl.cc (struct named_label_entry): Add in_assume member.
	(poplevel_named_label_1): Set it.
	(check_previous_goto_1): Diagnose entering assume attribute
	condition.
	(check_goto): Likewise.
	* constexpr.cc: Include fold-const.h.
	(cxx_eval_internal_function): Handle IFN_ASSUME.
	(potential_constant_expression_1): Likewise.
	* pt.cc (tsubst_copy_and_build): Likewise.
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.
	* g++.dg/cpp23/attr-assume5.C: New test.

--- gcc/internal-fn.def.jj	2022-09-22 00:14:19.568087771 +0200
+++ gcc/internal-fn.def	2022-09-22 00:24:00.502186674 +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-09-22 00:14:19.605087267 +0200
+++ gcc/internal-fn.h	2022-09-22 00:24:00.502186674 +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-09-22 00:14:19.550088015 +0200
+++ gcc/internal-fn.cc	2022-09-22 00:24:00.910181125 +0200
@@ -4508,3 +4508,9 @@ expand_TRAP (internal_fn, gcall *)
 {
   expand_builtin_trap ();
 }
+
+void
+expand_ASSUME (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
--- gcc/gimplify.cc.jj	2022-09-22 00:14:19.391090178 +0200
+++ gcc/gimplify.cc	2022-09-22 00:24:00.954180527 +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_operand_p_2 (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-09-22 00:14:19.325091076 +0200
+++ gcc/fold-const.h	2022-09-22 00:24:00.955180513 +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_operand_p_2 (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-09-22 00:14:19.313091239 +0200
+++ gcc/fold-const.cc	2022-09-22 00:24:00.990180037 +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_operand_p_2: determine if an
+   operand is simple enough to be evaluated unconditionally.  */
 
 static bool
 simple_operand_p (const_tree exp)
@@ -4897,12 +4896,11 @@ 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
+bool
 simple_operand_p_2 (tree exp)
 {
   enum tree_code code;
--- gcc/attribs.h.jj	2022-01-11 23:11:21.543302056 +0100
+++ gcc/attribs.h	2022-09-22 10:50:46.299971495 +0200
@@ -82,6 +82,10 @@ extern tree merge_type_attributes (tree,
 
 extern tree remove_attribute (const char *, tree);
 
+/* Similarly but also with specific attribute namespace.  */
+
+extern tree remove_attribute (const char *, const char *, tree);
+
 /* Given two attributes lists, return a list of their union.  */
 
 extern tree merge_attributes (tree, tree);
@@ -113,6 +117,10 @@ extern int attribute_list_contained (con
    for size.  */
 extern tree private_lookup_attribute (const char *attr_name, size_t attr_len,
 				      tree list);
+extern tree private_lookup_attribute (const char *attr_ns,
+				      const char *attr_name,
+				      size_t attr_ns_len, size_t attr_len,
+				      tree list);
 
 extern unsigned decls_mismatched_attributes (tree, tree, tree,
 					     const char* const[],
@@ -209,6 +217,36 @@ lookup_attribute (const char *attr_name,
     }
 }
 
+/* Similar to lookup_attribute, but also match the attribute namespace.  */
+
+static inline tree
+lookup_attribute (const char *attr_ns, const char *attr_name, tree list)
+{
+  if (CHECKING_P && attr_name[0] != '_')
+    {
+      size_t attr_len = strlen (attr_name);
+      gcc_checking_assert (!canonicalize_attr_name (attr_name, attr_len));
+    }
+  if (CHECKING_P && attr_ns && attr_ns[0] != '_')
+    {
+      size_t attr_ns_len = strlen (attr_ns);
+      gcc_checking_assert (!canonicalize_attr_name (attr_ns, attr_ns_len));
+    }
+  /* In most cases, list is NULL_TREE.  */
+  if (list == NULL_TREE)
+    return NULL_TREE;
+  else
+    {
+      size_t attr_ns_len = attr_ns ? strlen (attr_ns) : 0;
+      size_t attr_len = strlen (attr_name);
+      /* Do the strlen() before calling the out-of-line implementation.
+	 In most cases attr_name is a string constant, and the compiler
+	 will optimize the strlen() away.  */
+      return private_lookup_attribute (attr_ns, attr_name,
+				       attr_ns_len, attr_len, list);
+    }
+}
+
 /* Given an attribute name ATTR_NAME and a list of attributes LIST,
    return a pointer to the attribute's list first element if the attribute
    starts with ATTR_NAME.  ATTR_NAME must be in the form 'text' (not
--- gcc/attribs.cc.jj	2022-05-17 09:01:11.988655748 +0200
+++ gcc/attribs.cc	2022-09-22 10:54:44.693705319 +0200
@@ -1639,6 +1639,36 @@ remove_attribute (const char *attr_name,
   return list;
 }
 
+/* Similarly but also match namespace on the removed attributes.  */
+
+tree
+remove_attribute (const char *attr_ns, const char *attr_name, tree list)
+{
+  tree *p;
+  gcc_checking_assert (attr_name[0] != '_');
+  gcc_checking_assert (attr_ns == NULL || attr_ns[0] != '_');
+
+  for (p = &list; *p;)
+    {
+      tree l = *p;
+
+      tree attr = get_attribute_name (l);
+      if (is_attribute_p (attr_name, attr))
+	{
+	  tree ns = get_attribute_namespace (l);
+	  if ((ns == NULL_TREE && attr_ns == NULL)
+	      || (ns && attr_ns && is_attribute_p (attr_ns, ns)))
+	    {
+	      *p = TREE_CHAIN (l);
+	      continue;
+	    }
+	}
+      p = &TREE_CHAIN (l);
+    }
+
+  return list;
+}
+
 /* Return an attribute list that is the union of a1 and a2.  */
 
 tree
@@ -2033,6 +2063,39 @@ private_lookup_attribute (const char *at
       list = TREE_CHAIN (list);
     }
 
+  return list;
+}
+
+/* Similarly but with also attribute namespace.  */
+
+tree
+private_lookup_attribute (const char *attr_ns, const char *attr_name,
+			  size_t attr_ns_len, size_t attr_len, tree list)
+{
+  while (list)
+    {
+      tree attr = get_attribute_name (list);
+      size_t ident_len = IDENTIFIER_LENGTH (attr);
+      if (cmp_attribs (attr_name, attr_len, IDENTIFIER_POINTER (attr),
+		       ident_len))
+	{
+	  tree ns = get_attribute_namespace (list);
+	  if (ns == NULL_TREE)
+	    {
+	      if (attr_ns == NULL)
+		break;
+	    }
+	  else if (attr_ns)
+	    {
+	      ident_len = IDENTIFIER_LENGTH (ns);
+	      if (cmp_attribs (attr_ns, attr_ns_len, IDENTIFIER_POINTER (ns),
+			       ident_len))
+		break;
+	    }
+	}
+      list = TREE_CHAIN (list);
+    }
+
   return list;
 }
 
--- gcc/doc/extend.texi.jj	2022-09-22 00:14:19.265091892 +0200
+++ gcc/doc/extend.texi	2022-09-22 00:24:01.048179249 +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-09-22 00:14:18.695099644 +0200
+++ gcc/c-family/c-attribs.cc	2022-09-22 00:24:01.074178895 +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-09-22 00:14:18.743098991 +0200
+++ gcc/c-family/c-lex.cc	2022-09-22 00:24:01.074178895 +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-09-22 00:14:18.798098243 +0200
+++ gcc/c/c-parser.cc	2022-09-22 11:20:07.661847350 +0200
@@ -1808,6 +1808,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
@@ -2022,6 +2062,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
@@ -2039,13 +2087,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)
@@ -2145,6 +2195,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;
@@ -4583,7 +4636,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;
@@ -4602,6 +4656,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)))
     {
@@ -4638,6 +4693,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,
@@ -4721,7 +4793,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))
@@ -4967,9 +5041,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);
@@ -6248,8 +6326,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))
@@ -6266,9 +6357,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-09-22 00:14:18.970095904 +0200
+++ gcc/cp/cp-tree.h	2022-09-22 00:24:01.094178623 +0200
@@ -8235,6 +8235,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);
 
--- gcc/cp/name-lookup.h.jj	2022-09-22 00:14:19.088094299 +0200
+++ gcc/cp/name-lookup.h	2022-09-22 00:24:01.094178623 +0200
@@ -212,7 +212,8 @@ enum scope_kind {
 			explicit specialization is introduced by
 			"template <>", this scope is always empty.  */
   sk_transaction,    /* A synchronized or atomic statement.  */
-  sk_omp	     /* An OpenMP structured block.  */
+  sk_omp,	     /* An OpenMP structured block.  */
+  sk_assume	     /* The scope of assume attribute.  */
 };
 
 struct GTY(()) cp_class_binding {
--- gcc/cp/parser.cc.jj	2022-09-22 00:14:19.126093782 +0200
+++ gcc/cp/parser.cc	2022-09-22 11:10:57.813376547 +0200
@@ -2251,7 +2251,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
@@ -8542,10 +8542,27 @@ 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)
+	      {
+		enum scope_kind prev_kind = sk_block;
+		if (current_binding_level)
+		  {
+		    prev_kind = current_binding_level->kind;
+		    current_binding_level->kind = sk_assume;
+		  }
+		/* Parse conditional-expression.  */
+		expr = cp_parser_binary_expression (parser, 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))
+		  expr = cp_parser_question_colon_clause (parser, expr);
+		if (current_binding_level)
+		  current_binding_level->kind = prev_kind;
+	      }
+	    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 ();
@@ -12625,6 +12642,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))
 	{
@@ -12648,7 +12668,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,
@@ -12840,6 +12860,8 @@ cp_parser_expression_statement (cp_parse
 	}
     }
 
+  attr = process_stmt_assume_attribute (attr, statement, loc);
+
   /* Handle [[fallthrough]];.  */
   if (attribute_fallthrough_p (attr))
     {
@@ -28971,6 +28993,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,
@@ -29222,6 +29246,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))
@@ -29266,8 +29293,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-09-22 00:14:18.929096461 +0200
+++ gcc/cp/cp-gimplify.cc	2022-09-22 11:01:38.217041306 +0200
@@ -3064,6 +3064,49 @@ 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)
+	{
+	  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/decl.cc.jj	2022-09-22 00:14:55.478599363 +0200
+++ gcc/cp/decl.cc	2022-09-22 00:24:01.121178256 +0200
@@ -223,6 +223,7 @@ struct GTY((for_user)) named_label_entry
   bool in_transaction_scope;
   bool in_constexpr_if;
   bool in_consteval_if;
+  bool in_assume;
 };
 
 #define named_labels cp_function_chain->x_named_labels
@@ -543,6 +544,8 @@ poplevel_named_label_1 (named_label_entr
 	    ent->in_constexpr_if = true;
 	  else if (level_for_consteval_if (bl->level_chain))
 	    ent->in_consteval_if = true;
+	  else if (bl->level_chain->kind == sk_assume)
+	    ent->in_assume = true;
 	  break;
 	default:
 	  break;
@@ -3487,7 +3490,7 @@ check_previous_goto_1 (tree decl, cp_bin
   bool complained = false;
   int identified = 0;
   bool saw_eh = false, saw_omp = false, saw_tm = false, saw_cxif = false;
-  bool saw_ceif = false;
+  bool saw_ceif = false, saw_assume = false;
 
   if (exited_omp)
     {
@@ -3573,6 +3576,11 @@ check_previous_goto_1 (tree decl, cp_bin
 	      loc = EXPR_LOCATION (b->level_chain->this_entity);
 	      saw_ceif = true;
 	    }
+	  else if (!saw_assume && b->level_chain->kind == sk_assume)
+	    {
+	      inf = G_("  enters %<assume%> attribute condition");
+	      saw_assume = true;
+	    }
 	  break;
 
 	default:
@@ -3650,12 +3658,13 @@ check_goto (tree decl)
 
   if (ent->in_try_scope || ent->in_catch_scope || ent->in_transaction_scope
       || ent->in_constexpr_if || ent->in_consteval_if
-      || ent->in_omp_scope || !vec_safe_is_empty (ent->bad_decls))
+      || ent->in_omp_scope || ent->in_assume
+      || !vec_safe_is_empty (ent->bad_decls))
     {
       diagnostic_t diag_kind = DK_PERMERROR;
       if (ent->in_try_scope || ent->in_catch_scope || ent->in_constexpr_if
 	  || ent->in_consteval_if || ent->in_transaction_scope
-	  || ent->in_omp_scope)
+	  || ent->in_omp_scope || ent->in_assume)
 	diag_kind = DK_ERROR;
       complained = identify_goto (decl, DECL_SOURCE_LOCATION (decl),
 				  &input_location, diag_kind);
@@ -3703,6 +3712,8 @@ check_goto (tree decl)
 	inform (input_location, "  enters %<constexpr if%> statement");
       else if (ent->in_consteval_if)
 	inform (input_location, "  enters %<consteval if%> statement");
+      else if (ent->in_assume)
+	inform (input_location, "  enters %<assume%> attribute condition");
     }
 
   if (ent->in_omp_scope)
--- gcc/cp/constexpr.cc.jj	2022-09-22 00:14:18.879097141 +0200
+++ gcc/cp/constexpr.cc	2022-09-22 00:24:01.123178229 +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)						\
@@ -1837,6 +1838,33 @@ 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 (simple_operand_p_2 (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)
+		error_at (EXPR_LOCATION (t),
+			  "failed %<assume%> attribute assumption");
+	      *non_constant_p = true;
+	      return t;
+	    }
+	}
+      return void_node;
+
     case IFN_ADD_OVERFLOW:
       opcode = PLUS_EXPR;
       break;
@@ -8706,6 +8734,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-09-22 00:14:19.185092980 +0200
+++ gcc/cp/pt.cc	2022-09-22 00:24:01.150177861 +0200
@@ -21152,6 +21152,34 @@ 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
+		{
+		  if (!type_dependent_expression_p ((*call_args)[0]))
+		    (*call_args)[0]
+		      = contextual_conv_bool ((*call_args)[0],
+					      tf_warning_or_error);
+		  if (error_operand_p ((*call_args)[0]))
+		    {
+		      ret = error_mark_node;
+		      break;
+		    }
+		  ret = build_call_expr_internal_loc (EXPR_LOCATION (t),
+						      IFN_ASSUME,
+						      void_type_node, 1,
+						      (*call_args)[0]);
+		  RETURN (ret);
+		}
+	      break;
+
 	    default:
 	      /* Unsupported internal function with arguments.  */
 	      gcc_unreachable ();
--- gcc/testsuite/gcc.dg/attr-assume-1.c.jj	2022-09-22 00:24:01.151177848 +0200
+++ gcc/testsuite/gcc.dg/attr-assume-1.c	2022-09-22 00:24:01.151177848 +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-09-22 00:24:01.151177848 +0200
+++ gcc/testsuite/gcc.dg/attr-assume-2.c	2022-09-22 00:24:01.151177848 +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-09-22 00:24:01.151177848 +0200
+++ gcc/testsuite/gcc.dg/attr-assume-3.c	2022-09-22 00:24:01.151177848 +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-09-22 00:14:19.718085731 +0200
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C	2022-09-22 00:24:01.176177508 +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 != 201811
+#  error "__cpp_char8_t != 201811"
+#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 != 201811
-#  error "__cpp_char8_t != 201811"
-#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-09-22 00:14:19.703085935 +0200
+++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2022-09-22 00:24:01.196177236 +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 != 201811
+#  error "__cpp_char8_t != 201811"
+#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 != 201811
-#  error "__cpp_char8_t != 201811"
-#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
@@ -563,3 +565,15 @@
 #elif __cpp_named_character_escapes != 202207
 #  error "__cpp_named_character_escapes != 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-09-22 00:24:01.196177236 +0200
+++ gcc/testsuite/g++.dg/cpp23/attr-assume1.C	2022-09-22 00:24:01.196177236 +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-09-22 00:24:01.214176991 +0200
+++ gcc/testsuite/g++.dg/cpp23/attr-assume2.C	2022-09-22 00:24:01.196177236 +0200
@@ -0,0 +1,72 @@
+// 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
+  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> ();
--- gcc/testsuite/g++.dg/cpp23/attr-assume3.C.jj	2022-09-22 00:24:01.214176991 +0200
+++ gcc/testsuite/g++.dg/cpp23/attr-assume3.C	2022-09-22 00:24:01.214176991 +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-09-22 00:24:01.214176991 +0200
+++ gcc/testsuite/g++.dg/cpp23/attr-assume4.C	2022-09-22 00:24:01.214176991 +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> ();
--- gcc/testsuite/g++.dg/cpp23/attr-assume5.C.jj	2022-09-22 00:24:01.214176991 +0200
+++ gcc/testsuite/g++.dg/cpp23/attr-assume5.C	2022-09-22 00:24:01.214176991 +0200
@@ -0,0 +1,43 @@
+// P1774R8 - Portable assumptions
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+void
+foo (int x)
+{
+  if (x == 1)
+    goto l1;						// { dg-message "from here" }
+  else if (x == 2)
+    goto l2;						// { dg-message "from here" }
+  else if (x == 3)
+    goto l3;						// { dg-message "from here" }
+  [[assume (({ l0:; if (x == 0) goto l0; true; }))]];
+  [[assume (({ if (x == 0) throw 1; true; }))]];
+  [[assume (({ l1:; true; }))]];			// { dg-error "jump to label 'l1'" }
+							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
+  [[gnu::assume (({ l2:; true; }))]];			// { dg-error "jump to label 'l2'" }
+							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
+  __attribute__((assume (({ l3:; true; }))));		// { dg-error "jump to label 'l3'" }
+							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
+  [[assume (({ l4:; true; }))]];			// { dg-error "jump to label 'l4'" }
+  [[gnu::assume (({ l5:; true; }))]];			// { dg-error "jump to label 'l5'" }
+  __attribute__((assume (({ l6:; true; }))));		// { dg-error "jump to label 'l6'" }
+  switch (x)
+    {
+    case 7:
+      [[assume (({ case 8:; true; }))]];		// { dg-error "jump to case label" }
+							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
+      [[assume (({ default:; true; }))]];		// { dg-error "jump to case label" }
+							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
+      break;
+    }
+  if (x == 4)
+    goto l4;						// { dg-message "from here" }
+							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
+  else if (x == 5)
+    goto l5;						// { dg-message "from here" }
+							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
+  else if (x == 6)
+    goto l6;						// { dg-message "from here" }
+							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
+}

	Jakub


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] c++, c: Implement C++23 P1774R8 - Portable assumptions [PR106654]
  2022-09-22  9:55 [PATCH] c++, c: Implement C++23 P1774R8 - Portable assumptions [PR106654] 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 19:22   ` [PATCH] c++, c, v2: Implement C++23 P1774R8 - Portable assumptions [PR106654] Jakub Jelinek
  0 siblings, 2 replies; 9+ messages in thread
From: Jason Merrill @ 2022-09-30 20:39 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 9/22/22 05:55, Jakub Jelinek wrote:
> Hi!
> 
> The following patch implements C++23 P1774R8 - Portable assumptions
> paper, by introducing support for [[assume (cond)]]; attribute for C++.
> In addition to that the patch adds [[gnu::assume (cond)]]; and
> __attribute__((assume (cond))); support to both C and C++.
> As described in C++23, the attribute argument is conditional-expression
> rather than the usual assignment-expression for attribute arguments,
> the condition is contextually converted to bool (for C truthvalue conversion
> is done on it) and is never evaluated at runtime.
> For C++ constant expression evaluation, I only check the simplest conditions
> for undefined behavior, because otherwise I'd need to undo changes to
> *ctx->global which happened during the evaluation (but I believe the spec
> allows that and we can further improve later).
> The patch uses a new internal function, .ASSUME, to hold the condition
> in the FEs.  At gimplification time, if the condition is simple/without
> side-effects, it is gimplified as if (cond) ; else __builtin_unreachable ();
> and otherwise for now dropped on the floor.  The intent is to incrementally
> outline the conditions into separate artificial functions and use
> .ASSUME further to tell the ranger and perhaps other optimization passes
> about the assumptions, as detailed in the PR.
> 
> When implementing it, I found that assume entry hasn't been added to
> https://eel.is/c++draft/cpp.cond#6
> Jonathan said he'll file a NB comment about it, this patch assumes it
> has been added into the table as 202207L when the paper has been voted in.
> 
> With the attributes for both C/C++, I'd say we don't need to add
> __builtin_assume with similar purpose, especially when __builtin_assume
> in LLVM is just weird.  It is strange for side-effects in function call's
> argument not to be evaluated, and LLVM in that case (annoyingly) warns
> and ignores the side-effects (but doesn't do then anything with it),
> if there are no side-effects, it will work like our
> if (!cond) __builtin_unreachable ();
> 
> During bootstrap/regtest, I've discovered a problem with the way we
> handle scoped attributes.  For declaration or type attributes for attributes
> we don't know anything about we just don't add them to the declarations or
> types, so later in the FEs and middle-end it is fine to use lookup_attribute
> etc. which just check the attribute name and not namespace because
> non-standard non-GNU attributes just won't show there.  But in the case of
> attributes on statements, nothing has filtered out the unknown attributes,
> so with my earlier patch e.g. c-c++-common/Wno-attributes-6.c test failed
> because it uses:
> [[vendor::assume(1 + 1 == 2)]];
> with -Wno-attributes=vendor::assume and lookup_attribute ("assume", )
> finds such attribute and handled it that way.
> So, for those cases, this patch introduces lookup_attribute and
> remove_attribute overloads which specify also the namespace.
> I think the fallthrough, hot, cold, likely, unlikely attribute handling
> will need to use the new APIs too, so that we don't handle
> msft::fallthrough attribute as something we'd know.

Sounds good.

> Earlier version (without the attribs.{h,cc} changes and the 3 argument
> lookup_attribute/remove_attribute uses instead of 2) has been successfully
> bootstrapped/regtested on x86_64-linux and i686-linux with the
> FAIL: c-c++-common/Wno-attributes-6.c  -Wc++-compat  (test for excess errors)
> regression, ok for trunk if this passes full bootstrap/regtest again
> (note, I've of course checked it already with
> GXX_TESTSUITE_STDS=98,11,14,17,20,2b make check-gcc check-g++ \
>    RUNTESTFLAGS="dg.exp='feat* attr-assume* Wno-attrib*'"
> )?
> 
> 2022-09-22  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_operand_p_2): Declare.

This needs a better name if it's going to be a public interface.

The usage also needs rationale for why this is the right predicate for 
assume, rather than just no-side-effects.  Surely the latter is right 
for constexpr, at least?

> 	* fold-const.cc (simple_operand_p_2): Remove forward declaration.
> 	No longer static.  Adjust function comment and fix a typo in it.
> 	(simple_operand_p): Adjust function comment.
> 	* 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.
> 	* 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.
> 	* name-lookup.h (enum scope_kind): Add sk_assume.
> 	* parser.cc (assume_attr): New enumerator.
> 	(cp_parser_parenthesized_expression_list): Handle assume_attr.
> 	(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.
> 	* decl.cc (struct named_label_entry): Add in_assume member.
> 	(poplevel_named_label_1): Set it.
> 	(check_previous_goto_1): Diagnose entering assume attribute
> 	condition.
> 	(check_goto): Likewise.
> 	* constexpr.cc: Include fold-const.h.
> 	(cxx_eval_internal_function): Handle IFN_ASSUME.
> 	(potential_constant_expression_1): Likewise.
> 	* pt.cc (tsubst_copy_and_build): Likewise.
> 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.
> 	* g++.dg/cpp23/attr-assume5.C: New test.
> 
> --- gcc/internal-fn.def.jj	2022-09-22 00:14:19.568087771 +0200
> +++ gcc/internal-fn.def	2022-09-22 00:24:00.502186674 +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-09-22 00:14:19.605087267 +0200
> +++ gcc/internal-fn.h	2022-09-22 00:24:00.502186674 +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-09-22 00:14:19.550088015 +0200
> +++ gcc/internal-fn.cc	2022-09-22 00:24:00.910181125 +0200
> @@ -4508,3 +4508,9 @@ expand_TRAP (internal_fn, gcall *)
>   {
>     expand_builtin_trap ();
>   }
> +
> +void
> +expand_ASSUME (internal_fn, gcall *)
> +{
> +  gcc_unreachable ();
> +}
> --- gcc/gimplify.cc.jj	2022-09-22 00:14:19.391090178 +0200
> +++ gcc/gimplify.cc	2022-09-22 00:24:00.954180527 +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_operand_p_2 (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-09-22 00:14:19.325091076 +0200
> +++ gcc/fold-const.h	2022-09-22 00:24:00.955180513 +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_operand_p_2 (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-09-22 00:14:19.313091239 +0200
> +++ gcc/fold-const.cc	2022-09-22 00:24:00.990180037 +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_operand_p_2: determine if an
> +   operand is simple enough to be evaluated unconditionally.  */
>   
>   static bool
>   simple_operand_p (const_tree exp)
> @@ -4897,12 +4896,11 @@ 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
> +bool
>   simple_operand_p_2 (tree exp)
>   {
>     enum tree_code code;
> --- gcc/attribs.h.jj	2022-01-11 23:11:21.543302056 +0100
> +++ gcc/attribs.h	2022-09-22 10:50:46.299971495 +0200
> @@ -82,6 +82,10 @@ extern tree merge_type_attributes (tree,
>   
>   extern tree remove_attribute (const char *, tree);
>   
> +/* Similarly but also with specific attribute namespace.  */
> +
> +extern tree remove_attribute (const char *, const char *, tree);
> +
>   /* Given two attributes lists, return a list of their union.  */
>   
>   extern tree merge_attributes (tree, tree);
> @@ -113,6 +117,10 @@ extern int attribute_list_contained (con
>      for size.  */
>   extern tree private_lookup_attribute (const char *attr_name, size_t attr_len,
>   				      tree list);
> +extern tree private_lookup_attribute (const char *attr_ns,
> +				      const char *attr_name,
> +				      size_t attr_ns_len, size_t attr_len,
> +				      tree list);
>   
>   extern unsigned decls_mismatched_attributes (tree, tree, tree,
>   					     const char* const[],
> @@ -209,6 +217,36 @@ lookup_attribute (const char *attr_name,
>       }
>   }
>   
> +/* Similar to lookup_attribute, but also match the attribute namespace.  */
> +
> +static inline tree
> +lookup_attribute (const char *attr_ns, const char *attr_name, tree list)
> +{
> +  if (CHECKING_P && attr_name[0] != '_')
> +    {
> +      size_t attr_len = strlen (attr_name);
> +      gcc_checking_assert (!canonicalize_attr_name (attr_name, attr_len));
> +    }
> +  if (CHECKING_P && attr_ns && attr_ns[0] != '_')
> +    {
> +      size_t attr_ns_len = strlen (attr_ns);
> +      gcc_checking_assert (!canonicalize_attr_name (attr_ns, attr_ns_len));
> +    }
> +  /* In most cases, list is NULL_TREE.  */
> +  if (list == NULL_TREE)
> +    return NULL_TREE;
> +  else
> +    {
> +      size_t attr_ns_len = attr_ns ? strlen (attr_ns) : 0;
> +      size_t attr_len = strlen (attr_name);
> +      /* Do the strlen() before calling the out-of-line implementation.
> +	 In most cases attr_name is a string constant, and the compiler
> +	 will optimize the strlen() away.  */
> +      return private_lookup_attribute (attr_ns, attr_name,
> +				       attr_ns_len, attr_len, list);
> +    }
> +}
> +
>   /* Given an attribute name ATTR_NAME and a list of attributes LIST,
>      return a pointer to the attribute's list first element if the attribute
>      starts with ATTR_NAME.  ATTR_NAME must be in the form 'text' (not
> --- gcc/attribs.cc.jj	2022-05-17 09:01:11.988655748 +0200
> +++ gcc/attribs.cc	2022-09-22 10:54:44.693705319 +0200
> @@ -1639,6 +1639,36 @@ remove_attribute (const char *attr_name,
>     return list;
>   }
>   
> +/* Similarly but also match namespace on the removed attributes.  */
> +
> +tree
> +remove_attribute (const char *attr_ns, const char *attr_name, tree list)
> +{
> +  tree *p;
> +  gcc_checking_assert (attr_name[0] != '_');
> +  gcc_checking_assert (attr_ns == NULL || attr_ns[0] != '_');
> +
> +  for (p = &list; *p;)
> +    {
> +      tree l = *p;
> +
> +      tree attr = get_attribute_name (l);
> +      if (is_attribute_p (attr_name, attr))
> +	{
> +	  tree ns = get_attribute_namespace (l);
> +	  if ((ns == NULL_TREE && attr_ns == NULL)
> +	      || (ns && attr_ns && is_attribute_p (attr_ns, ns)))
> +	    {
> +	      *p = TREE_CHAIN (l);
> +	      continue;
> +	    }
> +	}
> +      p = &TREE_CHAIN (l);
> +    }
> +
> +  return list;
> +}
> +
>   /* Return an attribute list that is the union of a1 and a2.  */
>   
>   tree
> @@ -2033,6 +2063,39 @@ private_lookup_attribute (const char *at
>         list = TREE_CHAIN (list);
>       }
>   
> +  return list;
> +}
> +
> +/* Similarly but with also attribute namespace.  */
> +
> +tree
> +private_lookup_attribute (const char *attr_ns, const char *attr_name,
> +			  size_t attr_ns_len, size_t attr_len, tree list)
> +{
> +  while (list)
> +    {
> +      tree attr = get_attribute_name (list);
> +      size_t ident_len = IDENTIFIER_LENGTH (attr);
> +      if (cmp_attribs (attr_name, attr_len, IDENTIFIER_POINTER (attr),
> +		       ident_len))
> +	{
> +	  tree ns = get_attribute_namespace (list);
> +	  if (ns == NULL_TREE)
> +	    {
> +	      if (attr_ns == NULL)
> +		break;
> +	    }
> +	  else if (attr_ns)
> +	    {
> +	      ident_len = IDENTIFIER_LENGTH (ns);
> +	      if (cmp_attribs (attr_ns, attr_ns_len, IDENTIFIER_POINTER (ns),
> +			       ident_len))
> +		break;
> +	    }
> +	}
> +      list = TREE_CHAIN (list);
> +    }
> +
>     return list;
>   }
>   
> --- gcc/doc/extend.texi.jj	2022-09-22 00:14:19.265091892 +0200
> +++ gcc/doc/extend.texi	2022-09-22 00:24:01.048179249 +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-09-22 00:14:18.695099644 +0200
> +++ gcc/c-family/c-attribs.cc	2022-09-22 00:24:01.074178895 +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-09-22 00:14:18.743098991 +0200
> +++ gcc/c-family/c-lex.cc	2022-09-22 00:24:01.074178895 +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-09-22 00:14:18.798098243 +0200
> +++ gcc/c/c-parser.cc	2022-09-22 11:20:07.661847350 +0200
> @@ -1808,6 +1808,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
> @@ -2022,6 +2062,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
> @@ -2039,13 +2087,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)
> @@ -2145,6 +2195,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;
> @@ -4583,7 +4636,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;
> @@ -4602,6 +4656,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)))
>       {
> @@ -4638,6 +4693,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,
> @@ -4721,7 +4793,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))
> @@ -4967,9 +5041,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);
> @@ -6248,8 +6326,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))
> @@ -6266,9 +6357,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-09-22 00:14:18.970095904 +0200
> +++ gcc/cp/cp-tree.h	2022-09-22 00:24:01.094178623 +0200
> @@ -8235,6 +8235,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);
>   
> --- gcc/cp/name-lookup.h.jj	2022-09-22 00:14:19.088094299 +0200
> +++ gcc/cp/name-lookup.h	2022-09-22 00:24:01.094178623 +0200
> @@ -212,7 +212,8 @@ enum scope_kind {
>   			explicit specialization is introduced by
>   			"template <>", this scope is always empty.  */
>     sk_transaction,    /* A synchronized or atomic statement.  */
> -  sk_omp	     /* An OpenMP structured block.  */
> +  sk_omp,	     /* An OpenMP structured block.  */
> +  sk_assume	     /* The scope of assume attribute.  */
>   };
>   
>   struct GTY(()) cp_class_binding {
> --- gcc/cp/parser.cc.jj	2022-09-22 00:14:19.126093782 +0200
> +++ gcc/cp/parser.cc	2022-09-22 11:10:57.813376547 +0200
> @@ -2251,7 +2251,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
> @@ -8542,10 +8542,27 @@ 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)
> +	      {
> +		enum scope_kind prev_kind = sk_block;
> +		if (current_binding_level)
> +		  {
> +		    prev_kind = current_binding_level->kind;
> +		    current_binding_level->kind = sk_assume;
> +		  }
> +		/* Parse conditional-expression.  */

Let's factor this out of here and cp_parser_constant_expression rather 
than duplicate it.

> +		expr = cp_parser_binary_expression (parser, 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))
> +		  expr = cp_parser_question_colon_clause (parser, expr);
> +		if (current_binding_level)
> +		  current_binding_level->kind = prev_kind;
> +	      }
> +	    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 ();
> @@ -12625,6 +12642,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))
>   	{
> @@ -12648,7 +12668,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,
> @@ -12840,6 +12860,8 @@ cp_parser_expression_statement (cp_parse
>   	}
>       }
>   
> +  attr = process_stmt_assume_attribute (attr, statement, loc);
> +
>     /* Handle [[fallthrough]];.  */
>     if (attribute_fallthrough_p (attr))
>       {
> @@ -28971,6 +28993,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,
> @@ -29222,6 +29246,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))
> @@ -29266,8 +29293,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-09-22 00:14:18.929096461 +0200
> +++ gcc/cp/cp-gimplify.cc	2022-09-22 11:01:38.217041306 +0200
> @@ -3064,6 +3064,49 @@ 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)
> +	{

Need auto_diagnostic_group.

> +	  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/decl.cc.jj	2022-09-22 00:14:55.478599363 +0200
> +++ gcc/cp/decl.cc	2022-09-22 00:24:01.121178256 +0200
> @@ -223,6 +223,7 @@ struct GTY((for_user)) named_label_entry
>     bool in_transaction_scope;
>     bool in_constexpr_if;
>     bool in_consteval_if;
> +  bool in_assume;

I think it would be better to reject jumps into statement-expressions 
like the C front-end.

>   };
>   
>   #define named_labels cp_function_chain->x_named_labels
> @@ -543,6 +544,8 @@ poplevel_named_label_1 (named_label_entr
>   	    ent->in_constexpr_if = true;
>   	  else if (level_for_consteval_if (bl->level_chain))
>   	    ent->in_consteval_if = true;
> +	  else if (bl->level_chain->kind == sk_assume)
> +	    ent->in_assume = true;
>   	  break;
>   	default:
>   	  break;
> @@ -3487,7 +3490,7 @@ check_previous_goto_1 (tree decl, cp_bin
>     bool complained = false;
>     int identified = 0;
>     bool saw_eh = false, saw_omp = false, saw_tm = false, saw_cxif = false;
> -  bool saw_ceif = false;
> +  bool saw_ceif = false, saw_assume = false;
>   
>     if (exited_omp)
>       {
> @@ -3573,6 +3576,11 @@ check_previous_goto_1 (tree decl, cp_bin
>   	      loc = EXPR_LOCATION (b->level_chain->this_entity);
>   	      saw_ceif = true;
>   	    }
> +	  else if (!saw_assume && b->level_chain->kind == sk_assume)
> +	    {
> +	      inf = G_("  enters %<assume%> attribute condition");
> +	      saw_assume = true;
> +	    }
>   	  break;
>   
>   	default:
> @@ -3650,12 +3658,13 @@ check_goto (tree decl)
>   
>     if (ent->in_try_scope || ent->in_catch_scope || ent->in_transaction_scope
>         || ent->in_constexpr_if || ent->in_consteval_if
> -      || ent->in_omp_scope || !vec_safe_is_empty (ent->bad_decls))
> +      || ent->in_omp_scope || ent->in_assume
> +      || !vec_safe_is_empty (ent->bad_decls))
>       {
>         diagnostic_t diag_kind = DK_PERMERROR;
>         if (ent->in_try_scope || ent->in_catch_scope || ent->in_constexpr_if
>   	  || ent->in_consteval_if || ent->in_transaction_scope
> -	  || ent->in_omp_scope)
> +	  || ent->in_omp_scope || ent->in_assume)
>   	diag_kind = DK_ERROR;
>         complained = identify_goto (decl, DECL_SOURCE_LOCATION (decl),
>   				  &input_location, diag_kind);
> @@ -3703,6 +3712,8 @@ check_goto (tree decl)
>   	inform (input_location, "  enters %<constexpr if%> statement");
>         else if (ent->in_consteval_if)
>   	inform (input_location, "  enters %<consteval if%> statement");
> +      else if (ent->in_assume)
> +	inform (input_location, "  enters %<assume%> attribute condition");
>       }
>   
>     if (ent->in_omp_scope)
> --- gcc/cp/constexpr.cc.jj	2022-09-22 00:14:18.879097141 +0200
> +++ gcc/cp/constexpr.cc	2022-09-22 00:24:01.123178229 +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)						\
> @@ -1837,6 +1838,33 @@ 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 (simple_operand_p_2 (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)
> +		error_at (EXPR_LOCATION (t),
> +			  "failed %<assume%> attribute assumption");

Maybe share some code for explaining the failure with finish_static_assert?

> +	      *non_constant_p = true;
> +	      return t;
> +	    }
> +	}
> +      return void_node;
> +
>       case IFN_ADD_OVERFLOW:
>         opcode = PLUS_EXPR;
>         break;
> @@ -8706,6 +8734,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-09-22 00:14:19.185092980 +0200
> +++ gcc/cp/pt.cc	2022-09-22 00:24:01.150177861 +0200
> @@ -21152,6 +21152,34 @@ 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
> +		{
> +		  if (!type_dependent_expression_p ((*call_args)[0]))
> +		    (*call_args)[0]
> +		      = contextual_conv_bool ((*call_args)[0],
> +					      tf_warning_or_error);
> +		  if (error_operand_p ((*call_args)[0]))
> +		    {
> +		      ret = error_mark_node;
> +		      break;
> +		    }
> +		  ret = build_call_expr_internal_loc (EXPR_LOCATION (t),
> +						      IFN_ASSUME,
> +						      void_type_node, 1,
> +						      (*call_args)[0]);
> +		  RETURN (ret);
> +		}
> +	      break;
> +
>   	    default:
>   	      /* Unsupported internal function with arguments.  */
>   	      gcc_unreachable ();
> --- gcc/testsuite/gcc.dg/attr-assume-1.c.jj	2022-09-22 00:24:01.151177848 +0200
> +++ gcc/testsuite/gcc.dg/attr-assume-1.c	2022-09-22 00:24:01.151177848 +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-09-22 00:24:01.151177848 +0200
> +++ gcc/testsuite/gcc.dg/attr-assume-2.c	2022-09-22 00:24:01.151177848 +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-09-22 00:24:01.151177848 +0200
> +++ gcc/testsuite/gcc.dg/attr-assume-3.c	2022-09-22 00:24:01.151177848 +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-09-22 00:14:19.718085731 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C	2022-09-22 00:24:01.176177508 +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 != 201811
> +#  error "__cpp_char8_t != 201811"
> +#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 != 201811
> -#  error "__cpp_char8_t != 201811"
> -#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-09-22 00:14:19.703085935 +0200
> +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2022-09-22 00:24:01.196177236 +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 != 201811
> +#  error "__cpp_char8_t != 201811"
> +#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 != 201811
> -#  error "__cpp_char8_t != 201811"
> -#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
> @@ -563,3 +565,15 @@
>   #elif __cpp_named_character_escapes != 202207
>   #  error "__cpp_named_character_escapes != 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-09-22 00:24:01.196177236 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume1.C	2022-09-22 00:24:01.196177236 +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-09-22 00:24:01.214176991 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume2.C	2022-09-22 00:24:01.196177236 +0200
> @@ -0,0 +1,72 @@
> +// 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
> +  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> ();
> --- gcc/testsuite/g++.dg/cpp23/attr-assume3.C.jj	2022-09-22 00:24:01.214176991 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume3.C	2022-09-22 00:24:01.214176991 +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-09-22 00:24:01.214176991 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume4.C	2022-09-22 00:24:01.214176991 +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> ();
> --- gcc/testsuite/g++.dg/cpp23/attr-assume5.C.jj	2022-09-22 00:24:01.214176991 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume5.C	2022-09-22 00:24:01.214176991 +0200
> @@ -0,0 +1,43 @@
> +// P1774R8 - Portable assumptions
> +// { dg-do compile { target c++11 } }
> +// { dg-options "" }
> +
> +void
> +foo (int x)
> +{
> +  if (x == 1)
> +    goto l1;						// { dg-message "from here" }
> +  else if (x == 2)
> +    goto l2;						// { dg-message "from here" }
> +  else if (x == 3)
> +    goto l3;						// { dg-message "from here" }
> +  [[assume (({ l0:; if (x == 0) goto l0; true; }))]];
> +  [[assume (({ if (x == 0) throw 1; true; }))]];
> +  [[assume (({ l1:; true; }))]];			// { dg-error "jump to label 'l1'" }
> +							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> +  [[gnu::assume (({ l2:; true; }))]];			// { dg-error "jump to label 'l2'" }
> +							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> +  __attribute__((assume (({ l3:; true; }))));		// { dg-error "jump to label 'l3'" }
> +							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> +  [[assume (({ l4:; true; }))]];			// { dg-error "jump to label 'l4'" }
> +  [[gnu::assume (({ l5:; true; }))]];			// { dg-error "jump to label 'l5'" }
> +  __attribute__((assume (({ l6:; true; }))));		// { dg-error "jump to label 'l6'" }
> +  switch (x)
> +    {
> +    case 7:
> +      [[assume (({ case 8:; true; }))]];		// { dg-error "jump to case label" }
> +							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> +      [[assume (({ default:; true; }))]];		// { dg-error "jump to case label" }
> +							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> +      break;
> +    }
> +  if (x == 4)
> +    goto l4;						// { dg-message "from here" }
> +							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> +  else if (x == 5)
> +    goto l5;						// { dg-message "from here" }
> +							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> +  else if (x == 6)
> +    goto l6;						// { dg-message "from here" }
> +							// { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> +}
> 
> 	Jakub
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH] c++: Disallow jumps into statement expressions
  2022-09-30 20:39 ` Jason Merrill
@ 2022-10-02 11:35   ` 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
  1 sibling, 1 reply; 9+ messages in thread
From: Jakub Jelinek @ 2022-10-02 11:35 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

On Fri, Sep 30, 2022 at 04:39:25PM -0400, Jason Merrill wrote:
> > --- gcc/cp/decl.cc.jj	2022-09-22 00:14:55.478599363 +0200
> > +++ gcc/cp/decl.cc	2022-09-22 00:24:01.121178256 +0200
> > @@ -223,6 +223,7 @@ struct GTY((for_user)) named_label_entry
> >     bool in_transaction_scope;
> >     bool in_constexpr_if;
> >     bool in_consteval_if;
> > +  bool in_assume;
> 
> I think it would be better to reject jumps into statement-expressions like
> the C front-end.

Ok, here is a self-contained patch that does that.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2022-10-01  Jakub Jelinek  <jakub@redhat.com>

	* cp-tree.h (BCS_STMT_EXPR): New enumerator.
	* name-lookup.h (enum scope_kind): Add sk_stmt_expr.
	* name-lookup.cc (begin_scope): Handle sk_stmt_expr like sk_block.
	* semantics.cc (begin_compound_stmt): For BCS_STMT_EXPR use
	sk_stmt_expr.
	* parser.cc (cp_parser_statement_expr): Use BCS_STMT_EXPR instead of
	BCS_NORMAL.
	* decl.cc (struct named_label_entry): Add in_stmt_expr.
	(poplevel_named_label_1): Handle sk_stmt_expr.
	(check_previous_goto_1): Diagnose entering of statement expression.
	(check_goto): Likewise.

	* g++.dg/ext/stmtexpr24.C: New test.

--- gcc/cp/cp-tree.h.jj	2022-09-30 18:38:55.351607176 +0200
+++ gcc/cp/cp-tree.h	2022-10-01 13:06:20.731720730 +0200
@@ -7599,7 +7599,8 @@ enum {
   BCS_NO_SCOPE = 1,
   BCS_TRY_BLOCK = 2,
   BCS_FN_BODY = 4,
-  BCS_TRANSACTION = 8
+  BCS_TRANSACTION = 8,
+  BCS_STMT_EXPR = 16
 };
 extern tree begin_compound_stmt			(unsigned int);
 
--- gcc/cp/name-lookup.h.jj	2022-09-23 09:02:31.103668514 +0200
+++ gcc/cp/name-lookup.h	2022-10-01 13:37:50.158404107 +0200
@@ -200,6 +200,7 @@ enum scope_kind {
 			init-statement.  */
   sk_cond,	     /* The scope of the variable declared in the condition
 			of an if or switch statement.  */
+  sk_stmt_expr,	     /* GNU statement expression block.  */
   sk_function_parms, /* The scope containing function parameters.  */
   sk_class,	     /* The scope containing the members of a class.  */
   sk_scoped_enum,    /* The scope containing the enumerators of a C++11
--- gcc/cp/name-lookup.cc.jj	2022-09-13 09:21:28.123540623 +0200
+++ gcc/cp/name-lookup.cc	2022-10-01 13:37:26.383732959 +0200
@@ -4296,6 +4296,7 @@ begin_scope (scope_kind kind, tree entit
     case sk_scoped_enum:
     case sk_transaction:
     case sk_omp:
+    case sk_stmt_expr:
       scope->keep = keep_next_level_flag;
       break;
 
--- gcc/cp/semantics.cc.jj	2022-09-30 18:38:50.337675080 +0200
+++ gcc/cp/semantics.cc	2022-10-01 13:09:34.958970367 +0200
@@ -1761,6 +1761,8 @@ begin_compound_stmt (unsigned int flags)
 	sk = sk_try;
       else if (flags & BCS_TRANSACTION)
 	sk = sk_transaction;
+      else if (flags & BCS_STMT_EXPR)
+	sk = sk_stmt_expr;
       r = do_pushlevel (sk);
     }
 
--- gcc/cp/parser.cc.jj	2022-09-30 18:38:55.374606864 +0200
+++ gcc/cp/parser.cc	2022-10-01 13:08:27.367927479 +0200
@@ -5272,7 +5272,7 @@ cp_parser_statement_expr (cp_parser *par
   /* Start the statement-expression.  */
   tree expr = begin_stmt_expr ();
   /* Parse the compound-statement.  */
-  cp_parser_compound_statement (parser, expr, BCS_NORMAL, false);
+  cp_parser_compound_statement (parser, expr, BCS_STMT_EXPR, false);
   /* Finish up.  */
   expr = finish_stmt_expr (expr, false);
   /* Consume the ')'.  */
--- gcc/cp/decl.cc.jj	2022-09-27 08:27:47.671428567 +0200
+++ gcc/cp/decl.cc	2022-10-01 13:14:57.990434730 +0200
@@ -223,6 +223,7 @@ struct GTY((for_user)) named_label_entry
   bool in_transaction_scope;
   bool in_constexpr_if;
   bool in_consteval_if;
+  bool in_stmt_expr;
 };
 
 #define named_labels cp_function_chain->x_named_labels
@@ -538,6 +539,9 @@ poplevel_named_label_1 (named_label_entr
 	case sk_transaction:
 	  ent->in_transaction_scope = true;
 	  break;
+	case sk_stmt_expr:
+	  ent->in_stmt_expr = true;
+	  break;
 	case sk_block:
 	  if (level_for_constexpr_if (bl->level_chain))
 	    ent->in_constexpr_if = true;
@@ -3487,7 +3491,7 @@ check_previous_goto_1 (tree decl, cp_bin
   bool complained = false;
   int identified = 0;
   bool saw_eh = false, saw_omp = false, saw_tm = false, saw_cxif = false;
-  bool saw_ceif = false;
+  bool saw_ceif = false, saw_se = false;
 
   if (exited_omp)
     {
@@ -3560,6 +3564,12 @@ check_previous_goto_1 (tree decl, cp_bin
 	  saw_tm = true;
 	  break;
 
+	case sk_stmt_expr:
+	  if (!saw_se)
+	    inf = G_("  enters statement expression");
+	  saw_se = true;
+	  break;
+
 	case sk_block:
 	  if (!saw_cxif && level_for_constexpr_if (b->level_chain))
 	    {
@@ -3650,12 +3660,13 @@ check_goto (tree decl)
 
   if (ent->in_try_scope || ent->in_catch_scope || ent->in_transaction_scope
       || ent->in_constexpr_if || ent->in_consteval_if
-      || ent->in_omp_scope || !vec_safe_is_empty (ent->bad_decls))
+      || ent->in_omp_scope || ent->in_stmt_expr
+      || !vec_safe_is_empty (ent->bad_decls))
     {
       diagnostic_t diag_kind = DK_PERMERROR;
       if (ent->in_try_scope || ent->in_catch_scope || ent->in_constexpr_if
 	  || ent->in_consteval_if || ent->in_transaction_scope
-	  || ent->in_omp_scope)
+	  || ent->in_omp_scope || ent->in_stmt_expr)
 	diag_kind = DK_ERROR;
       complained = identify_goto (decl, DECL_SOURCE_LOCATION (decl),
 				  &input_location, diag_kind);
@@ -3703,6 +3714,8 @@ check_goto (tree decl)
 	inform (input_location, "  enters %<constexpr if%> statement");
       else if (ent->in_consteval_if)
 	inform (input_location, "  enters %<consteval if%> statement");
+      else if (ent->in_stmt_expr)
+	inform (input_location, "  enters statement expression");
     }
 
   if (ent->in_omp_scope)
--- gcc/testsuite/g++.dg/ext/stmtexpr24.C.jj	2022-10-01 13:34:01.471565458 +0200
+++ gcc/testsuite/g++.dg/ext/stmtexpr24.C	2022-10-01 13:39:56.078662362 +0200
@@ -0,0 +1,27 @@
+// { dg-do compile }
+// { dg-options "" }
+
+void
+foo (int x)
+{
+  bool a = false;
+  if (x == 1)
+    goto l1;						// { dg-message "from here" }
+  a = ({ l0:; if (x == 0) goto l0; true; });
+  a = ({ if (x == 0) throw 1; true; });
+  a = ({ l1:; true; });					// { dg-error "jump to label 'l1'" }
+							// { dg-message "enters statement expression" "" { target *-*-* } .-1 }
+  a = ({ l2:; true; });					// { dg-error "jump to label 'l2'" }
+  switch (x)
+    {
+    case 2:
+      a = ({ case 3:; true; });				// { dg-error "jump to case label" }
+							// { dg-message "enters statement expression" "" { target *-*-* } .-1 }
+      a = ({ default:; true; });			// { dg-error "jump to case label" }
+							// { dg-message "enters statement expression" "" { target *-*-* } .-1 }
+      break;
+    }
+  if (x == 4)
+    goto l2;						// { dg-message "from here" }
+							// { dg-message "enters statement expression" "" { target *-*-* } .-1 }
+}


	Jakub


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] c++: Disallow jumps into statement expressions
  2022-10-02 11:35   ` [PATCH] c++: Disallow jumps into statement expressions Jakub Jelinek
@ 2022-10-03 15:02     ` Jason Merrill
  0 siblings, 0 replies; 9+ messages in thread
From: Jason Merrill @ 2022-10-03 15:02 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 10/2/22 07:35, Jakub Jelinek wrote:
> On Fri, Sep 30, 2022 at 04:39:25PM -0400, Jason Merrill wrote:
>>> --- gcc/cp/decl.cc.jj	2022-09-22 00:14:55.478599363 +0200
>>> +++ gcc/cp/decl.cc	2022-09-22 00:24:01.121178256 +0200
>>> @@ -223,6 +223,7 @@ struct GTY((for_user)) named_label_entry
>>>      bool in_transaction_scope;
>>>      bool in_constexpr_if;
>>>      bool in_consteval_if;
>>> +  bool in_assume;
>>
>> I think it would be better to reject jumps into statement-expressions like
>> the C front-end.
> 
> Ok, here is a self-contained patch that does that.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK, thanks.

> 2022-10-01  Jakub Jelinek  <jakub@redhat.com>
> 
> 	* cp-tree.h (BCS_STMT_EXPR): New enumerator.
> 	* name-lookup.h (enum scope_kind): Add sk_stmt_expr.
> 	* name-lookup.cc (begin_scope): Handle sk_stmt_expr like sk_block.
> 	* semantics.cc (begin_compound_stmt): For BCS_STMT_EXPR use
> 	sk_stmt_expr.
> 	* parser.cc (cp_parser_statement_expr): Use BCS_STMT_EXPR instead of
> 	BCS_NORMAL.
> 	* decl.cc (struct named_label_entry): Add in_stmt_expr.
> 	(poplevel_named_label_1): Handle sk_stmt_expr.
> 	(check_previous_goto_1): Diagnose entering of statement expression.
> 	(check_goto): Likewise.
> 
> 	* g++.dg/ext/stmtexpr24.C: New test.
> 
> --- gcc/cp/cp-tree.h.jj	2022-09-30 18:38:55.351607176 +0200
> +++ gcc/cp/cp-tree.h	2022-10-01 13:06:20.731720730 +0200
> @@ -7599,7 +7599,8 @@ enum {
>     BCS_NO_SCOPE = 1,
>     BCS_TRY_BLOCK = 2,
>     BCS_FN_BODY = 4,
> -  BCS_TRANSACTION = 8
> +  BCS_TRANSACTION = 8,
> +  BCS_STMT_EXPR = 16
>   };
>   extern tree begin_compound_stmt			(unsigned int);
>   
> --- gcc/cp/name-lookup.h.jj	2022-09-23 09:02:31.103668514 +0200
> +++ gcc/cp/name-lookup.h	2022-10-01 13:37:50.158404107 +0200
> @@ -200,6 +200,7 @@ enum scope_kind {
>   			init-statement.  */
>     sk_cond,	     /* The scope of the variable declared in the condition
>   			of an if or switch statement.  */
> +  sk_stmt_expr,	     /* GNU statement expression block.  */
>     sk_function_parms, /* The scope containing function parameters.  */
>     sk_class,	     /* The scope containing the members of a class.  */
>     sk_scoped_enum,    /* The scope containing the enumerators of a C++11
> --- gcc/cp/name-lookup.cc.jj	2022-09-13 09:21:28.123540623 +0200
> +++ gcc/cp/name-lookup.cc	2022-10-01 13:37:26.383732959 +0200
> @@ -4296,6 +4296,7 @@ begin_scope (scope_kind kind, tree entit
>       case sk_scoped_enum:
>       case sk_transaction:
>       case sk_omp:
> +    case sk_stmt_expr:
>         scope->keep = keep_next_level_flag;
>         break;
>   
> --- gcc/cp/semantics.cc.jj	2022-09-30 18:38:50.337675080 +0200
> +++ gcc/cp/semantics.cc	2022-10-01 13:09:34.958970367 +0200
> @@ -1761,6 +1761,8 @@ begin_compound_stmt (unsigned int flags)
>   	sk = sk_try;
>         else if (flags & BCS_TRANSACTION)
>   	sk = sk_transaction;
> +      else if (flags & BCS_STMT_EXPR)
> +	sk = sk_stmt_expr;
>         r = do_pushlevel (sk);
>       }
>   
> --- gcc/cp/parser.cc.jj	2022-09-30 18:38:55.374606864 +0200
> +++ gcc/cp/parser.cc	2022-10-01 13:08:27.367927479 +0200
> @@ -5272,7 +5272,7 @@ cp_parser_statement_expr (cp_parser *par
>     /* Start the statement-expression.  */
>     tree expr = begin_stmt_expr ();
>     /* Parse the compound-statement.  */
> -  cp_parser_compound_statement (parser, expr, BCS_NORMAL, false);
> +  cp_parser_compound_statement (parser, expr, BCS_STMT_EXPR, false);
>     /* Finish up.  */
>     expr = finish_stmt_expr (expr, false);
>     /* Consume the ')'.  */
> --- gcc/cp/decl.cc.jj	2022-09-27 08:27:47.671428567 +0200
> +++ gcc/cp/decl.cc	2022-10-01 13:14:57.990434730 +0200
> @@ -223,6 +223,7 @@ struct GTY((for_user)) named_label_entry
>     bool in_transaction_scope;
>     bool in_constexpr_if;
>     bool in_consteval_if;
> +  bool in_stmt_expr;
>   };
>   
>   #define named_labels cp_function_chain->x_named_labels
> @@ -538,6 +539,9 @@ poplevel_named_label_1 (named_label_entr
>   	case sk_transaction:
>   	  ent->in_transaction_scope = true;
>   	  break;
> +	case sk_stmt_expr:
> +	  ent->in_stmt_expr = true;
> +	  break;
>   	case sk_block:
>   	  if (level_for_constexpr_if (bl->level_chain))
>   	    ent->in_constexpr_if = true;
> @@ -3487,7 +3491,7 @@ check_previous_goto_1 (tree decl, cp_bin
>     bool complained = false;
>     int identified = 0;
>     bool saw_eh = false, saw_omp = false, saw_tm = false, saw_cxif = false;
> -  bool saw_ceif = false;
> +  bool saw_ceif = false, saw_se = false;
>   
>     if (exited_omp)
>       {
> @@ -3560,6 +3564,12 @@ check_previous_goto_1 (tree decl, cp_bin
>   	  saw_tm = true;
>   	  break;
>   
> +	case sk_stmt_expr:
> +	  if (!saw_se)
> +	    inf = G_("  enters statement expression");
> +	  saw_se = true;
> +	  break;
> +
>   	case sk_block:
>   	  if (!saw_cxif && level_for_constexpr_if (b->level_chain))
>   	    {
> @@ -3650,12 +3660,13 @@ check_goto (tree decl)
>   
>     if (ent->in_try_scope || ent->in_catch_scope || ent->in_transaction_scope
>         || ent->in_constexpr_if || ent->in_consteval_if
> -      || ent->in_omp_scope || !vec_safe_is_empty (ent->bad_decls))
> +      || ent->in_omp_scope || ent->in_stmt_expr
> +      || !vec_safe_is_empty (ent->bad_decls))
>       {
>         diagnostic_t diag_kind = DK_PERMERROR;
>         if (ent->in_try_scope || ent->in_catch_scope || ent->in_constexpr_if
>   	  || ent->in_consteval_if || ent->in_transaction_scope
> -	  || ent->in_omp_scope)
> +	  || ent->in_omp_scope || ent->in_stmt_expr)
>   	diag_kind = DK_ERROR;
>         complained = identify_goto (decl, DECL_SOURCE_LOCATION (decl),
>   				  &input_location, diag_kind);
> @@ -3703,6 +3714,8 @@ check_goto (tree decl)
>   	inform (input_location, "  enters %<constexpr if%> statement");
>         else if (ent->in_consteval_if)
>   	inform (input_location, "  enters %<consteval if%> statement");
> +      else if (ent->in_stmt_expr)
> +	inform (input_location, "  enters statement expression");
>       }
>   
>     if (ent->in_omp_scope)
> --- gcc/testsuite/g++.dg/ext/stmtexpr24.C.jj	2022-10-01 13:34:01.471565458 +0200
> +++ gcc/testsuite/g++.dg/ext/stmtexpr24.C	2022-10-01 13:39:56.078662362 +0200
> @@ -0,0 +1,27 @@
> +// { dg-do compile }
> +// { dg-options "" }
> +
> +void
> +foo (int x)
> +{
> +  bool a = false;
> +  if (x == 1)
> +    goto l1;						// { dg-message "from here" }
> +  a = ({ l0:; if (x == 0) goto l0; true; });
> +  a = ({ if (x == 0) throw 1; true; });
> +  a = ({ l1:; true; });					// { dg-error "jump to label 'l1'" }
> +							// { dg-message "enters statement expression" "" { target *-*-* } .-1 }
> +  a = ({ l2:; true; });					// { dg-error "jump to label 'l2'" }
> +  switch (x)
> +    {
> +    case 2:
> +      a = ({ case 3:; true; });				// { dg-error "jump to case label" }
> +							// { dg-message "enters statement expression" "" { target *-*-* } .-1 }
> +      a = ({ default:; true; });			// { dg-error "jump to case label" }
> +							// { dg-message "enters statement expression" "" { target *-*-* } .-1 }
> +      break;
> +    }
> +  if (x == 4)
> +    goto l2;						// { dg-message "from here" }
> +							// { dg-message "enters statement expression" "" { target *-*-* } .-1 }
> +}
> 
> 
> 	Jakub
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH] c++, c, v2: Implement C++23 P1774R8 - Portable assumptions [PR106654]
  2022-09-30 20:39 ` Jason Merrill
  2022-10-02 11:35   ` [PATCH] c++: Disallow jumps into statement expressions Jakub Jelinek
@ 2022-10-03 19:22   ` Jakub Jelinek
  2022-10-04 10:20     ` Jakub Jelinek
  2022-10-04 20:42     ` Jason Merrill
  1 sibling, 2 replies; 9+ messages in thread
From: Jakub Jelinek @ 2022-10-03 19:22 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

On Fri, Sep 30, 2022 at 04:39:25PM -0400, Jason Merrill wrote:
> > 	* fold-const.h (simple_operand_p_2): Declare.
> 
> This needs a better name if it's going to be a public interface.
> 
> The usage also needs rationale for why this is the right predicate for
> assume, rather than just no-side-effects.  Surely the latter is right for
> constexpr, at least?

You're right that for the constexpr case !TREE_SIDE_EFFECTS is all we need,
including const/pure function calls.
For the gimplification case, TREE_SIDE_EFFECTS isn't good enough.
TREE_SIDE_EFFECTS is documented as:
/* In any expression, decl, or constant, nonzero means it has side effects or
   reevaluation of the whole expression could produce a different value.
   This is set if any subexpression is a function call, a side effect or a
   reference to a volatile variable.  In a ..._DECL, this is set only if the
   declaration said `volatile'.  This will never be set for a constant.  */
so !TREE_SIDE_EFFECTS expressions can be safely evaluated multiple times
instead of just once.
But we need more than that, we need basically the same requirements as
when trying to hoist an expression from inside of if (0) block to before
that block (or just any conditional guarded block where we don't know the
condition value).  And so we need to ensure that we don't get any traps,
raise exceptions etc. or do anything else with observable effects.
And on top of that, we'd better limit it to something small, because
if we have a condition with hundreds of non-side-effect operations in it,
it will affect inlining limits and we'd need to trust that DCE will clean up
everything as unused.
> 
> Let's factor this out of here and cp_parser_constant_expression rather than
> duplicate it.

Done.

> > +  for (; attr; attr = lookup_attribute ("gnu", "assume", TREE_CHAIN (attr)))
> > +    {
> > +      tree args = TREE_VALUE (attr);
> > +      int nargs = list_length (args);
> > +      if (nargs != 1)
> > +	{
> 
> Need auto_diagnostic_group.

Added (and while playing with finish_static_assert noticed that
it doesn't use that either).
Now that I look, attribs.cc (decl_attributes) doesn't do that either,
will test a separate patch for that.

> > +  bool in_assume;
> 
> I think it would be better to reject jumps into statement-expressions like
> the C front-end.

Already committed, thanks for the review.

> > +	      if (!*non_constant_p && !ctx->quiet)
> > +		error_at (EXPR_LOCATION (t),
> > +			  "failed %<assume%> attribute assumption");
> 
> Maybe share some code for explaining the failure with finish_static_assert?

I couldn't share the find_failing_clause stuff (but fortunately it is
short), because it should call different function to evaluate it, but I can
share the reporting.

Here is a lightly tested updated patch which I'll bootstrap/regtest tonight.

2022-10-03  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.
	* 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.
	* 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.
	* parser.cc (assume_attr): New enumerator.
	(cp_parser_parenthesized_expression_list): Handle assume_attr.
	(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_assume_clause_r, find_failing_assume_clause): New
	functions.
	(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.
	(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-09-23 09:02:31.516662825 +0200
+++ gcc/internal-fn.def	2022-10-03 18:31:16.124032648 +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-09-23 09:02:31.529662647 +0200
+++ gcc/internal-fn.h	2022-10-03 18:31:16.124032648 +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-03 18:00:58.590660495 +0200
+++ gcc/internal-fn.cc	2022-10-03 18:31:16.125032634 +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-01 13:01:34.229752667 +0200
+++ gcc/gimplify.cc	2022-10-03 19:23:47.279117497 +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-09-23 09:02:31.360664974 +0200
+++ gcc/fold-const.h	2022-10-03 19:20:05.159136771 +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-09-23 09:02:31.353665070 +0200
+++ gcc/fold-const.cc	2022-10-03 19:23:16.032542215 +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/attribs.h.jj	2022-09-23 09:02:30.701674050 +0200
+++ gcc/attribs.h	2022-10-03 18:31:16.130032566 +0200
@@ -82,6 +82,10 @@ extern tree merge_type_attributes (tree,
 
 extern tree remove_attribute (const char *, tree);
 
+/* Similarly but also with specific attribute namespace.  */
+
+extern tree remove_attribute (const char *, const char *, tree);
+
 /* Given two attributes lists, return a list of their union.  */
 
 extern tree merge_attributes (tree, tree);
@@ -113,6 +117,10 @@ extern int attribute_list_contained (con
    for size.  */
 extern tree private_lookup_attribute (const char *attr_name, size_t attr_len,
 				      tree list);
+extern tree private_lookup_attribute (const char *attr_ns,
+				      const char *attr_name,
+				      size_t attr_ns_len, size_t attr_len,
+				      tree list);
 
 extern unsigned decls_mismatched_attributes (tree, tree, tree,
 					     const char* const[],
@@ -209,6 +217,36 @@ lookup_attribute (const char *attr_name,
     }
 }
 
+/* Similar to lookup_attribute, but also match the attribute namespace.  */
+
+static inline tree
+lookup_attribute (const char *attr_ns, const char *attr_name, tree list)
+{
+  if (CHECKING_P && attr_name[0] != '_')
+    {
+      size_t attr_len = strlen (attr_name);
+      gcc_checking_assert (!canonicalize_attr_name (attr_name, attr_len));
+    }
+  if (CHECKING_P && attr_ns && attr_ns[0] != '_')
+    {
+      size_t attr_ns_len = strlen (attr_ns);
+      gcc_checking_assert (!canonicalize_attr_name (attr_ns, attr_ns_len));
+    }
+  /* In most cases, list is NULL_TREE.  */
+  if (list == NULL_TREE)
+    return NULL_TREE;
+  else
+    {
+      size_t attr_ns_len = attr_ns ? strlen (attr_ns) : 0;
+      size_t attr_len = strlen (attr_name);
+      /* Do the strlen() before calling the out-of-line implementation.
+	 In most cases attr_name is a string constant, and the compiler
+	 will optimize the strlen() away.  */
+      return private_lookup_attribute (attr_ns, attr_name,
+				       attr_ns_len, attr_len, list);
+    }
+}
+
 /* Given an attribute name ATTR_NAME and a list of attributes LIST,
    return a pointer to the attribute's list first element if the attribute
    starts with ATTR_NAME.  ATTR_NAME must be in the form 'text' (not
--- gcc/attribs.cc.jj	2022-09-23 09:10:12.551309187 +0200
+++ gcc/attribs.cc	2022-10-03 18:31:16.130032566 +0200
@@ -1642,6 +1642,36 @@ remove_attribute (const char *attr_name,
   return list;
 }
 
+/* Similarly but also match namespace on the removed attributes.  */
+
+tree
+remove_attribute (const char *attr_ns, const char *attr_name, tree list)
+{
+  tree *p;
+  gcc_checking_assert (attr_name[0] != '_');
+  gcc_checking_assert (attr_ns == NULL || attr_ns[0] != '_');
+
+  for (p = &list; *p;)
+    {
+      tree l = *p;
+
+      tree attr = get_attribute_name (l);
+      if (is_attribute_p (attr_name, attr))
+	{
+	  tree ns = get_attribute_namespace (l);
+	  if ((ns == NULL_TREE && attr_ns == NULL)
+	      || (ns && attr_ns && is_attribute_p (attr_ns, ns)))
+	    {
+	      *p = TREE_CHAIN (l);
+	      continue;
+	    }
+	}
+      p = &TREE_CHAIN (l);
+    }
+
+  return list;
+}
+
 /* Return an attribute list that is the union of a1 and a2.  */
 
 tree
@@ -2036,6 +2066,39 @@ private_lookup_attribute (const char *at
       list = TREE_CHAIN (list);
     }
 
+  return list;
+}
+
+/* Similarly but with also attribute namespace.  */
+
+tree
+private_lookup_attribute (const char *attr_ns, const char *attr_name,
+			  size_t attr_ns_len, size_t attr_len, tree list)
+{
+  while (list)
+    {
+      tree attr = get_attribute_name (list);
+      size_t ident_len = IDENTIFIER_LENGTH (attr);
+      if (cmp_attribs (attr_name, attr_len, IDENTIFIER_POINTER (attr),
+		       ident_len))
+	{
+	  tree ns = get_attribute_namespace (list);
+	  if (ns == NULL_TREE)
+	    {
+	      if (attr_ns == NULL)
+		break;
+	    }
+	  else if (attr_ns)
+	    {
+	      ident_len = IDENTIFIER_LENGTH (ns);
+	      if (cmp_attribs (attr_ns, attr_ns_len, IDENTIFIER_POINTER (ns),
+			       ident_len))
+		break;
+	    }
+	}
+      list = TREE_CHAIN (list);
+    }
+
   return list;
 }
 
--- gcc/doc/extend.texi.jj	2022-09-23 09:02:31.298665828 +0200
+++ gcc/doc/extend.texi	2022-10-03 18:31:16.195031679 +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-09-23 09:02:30.733673609 +0200
+++ gcc/c-family/c-attribs.cc	2022-10-03 18:31:16.196031665 +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-03 18:00:52.696739992 +0200
+++ gcc/c-family/c-lex.cc	2022-10-03 18:31:16.197031652 +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-09-30 18:38:50.326675229 +0200
+++ gcc/c/c-parser.cc	2022-10-03 18:31:16.200031611 +0200
@@ -1819,6 +1819,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
@@ -2033,6 +2073,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
@@ -2050,13 +2098,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)
@@ -2156,6 +2206,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;
@@ -4594,7 +4647,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;
@@ -4613,6 +4667,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)))
     {
@@ -4649,6 +4704,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,
@@ -4732,7 +4804,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))
@@ -4978,9 +5052,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);
@@ -6260,8 +6338,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))
@@ -6278,9 +6369,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-03 18:04:30.165806063 +0200
+++ gcc/cp/cp-tree.h	2022-10-03 20:30:43.606740699 +0200
@@ -7714,6 +7714,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);
@@ -8241,6 +8242,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);
 
--- gcc/cp/parser.cc.jj	2022-10-03 18:04:30.190805728 +0200
+++ gcc/cp/parser.cc	2022-10-03 19:41:32.099672997 +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
@@ -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);
 
 	    if (wrap_locations_p)
 	      expr.maybe_add_location_wrapper ();
@@ -10310,7 +10312,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 +10380,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 +10727,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 +12520,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 +12546,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 +12738,8 @@ cp_parser_expression_statement (cp_parse
 	}
     }
 
+  attr = process_stmt_assume_attribute (attr, statement, loc);
+
   /* Handle [[fallthrough]];.  */
   if (attribute_fallthrough_p (attr))
     {
@@ -28876,6 +28898,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 +29151,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 +29198,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-02 16:39:42.714535406 +0200
+++ gcc/cp/cp-gimplify.cc	2022-10-03 19:42:37.279789628 +0200
@@ -3079,6 +3079,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-02 16:39:42.657536172 +0200
+++ gcc/cp/constexpr.cc	2022-10-03 20:34:16.467862147 +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,47 @@ 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_assume_clause_r (constexpr_ctx *ctx, tree expr)
+{
+  if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR)
+    {
+      /* First check the left side...  */
+      tree e = find_failing_assume_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_assume_clause_r (ctx, TREE_OPERAND (expr, 1));
+      return e;
+    }
+  tree e = contextual_conv_bool (expr, tf_none);
+  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);
+  if (integer_zerop (e))
+    /* This is the failing clause.  */
+    return expr;
+  return NULL_TREE;
+}
+
+/* Wrapper for find_failing_assume_clause_r.  */
+
+static tree
+find_failing_assume_clause (constexpr_ctx *ctx, tree expr)
+{
+  if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR)
+    if (tree e = find_failing_assume_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 +1879,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_assume_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 +8790,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-03 18:00:58.585660563 +0200
+++ gcc/cp/pt.cc	2022-10-03 18:31:16.213031434 +0200
@@ -21163,6 +21163,34 @@ 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
+		{
+		  if (!type_dependent_expression_p ((*call_args)[0]))
+		    (*call_args)[0]
+		      = contextual_conv_bool ((*call_args)[0],
+					      tf_warning_or_error);
+		  if (error_operand_p ((*call_args)[0]))
+		    {
+		      ret = error_mark_node;
+		      break;
+		    }
+		  ret = build_call_expr_internal_loc (EXPR_LOCATION (t),
+						      IFN_ASSUME,
+						      void_type_node, 1,
+						      (*call_args)[0]);
+		  RETURN (ret);
+		}
+	      break;
+
 	    default:
 	      /* Unsupported internal function with arguments.  */
 	      gcc_unreachable ();
--- gcc/cp/semantics.cc.jj	2022-10-03 18:04:30.183805822 +0200
+++ gcc/cp/semantics.cc	2022-10-03 20:24:19.807930896 +0200
@@ -11206,6 +11206,33 @@ find_failing_clause (tree expr)
   return expr;
 }
 
+/* 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).  */
+
+void
+diagnose_failing_condition (tree bad, location_t cloc, bool show_expr_p)
+{
+  /* 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))))
+    {
+      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);
+}
+
 /* Build a STATIC_ASSERT for a static assertion with the condition
    CONDITION and the message text MESSAGE.  LOCATION is the location
    of the static assertion in the source code.  When MEMBER_P, this
@@ -11274,8 +11301,8 @@ finish_static_assert (tree condition, tr
 	  /* 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)
@@ -11284,21 +11311,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-03 18:31:16.213031434 +0200
+++ gcc/testsuite/gcc.dg/attr-assume-1.c	2022-10-03 18:31:16.213031434 +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-03 18:31:16.213031434 +0200
+++ gcc/testsuite/gcc.dg/attr-assume-2.c	2022-10-03 18:31:16.213031434 +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-03 18:31:16.213031434 +0200
+++ gcc/testsuite/gcc.dg/attr-assume-3.c	2022-10-03 18:31:16.213031434 +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-09-26 20:02:45.899351135 +0200
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C	2022-10-03 18:32:49.311760844 +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-09-28 13:47:07.609170225 +0200
+++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2022-10-03 18:35:54.584232290 +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-03 18:31:16.253030888 +0200
+++ gcc/testsuite/g++.dg/cpp23/attr-assume1.C	2022-10-03 18:31:16.253030888 +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-03 18:31:16.253030888 +0200
+++ gcc/testsuite/g++.dg/cpp23/attr-assume2.C	2022-10-03 20:41:56.125649436 +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-03 18:31:16.254030874 +0200
+++ gcc/testsuite/g++.dg/cpp23/attr-assume3.C	2022-10-03 18:31:16.254030874 +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-03 18:31:16.254030874 +0200
+++ gcc/testsuite/g++.dg/cpp23/attr-assume4.C	2022-10-03 18:31:16.254030874 +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


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] c++, c, v2: Implement C++23 P1774R8 - Portable assumptions [PR106654]
  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
  1 sibling, 0 replies; 9+ messages in thread
From: Jakub Jelinek @ 2022-10-04 10:20 UTC (permalink / raw)
  To: Jason Merrill, gcc-patches

On Mon, Oct 03, 2022 at 09:22:42PM +0200, Jakub Jelinek via Gcc-patches wrote:
> Here is a lightly tested updated patch which I'll bootstrap/regtest tonight.

Bootstrap/regtest passed on both x86_64-linux and i686-linux.

	Jakub


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] c++, c, v2: Implement C++23 P1774R8 - Portable assumptions [PR106654]
  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
  1 sibling, 1 reply; 9+ messages in thread
From: Jason Merrill @ 2022-10-04 20:42 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 10/3/22 15:22, Jakub Jelinek wrote:
> On Fri, Sep 30, 2022 at 04:39:25PM -0400, Jason Merrill wrote:
>>> 	* fold-const.h (simple_operand_p_2): Declare.
>>
>> This needs a better name if it's going to be a public interface.
>>
>> The usage also needs rationale for why this is the right predicate for
>> assume, rather than just no-side-effects.  Surely the latter is right for
>> constexpr, at least?
> 
> You're right that for the constexpr case !TREE_SIDE_EFFECTS is all we need,
> including const/pure function calls.
> For the gimplification case, TREE_SIDE_EFFECTS isn't good enough.
> TREE_SIDE_EFFECTS is documented as:
> /* In any expression, decl, or constant, nonzero means it has side effects or
>     reevaluation of the whole expression could produce a different value.
>     This is set if any subexpression is a function call, a side effect or a
>     reference to a volatile variable.  In a ..._DECL, this is set only if the
>     declaration said `volatile'.  This will never be set for a constant.  */
> so !TREE_SIDE_EFFECTS expressions can be safely evaluated multiple times
> instead of just once.
> But we need more than that, we need basically the same requirements as
> when trying to hoist an expression from inside of if (0) block to before
> that block (or just any conditional guarded block where we don't know the
> condition value).  And so we need to ensure that we don't get any traps,
> raise exceptions etc. or do anything else with observable effects.
> And on top of that, we'd better limit it to something small, because
> if we have a condition with hundreds of non-side-effect operations in it,
> it will affect inlining limits and we'd need to trust that DCE will clean up
> everything as unused.
>>
>> Let's factor this out of here and cp_parser_constant_expression rather than
>> duplicate it.
> 
> Done.
> 
>>> +  for (; attr; attr = lookup_attribute ("gnu", "assume", TREE_CHAIN (attr)))
>>> +    {
>>> +      tree args = TREE_VALUE (attr);
>>> +      int nargs = list_length (args);
>>> +      if (nargs != 1)
>>> +	{
>>
>> Need auto_diagnostic_group.
> 
> Added (and while playing with finish_static_assert noticed that
> it doesn't use that either).
> Now that I look, attribs.cc (decl_attributes) doesn't do that either,
> will test a separate patch for that.
> 
>>> +  bool in_assume;
>>
>> I think it would be better to reject jumps into statement-expressions like
>> the C front-end.
> 
> Already committed, thanks for the review.
> 
>>> +	      if (!*non_constant_p && !ctx->quiet)
>>> +		error_at (EXPR_LOCATION (t),
>>> +			  "failed %<assume%> attribute assumption");
>>
>> Maybe share some code for explaining the failure with finish_static_assert?
> 
> I couldn't share the find_failing_clause stuff (but fortunately it is
> short), because it should call different function to evaluate it, but I can
> share the reporting.

It could choose which function to call based on whether the 
constexpr_ctx parameter is null?

> Here is a lightly tested updated patch which I'll bootstrap/regtest tonight.
> 
> 2022-10-03  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.
> 	* 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.

> 	* 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.
> 	* parser.cc (assume_attr): New enumerator.
> 	(cp_parser_parenthesized_expression_list): Handle assume_attr.
> 	(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_assume_clause_r, find_failing_assume_clause): New
> 	functions.
> 	(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.
> 	(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-09-23 09:02:31.516662825 +0200
> +++ gcc/internal-fn.def	2022-10-03 18:31:16.124032648 +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-09-23 09:02:31.529662647 +0200
> +++ gcc/internal-fn.h	2022-10-03 18:31:16.124032648 +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-03 18:00:58.590660495 +0200
> +++ gcc/internal-fn.cc	2022-10-03 18:31:16.125032634 +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-01 13:01:34.229752667 +0200
> +++ gcc/gimplify.cc	2022-10-03 19:23:47.279117497 +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-09-23 09:02:31.360664974 +0200
> +++ gcc/fold-const.h	2022-10-03 19:20:05.159136771 +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-09-23 09:02:31.353665070 +0200
> +++ gcc/fold-const.cc	2022-10-03 19:23:16.032542215 +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/attribs.h.jj	2022-09-23 09:02:30.701674050 +0200
> +++ gcc/attribs.h	2022-10-03 18:31:16.130032566 +0200
> @@ -82,6 +82,10 @@ extern tree merge_type_attributes (tree,
>   
>   extern tree remove_attribute (const char *, tree);
>   
> +/* Similarly but also with specific attribute namespace.  */
> +
> +extern tree remove_attribute (const char *, const char *, tree);
> +
>   /* Given two attributes lists, return a list of their union.  */
>   
>   extern tree merge_attributes (tree, tree);
> @@ -113,6 +117,10 @@ extern int attribute_list_contained (con
>      for size.  */
>   extern tree private_lookup_attribute (const char *attr_name, size_t attr_len,
>   				      tree list);
> +extern tree private_lookup_attribute (const char *attr_ns,
> +				      const char *attr_name,
> +				      size_t attr_ns_len, size_t attr_len,
> +				      tree list);
>   
>   extern unsigned decls_mismatched_attributes (tree, tree, tree,
>   					     const char* const[],
> @@ -209,6 +217,36 @@ lookup_attribute (const char *attr_name,
>       }
>   }
>   
> +/* Similar to lookup_attribute, but also match the attribute namespace.  */
> +
> +static inline tree
> +lookup_attribute (const char *attr_ns, const char *attr_name, tree list)
> +{
> +  if (CHECKING_P && attr_name[0] != '_')
> +    {
> +      size_t attr_len = strlen (attr_name);
> +      gcc_checking_assert (!canonicalize_attr_name (attr_name, attr_len));
> +    }
> +  if (CHECKING_P && attr_ns && attr_ns[0] != '_')
> +    {
> +      size_t attr_ns_len = strlen (attr_ns);
> +      gcc_checking_assert (!canonicalize_attr_name (attr_ns, attr_ns_len));
> +    }
> +  /* In most cases, list is NULL_TREE.  */
> +  if (list == NULL_TREE)
> +    return NULL_TREE;
> +  else
> +    {
> +      size_t attr_ns_len = attr_ns ? strlen (attr_ns) : 0;
> +      size_t attr_len = strlen (attr_name);
> +      /* Do the strlen() before calling the out-of-line implementation.
> +	 In most cases attr_name is a string constant, and the compiler
> +	 will optimize the strlen() away.  */
> +      return private_lookup_attribute (attr_ns, attr_name,
> +				       attr_ns_len, attr_len, list);
> +    }
> +}
> +
>   /* Given an attribute name ATTR_NAME and a list of attributes LIST,
>      return a pointer to the attribute's list first element if the attribute
>      starts with ATTR_NAME.  ATTR_NAME must be in the form 'text' (not
> --- gcc/attribs.cc.jj	2022-09-23 09:10:12.551309187 +0200
> +++ gcc/attribs.cc	2022-10-03 18:31:16.130032566 +0200
> @@ -1642,6 +1642,36 @@ remove_attribute (const char *attr_name,
>     return list;
>   }
>   
> +/* Similarly but also match namespace on the removed attributes.  */
> +
> +tree
> +remove_attribute (const char *attr_ns, const char *attr_name, tree list)
> +{
> +  tree *p;
> +  gcc_checking_assert (attr_name[0] != '_');
> +  gcc_checking_assert (attr_ns == NULL || attr_ns[0] != '_');
> +
> +  for (p = &list; *p;)
> +    {
> +      tree l = *p;
> +
> +      tree attr = get_attribute_name (l);
> +      if (is_attribute_p (attr_name, attr))
> +	{
> +	  tree ns = get_attribute_namespace (l);
> +	  if ((ns == NULL_TREE && attr_ns == NULL)
> +	      || (ns && attr_ns && is_attribute_p (attr_ns, ns)))
> +	    {
> +	      *p = TREE_CHAIN (l);
> +	      continue;
> +	    }
> +	}
> +      p = &TREE_CHAIN (l);
> +    }
> +
> +  return list;
> +}
> +
>   /* Return an attribute list that is the union of a1 and a2.  */
>   
>   tree
> @@ -2036,6 +2066,39 @@ private_lookup_attribute (const char *at
>         list = TREE_CHAIN (list);
>       }
>   
> +  return list;
> +}
> +
> +/* Similarly but with also attribute namespace.  */
> +
> +tree
> +private_lookup_attribute (const char *attr_ns, const char *attr_name,
> +			  size_t attr_ns_len, size_t attr_len, tree list)
> +{
> +  while (list)
> +    {
> +      tree attr = get_attribute_name (list);
> +      size_t ident_len = IDENTIFIER_LENGTH (attr);
> +      if (cmp_attribs (attr_name, attr_len, IDENTIFIER_POINTER (attr),
> +		       ident_len))
> +	{
> +	  tree ns = get_attribute_namespace (list);
> +	  if (ns == NULL_TREE)
> +	    {
> +	      if (attr_ns == NULL)
> +		break;
> +	    }
> +	  else if (attr_ns)
> +	    {
> +	      ident_len = IDENTIFIER_LENGTH (ns);
> +	      if (cmp_attribs (attr_ns, attr_ns_len, IDENTIFIER_POINTER (ns),
> +			       ident_len))
> +		break;
> +	    }
> +	}
> +      list = TREE_CHAIN (list);
> +    }
> +
>     return list;
>   }
>   
> --- gcc/doc/extend.texi.jj	2022-09-23 09:02:31.298665828 +0200
> +++ gcc/doc/extend.texi	2022-10-03 18:31:16.195031679 +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-09-23 09:02:30.733673609 +0200
> +++ gcc/c-family/c-attribs.cc	2022-10-03 18:31:16.196031665 +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-03 18:00:52.696739992 +0200
> +++ gcc/c-family/c-lex.cc	2022-10-03 18:31:16.197031652 +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-09-30 18:38:50.326675229 +0200
> +++ gcc/c/c-parser.cc	2022-10-03 18:31:16.200031611 +0200
> @@ -1819,6 +1819,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
> @@ -2033,6 +2073,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
> @@ -2050,13 +2098,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)
> @@ -2156,6 +2206,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;
> @@ -4594,7 +4647,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;
> @@ -4613,6 +4667,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)))
>       {
> @@ -4649,6 +4704,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,
> @@ -4732,7 +4804,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))
> @@ -4978,9 +5052,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);
> @@ -6260,8 +6338,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))
> @@ -6278,9 +6369,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-03 18:04:30.165806063 +0200
> +++ gcc/cp/cp-tree.h	2022-10-03 20:30:43.606740699 +0200
> @@ -7714,6 +7714,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);
> @@ -8241,6 +8242,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);
>   
> --- gcc/cp/parser.cc.jj	2022-10-03 18:04:30.190805728 +0200
> +++ gcc/cp/parser.cc	2022-10-03 19:41:32.099672997 +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
> @@ -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.

>   
>   	    if (wrap_locations_p)
>   	      expr.maybe_add_location_wrapper ();
> @@ -10310,7 +10312,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 +10380,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 +10727,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 +12520,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 +12546,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 +12738,8 @@ cp_parser_expression_statement (cp_parse
>   	}
>       }
>   
> +  attr = process_stmt_assume_attribute (attr, statement, loc);
> +
>     /* Handle [[fallthrough]];.  */
>     if (attribute_fallthrough_p (attr))
>       {
> @@ -28876,6 +28898,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 +29151,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 +29198,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-02 16:39:42.714535406 +0200
> +++ gcc/cp/cp-gimplify.cc	2022-10-03 19:42:37.279789628 +0200
> @@ -3079,6 +3079,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-02 16:39:42.657536172 +0200
> +++ gcc/cp/constexpr.cc	2022-10-03 20:34:16.467862147 +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,47 @@ 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_assume_clause_r (constexpr_ctx *ctx, tree expr)
> +{
> +  if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR)
> +    {
> +      /* First check the left side...  */
> +      tree e = find_failing_assume_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_assume_clause_r (ctx, TREE_OPERAND (expr, 1));
> +      return e;
> +    }
> +  tree e = contextual_conv_bool (expr, tf_none);
> +  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);
> +  if (integer_zerop (e))
> +    /* This is the failing clause.  */
> +    return expr;
> +  return NULL_TREE;
> +}
> +
> +/* Wrapper for find_failing_assume_clause_r.  */
> +
> +static tree
> +find_failing_assume_clause (constexpr_ctx *ctx, tree expr)
> +{
> +  if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR)
> +    if (tree e = find_failing_assume_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 +1879,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_assume_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 +8790,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-03 18:00:58.585660563 +0200
> +++ gcc/cp/pt.cc	2022-10-03 18:31:16.213031434 +0200
> @@ -21163,6 +21163,34 @@ 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
> +		{

Maybe

	tree &arg = (*call_args)[0];

> +		  if (!type_dependent_expression_p ((*call_args)[0]))
> +		    (*call_args)[0]
> +		      = contextual_conv_bool ((*call_args)[0],
> +					      tf_warning_or_error);
> +		  if (error_operand_p ((*call_args)[0]))
> +		    {
> +		      ret = error_mark_node;
> +		      break;
> +		    }
> +		  ret = build_call_expr_internal_loc (EXPR_LOCATION (t),
> +						      IFN_ASSUME,
> +						      void_type_node, 1,
> +						      (*call_args)[0]);
> +		  RETURN (ret);
> +		}
> +	      break;
> +
>   	    default:
>   	      /* Unsupported internal function with arguments.  */
>   	      gcc_unreachable ();
> --- gcc/cp/semantics.cc.jj	2022-10-03 18:04:30.183805822 +0200
> +++ gcc/cp/semantics.cc	2022-10-03 20:24:19.807930896 +0200
> @@ -11206,6 +11206,33 @@ find_failing_clause (tree expr)
>     return expr;
>   }
>   
> +/* 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).  */
> +
> +void
> +diagnose_failing_condition (tree bad, location_t cloc, bool show_expr_p)
> +{
> +  /* 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))))
> +    {
> +      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);
> +}
> +
>   /* Build a STATIC_ASSERT for a static assertion with the condition
>      CONDITION and the message text MESSAGE.  LOCATION is the location
>      of the static assertion in the source code.  When MEMBER_P, this
> @@ -11274,8 +11301,8 @@ finish_static_assert (tree condition, tr
>   	  /* 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)
> @@ -11284,21 +11311,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-03 18:31:16.213031434 +0200
> +++ gcc/testsuite/gcc.dg/attr-assume-1.c	2022-10-03 18:31:16.213031434 +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-03 18:31:16.213031434 +0200
> +++ gcc/testsuite/gcc.dg/attr-assume-2.c	2022-10-03 18:31:16.213031434 +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-03 18:31:16.213031434 +0200
> +++ gcc/testsuite/gcc.dg/attr-assume-3.c	2022-10-03 18:31:16.213031434 +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-09-26 20:02:45.899351135 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C	2022-10-03 18:32:49.311760844 +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-09-28 13:47:07.609170225 +0200
> +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2022-10-03 18:35:54.584232290 +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-03 18:31:16.253030888 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume1.C	2022-10-03 18:31:16.253030888 +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-03 18:31:16.253030888 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume2.C	2022-10-03 20:41:56.125649436 +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-03 18:31:16.254030874 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume3.C	2022-10-03 18:31:16.254030874 +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-03 18:31:16.254030874 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume4.C	2022-10-03 18:31:16.254030874 +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
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH] c++, c, v3: Implement C++23 P1774R8 - Portable assumptions [PR106654]
  2022-10-04 20:42     ` Jason Merrill
@ 2022-10-05  9:55       ` Jakub Jelinek
  2022-10-05 12:33         ` Jason Merrill
  0 siblings, 1 reply; 9+ messages in thread
From: Jakub Jelinek @ 2022-10-05  9:55 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

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?

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


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] c++, c, v3: Implement C++23 P1774R8 - Portable assumptions [PR106654]
  2022-10-05  9:55       ` [PATCH] c++, c, v3: " Jakub Jelinek
@ 2022-10-05 12:33         ` Jason Merrill
  0 siblings, 0 replies; 9+ messages in thread
From: Jason Merrill @ 2022-10-05 12:33 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

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
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2022-10-05 12:33 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-22  9:55 [PATCH] c++, c: Implement C++23 P1774R8 - Portable assumptions [PR106654] 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 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).