From: Jason Merrill <jason@redhat.com>
To: Jakub Jelinek <jakub@redhat.com>
Cc: gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++, c: Implement C++23 P1774R8 - Portable assumptions [PR106654]
Date: Fri, 30 Sep 2022 16:39:25 -0400 [thread overview]
Message-ID: <8b360a15-5255-3fbe-16f4-84eaf0fa9612@redhat.com> (raw)
In-Reply-To: <YywxFT4inNz7GujY@tucnak>
On 9/22/22 05:55, Jakub Jelinek wrote:
> Hi!
>
> The following patch implements C++23 P1774R8 - Portable assumptions
> paper, by introducing support for [[assume (cond)]]; attribute for C++.
> In addition to that the patch adds [[gnu::assume (cond)]]; and
> __attribute__((assume (cond))); support to both C and C++.
> As described in C++23, the attribute argument is conditional-expression
> rather than the usual assignment-expression for attribute arguments,
> the condition is contextually converted to bool (for C truthvalue conversion
> is done on it) and is never evaluated at runtime.
> For C++ constant expression evaluation, I only check the simplest conditions
> for undefined behavior, because otherwise I'd need to undo changes to
> *ctx->global which happened during the evaluation (but I believe the spec
> allows that and we can further improve later).
> The patch uses a new internal function, .ASSUME, to hold the condition
> in the FEs. At gimplification time, if the condition is simple/without
> side-effects, it is gimplified as if (cond) ; else __builtin_unreachable ();
> and otherwise for now dropped on the floor. The intent is to incrementally
> outline the conditions into separate artificial functions and use
> .ASSUME further to tell the ranger and perhaps other optimization passes
> about the assumptions, as detailed in the PR.
>
> When implementing it, I found that assume entry hasn't been added to
> https://eel.is/c++draft/cpp.cond#6
> Jonathan said he'll file a NB comment about it, this patch assumes it
> has been added into the table as 202207L when the paper has been voted in.
>
> With the attributes for both C/C++, I'd say we don't need to add
> __builtin_assume with similar purpose, especially when __builtin_assume
> in LLVM is just weird. It is strange for side-effects in function call's
> argument not to be evaluated, and LLVM in that case (annoyingly) warns
> and ignores the side-effects (but doesn't do then anything with it),
> if there are no side-effects, it will work like our
> if (!cond) __builtin_unreachable ();
>
> During bootstrap/regtest, I've discovered a problem with the way we
> handle scoped attributes. For declaration or type attributes for attributes
> we don't know anything about we just don't add them to the declarations or
> types, so later in the FEs and middle-end it is fine to use lookup_attribute
> etc. which just check the attribute name and not namespace because
> non-standard non-GNU attributes just won't show there. But in the case of
> attributes on statements, nothing has filtered out the unknown attributes,
> so with my earlier patch e.g. c-c++-common/Wno-attributes-6.c test failed
> because it uses:
> [[vendor::assume(1 + 1 == 2)]];
> with -Wno-attributes=vendor::assume and lookup_attribute ("assume", )
> finds such attribute and handled it that way.
> So, for those cases, this patch introduces lookup_attribute and
> remove_attribute overloads which specify also the namespace.
> I think the fallthrough, hot, cold, likely, unlikely attribute handling
> will need to use the new APIs too, so that we don't handle
> msft::fallthrough attribute as something we'd know.
Sounds good.
> Earlier version (without the attribs.{h,cc} changes and the 3 argument
> lookup_attribute/remove_attribute uses instead of 2) has been successfully
> bootstrapped/regtested on x86_64-linux and i686-linux with the
> FAIL: c-c++-common/Wno-attributes-6.c -Wc++-compat (test for excess errors)
> regression, ok for trunk if this passes full bootstrap/regtest again
> (note, I've of course checked it already with
> GXX_TESTSUITE_STDS=98,11,14,17,20,2b make check-gcc check-g++ \
> RUNTESTFLAGS="dg.exp='feat* attr-assume* Wno-attrib*'"
> )?
>
> 2022-09-22 Jakub Jelinek <jakub@redhat.com>
>
> PR c++/106654
> gcc/
> * internal-fn.def (ASSUME): New internal function.
> * internal-fn.h (expand_ASSUME): Declare.
> * internal-fn.cc (expand_ASSUME): Define.
> * gimplify.cc (gimplify_call_expr): Gimplify IFN_ASSUME.
> * fold-const.h (simple_operand_p_2): Declare.
This needs a better name if it's going to be a public interface.
The usage also needs rationale for why this is the right predicate for
assume, rather than just no-side-effects. Surely the latter is right
for constexpr, at least?
> * fold-const.cc (simple_operand_p_2): Remove forward declaration.
> No longer static. Adjust function comment and fix a typo in it.
> (simple_operand_p): Adjust function comment.
> * attribs.h (remove_attribute): Declare overload with additional
> attr_ns argument.
> (private_lookup_attribute): Declare overload with additional
> attr_ns and attr_ns_len arguments.
> (lookup_attribute): New overload with additional attr_ns argument.
> * attribs.cc (remove_attribute): New overload with additional
> attr_ns argument.
> (private_lookup_attribute): New overload with additional
> attr_ns and attr_ns_len arguments.
> * doc/extend.texi: Document assume attribute. Move fallthrough
> attribute example to its section.
> gcc/c-family/
> * c-attribs.cc (handle_assume_attribute): New function.
> (c_common_attribute_table): Add entry for assume attribute.
> * c-lex.cc (c_common_has_attribute): Handle
> __have_cpp_attribute (assume).
> gcc/c/
> * c-parser.cc (handle_assume_attribute): New function.
> (c_parser_declaration_or_fndef): Handle assume attribute.
> (c_parser_attribute_arguments): Add assume_attr argument,
> if true, parse first argument as conditional expression.
> (c_parser_gnu_attribute, c_parser_std_attribute): Adjust
> c_parser_attribute_arguments callers.
> (c_parser_statement_after_labels) <case RID_ATTRIBUTE>: Handle
> assume attribute.
> gcc/cp/
> * cp-tree.h (process_stmt_assume_attribute): Implement C++23
> P1774R8 - Portable assumptions. Declare.
> * name-lookup.h (enum scope_kind): Add sk_assume.
> * parser.cc (assume_attr): New enumerator.
> (cp_parser_parenthesized_expression_list): Handle assume_attr.
> (cp_parser_statement): Handle assume attribute.
> (cp_parser_expression_statement): Likewise.
> (cp_parser_gnu_attribute_list): Use assume_attr for assume
> attribute.
> (cp_parser_std_attribute): Likewise. Handle standard assume
> attribute like gnu::assume.
> * cp-gimplify.cc (process_stmt_assume_attribute): New function.
> * decl.cc (struct named_label_entry): Add in_assume member.
> (poplevel_named_label_1): Set it.
> (check_previous_goto_1): Diagnose entering assume attribute
> condition.
> (check_goto): Likewise.
> * constexpr.cc: Include fold-const.h.
> (cxx_eval_internal_function): Handle IFN_ASSUME.
> (potential_constant_expression_1): Likewise.
> * pt.cc (tsubst_copy_and_build): Likewise.
> gcc/testsuite/
> * gcc.dg/attr-assume-1.c: New test.
> * gcc.dg/attr-assume-2.c: New test.
> * gcc.dg/attr-assume-3.c: New test.
> * g++.dg/cpp2a/feat-cxx2a.C: Add colon to C++20 features
> comment, add C++20 attributes comment and move C++20
> new features after the attributes before them.
> * g++.dg/cpp23/feat-cxx2b.C: Likewise. Test
> __has_cpp_attribute(assume).
> * g++.dg/cpp23/attr-assume1.C: New test.
> * g++.dg/cpp23/attr-assume2.C: New test.
> * g++.dg/cpp23/attr-assume3.C: New test.
> * g++.dg/cpp23/attr-assume4.C: New test.
> * g++.dg/cpp23/attr-assume5.C: New test.
>
> --- gcc/internal-fn.def.jj 2022-09-22 00:14:19.568087771 +0200
> +++ gcc/internal-fn.def 2022-09-22 00:24:00.502186674 +0200
> @@ -462,6 +462,10 @@ DEF_INTERNAL_FN (TRAP, ECF_CONST | ECF_L
> | ECF_NOTHROW | ECF_COLD | ECF_LOOPING_CONST_OR_PURE,
> NULL)
>
> +/* [[assume (cond)]]. */
> +DEF_INTERNAL_FN (ASSUME, ECF_CONST | ECF_LEAF | ECF_NOTHROW
> + | ECF_LOOPING_CONST_OR_PURE, NULL)
> +
> #undef DEF_INTERNAL_INT_FN
> #undef DEF_INTERNAL_FLT_FN
> #undef DEF_INTERNAL_FLT_FLOATN_FN
> --- gcc/internal-fn.h.jj 2022-09-22 00:14:19.605087267 +0200
> +++ gcc/internal-fn.h 2022-09-22 00:24:00.502186674 +0200
> @@ -243,6 +243,7 @@ extern void expand_PHI (internal_fn, gca
> extern void expand_SHUFFLEVECTOR (internal_fn, gcall *);
> extern void expand_SPACESHIP (internal_fn, gcall *);
> extern void expand_TRAP (internal_fn, gcall *);
> +extern void expand_ASSUME (internal_fn, gcall *);
>
> extern bool vectorized_internal_fn_supported_p (internal_fn, tree);
>
> --- gcc/internal-fn.cc.jj 2022-09-22 00:14:19.550088015 +0200
> +++ gcc/internal-fn.cc 2022-09-22 00:24:00.910181125 +0200
> @@ -4508,3 +4508,9 @@ expand_TRAP (internal_fn, gcall *)
> {
> expand_builtin_trap ();
> }
> +
> +void
> +expand_ASSUME (internal_fn, gcall *)
> +{
> + gcc_unreachable ();
> +}
> --- gcc/gimplify.cc.jj 2022-09-22 00:14:19.391090178 +0200
> +++ gcc/gimplify.cc 2022-09-22 00:24:00.954180527 +0200
> @@ -3554,6 +3554,25 @@ gimplify_call_expr (tree *expr_p, gimple
> enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
> auto_vec<tree> vargs (nargs);
>
> + if (ifn == IFN_ASSUME)
> + {
> + if (simple_operand_p_2 (CALL_EXPR_ARG (*expr_p, 0)))
> + {
> + /* If the [[assume (cond)]]; condition is simple
> + enough and can be evaluated unconditionally
> + without side-effects, expand it as
> + if (!cond) __builtin_unreachable (); */
> + tree fndecl = builtin_decl_explicit (BUILT_IN_UNREACHABLE);
> + *expr_p = build3 (COND_EXPR, void_type_node,
> + CALL_EXPR_ARG (*expr_p, 0), void_node,
> + build_call_expr_loc (EXPR_LOCATION (*expr_p),
> + fndecl, 0));
> + return GS_OK;
> + }
> + /* FIXME: Otherwise expand it specially. */
> + return GS_ALL_DONE;
> + }
> +
> for (i = 0; i < nargs; i++)
> {
> gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
> --- gcc/fold-const.h.jj 2022-09-22 00:14:19.325091076 +0200
> +++ gcc/fold-const.h 2022-09-22 00:24:00.955180513 +0200
> @@ -215,6 +215,7 @@ extern tree build_range_check (location_
> extern bool merge_ranges (int *, tree *, tree *, int, tree, tree, int,
> tree, tree);
> extern tree sign_bit_p (tree, const_tree);
> +extern bool simple_operand_p_2 (tree);
> extern tree exact_inverse (tree, tree);
> extern bool expr_not_equal_to (tree t, const wide_int &);
> extern tree const_unop (enum tree_code, tree, tree);
> --- gcc/fold-const.cc.jj 2022-09-22 00:14:19.313091239 +0200
> +++ gcc/fold-const.cc 2022-09-22 00:24:00.990180037 +0200
> @@ -130,7 +130,6 @@ static tree eval_subst (location_t, tree
> static tree optimize_bit_field_compare (location_t, enum tree_code,
> tree, tree, tree);
> static bool simple_operand_p (const_tree);
> -static bool simple_operand_p_2 (tree);
> static tree range_binop (enum tree_code, tree, tree, int, tree, int);
> static tree range_predecessor (tree);
> static tree range_successor (tree);
> @@ -4868,8 +4867,8 @@ sign_bit_p (tree exp, const_tree val)
> return NULL_TREE;
> }
>
> -/* Subroutine for fold_truth_andor_1: determine if an operand is simple enough
> - to be evaluated unconditionally. */
> +/* Subroutine for fold_truth_andor_1 and simple_operand_p_2: determine if an
> + operand is simple enough to be evaluated unconditionally. */
>
> static bool
> simple_operand_p (const_tree exp)
> @@ -4897,12 +4896,11 @@ simple_operand_p (const_tree exp)
> && (! TREE_STATIC (exp) || DECL_REGISTER (exp))));
> }
>
> -/* Subroutine for fold_truth_andor: determine if an operand is simple enough
> - to be evaluated unconditionally.
> - I addition to simple_operand_p, we assume that comparisons, conversions,
> +/* Determine if an operand is simple enough to be evaluated unconditionally.
> + In addition to simple_operand_p, we assume that comparisons, conversions,
> and logic-not operations are simple, if their operands are simple, too. */
>
> -static bool
> +bool
> simple_operand_p_2 (tree exp)
> {
> enum tree_code code;
> --- gcc/attribs.h.jj 2022-01-11 23:11:21.543302056 +0100
> +++ gcc/attribs.h 2022-09-22 10:50:46.299971495 +0200
> @@ -82,6 +82,10 @@ extern tree merge_type_attributes (tree,
>
> extern tree remove_attribute (const char *, tree);
>
> +/* Similarly but also with specific attribute namespace. */
> +
> +extern tree remove_attribute (const char *, const char *, tree);
> +
> /* Given two attributes lists, return a list of their union. */
>
> extern tree merge_attributes (tree, tree);
> @@ -113,6 +117,10 @@ extern int attribute_list_contained (con
> for size. */
> extern tree private_lookup_attribute (const char *attr_name, size_t attr_len,
> tree list);
> +extern tree private_lookup_attribute (const char *attr_ns,
> + const char *attr_name,
> + size_t attr_ns_len, size_t attr_len,
> + tree list);
>
> extern unsigned decls_mismatched_attributes (tree, tree, tree,
> const char* const[],
> @@ -209,6 +217,36 @@ lookup_attribute (const char *attr_name,
> }
> }
>
> +/* Similar to lookup_attribute, but also match the attribute namespace. */
> +
> +static inline tree
> +lookup_attribute (const char *attr_ns, const char *attr_name, tree list)
> +{
> + if (CHECKING_P && attr_name[0] != '_')
> + {
> + size_t attr_len = strlen (attr_name);
> + gcc_checking_assert (!canonicalize_attr_name (attr_name, attr_len));
> + }
> + if (CHECKING_P && attr_ns && attr_ns[0] != '_')
> + {
> + size_t attr_ns_len = strlen (attr_ns);
> + gcc_checking_assert (!canonicalize_attr_name (attr_ns, attr_ns_len));
> + }
> + /* In most cases, list is NULL_TREE. */
> + if (list == NULL_TREE)
> + return NULL_TREE;
> + else
> + {
> + size_t attr_ns_len = attr_ns ? strlen (attr_ns) : 0;
> + size_t attr_len = strlen (attr_name);
> + /* Do the strlen() before calling the out-of-line implementation.
> + In most cases attr_name is a string constant, and the compiler
> + will optimize the strlen() away. */
> + return private_lookup_attribute (attr_ns, attr_name,
> + attr_ns_len, attr_len, list);
> + }
> +}
> +
> /* Given an attribute name ATTR_NAME and a list of attributes LIST,
> return a pointer to the attribute's list first element if the attribute
> starts with ATTR_NAME. ATTR_NAME must be in the form 'text' (not
> --- gcc/attribs.cc.jj 2022-05-17 09:01:11.988655748 +0200
> +++ gcc/attribs.cc 2022-09-22 10:54:44.693705319 +0200
> @@ -1639,6 +1639,36 @@ remove_attribute (const char *attr_name,
> return list;
> }
>
> +/* Similarly but also match namespace on the removed attributes. */
> +
> +tree
> +remove_attribute (const char *attr_ns, const char *attr_name, tree list)
> +{
> + tree *p;
> + gcc_checking_assert (attr_name[0] != '_');
> + gcc_checking_assert (attr_ns == NULL || attr_ns[0] != '_');
> +
> + for (p = &list; *p;)
> + {
> + tree l = *p;
> +
> + tree attr = get_attribute_name (l);
> + if (is_attribute_p (attr_name, attr))
> + {
> + tree ns = get_attribute_namespace (l);
> + if ((ns == NULL_TREE && attr_ns == NULL)
> + || (ns && attr_ns && is_attribute_p (attr_ns, ns)))
> + {
> + *p = TREE_CHAIN (l);
> + continue;
> + }
> + }
> + p = &TREE_CHAIN (l);
> + }
> +
> + return list;
> +}
> +
> /* Return an attribute list that is the union of a1 and a2. */
>
> tree
> @@ -2033,6 +2063,39 @@ private_lookup_attribute (const char *at
> list = TREE_CHAIN (list);
> }
>
> + return list;
> +}
> +
> +/* Similarly but with also attribute namespace. */
> +
> +tree
> +private_lookup_attribute (const char *attr_ns, const char *attr_name,
> + size_t attr_ns_len, size_t attr_len, tree list)
> +{
> + while (list)
> + {
> + tree attr = get_attribute_name (list);
> + size_t ident_len = IDENTIFIER_LENGTH (attr);
> + if (cmp_attribs (attr_name, attr_len, IDENTIFIER_POINTER (attr),
> + ident_len))
> + {
> + tree ns = get_attribute_namespace (list);
> + if (ns == NULL_TREE)
> + {
> + if (attr_ns == NULL)
> + break;
> + }
> + else if (attr_ns)
> + {
> + ident_len = IDENTIFIER_LENGTH (ns);
> + if (cmp_attribs (attr_ns, attr_ns_len, IDENTIFIER_POINTER (ns),
> + ident_len))
> + break;
> + }
> + }
> + list = TREE_CHAIN (list);
> + }
> +
> return list;
> }
>
> --- gcc/doc/extend.texi.jj 2022-09-22 00:14:19.265091892 +0200
> +++ gcc/doc/extend.texi 2022-09-22 00:24:01.048179249 +0200
> @@ -9187,6 +9187,20 @@ available for functions (@pxref{Function
> (@pxref{Variable Attributes}), labels (@pxref{Label Attributes}), enumerators
> (@pxref{Enumerator Attributes}), and for types (@pxref{Type Attributes}).
>
> +@table @code
> +@item fallthrough
> +@cindex @code{fallthrough} statement attribute
> +The @code{fallthrough} attribute with a null statement serves as a
> +fallthrough statement. It hints to the compiler that a statement
> +that falls through to another case label, or user-defined label
> +in a switch statement is intentional and thus the
> +@option{-Wimplicit-fallthrough} warning must not trigger. The
> +fallthrough attribute may appear at most once in each attribute
> +list, and may not be mixed with other attributes. It can only
> +be used in a switch statement (the compiler will issue an error
> +otherwise), after a preceding statement and before a logically
> +succeeding case label, or user-defined label.
> +
> This example uses the @code{fallthrough} statement attribute to indicate that
> the @option{-Wimplicit-fallthrough} warning should not be emitted:
>
> @@ -9201,19 +9215,28 @@ switch (cond)
> @}
> @end smallexample
>
> -@table @code
> -@item fallthrough
> -@cindex @code{fallthrough} statement attribute
> -The @code{fallthrough} attribute with a null statement serves as a
> -fallthrough statement. It hints to the compiler that a statement
> -that falls through to another case label, or user-defined label
> -in a switch statement is intentional and thus the
> -@option{-Wimplicit-fallthrough} warning must not trigger. The
> -fallthrough attribute may appear at most once in each attribute
> -list, and may not be mixed with other attributes. It can only
> -be used in a switch statement (the compiler will issue an error
> -otherwise), after a preceding statement and before a logically
> -succeeding case label, or user-defined label.
> +@item assume
> +@cindex @code{assume} statement attribute
> +The @code{assume} attribute with a null statement serves as portable
> +assumption. It should have a single argument, a conditional expression,
> +which is not evaluated. If the argument would evaluate to true
> +at the point where it appears, it has no effect, otherwise there
> +is undefined behavior. This is a GNU variant of the ISO C++23
> +standard @code{assume} attribute, but it can be used in any version of
> +both C and C++.
> +
> +@smallexample
> +int
> +foo (int x, int y)
> +@{
> + __attribute__((assume(x == 42)));
> + __attribute__((assume(++y == 43)));
> + return x + y;
> +@}
> +@end smallexample
> +
> +@code{y} is not actually incremented and the compiler can but does not
> +have to optimize it to just @code{return 42 + 42;}.
>
> @end table
>
> --- gcc/c-family/c-attribs.cc.jj 2022-09-22 00:14:18.695099644 +0200
> +++ gcc/c-family/c-attribs.cc 2022-09-22 00:24:01.074178895 +0200
> @@ -144,6 +144,7 @@ static tree handle_type_generic_attribut
> static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
> static tree handle_alloc_align_attribute (tree *, tree, tree, int, bool *);
> static tree handle_assume_aligned_attribute (tree *, tree, tree, int, bool *);
> +static tree handle_assume_attribute (tree *, tree, tree, int, bool *);
> static tree handle_target_attribute (tree *, tree, tree, int, bool *);
> static tree handle_target_clones_attribute (tree *, tree, tree, int, bool *);
> static tree handle_optimize_attribute (tree *, tree, tree, int, bool *);
> @@ -530,6 +531,8 @@ const struct attribute_spec c_common_att
> handle_designated_init_attribute, NULL },
> { "fallthrough", 0, 0, false, false, false, false,
> handle_fallthrough_attribute, NULL },
> + { "assume", 1, 1, false, false, false, false,
> + handle_assume_attribute, NULL },
> { "patchable_function_entry", 1, 2, true, false, false, false,
> handle_patchable_function_entry_attribute,
> NULL },
> @@ -5738,6 +5741,18 @@ handle_fallthrough_attribute (tree *, tr
> {
> pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored", name);
> *no_add_attrs = true;
> + return NULL_TREE;
> +}
> +
> +/* Handle a "assume" attribute; arguments as in struct
> + attribute_spec.handler. */
> +
> +tree
> +handle_assume_attribute (tree *, tree name, tree, int,
> + bool *no_add_attrs)
> +{
> + pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored", name);
> + *no_add_attrs = true;
> return NULL_TREE;
> }
>
> --- gcc/c-family/c-lex.cc.jj 2022-09-22 00:14:18.743098991 +0200
> +++ gcc/c-family/c-lex.cc 2022-09-22 00:24:01.074178895 +0200
> @@ -378,6 +378,8 @@ c_common_has_attribute (cpp_reader *pfil
> result = 201803;
> else if (is_attribute_p ("nodiscard", attr_name))
> result = 201907;
> + else if (is_attribute_p ("assume", attr_name))
> + result = 202207;
> }
> else
> {
> --- gcc/c/c-parser.cc.jj 2022-09-22 00:14:18.798098243 +0200
> +++ gcc/c/c-parser.cc 2022-09-22 11:20:07.661847350 +0200
> @@ -1808,6 +1808,46 @@ add_debug_begin_stmt (location_t loc)
> add_stmt (stmt);
> }
>
> +/* Helper function for c_parser_declaration_or_fndef and
> + Handle assume attribute(s). */
> +
> +static tree
> +handle_assume_attribute (location_t here, tree attrs, bool nested)
> +{
> + if (nested)
> + for (tree attr = lookup_attribute ("gnu", "assume", attrs); attr;
> + attr = lookup_attribute ("gnu", "assume", TREE_CHAIN (attr)))
> + {
> + tree args = TREE_VALUE (attr);
> + int nargs = list_length (args);
> + if (nargs != 1)
> + {
> + error_at (here, "wrong number of arguments specified "
> + "for %qE attribute",
> + get_attribute_name (attr));
> + inform (here, "expected %i, found %i", 1, nargs);
> + }
> + else
> + {
> + tree arg = TREE_VALUE (args);
> + arg = c_objc_common_truthvalue_conversion (here, arg);
> + arg = c_fully_fold (arg, false, NULL);
> + if (arg != error_mark_node)
> + {
> + tree fn = build_call_expr_internal_loc (here, IFN_ASSUME,
> + void_type_node, 1,
> + arg);
> + add_stmt (fn);
> + }
> + }
> + }
> + else
> + pedwarn (here, OPT_Wattributes,
> + "%<assume%> attribute at top level");
> +
> + return remove_attribute ("gnu", "assume", attrs);
> +}
> +
> /* Parse a declaration or function definition (C90 6.5, 6.7.1, C99
> 6.7, 6.9.1, C11 6.7, 6.9.1). If FNDEF_OK is true, a function definition
> is accepted; otherwise (old-style parameter declarations) only other
> @@ -2022,6 +2062,14 @@ c_parser_declaration_or_fndef (c_parser
> bool auto_type_p = specs->typespec_word == cts_auto_type;
> if (c_parser_next_token_is (parser, CPP_SEMICOLON))
> {
> + bool handled_assume = false;
> + if (specs->typespec_kind == ctsk_none
> + && lookup_attribute ("gnu", "assume", specs->attrs))
> + {
> + handled_assume = true;
> + specs->attrs
> + = handle_assume_attribute (here, specs->attrs, nested);
> + }
> if (auto_type_p)
> error_at (here, "%<__auto_type%> in empty declaration");
> else if (specs->typespec_kind == ctsk_none
> @@ -2039,13 +2087,15 @@ c_parser_declaration_or_fndef (c_parser
> pedwarn (here, OPT_Wattributes,
> "%<fallthrough%> attribute at top level");
> }
> - else if (empty_ok && !(have_attrs
> - && specs->non_std_attrs_seen_p))
> + else if (empty_ok
> + && !(have_attrs && specs->non_std_attrs_seen_p)
> + && !handled_assume)
> shadow_tag (specs);
> else
> {
> shadow_tag_warned (specs, 1);
> - pedwarn (here, 0, "empty declaration");
> + if (!handled_assume)
> + pedwarn (here, 0, "empty declaration");
> }
> c_parser_consume_token (parser);
> if (oacc_routine_data)
> @@ -2145,6 +2195,9 @@ c_parser_declaration_or_fndef (c_parser
> else if (attribute_fallthrough_p (specs->attrs))
> warning_at (here, OPT_Wattributes,
> "%<fallthrough%> attribute not followed by %<;%>");
> + else if (lookup_attribute ("gnu", "assume", specs->attrs))
> + warning_at (here, OPT_Wattributes,
> + "%<assume%> attribute not followed by %<;%>");
>
> pending_xref_error ();
> prefix_attrs = specs->attrs;
> @@ -4583,7 +4636,8 @@ c_parser_gnu_attribute_any_word (c_parse
>
> static tree
> c_parser_attribute_arguments (c_parser *parser, bool takes_identifier,
> - bool require_string, bool allow_empty_args)
> + bool require_string, bool assume_attr,
> + bool allow_empty_args)
> {
> vec<tree, va_gc> *expr_list;
> tree attr_args;
> @@ -4602,6 +4656,7 @@ c_parser_attribute_arguments (c_parser *
> == CPP_CLOSE_PAREN))
> && (takes_identifier
> || (c_dialect_objc ()
> + && !assume_attr
> && c_parser_peek_token (parser)->id_kind
> == C_ID_CLASSNAME)))
> {
> @@ -4638,6 +4693,23 @@ c_parser_attribute_arguments (c_parser *
> tree string = c_parser_string_literal (parser, false, true).value;
> attr_args = build_tree_list (NULL_TREE, string);
> }
> + else if (assume_attr)
> + {
> + tree cond
> + = c_parser_conditional_expression (parser, NULL, NULL_TREE).value;
> + if (!c_parser_next_token_is (parser, CPP_COMMA))
> + attr_args = build_tree_list (NULL_TREE, cond);
> + else
> + {
> + tree tree_list;
> + c_parser_consume_token (parser);
> + expr_list = c_parser_expr_list (parser, false, true,
> + NULL, NULL, NULL, NULL);
> + tree_list = build_tree_list_vec (expr_list);
> + attr_args = tree_cons (NULL_TREE, cond, tree_list);
> + release_tree_vector (expr_list);
> + }
> + }
> else
> {
> expr_list = c_parser_expr_list (parser, false, true,
> @@ -4721,7 +4793,9 @@ c_parser_gnu_attribute (c_parser *parser
> tree attr_args
> = c_parser_attribute_arguments (parser,
> attribute_takes_identifier_p (attr_name),
> - false, true);
> + false,
> + is_attribute_p ("assume", attr_name),
> + true);
>
> attr = build_tree_list (attr_name, attr_args);
> if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
> @@ -4967,9 +5041,13 @@ c_parser_std_attribute (c_parser *parser
> = (ns == NULL_TREE
> && (strcmp (IDENTIFIER_POINTER (name), "deprecated") == 0
> || strcmp (IDENTIFIER_POINTER (name), "nodiscard") == 0));
> + bool assume_attr
> + = (ns != NULL_TREE
> + && strcmp (IDENTIFIER_POINTER (ns), "gnu") == 0
> + && strcmp (IDENTIFIER_POINTER (name), "assume") == 0);
> TREE_VALUE (attribute)
> = c_parser_attribute_arguments (parser, takes_identifier,
> - require_string, false);
> + require_string, assume_attr, false);
> }
> else
> c_parser_balanced_token_sequence (parser);
> @@ -6248,8 +6326,21 @@ c_parser_statement_after_labels (c_parse
> break;
> case RID_ATTRIBUTE:
> {
> - /* Allow '__attribute__((fallthrough));'. */
> + /* Allow '__attribute__((fallthrough));' or
> + '__attribute__((assume(cond)));'. */
> tree attrs = c_parser_gnu_attributes (parser);
> + bool has_assume = lookup_attribute ("assume", attrs);
> + if (has_assume)
> + {
> + if (c_parser_next_token_is (parser, CPP_SEMICOLON))
> + attrs = handle_assume_attribute (loc, attrs, true);
> + else
> + {
> + warning_at (loc, OPT_Wattributes,
> + "%<assume%> attribute not followed by %<;%>");
> + has_assume = false;
> + }
> + }
> if (attribute_fallthrough_p (attrs))
> {
> if (c_parser_next_token_is (parser, CPP_SEMICOLON))
> @@ -6266,9 +6357,13 @@ c_parser_statement_after_labels (c_parse
> "%<fallthrough%> attribute not followed "
> "by %<;%>");
> }
> + else if (has_assume)
> + /* Eat the ';'. */
> + c_parser_consume_token (parser);
> else if (attrs != NULL_TREE)
> - warning_at (loc, OPT_Wattributes, "only attribute %<fallthrough%>"
> - " can be applied to a null statement");
> + warning_at (loc, OPT_Wattributes,
> + "only attribute %<fallthrough%> or %<assume%> can "
> + "be applied to a null statement");
> break;
> }
> default:
> --- gcc/cp/cp-tree.h.jj 2022-09-22 00:14:18.970095904 +0200
> +++ gcc/cp/cp-tree.h 2022-09-22 00:24:01.094178623 +0200
> @@ -8235,6 +8235,7 @@ extern tree predeclare_vla (tree);
> extern void clear_fold_cache (void);
> extern tree lookup_hotness_attribute (tree);
> extern tree process_stmt_hotness_attribute (tree, location_t);
> +extern tree process_stmt_assume_attribute (tree, tree, location_t);
> extern bool simple_empty_class_p (tree, tree, tree_code);
> extern tree fold_builtin_source_location (location_t);
>
> --- gcc/cp/name-lookup.h.jj 2022-09-22 00:14:19.088094299 +0200
> +++ gcc/cp/name-lookup.h 2022-09-22 00:24:01.094178623 +0200
> @@ -212,7 +212,8 @@ enum scope_kind {
> explicit specialization is introduced by
> "template <>", this scope is always empty. */
> sk_transaction, /* A synchronized or atomic statement. */
> - sk_omp /* An OpenMP structured block. */
> + sk_omp, /* An OpenMP structured block. */
> + sk_assume /* The scope of assume attribute. */
> };
>
> struct GTY(()) cp_class_binding {
> --- gcc/cp/parser.cc.jj 2022-09-22 00:14:19.126093782 +0200
> +++ gcc/cp/parser.cc 2022-09-22 11:10:57.813376547 +0200
> @@ -2251,7 +2251,7 @@ static vec<tree, va_gc> *cp_parser_paren
> (cp_parser *, int, bool, bool, bool *, location_t * = NULL,
> bool = false);
> /* Values for the second parameter of cp_parser_parenthesized_expression_list. */
> -enum { non_attr = 0, normal_attr = 1, id_attr = 2 };
> +enum { non_attr = 0, normal_attr = 1, id_attr = 2, assume_attr = 3 };
> static void cp_parser_pseudo_destructor_name
> (cp_parser *, tree, tree *, tree *);
> static cp_expr cp_parser_unary_expression
> @@ -8542,10 +8542,27 @@ cp_parser_parenthesized_expression_list
> }
> else
> {
> - expr
> - = cp_parser_parenthesized_expression_list_elt (parser, cast_p,
> - allow_expansion_p,
> - non_constant_p);
> + if (is_attribute_list == assume_attr)
> + {
> + enum scope_kind prev_kind = sk_block;
> + if (current_binding_level)
> + {
> + prev_kind = current_binding_level->kind;
> + current_binding_level->kind = sk_assume;
> + }
> + /* Parse conditional-expression. */
Let's factor this out of here and cp_parser_constant_expression rather
than duplicate it.
> + expr = cp_parser_binary_expression (parser, false, false,
> + PREC_NOT_OPERATOR, NULL);
> + /* If the next token is a `?' then we're actually looking at
> + a conditional-expression; otherwise we're done. */
> + if (cp_lexer_next_token_is (parser->lexer, CPP_QUERY))
> + expr = cp_parser_question_colon_clause (parser, expr);
> + if (current_binding_level)
> + current_binding_level->kind = prev_kind;
> + }
> + else
> + expr = cp_parser_parenthesized_expression_list_elt
> + (parser, cast_p, allow_expansion_p, non_constant_p);
>
> if (wrap_locations_p)
> expr.maybe_add_location_wrapper ();
> @@ -12625,6 +12642,9 @@ cp_parser_statement (cp_parser* parser,
> /* Look for an expression-statement instead. */
> statement = cp_parser_expression_statement (parser, in_statement_expr);
>
> + std_attrs = process_stmt_assume_attribute (std_attrs, statement,
> + attrs_loc);
> +
> /* Handle [[fallthrough]];. */
> if (attribute_fallthrough_p (std_attrs))
> {
> @@ -12648,7 +12668,7 @@ cp_parser_statement (cp_parser* parser,
> if (statement && STATEMENT_CODE_P (TREE_CODE (statement)))
> SET_EXPR_LOCATION (statement, statement_location);
>
> - /* Allow "[[fallthrough]];", but warn otherwise. */
> + /* Allow "[[fallthrough]];" or "[[assume(cond)]];", but warn otherwise. */
> if (std_attrs != NULL_TREE)
> warning_at (attrs_loc,
> OPT_Wattributes,
> @@ -12840,6 +12860,8 @@ cp_parser_expression_statement (cp_parse
> }
> }
>
> + attr = process_stmt_assume_attribute (attr, statement, loc);
> +
> /* Handle [[fallthrough]];. */
> if (attribute_fallthrough_p (attr))
> {
> @@ -28971,6 +28993,8 @@ cp_parser_gnu_attribute_list (cp_parser*
> vec<tree, va_gc> *vec;
> int attr_flag = (attribute_takes_identifier_p (identifier)
> ? id_attr : normal_attr);
> + if (is_attribute_p ("assume", identifier))
> + attr_flag = assume_attr;
> vec = cp_parser_parenthesized_expression_list
> (parser, attr_flag, /*cast_p=*/false,
> /*allow_expansion_p=*/false,
> @@ -29222,6 +29246,9 @@ cp_parser_std_attribute (cp_parser *pars
> /* C++17 fallthrough attribute is equivalent to GNU's. */
> else if (is_attribute_p ("fallthrough", attr_id))
> TREE_PURPOSE (TREE_PURPOSE (attribute)) = gnu_identifier;
> + /* C++23 assume attribute is equivalent to GNU's. */
> + else if (is_attribute_p ("assume", attr_id))
> + TREE_PURPOSE (TREE_PURPOSE (attribute)) = gnu_identifier;
> /* Transactional Memory TS optimize_for_synchronized attribute is
> equivalent to GNU transaction_callable. */
> else if (is_attribute_p ("optimize_for_synchronized", attr_id))
> @@ -29266,8 +29293,12 @@ cp_parser_std_attribute (cp_parser *pars
> return error_mark_node;
> }
>
> - if (attr_ns == gnu_identifier
> - && attribute_takes_identifier_p (attr_id))
> + if (is_attribute_p ("assume", attr_id)
> + && (attr_ns == NULL_TREE || attr_ns == gnu_identifier))
> + /* The assume attribute needs special handling of the argument. */
> + attr_flag = assume_attr;
> + else if (attr_ns == gnu_identifier
> + && attribute_takes_identifier_p (attr_id))
> /* A GNU attribute that takes an identifier in parameter. */
> attr_flag = id_attr;
>
> --- gcc/cp/cp-gimplify.cc.jj 2022-09-22 00:14:18.929096461 +0200
> +++ gcc/cp/cp-gimplify.cc 2022-09-22 11:01:38.217041306 +0200
> @@ -3064,6 +3064,49 @@ process_stmt_hotness_attribute (tree std
> return std_attrs;
> }
>
> +/* If [[assume (cond)]] appears on this statement, handle it. */
> +
> +tree
> +process_stmt_assume_attribute (tree std_attrs, tree statement,
> + location_t attrs_loc)
> +{
> + if (std_attrs == error_mark_node)
> + return std_attrs;
> + tree attr = lookup_attribute ("gnu", "assume", std_attrs);
> + if (!attr)
> + return std_attrs;
> + /* The next token after the assume attribute is not ';'. */
> + if (statement)
> + {
> + warning_at (attrs_loc, OPT_Wattributes,
> + "%<assume%> attribute not followed by %<;%>");
> + attr = NULL_TREE;
> + }
> + for (; attr; attr = lookup_attribute ("gnu", "assume", TREE_CHAIN (attr)))
> + {
> + tree args = TREE_VALUE (attr);
> + int nargs = list_length (args);
> + if (nargs != 1)
> + {
Need auto_diagnostic_group.
> + error_at (attrs_loc, "wrong number of arguments specified for "
> + "%qE attribute", get_attribute_name (attr));
> + inform (attrs_loc, "expected %i, found %i", 1, nargs);
> + }
> + else
> + {
> + tree arg = TREE_VALUE (args);
> + if (!type_dependent_expression_p (arg))
> + arg = contextual_conv_bool (arg, tf_warning_or_error);
> + if (error_operand_p (arg))
> + continue;
> + statement = build_call_expr_internal_loc (attrs_loc, IFN_ASSUME,
> + void_type_node, 1, arg);
> + finish_expr_stmt (statement);
> + }
> + }
> + return remove_attribute ("gnu", "assume", std_attrs);
> +}
> +
> /* Helper of fold_builtin_source_location, return the
> std::source_location::__impl type after performing verification
> on it. LOC is used for reporting any errors. */
> --- gcc/cp/decl.cc.jj 2022-09-22 00:14:55.478599363 +0200
> +++ gcc/cp/decl.cc 2022-09-22 00:24:01.121178256 +0200
> @@ -223,6 +223,7 @@ struct GTY((for_user)) named_label_entry
> bool in_transaction_scope;
> bool in_constexpr_if;
> bool in_consteval_if;
> + bool in_assume;
I think it would be better to reject jumps into statement-expressions
like the C front-end.
> };
>
> #define named_labels cp_function_chain->x_named_labels
> @@ -543,6 +544,8 @@ poplevel_named_label_1 (named_label_entr
> ent->in_constexpr_if = true;
> else if (level_for_consteval_if (bl->level_chain))
> ent->in_consteval_if = true;
> + else if (bl->level_chain->kind == sk_assume)
> + ent->in_assume = true;
> break;
> default:
> break;
> @@ -3487,7 +3490,7 @@ check_previous_goto_1 (tree decl, cp_bin
> bool complained = false;
> int identified = 0;
> bool saw_eh = false, saw_omp = false, saw_tm = false, saw_cxif = false;
> - bool saw_ceif = false;
> + bool saw_ceif = false, saw_assume = false;
>
> if (exited_omp)
> {
> @@ -3573,6 +3576,11 @@ check_previous_goto_1 (tree decl, cp_bin
> loc = EXPR_LOCATION (b->level_chain->this_entity);
> saw_ceif = true;
> }
> + else if (!saw_assume && b->level_chain->kind == sk_assume)
> + {
> + inf = G_(" enters %<assume%> attribute condition");
> + saw_assume = true;
> + }
> break;
>
> default:
> @@ -3650,12 +3658,13 @@ check_goto (tree decl)
>
> if (ent->in_try_scope || ent->in_catch_scope || ent->in_transaction_scope
> || ent->in_constexpr_if || ent->in_consteval_if
> - || ent->in_omp_scope || !vec_safe_is_empty (ent->bad_decls))
> + || ent->in_omp_scope || ent->in_assume
> + || !vec_safe_is_empty (ent->bad_decls))
> {
> diagnostic_t diag_kind = DK_PERMERROR;
> if (ent->in_try_scope || ent->in_catch_scope || ent->in_constexpr_if
> || ent->in_consteval_if || ent->in_transaction_scope
> - || ent->in_omp_scope)
> + || ent->in_omp_scope || ent->in_assume)
> diag_kind = DK_ERROR;
> complained = identify_goto (decl, DECL_SOURCE_LOCATION (decl),
> &input_location, diag_kind);
> @@ -3703,6 +3712,8 @@ check_goto (tree decl)
> inform (input_location, " enters %<constexpr if%> statement");
> else if (ent->in_consteval_if)
> inform (input_location, " enters %<consteval if%> statement");
> + else if (ent->in_assume)
> + inform (input_location, " enters %<assume%> attribute condition");
> }
>
> if (ent->in_omp_scope)
> --- gcc/cp/constexpr.cc.jj 2022-09-22 00:14:18.879097141 +0200
> +++ gcc/cp/constexpr.cc 2022-09-22 00:24:01.123178229 +0200
> @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.
> #include "opts.h"
> #include "stringpool.h"
> #include "attribs.h"
> +#include "fold-const.h"
>
> static bool verify_constant (tree, bool, bool *, bool *);
> #define VERIFY_CONSTANT(X) \
> @@ -1837,6 +1838,33 @@ cxx_eval_internal_function (const conste
> case IFN_FALLTHROUGH:
> return void_node;
>
> + case IFN_ASSUME:
> + /* For now, restrict constexpr evaluation of [[assume (cond)]]
> + only to the cases which don't have side-effects. Evaluating
> + it even when it does would mean we'd need to somehow undo
> + all the side-effects e.g. in ctx->global->values. */
> + if (simple_operand_p_2 (CALL_EXPR_ARG (t, 0))
> + /* And it needs to be a potential constant expression. */
> + && potential_rvalue_constant_expression (CALL_EXPR_ARG (t, 0)))
> + {
> + constexpr_ctx new_ctx = *ctx;
> + new_ctx.quiet = true;
> + tree arg = CALL_EXPR_ARG (t, 0);
> + bool new_non_constant_p = false, new_overflow_p = false;
> + arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
> + &new_non_constant_p,
> + &new_overflow_p);
> + if (!new_non_constant_p && !new_overflow_p && integer_zerop (arg))
> + {
> + if (!*non_constant_p && !ctx->quiet)
> + error_at (EXPR_LOCATION (t),
> + "failed %<assume%> attribute assumption");
Maybe share some code for explaining the failure with finish_static_assert?
> + *non_constant_p = true;
> + return t;
> + }
> + }
> + return void_node;
> +
> case IFN_ADD_OVERFLOW:
> opcode = PLUS_EXPR;
> break;
> @@ -8706,6 +8734,7 @@ potential_constant_expression_1 (tree t,
> case IFN_UBSAN_BOUNDS:
> case IFN_UBSAN_VPTR:
> case IFN_FALLTHROUGH:
> + case IFN_ASSUME:
> return true;
>
> case IFN_ADD_OVERFLOW:
> --- gcc/cp/pt.cc.jj 2022-09-22 00:14:19.185092980 +0200
> +++ gcc/cp/pt.cc 2022-09-22 00:24:01.150177861 +0200
> @@ -21152,6 +21152,34 @@ tsubst_copy_and_build (tree t,
> break;
> }
>
> + case IFN_ASSUME:
> + gcc_assert (nargs == 1);
> + if (vec_safe_length (call_args) != 1)
> + {
> + error_at (cp_expr_loc_or_input_loc (t),
> + "wrong number of arguments to "
> + "%<assume%> attribute");
> + ret = error_mark_node;
> + }
> + else
> + {
> + if (!type_dependent_expression_p ((*call_args)[0]))
> + (*call_args)[0]
> + = contextual_conv_bool ((*call_args)[0],
> + tf_warning_or_error);
> + if (error_operand_p ((*call_args)[0]))
> + {
> + ret = error_mark_node;
> + break;
> + }
> + ret = build_call_expr_internal_loc (EXPR_LOCATION (t),
> + IFN_ASSUME,
> + void_type_node, 1,
> + (*call_args)[0]);
> + RETURN (ret);
> + }
> + break;
> +
> default:
> /* Unsupported internal function with arguments. */
> gcc_unreachable ();
> --- gcc/testsuite/gcc.dg/attr-assume-1.c.jj 2022-09-22 00:24:01.151177848 +0200
> +++ gcc/testsuite/gcc.dg/attr-assume-1.c 2022-09-22 00:24:01.151177848 +0200
> @@ -0,0 +1,69 @@
> +/* Portable assumptions */
> +/* { dg-do run } */
> +/* { dg-options "-std=c2x" } */
> +
> +int
> +f1 (int i)
> +{
> + [[gnu::assume (i == 42)]];
> + return i;
> +}
> +
> +int
> +f2 (int i)
> +{
> + __attribute__ ((assume (++i == 44)));
> + return i;
> +}
> +
> +int a;
> +int *volatile c;
> +
> +int
> +f3 ()
> +{
> + ++a;
> + return 1;
> +}
> +
> +int
> +f4 (double x)
> +{
> + [[gnu::assume (__builtin_isfinite (x) && x >= 0.0)]];
> + return __builtin_isfinite (__builtin_sqrt (x));
> +}
> +
> +double
> +f5 (double x)
> +{
> + __attribute__((assume (__builtin_isfinite (__builtin_sqrt (x)))));
> + return __builtin_sqrt (x);
> +}
> +
> +int
> +f6 (int x)
> +{
> + [[gnu::assume (x == 93 ? 1 : 0)]];
> + return x;
> +}
> +
> +int
> +main ()
> +{
> + int b = 42;
> + double d = 42.0, e = 43.0;
> + c = &b;
> + [[__gnu__::__assume__ (f3 ())]];
> + if (a)
> + __builtin_abort ();
> + [[gnu::assume (++b == 43)]];
> + if (b != 42 || *c != 42)
> + __builtin_abort ();
> + __attribute__((assume (d < e)));
> + int i = 90, j = 91, k = 92;
> + [[gnu::__assume__ (i == 90), gnu::assume (j <= 91)]] [[gnu::assume (k >= 92)]]
> + ;
> + __attribute__((__assume__ (i == 90), assume (j <= 91))) __attribute__((assume (k >= 92)));
> + if (f6 (93) != 93)
> + __builtin_abort ();
> +}
> --- gcc/testsuite/gcc.dg/attr-assume-2.c.jj 2022-09-22 00:24:01.151177848 +0200
> +++ gcc/testsuite/gcc.dg/attr-assume-2.c 2022-09-22 00:24:01.151177848 +0200
> @@ -0,0 +1,66 @@
> +/* Portable assumptions */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x" } */
> +
> +[[gnu::__assume__ (1)]] void f1 (void); /* { dg-warning "'assume' attribute not followed by ';'" } */
> + /* { dg-warning "'assume' attribute ignored" "" { target *-*-* } .-1 } */
> +typedef int intx [[gnu::assume (1)]]; /* { dg-warning "'assume' attribute ignored" } */
> +[[__gnu__::assume (1)]]; /* { dg-warning "'assume' attribute at top level" } */
> +__attribute__((assume (1))) void f1b ();/* { dg-warning "'assume' attribute not followed by ';'" } */
> + /* { dg-warning "'assume' attribute ignored" "" { target *-*-* } .-1 } */
> +typedef int inty __attribute__((assume (1))); /* { dg-warning "'assume' attribute ignored" } */
> +
> +void
> +foo ()
> +{
> + int i;
> + [[gnu::assume]]; /* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
> + /* { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } */
> + [[gnu::__assume__ ()]]; /* { dg-error "parentheses must be omitted if attribute argument list is empty" } */
> + /* { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 } */
> + /* { dg-message "expected 1, found 0" "" { target *-*-* } .-2 } */
> + [[gnu::assume (1, 1)]]; /* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
> + /* { dg-message "expected 1, found 2" "" { target *-*-* } .-1 } */
> + [[gnu::assume (1)]] i = 1; /* { dg-warning "'assume' attribute ignored" } */
> + [[gnu::assume (i = 1)]]; /* { dg-error "expected" } */
> + /* { dg-warning "'assume' attribute ignored" "" { target *-*-* } .-1 } */
> + __attribute__((assume)); /* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
> + /* { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } */
> + __attribute__((assume ())); /* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
> + /* { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } */
> + __attribute__((assume (1, 1))); /* { dg-error "wrong number of arguments specified for 'assume' attribute" } */
> + /* { dg-message "expected 1, found 2" "" { target *-*-* } .-1 } */
> + __attribute__((assume (i = 1))); /* { dg-error "expected" } */
> +}
> +
> +int
> +f2 (int x)
> +{
> + __asm ("" : "+r" (x));
> + return x;
> +}
> +
> +int
> +f3 (int x)
> +{
> + [[gnu::assume (f2 (42) == 42)]];
> + return x;
> +}
> +
> +int
> +f3a (int x)
> +{
> + __attribute__((assume (f2 (42) == 42)));
> + return x;
> +}
> +
> +struct S {};
> +
> +int
> +f4 ()
> +{
> + struct S s;
> + [[gnu::assume (s)]]; /* { dg-error "used struct type value where scalar is required" } */
> + __attribute__((assume (s))); /* { dg-error "used struct type value where scalar is required" } */
> + return 0;
> +}
> --- gcc/testsuite/gcc.dg/attr-assume-3.c.jj 2022-09-22 00:24:01.151177848 +0200
> +++ gcc/testsuite/gcc.dg/attr-assume-3.c 2022-09-22 00:24:01.151177848 +0200
> @@ -0,0 +1,35 @@
> +/* Portable assumptions */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x" } */
> +
> +void
> +foo (int x)
> +{
> + if (x == 1)
> + goto l1; /* { dg-error "jump into statement expression" } */
> + else if (x == 2)
> + goto l2; /* { dg-error "jump into statement expression" } */
> + else if (x == 3)
> + goto l3; /* { dg-error "jump into statement expression" } */
> + [[gnu::assume (({ l0:; if (x == 0) goto l0; 1; }))]];
> + [[gnu::assume (({ if (x == 0) __builtin_abort (); 1; }))]];
> + [[gnu::assume (({ l1:; 1; }))]]; /* { dg-message "label 'l1' defined here" } */
> + [[gnu::assume (({ l2:; 1; }))]]; /* { dg-message "label 'l2' defined here" } */
> + __attribute__((assume (({ l3:; 1; })))); /* { dg-message "label 'l3' defined here" } */
> + [[gnu::assume (({ l4:; 1; }))]]; /* { dg-message "label 'l4' defined here" } */
> + [[gnu::assume (({ l5:; 1; }))]]; /* { dg-message "label 'l5' defined here" } */
> + __attribute__((assume (({ l6:; 1; })))); /* { dg-message "label 'l6' defined here" } */
> + switch (x) /* { dg-message "switch starts here" } */
> + {
> + case 7:
> + [[gnu::assume (({ case 8:; 1; }))]]; /* { dg-error "switch jumps into statement expression" } */
> + __attribute__((assume (({ default:; 1; })))); /* { dg-error "switch jumps into statement expression" } */
> + break;
> + }
> + if (x == 4)
> + goto l4; /* { dg-error "jump into statement expression" } */
> + else if (x == 5)
> + goto l5; /* { dg-error "jump into statement expression" } */
> + else if (x == 6)
> + goto l6; /* { dg-error "jump into statement expression" } */
> +}
> --- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C.jj 2022-09-22 00:14:19.718085731 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C 2022-09-22 00:24:01.176177508 +0200
> @@ -422,7 +422,7 @@
> # error "__cpp_nontype_template_parameter_auto != 201606"
> #endif
>
> -// C++20 features
> +// C++20 features:
>
> #ifndef __cpp_conditional_explicit
> # error "__cpp_conditional_explicit"
> @@ -460,6 +460,44 @@
> # error "__cpp_aggregate_paren_init != 201902"
> #endif
>
> +#ifndef __cpp_char8_t
> +# error "__cpp_char8_t"
> +#elif __cpp_char8_t != 201811
> +# error "__cpp_char8_t != 201811"
> +#endif
> +
> +#ifndef __cpp_designated_initializers
> +# error "__cpp_designated_initializers"
> +#elif __cpp_designated_initializers != 201707
> +# error "__cpp_designated_initializers != 201707"
> +#endif
> +
> +#ifndef __cpp_constexpr_in_decltype
> +# error "__cpp_constexpr_in_decltype"
> +#elif __cpp_constexpr_in_decltype != 201711
> +# error "__cpp_constexpr_in_decltype != 201711"
> +#endif
> +
> +#ifndef __cpp_consteval
> +# error "__cpp_consteval"
> +#elif __cpp_consteval != 201811
> +# error "__cpp_consteval != 201811"
> +#endif
> +
> +#ifndef __cpp_concepts
> +# error "__cpp_concepts"
> +#elif __cpp_concepts != 202002
> +# error "__cpp_concepts != 202002"
> +#endif
> +
> +#ifndef __cpp_using_enum
> +# error "__cpp_using_enum"
> +#elif __cpp_using_enum != 201907
> +# error "__cpp_using_enum != 201907"
> +#endif
> +
> +// C++20 attributes:
> +
> #ifdef __has_cpp_attribute
>
> # if ! __has_cpp_attribute(maybe_unused)
> @@ -501,39 +539,3 @@
> #else
> # error "__has_cpp_attribute"
> #endif
> -
> -#ifndef __cpp_char8_t
> -# error "__cpp_char8_t"
> -#elif __cpp_char8_t != 201811
> -# error "__cpp_char8_t != 201811"
> -#endif
> -
> -#ifndef __cpp_designated_initializers
> -# error "__cpp_designated_initializers"
> -#elif __cpp_designated_initializers != 201707
> -# error "__cpp_designated_initializers != 201707"
> -#endif
> -
> -#ifndef __cpp_constexpr_in_decltype
> -# error "__cpp_constexpr_in_decltype"
> -#elif __cpp_constexpr_in_decltype != 201711
> -# error "__cpp_constexpr_in_decltype != 201711"
> -#endif
> -
> -#ifndef __cpp_consteval
> -# error "__cpp_consteval"
> -#elif __cpp_consteval != 201811
> -# error "__cpp_consteval != 201811"
> -#endif
> -
> -#ifndef __cpp_concepts
> -# error "__cpp_concepts"
> -#elif __cpp_concepts != 202002
> -# error "__cpp_concepts != 202002"
> -#endif
> -
> -#ifndef __cpp_using_enum
> -# error "__cpp_using_enum"
> -#elif __cpp_using_enum != 201907
> -# error "__cpp_using_enum != 201907"
> -#endif
> --- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj 2022-09-22 00:14:19.703085935 +0200
> +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C 2022-09-22 00:24:01.196177236 +0200
> @@ -422,7 +422,7 @@
> # error "__cpp_nontype_template_parameter_auto != 201606"
> #endif
>
> -// C++20 features
> +// C++20 features:
>
> #ifndef __cpp_conditional_explicit
> # error "__cpp_conditional_explicit"
> @@ -460,6 +460,44 @@
> # error "__cpp_aggregate_paren_init != 201902"
> #endif
>
> +#ifndef __cpp_char8_t
> +# error "__cpp_char8_t"
> +#elif __cpp_char8_t != 201811
> +# error "__cpp_char8_t != 201811"
> +#endif
> +
> +#ifndef __cpp_designated_initializers
> +# error "__cpp_designated_initializers"
> +#elif __cpp_designated_initializers != 201707
> +# error "__cpp_designated_initializers != 201707"
> +#endif
> +
> +#ifndef __cpp_constexpr_in_decltype
> +# error "__cpp_constexpr_in_decltype"
> +#elif __cpp_constexpr_in_decltype != 201711
> +# error "__cpp_constexpr_in_decltype != 201711"
> +#endif
> +
> +#ifndef __cpp_consteval
> +# error "__cpp_consteval"
> +#elif __cpp_consteval != 201811
> +# error "__cpp_consteval != 201811"
> +#endif
> +
> +#ifndef __cpp_concepts
> +# error "__cpp_concepts"
> +#elif __cpp_concepts != 202002
> +# error "__cpp_concepts != 202002"
> +#endif
> +
> +#ifndef __cpp_using_enum
> +# error "__cpp_using_enum"
> +#elif __cpp_using_enum != 201907
> +# error "__cpp_using_enum != 201907"
> +#endif
> +
> +// C++20 attributes:
> +
> #ifdef __has_cpp_attribute
>
> # if ! __has_cpp_attribute(maybe_unused)
> @@ -502,42 +540,6 @@
> # error "__has_cpp_attribute"
> #endif
>
> -#ifndef __cpp_char8_t
> -# error "__cpp_char8_t"
> -#elif __cpp_char8_t != 201811
> -# error "__cpp_char8_t != 201811"
> -#endif
> -
> -#ifndef __cpp_designated_initializers
> -# error "__cpp_designated_initializers"
> -#elif __cpp_designated_initializers != 201707
> -# error "__cpp_designated_initializers != 201707"
> -#endif
> -
> -#ifndef __cpp_constexpr_in_decltype
> -# error "__cpp_constexpr_in_decltype"
> -#elif __cpp_constexpr_in_decltype != 201711
> -# error "__cpp_constexpr_in_decltype != 201711"
> -#endif
> -
> -#ifndef __cpp_consteval
> -# error "__cpp_consteval"
> -#elif __cpp_consteval != 201811
> -# error "__cpp_consteval != 201811"
> -#endif
> -
> -#ifndef __cpp_concepts
> -# error "__cpp_concepts"
> -#elif __cpp_concepts != 202002
> -# error "__cpp_concepts != 202002"
> -#endif
> -
> -#ifndef __cpp_using_enum
> -# error "__cpp_using_enum"
> -#elif __cpp_using_enum != 201907
> -# error "__cpp_using_enum != 201907"
> -#endif
> -
> // C++23 features:
>
> #ifndef __cpp_size_t_suffix
> @@ -563,3 +565,15 @@
> #elif __cpp_named_character_escapes != 202207
> # error "__cpp_named_character_escapes != 202207"
> #endif
> +
> +// C++23 attributes:
> +
> +#ifdef __has_cpp_attribute
> +# if ! __has_cpp_attribute(assume)
> +# error "__has_cpp_attribute(assume)"
> +# elif __has_cpp_attribute(assume) != 202207
> +# error "__has_cpp_attribute(assume) != 202207"
> +# endif
> +#else
> +# error "__has_cpp_attribute"
> +#endif
> --- gcc/testsuite/g++.dg/cpp23/attr-assume1.C.jj 2022-09-22 00:24:01.196177236 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume1.C 2022-09-22 00:24:01.196177236 +0200
> @@ -0,0 +1,191 @@
> +// P1774R8 - Portable assumptions
> +// { dg-do run { target c++11 } }
> +
> +namespace std
> +{
> + constexpr bool
> + isfinite (float x)
> + { return __builtin_isfinite (x); }
> +
> + constexpr bool
> + isfinite (double x)
> + { return __builtin_isfinite (x); }
> +
> + constexpr bool
> + isfinite (long double x)
> + { return __builtin_isfinite (x); }
> +
> + constexpr float
> + sqrt (float x)
> + { return __builtin_sqrtf (x); }
> +
> + constexpr double
> + sqrt (double x)
> + { return __builtin_sqrt (x); }
> +
> + constexpr long double
> + sqrt (long double x)
> + { return __builtin_sqrtl (x); }
> +
> + extern "C" void
> + abort ();
> +}
> +
> +constexpr int
> +f1 (int i)
> +{
> +#if __cpp_constexpr >= 201603L
> + auto f = [=] { [[assume (i == 0)]]; };
> + return sizeof (f);
> +#else
> + return sizeof (int);
> +#endif
> +}
> +
> +void
> +f2 ()
> +{
> + static_assert (f1 (0) >= sizeof (int), "");
> +}
> +
> +int
> +f3 (int i)
> +{
> + [[assume (i == 42)]];
> + return i;
> +}
> +
> +int
> +f4 (int i)
> +{
> + [[assume (++i == 44)]];
> + return i;
> +}
> +
> +int a;
> +int *volatile c;
> +
> +bool
> +f5 ()
> +{
> + ++a;
> + return true;
> +}
> +
> +constexpr int
> +f6 ()
> +{
> +#if __cpp_constexpr >= 201304L
> + [[assume (f5 ())]];
> +#endif
> + return 1;
> +}
> +
> +template <int ...args>
> +bool
> +f7 ()
> +{
> +#if __cpp_fold_expressions >= 201411L
> + [[assume (((args >= 0) && ...))]];
> + return ((args >= 0) && ...);
> +#else
> + return true;
> +#endif
> +}
> +
> +bool
> +f8 (double x)
> +{
> + [[assume (std::isfinite (x) && x >= 0.0)]];
> + return std::isfinite (std::sqrt (x));
> +}
> +
> +double
> +f9 (double x)
> +{
> + [[assume (std::isfinite (std::sqrt (x)))]];
> + return std::sqrt (x);
> +}
> +
> +template <typename T, T N>
> +T
> +f10 (T x)
> +{
> + [[assume (x == N)]];
> + return x;
> +}
> +
> +int
> +f11 (int x)
> +{
> + [[assume (x == 93 ? true : throw 1)]];
> + return x;
> +}
> +
> +constexpr int
> +f12 (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> + [[assume (++x == 43)]];
> +#endif
> + return x;
> +}
> +
> +static_assert (f12 (42) == 42, "");
> +
> +struct S
> +{
> + operator bool () { return true; }
> +};
> +
> +int
> +f13 ()
> +{
> + S s;
> + [[assume (s)]];
> + return 0;
> +}
> +
> +template <typename T>
> +int
> +f14 ()
> +{
> + T t;
> + [[assume (t)]];
> + return 0;
> +}
> +
> +int
> +main ()
> +{
> + int b = 42;
> + double d = 42.0, e = 43.0;
> + c = &b;
> + [[assume (f5 ())]];
> + if (a)
> + std::abort ();
> + [[assume (++b == 43)]];
> + if (b != 42 || *c != 42)
> + std::abort ();
> + static_assert (f6 () == 1, "");
> + if (f6 () != 1)
> + std::abort ();
> + if (a)
> + std::abort ();
> + if (!f7 <0> () || !f7 <1, 2, 3, 4> ())
> + std::abort ();
> + [[assume (d < e)]];
> + if (f10 <int, 45> (45) != 45
> + || f10 <long long, 128LL> (128LL) != 128LL
> +#if __cpp_nontype_template_args >= 201911L
> + || f10 <long double, -42.0L> (-42.0L) != -42.0L
> +#endif
> + || false)
> + std::abort ();
> + int i = 90, j = 91, k = 92;
> + [[assume (i == 90), assume (j <= 91)]] [[assume (k >= 92)]];
> + if (f11 (93) != 93)
> + std::abort ();
> + if (f14 <S> () != 0)
> + std::abort ();
> +}
> --- gcc/testsuite/g++.dg/cpp23/attr-assume2.C.jj 2022-09-22 00:24:01.214176991 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume2.C 2022-09-22 00:24:01.196177236 +0200
> @@ -0,0 +1,72 @@
> +// P1774R8 - Portable assumptions
> +// { dg-do compile { target c++11 } }
> +
> +[[assume (true)]] void f1 (); // { dg-error "'assume' attribute ignored" }
> +typedef int intx [[assume (true)]]; // { dg-error "'assume' attribute ignored" }
> +[[assume (true)]]; // { dg-warning "attribute ignored" }
> +
> +void
> +foo ()
> +{
> + int i;
> + [[assume]]; // { dg-error "wrong number of arguments specified for 'assume' attribute" }
> + // { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
> + [[assume ()]]; // { dg-error "parentheses must be omitted if 'assume' attribute argument list is empty" }
> + // { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 }
> + // { dg-message "expected 1, found 0" "" { target *-*-* } .-2 }
> + [[assume (true, true)]]; // { dg-error "wrong number of arguments specified for 'assume' attribute" }
> + // { dg-message "expected 1, found 2" "" { target *-*-* } .-1 }
> + [[assume (true)]] i = 1; // { dg-warning "'assume' attribute not followed by ';'" }
> + [[assume (throw 1)]]; // { dg-error "expected primary-expression before 'throw'" }
> + [[assume (i = 1)]]; // { dg-error "expected '\\\)' before '=' token" }
> +}
> +
> +constexpr int
> +f2 (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> + [[assume (x == 42)]]; // { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
> +#endif
> + return x;
> +}
> +
> +constexpr int a = f2 (44);
> +
> +int
> +f3 (int x)
> +{
> + __asm ("" : "+r" (x));
> + return x;
> +}
> +
> +constexpr int
> +f4 (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> + [[assume (f3 (42) == 42)]];
> +#endif
> + return x;
> +}
> +
> +static_assert (f4 (42) == 42, "");
> +
> +struct S {};
> +
> +int
> +f5 ()
> +{
> + S s;
> + [[assume (s)]]; // { dg-error "could not convert 's' from 'S' to 'bool'" }
> + return 0;
> +}
> +
> +template <typename T>
> +int
> +f6 ()
> +{
> + T t;
> + [[assume (t)]]; // { dg-error "could not convert 't' from 'S' to 'bool'" }
> + return 0;
> +}
> +
> +int z = f6 <S> ();
> --- gcc/testsuite/g++.dg/cpp23/attr-assume3.C.jj 2022-09-22 00:24:01.214176991 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume3.C 2022-09-22 00:24:01.214176991 +0200
> @@ -0,0 +1,198 @@
> +// P1774R8 - Portable assumptions
> +// { dg-do run { target c++11 } }
> +
> +namespace std
> +{
> + constexpr bool
> + isfinite (float x)
> + { return __builtin_isfinite (x); }
> +
> + constexpr bool
> + isfinite (double x)
> + { return __builtin_isfinite (x); }
> +
> + constexpr bool
> + isfinite (long double x)
> + { return __builtin_isfinite (x); }
> +
> + constexpr float
> + sqrt (float x)
> + { return __builtin_sqrtf (x); }
> +
> + constexpr double
> + sqrt (double x)
> + { return __builtin_sqrt (x); }
> +
> + constexpr long double
> + sqrt (long double x)
> + { return __builtin_sqrtl (x); }
> +
> + extern "C" void
> + abort ();
> +}
> +
> +constexpr int
> +f1 (int i)
> +{
> +#if __cpp_constexpr >= 201603L
> + auto f = [=] { [[__assume__ (i == 0)]]; };
> + return sizeof (f);
> +#else
> + return sizeof (int);
> +#endif
> +}
> +
> +void
> +f2 ()
> +{
> + static_assert (f1 (0) >= sizeof (int), "");
> +}
> +
> +int
> +f3 (int i)
> +{
> + [[gnu::assume (i == 42)]];
> + return i;
> +}
> +
> +int
> +f4 (int i)
> +{
> + __attribute__ ((assume (++i == 44)));
> + return i;
> +}
> +
> +int a;
> +int *volatile c;
> +
> +bool
> +f5 ()
> +{
> + ++a;
> + return true;
> +}
> +
> +constexpr int
> +f6 ()
> +{
> +#if __cpp_constexpr >= 201304L
> + [[__assume__ (f5 ())]];
> +#endif
> + return 1;
> +}
> +
> +template <int ...args>
> +bool
> +f7 ()
> +{
> +#if __cpp_fold_expressions >= 201411L
> + [[__gnu__::__assume__ (((args >= 0) && ...))]];
> + return ((args >= 0) && ...);
> +#else
> + return true;
> +#endif
> +}
> +
> +bool
> +f8 (double x)
> +{
> + [[gnu::assume (std::isfinite (x) && x >= 0.0)]];
> + return std::isfinite (std::sqrt (x));
> +}
> +
> +double
> +f9 (double x)
> +{
> + __attribute__((assume (std::isfinite (std::sqrt (x)))));
> + return std::sqrt (x);
> +}
> +
> +template <typename T, T N>
> +T
> +f10 (T x)
> +{
> + [[__assume__ (x == N)]];
> + return x;
> +}
> +
> +int
> +f11 (int x)
> +{
> + [[gnu::assume (x == 93 ? true : throw 1)]];
> + return x;
> +}
> +
> +constexpr int
> +f12 (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> + __attribute__((assume (++x == 43)));
> +#endif
> + return x;
> +}
> +
> +static_assert (f12 (42) == 42, "");
> +
> +struct S
> +{
> + operator bool () { return true; }
> +};
> +
> +int
> +f13 ()
> +{
> + S s;
> + [[__gnu__::__assume__ (s)]];
> + return 0;
> +}
> +
> +template <typename T>
> +int
> +f14 ()
> +{
> + T t;
> + __attribute__((assume (t)));
> + return 0;
> +}
> +
> +int
> +main ()
> +{
> + int b = 42;
> + double d = 42.0, e = 43.0;
> + c = &b;
> + [[__assume__ (f5 ())]];
> + if (a)
> + std::abort ();
> + [[gnu::assume (++b == 43)]];
> + if (b != 42 || *c != 42)
> + std::abort ();
> + static_assert (f6 () == 1, "");
> + if (f6 () != 1)
> + std::abort ();
> + if (a)
> + std::abort ();
> + if (!f7 <0> () || !f7 <1, 2, 3, 4> ())
> + std::abort ();
> + __attribute__((assume (d < e)));
> + if (f10 <int, 45> (45) != 45
> + || f10 <long long, 128LL> (128LL) != 128LL
> +#if __cpp_nontype_template_args >= 201911L
> + || f10 <long double, -42.0L> (-42.0L) != -42.0L
> +#endif
> + || false)
> + std::abort ();
> + int i = 90, j = 91, k = 92;
> + [[__assume__ (i == 90), gnu::assume (j <= 91)]]
> +#if __cplusplus >= 201703L
> + [[using gnu:assume (k >= 92)]]
> +#else
> + [[gnu::assume (k >= 92)]]
> +#endif
> + ;
> + __attribute__((__assume__ (i == 90), assume (j <= 91))) __attribute__((assume (k >= 92)));
> + if (f11 (93) != 93)
> + std::abort ();
> + if (f14 <S> () != 0)
> + std::abort ();
> +}
> --- gcc/testsuite/g++.dg/cpp23/attr-assume4.C.jj 2022-09-22 00:24:01.214176991 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume4.C 2022-09-22 00:24:01.214176991 +0200
> @@ -0,0 +1,136 @@
> +// P1774R8 - Portable assumptions
> +// { dg-do compile { target c++11 } }
> +
> +[[__assume__ (true)]] void f1 (); // { dg-error "'assume' attribute ignored" }
> +typedef int intx [[__assume__ (true)]]; // { dg-error "'assume' attribute ignored" }
> +[[__assume__ (true)]]; // { dg-warning "attribute ignored" }
> +[[gnu::assume (true)]] void f1a (); // { dg-error "'assume' attribute ignored" }
> +typedef int inty [[gnu::__assume__ (true)]]; // { dg-error "'assume' attribute ignored" }
> +[[__gnu__::assume (true)]]; // { dg-warning "attribute ignored" }
> +__attribute__((assume (true))) void f1b (); // { dg-error "'assume' attribute ignored" }
> +typedef int intz __attribute__((assume (true)));// { dg-error "'assume' attribute ignored" }
> +
> +void
> +foo ()
> +{
> + int i;
> + [[__assume__]]; // { dg-error "wrong number of arguments specified for 'assume' attribute" }
> + // { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
> + [[__assume__ ()]]; // { dg-error "parentheses must be omitted if 'assume' attribute argument list is empty" }
> + // { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 }
> + // { dg-message "expected 1, found 0" "" { target *-*-* } .-2 }
> + [[__assume__ (true, true)]]; // { dg-error "wrong number of arguments specified for 'assume' attribute" }
> + // { dg-message "expected 1, found 2" "" { target *-*-* } .-1 }
> + [[__assume__ (true)]] i = 1; // { dg-warning "'assume' attribute not followed by ';'" }
> + [[__assume__ (throw 1)]]; // { dg-error "expected primary-expression before 'throw'" }
> + [[__assume__ (i = 1)]]; // { dg-error "expected '\\\)' before '=' token" }
> + [[gnu::assume]]; // { dg-error "wrong number of arguments specified for 'assume' attribute" }
> + // { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
> + [[gnu::assume ()]]; // { dg-error "parentheses must be omitted if 'assume' attribute argument list is empty" }
> + // { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 }
> + // { dg-message "expected 1, found 0" "" { target *-*-* } .-2 }
> + [[gnu::assume (true, true)]]; // { dg-error "wrong number of arguments specified for 'assume' attribute" }
> + // { dg-message "expected 1, found 2" "" { target *-*-* } .-1 }
> + [[gnu::assume (true)]] i = 1; // { dg-warning "'assume' attribute not followed by ';'" }
> + [[gnu::assume (throw 1)]]; // { dg-error "expected primary-expression before 'throw'" }
> + [[gnu::assume (i = 1)]]; // { dg-error "expected '\\\)' before '=' token" }
> + __attribute__((assume)); // { dg-error "wrong number of arguments specified for 'assume' attribute" }
> + // { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
> + __attribute__((assume ())); // { dg-error "wrong number of arguments specified for 'assume' attribute" }
> + // { dg-message "expected 1, found 0" "" { target *-*-* } .-1 }
> + __attribute__((assume (true, true))); // { dg-error "wrong number of arguments specified for 'assume' attribute" }
> + // { dg-message "expected 1, found 2" "" { target *-*-* } .-1 }
> + __attribute__((assume (true))) i = 1; // { dg-warning "'assume' attribute not followed by ';'" }
> + __attribute__((assume (throw 1))); // { dg-error "expected primary-expression before 'throw'" }
> + __attribute__((assume (i = 1))); // { dg-error "expected '\\\)' before '=' token" }
> +}
> +
> +constexpr int
> +f2 (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> + [[__assume__ (x == 42)]]; // { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
> +#endif
> + return x;
> +}
> +
> +constexpr int
> +f2a (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> + [[gnu::__assume__ (x == 42)]]; // { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
> +#endif
> + return x;
> +}
> +
> +constexpr int
> +f2b (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> + __attribute__((__assume__ (x == 42)));// { dg-error "failed 'assume' attribute assumption" "" { target c++14 } }
> +#endif
> + return x;
> +}
> +
> +constexpr int a = f2 (44);
> +constexpr int aa = f2a (44);
> +constexpr int ab = f2b (44);
> +
> +int
> +f3 (int x)
> +{
> + __asm ("" : "+r" (x));
> + return x;
> +}
> +
> +constexpr int
> +f4 (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> + [[__assume__ (f3 (42) == 42)]];
> +#endif
> + return x;
> +}
> +
> +constexpr int
> +f4a (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> + [[gnu::assume (f3 (42) == 42)]];
> +#endif
> + return x;
> +}
> +
> +constexpr int
> +f4b (int x)
> +{
> +#if __cpp_constexpr >= 201304L
> + __attribute__((assume (f3 (42) == 42)));
> +#endif
> + return x;
> +}
> +
> +static_assert (f4 (42) == 42, "");
> +static_assert (f4a (42) == 42, "");
> +static_assert (f4b (42) == 42, "");
> +
> +struct S {};
> +
> +int
> +f5 ()
> +{
> + S s;
> + [[gnu::assume (s)]]; // { dg-error "could not convert 's' from 'S' to 'bool'" }
> + return 0;
> +}
> +
> +template <typename T>
> +int
> +f6 ()
> +{
> + T t;
> + __attribute__((assume (t))); // { dg-error "could not convert 't' from 'S' to 'bool'" }
> + return 0;
> +}
> +
> +int z = f6 <S> ();
> --- gcc/testsuite/g++.dg/cpp23/attr-assume5.C.jj 2022-09-22 00:24:01.214176991 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume5.C 2022-09-22 00:24:01.214176991 +0200
> @@ -0,0 +1,43 @@
> +// P1774R8 - Portable assumptions
> +// { dg-do compile { target c++11 } }
> +// { dg-options "" }
> +
> +void
> +foo (int x)
> +{
> + if (x == 1)
> + goto l1; // { dg-message "from here" }
> + else if (x == 2)
> + goto l2; // { dg-message "from here" }
> + else if (x == 3)
> + goto l3; // { dg-message "from here" }
> + [[assume (({ l0:; if (x == 0) goto l0; true; }))]];
> + [[assume (({ if (x == 0) throw 1; true; }))]];
> + [[assume (({ l1:; true; }))]]; // { dg-error "jump to label 'l1'" }
> + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> + [[gnu::assume (({ l2:; true; }))]]; // { dg-error "jump to label 'l2'" }
> + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> + __attribute__((assume (({ l3:; true; })))); // { dg-error "jump to label 'l3'" }
> + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> + [[assume (({ l4:; true; }))]]; // { dg-error "jump to label 'l4'" }
> + [[gnu::assume (({ l5:; true; }))]]; // { dg-error "jump to label 'l5'" }
> + __attribute__((assume (({ l6:; true; })))); // { dg-error "jump to label 'l6'" }
> + switch (x)
> + {
> + case 7:
> + [[assume (({ case 8:; true; }))]]; // { dg-error "jump to case label" }
> + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> + [[assume (({ default:; true; }))]]; // { dg-error "jump to case label" }
> + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> + break;
> + }
> + if (x == 4)
> + goto l4; // { dg-message "from here" }
> + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> + else if (x == 5)
> + goto l5; // { dg-message "from here" }
> + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> + else if (x == 6)
> + goto l6; // { dg-message "from here" }
> + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 }
> +}
>
> Jakub
>
next prev parent reply other threads:[~2022-09-30 20:39 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-09-22 9:55 Jakub Jelinek
2022-09-30 20:39 ` Jason Merrill [this message]
2022-10-02 11:35 ` [PATCH] c++: Disallow jumps into statement expressions Jakub Jelinek
2022-10-03 15:02 ` Jason Merrill
2022-10-03 19:22 ` [PATCH] c++, c, v2: Implement C++23 P1774R8 - Portable assumptions [PR106654] Jakub Jelinek
2022-10-04 10:20 ` Jakub Jelinek
2022-10-04 20:42 ` Jason Merrill
2022-10-05 9:55 ` [PATCH] c++, c, v3: " Jakub Jelinek
2022-10-05 12:33 ` Jason Merrill
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8b360a15-5255-3fbe-16f4-84eaf0fa9612@redhat.com \
--to=jason@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jakub@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).