2022-10-05 Jakub Jelinek <ja...@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)
}
-/* 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