--- 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> ();
