On Tue, Jul 30, 2024 at 7:05 PM Jakub Jelinek <ja...@redhat.com> wrote:
>
> Hi!
>
> C23 added in N2956 ( https://open-std.org/JTC1/SC22/WG14/www/docs/n2956.htm )
> two new attributes, which are described as similar to GCC const and pure
> attributes, but they aren't really same and it seems that even the paper
> is missing some of the differences.
> The paper says unsequenced is the same as const on functions without pointer
> arguments and reproducible is the same as pure on such functions (except
> that they are function type attributes rather than function
> declaration ones), but it seems the paper doesn't consider the finiteness GCC
> relies on (aka non-DECL_LOOPING_CONST_OR_PURE_P) - the paper only talks
> about using the attributes for CSE etc., not for DCE.
>
> The following patch introduces (for now limited) support for those
> attributes, both as standard C23 attributes and as GNU extensions (the
> difference is that the patch is then less strict on where it allows them,
> like other function type attributes they can be specified on function
> declarations as well and apply to the type, while C23 standard ones must
> go on the function declarators (i.e. after closing paren after function
> parameters) or in type specifiers of function type.
>
> If function doesn't have any pointer/reference arguments (I wasn't sure
> whether it must be really just pure pointer arguments or whether say
> struct S { int s; int *p; } passed by value, or unions, or perhaps just
> transparent unions count, and whether variadic functions which can take
> pointer va_arg count too, so the check punts on all of those), the patch
> adds additional internal attribute with " noptr" suffix which then is used
> by flags_from_decl_or_type to handle those easy cases as
> ECF_CONST|ECF_LOOPING_CONST_OR_PURE or
> ECF_PURE|ECF_LOOPING_CONST_OR_PURE
> The harder cases aren't handled right now, I'd hope they can be handled
> incrementally.
>
> I wonder whether we shouldn't emit a warning for the
> gcc.dg/c23-attr-{reproducible,unsequenced}-5.c cases, while the standard
> clearly specifies that composite types should union the attributes and it
> is what GCC implements for decades, for ?: that feels dangerous for the
> new attributes, it would be much better to be conservative on say
> (cond ? unsequenced_function : normal_function) (args)
>
> There is no diagnostics on incorrect [[unsequenced]] or [[reproducible]]
> function definitions, while I think diagnosing non-const static/TLS
> declarations in the former could be easy, the rest feels hard.  E.g. the
> const/pure discovery can just punt on everything it doesn't understand,
> but complete diagnostics would need to understand it.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

I wonder if

int foo (uintrptr_t x) { *(int *)x = 1; return 1; }

is considered "noptr" by the standard but then by making a pointer out of
'x' invokes UB?

One more comment below.

> 2024-07-30  Jakub Jelinek  <ja...@redhat.com>
>
>         PR c/116130
> gcc/
>         * doc/extend.texi (unsequenced, reproducible): Document new function
>         type attributes.
>         * calls.cc (flags_from_decl_or_type): Handle "unsequenced noptr" and
>         "reproducible noptr" attributes.
> gcc/c-family/
>         * c-attribs.cc (c_common_gnu_attributes): Add entries for
>         "unsequenced", "reproducible", "unsequenced noptr" and
>         "reproducible noptr" attributes.
>         (c_maybe_contains_pointers_p): New function.
>         (handle_unsequenced_attribute): Likewise.
>         (handle_reproducible_attribute): Likewise.
>         * c-common.h (handle_unsequenced_attribute): Declare.
>         (handle_reproducible_attribute): Likewise.
>         * c-lex.cc (c_common_has_attribute): Return 202311 for standard
>         unsequenced and reproducible attributes.
> gcc/c/
>         * c-decl.cc (handle_std_unsequenced_attribute): New function.
>         (handle_std_reproducible_attribute): Likewise.
>         (std_attributes): Add entries for "unsequenced" and "reproducible"
>         attributes.
>         (c_warn_type_attributes): Add TYPE argument.  Allow unsequenced
>         or reproducible attributes if it is FUNCTION_TYPE.
>         (groktypename): Adjust c_warn_type_attributes caller.
>         (grokdeclarator): Likewise.
>         (finish_declspecs): Likewise.
>         * c-parser.cc (c_parser_declaration_or_fndef): Likewise.
>         * c-tree.h (c_warn_type_attributes): Add TYPE argument.
> gcc/testsuite/
>         * c-c++-common/attr-reproducible-1.c: New test.
>         * c-c++-common/attr-reproducible-2.c: New test.
>         * c-c++-common/attr-unsequenced-1.c: New test.
>         * c-c++-common/attr-unsequenced-2.c: New test.
>         * gcc.dg/c23-attr-reproducible-1.c: New test.
>         * gcc.dg/c23-attr-reproducible-2.c: New test.
>         * gcc.dg/c23-attr-reproducible-3.c: New test.
>         * gcc.dg/c23-attr-reproducible-4.c: New test.
>         * gcc.dg/c23-attr-reproducible-5.c: New test.
>         * gcc.dg/c23-attr-reproducible-6.c: New test.
>         * gcc.dg/c23-attr-unsequenced-1.c: New test.
>         * gcc.dg/c23-attr-unsequenced-2.c: New test.
>         * gcc.dg/c23-attr-unsequenced-3.c: New test.
>         * gcc.dg/c23-attr-unsequenced-4.c: New test.
>         * gcc.dg/c23-attr-unsequenced-5.c: New test.
>         * gcc.dg/c23-attr-unsequenced-6.c: New test.
>         * gcc.dg/c23-has-c-attribute-2.c: Add tests for unsequenced
>         and reproducible attributes.
>
> --- gcc/doc/extend.texi.jj      2024-07-29 09:16:15.159636903 +0200
> +++ gcc/doc/extend.texi 2024-07-30 15:59:01.158715051 +0200
> @@ -4024,6 +4024,68 @@ diagnosed.  Because a pure function cann
>  effects it does not make sense for such a function to return @code{void}.
>  Declaring such a function is diagnosed.
>
> +@cindex @code{unsequenced} function type attribute
> +@cindex functions that have no side effects
> +@item unsequenced
> +
> +This attribute is a GNU counterpart of the C23 @code{[[unsequenced]]}
> +attribute, used to specify function pointers to effectless, idempotent,
> +stateless and independent functions according to the C23 definition.
> +
> +Unlike the standard C23 attribute it can be also specified in attributes
> +which appertain to function declarations and applies to the their function
> +type even in that case.
> +
> +Unsequenced functions without pointer or reference arguments (in the
> +declaration or through @code{va_arg} on variadic functions) are similar
> +to functions with the @code{const} attribute, except that @code{const}
> +attribute also requires finitness.  So, both functions with @code{const}
> +and with @code{unsequenced} attributes can be optimized by common
> +subexpression elimination, but only functions with @code{const}
> +attribute can be optimized by dead code elimination if their result is
> +unused or is used only by dead code.  Unsequenced functions without pointer
> +or reference arguments with @code{void} return type are diagnosed because
> +they can't store any results and don't have other observable side-effects
> +either.
> +
> +Unsequenced functions with pointer or reference arguments can inspect
> +objects through the passed pointers/references or can store additional
> +results through those pointers/references.
> +
> +The @code{unsequenced} attribute imposes greater restrictions than
> +the similar @code{reproducible} attribute and fewer restrictions than
> +the @code{const} attribute, so during optimization @code{const} has
> +precedence over @code{unsequenced} which has precedence over
> +@code{reproducible}.
> +
> +@cindex @code{reproducible} function type attribute
> +@cindex functions that have no side effects
> +@item reproducible
> +
> +This attribute is a GNU counterpart of the C23 @code{[[reproducible]]}
> +attribute, used to specify function pointers to effectless and idempotent
> +functions according to the C23 definition.
> +
> +Unlike the standard C23 attribute it can be also specified in attributes
> +which appertain to function declarations and applies to the their function
> +type even in that case.
> +
> +Reproducible functions without pointer or reference arguments or which do
> +not modify objects referenced by those pointer/reference arguments are
> +similar to functions with the @code{pure} attribute, except that
> +@code{pure} attribute also requires finitness.  So, both functions with
> +@code{pure} and with @code{reproducible} attributes can be optimized by 
> common
> +subexpression elimination if the global state or anything reachable through
> +the pointer/reference arguments isn't modified, but only functions with
> +@code{pure} attribute can be optimized by dead code elimination if their 
> result is
> +unused or is used only by dead code.  Reproducible functions without pointer
> +or reference arguments with @code{void} return type are diagnosed because
> +they can't store any results and don't have other observable side-effects
> +either.
> +
> +Reproducible functions with pointer or reference arguments can store
> +additional results through those pointers or references.
> +
>  @cindex @code{retain} function attribute
>  @item retain
>  For ELF targets that support the GNU or FreeBSD OSABIs, this attribute
> --- gcc/calls.cc.jj     2024-07-18 09:20:31.624543703 +0200
> +++ gcc/calls.cc        2024-07-30 13:18:34.350571711 +0200
> @@ -852,6 +852,23 @@ flags_from_decl_or_type (const_tree exp)
>         flags |= ECF_XTHROW;
>
>        flags = special_function_p (exp, flags);
> +
> +      if ((flags & ECF_CONST) == 0
> +         && lookup_attribute ("unsequenced noptr",
> +                              TYPE_ATTRIBUTES (TREE_TYPE (exp))))
> +       {
> +         /* [[unsequenced]] with no pointers in arguments is like
> +            [[gnu::const]] without finite guarantee.  */
> +         flags |= ECF_CONST;
> +         if ((flags & ECF_PURE) == 0)
> +           flags |= ECF_LOOPING_CONST_OR_PURE;
> +       }
> +      if ((flags & (ECF_CONST | ECF_PURE)) == 0
> +         && lookup_attribute ("reproducible noptr",
> +                              TYPE_ATTRIBUTES (TREE_TYPE (exp))))
> +       /* [[reproducible]] with no pointers in arguments is like
> +          [[gnu::pure]] without finite guarantee.  */
> +       flags |= ECF_PURE | ECF_LOOPING_CONST_OR_PURE;
>      }
>    else if (TYPE_P (exp))
>      {
> @@ -862,6 +879,17 @@ flags_from_decl_or_type (const_tree exp)
>           && ((flags & ECF_CONST) != 0
>               || lookup_attribute ("transaction_pure", TYPE_ATTRIBUTES 
> (exp))))
>         flags |= ECF_TM_PURE;
> +
> +      if ((flags & ECF_CONST) == 0
> +         && lookup_attribute ("unsequenced noptr", TYPE_ATTRIBUTES (exp)))
> +       /* [[unsequenced]] with no pointers in arguments is like
> +          [[gnu::const]] without finite guarantee.  */
> +       flags |= ECF_CONST | ECF_LOOPING_CONST_OR_PURE;
> +      if ((flags & ECF_CONST) == 0
> +         && lookup_attribute ("reproducible noptr", TYPE_ATTRIBUTES (exp)))
> +       /* [[reproducible]] with no pointers in arguments is like
> +          [[gnu::pure]] without finite guarantee.  */
> +       flags |= ECF_PURE | ECF_LOOPING_CONST_OR_PURE;
>      }
>    else
>      gcc_unreachable ();
> --- gcc/c-family/c-attribs.cc.jj        2024-07-29 13:21:55.917705757 +0200
> +++ gcc/c-family/c-attribs.cc   2024-07-29 19:40:46.020519425 +0200
> @@ -444,6 +444,14 @@ const struct attribute_spec c_common_gnu
>    { "pure",                   0, 0, true,  false, false, false,
>                               handle_pure_attribute,
>                               attr_const_pure_exclusions },
> +  { "reproducible",           0, 0, false, true,  true,  false,
> +                             handle_reproducible_attribute, NULL },
> +  { "unsequenced",            0, 0, false, true,  true,  false,
> +                             handle_unsequenced_attribute, NULL },
> +  { "reproducible noptr",     0, 0, false, true,  true,  false,
> +                             handle_reproducible_attribute, NULL },
> +  { "unsequenced noptr",      0, 0, false, true,  true,  false,
> +                             handle_unsequenced_attribute, NULL },
>    { "transaction_callable",   0, 0, false, true,  false, false,
>                               handle_tm_attribute, NULL },
>    { "transaction_unsafe",     0, 0, false, true,  false, true,
> @@ -4280,6 +4288,101 @@ handle_pure_attribute (tree *node, tree
>    return NULL_TREE;
>  }
>
> +/* Return true if type TYPE contains or may contain any data pointers
> +   (may in case of C++ dependent types).  */
> +
> +static bool
> +c_maybe_contains_pointers_p (tree type)
> +{
> +  switch (TREE_CODE (type))
> +    {
> +    case POINTER_TYPE:
> +    case REFERENCE_TYPE:
> +      return true;
> +
> +    case RECORD_TYPE:
> +    case UNION_TYPE:
> +    case QUAL_UNION_TYPE:
> +      if (COMPLETE_TYPE_P (type))
> +       {
> +         for (tree fields = TYPE_FIELDS (type); fields; fields = DECL_CHAIN 
> (fields))
> +           if (TREE_CODE (fields) == FIELD_DECL
> +               && c_maybe_contains_pointers_p (TREE_TYPE (fields)))
> +             return true;
> +         return false;
> +       }
> +      return false;
> +
> +    case ARRAY_TYPE:
> +      return c_maybe_contains_pointers_p (TREE_TYPE (type));
> +
> +    case ENUMERAL_TYPE:
> +    case BOOLEAN_TYPE:
> +    case INTEGER_TYPE:
> +    case BITINT_TYPE:
> +    case REAL_TYPE:
> +    case NULLPTR_TYPE:
> +    case FIXED_POINT_TYPE:
> +    case COMPLEX_TYPE:
> +    case VECTOR_TYPE:
> +    case FUNCTION_TYPE:
> +    case METHOD_TYPE:
> +      return false;
> +
> +    default:
> +      return true;
> +    }
> +}
> +
> +/* Handle an "unsequenced" attribute; arguments as in
> +   struct attribute_spec.handler.  */
> +
> +tree
> +handle_unsequenced_attribute (tree *node, tree name, tree ARG_UNUSED (args),
> +                             int flags, bool *no_add_attrs)
> +{
> +  tree fntype = *node;
> +  for (tree argtype = TYPE_ARG_TYPES (fntype); argtype;
> +       argtype = TREE_CHAIN (argtype))
> +    if (argtype == void_list_node)

I think this warrants a comment that the attribute on variadic functions
is treated as receiving pointers.

> +      {
> +       if (VOID_TYPE_P (TREE_TYPE (fntype)))
> +         warning (OPT_Wattributes, "%qE attribute on function type "
> +                  "without pointer arguments returning %<void%>", name);
> +       const char *name2;
> +       if (IDENTIFIER_LENGTH (name) == sizeof ("unsequenced") - 1)
> +         name2 = "unsequenced noptr";
> +       else
> +         name2 = "reproducible noptr";
> +       if (!lookup_attribute (name2, TYPE_ATTRIBUTES (fntype)))
> +         {
> +           *no_add_attrs = true;

shouldn't you set *no_add_attrs also when the noptr attribute is
already there?  Because otherwise you'll get the non-noptr attr added?

> +           gcc_assert ((flags & (int) ATTR_FLAG_TYPE_IN_PLACE) == 0);
> +           tree attr = tree_cons (get_identifier (name2), NULL_TREE,
> +                                  TYPE_ATTRIBUTES (fntype));
> +           if (!lookup_attribute (IDENTIFIER_POINTER (name),
> +                                  TYPE_ATTRIBUTES (fntype)))
> +             attr = tree_cons (name, NULL_TREE, attr);
> +           *node = build_type_attribute_variant (*node, attr);
> +         }
> +       return NULL_TREE;
> +      }
> +    else if (c_maybe_contains_pointers_p (TREE_VALUE (argtype)))
> +      break;
> +  return NULL_TREE;
> +}
> +
> +/* Handle a "reproducible" attribute; arguments as in
> +   struct attribute_spec.handler.  */
> +
> +tree
> +handle_reproducible_attribute (tree *node, tree name, tree args, int flags,
> +                              bool *no_add_attrs)
> +{
> +  return handle_unsequenced_attribute (node, name, args, flags, 
> no_add_attrs);
> +}
> +
>  /* Digest an attribute list destined for a transactional memory statement.
>     ALLOWED is the set of attributes that are allowed for this statement;
>     return the attribute we parsed.  Multiple attributes are never allowed.  
> */
> --- gcc/c-family/c-common.h.jj  2024-07-24 15:47:15.122478781 +0200
> +++ gcc/c-family/c-common.h     2024-07-29 19:03:55.569066565 +0200
> @@ -864,6 +864,8 @@ extern void check_function_format (const
>  extern bool attribute_fallthrough_p (tree);
>  extern tree handle_format_attribute (tree *, tree, tree, int, bool *);
>  extern tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
> +extern tree handle_unsequenced_attribute (tree *, tree, tree, int, bool *);
> +extern tree handle_reproducible_attribute (tree *, tree, tree, int, bool *);
>  extern bool c_common_handle_option (size_t, const char *, HOST_WIDE_INT, int,
>                                     location_t,
>                                     const struct cl_option_handlers *);
> --- gcc/c-family/c-lex.cc.jj    2024-07-29 13:21:55.935705521 +0200
> +++ gcc/c-family/c-lex.cc       2024-07-29 17:14:49.393945048 +0200
> @@ -445,7 +445,9 @@ c_common_has_attribute (cpp_reader *pfil
>                   || is_attribute_p ("maybe_unused", attr_name)
>                   || is_attribute_p ("nodiscard", attr_name)
>                   || is_attribute_p ("noreturn", attr_name)
> -                 || is_attribute_p ("_Noreturn", attr_name))
> +                 || is_attribute_p ("_Noreturn", attr_name)
> +                 || is_attribute_p ("reproducible", attr_name)
> +                 || is_attribute_p ("unsequenced", attr_name))
>                 result = 202311;
>             }
>           if (result)
> --- gcc/c/c-decl.cc.jj  2024-07-29 13:21:55.943705415 +0200
> +++ gcc/c/c-decl.cc     2024-07-30 12:46:40.166828455 +0200
> @@ -4702,6 +4702,39 @@ handle_std_noreturn_attribute (tree *nod
>      }
>  }
>
> +/* Handle the standard [[unsequenced]] attribute.  */
> +
> +static tree
> +handle_std_unsequenced_attribute (tree *node, tree name, tree args,
> +                                 int flags, bool *no_add_attrs)
> +{
> +  /* Unlike GNU __attribute__ ((unsequenced)), the standard [[unsequenced]]
> +     should be only applied to function declarators or type specifiers which
> +     have function type.  */
> +  if (node[2])
> +    {
> +      auto_diagnostic_group d;
> +      if (pedwarn (input_location, OPT_Wattributes,
> +                  "standard %qE attribute can only be applied to function "
> +                  "declarators or type specifiers with function type", name))
> +       inform (input_location, "did you mean to specify it after %<)%> "
> +                               "following function parameters?");
> +      *no_add_attrs = true;
> +      return NULL_TREE;
> +    }
> +  return handle_unsequenced_attribute (node, name, args, flags, 
> no_add_attrs);
> +}
> +
> +/* Handle the standard [[reproducible]] attribute.  */
> +
> +static tree
> +handle_std_reproducible_attribute (tree *node, tree name, tree args,
> +                                  int flags, bool *no_add_attrs)
> +{
> +  return handle_std_unsequenced_attribute (node, name, args, flags,
> +                                          no_add_attrs);
> +}
> +
>  /* Table of supported standard (C23) attributes.  */
>  static const attribute_spec std_attributes[] =
>  {
> @@ -4718,7 +4751,11 @@ static const attribute_spec std_attribut
>    { "nodiscard", 0, 1, false, false, false, false,
>      handle_nodiscard_attribute, NULL },
>    { "noreturn", 0, 0, false, false, false, false,
> -    handle_std_noreturn_attribute, NULL }
> +    handle_std_noreturn_attribute, NULL },
> +  { "reproducible", 0, 0, false, true, true, false,
> +    handle_std_reproducible_attribute, NULL },
> +  { "unsequenced", 0, 0, false, true, true, false,
> +    handle_std_unsequenced_attribute, NULL }
>  };
>
>  const scoped_attribute_specs std_attribute_table =
> @@ -4911,12 +4948,24 @@ c_warn_unused_attributes (tree attrs)
>     list of attributes with them removed.  */
>
>  tree
> -c_warn_type_attributes (tree attrs)
> +c_warn_type_attributes (tree type, tree attrs)
>  {
>    tree *attr_ptr = &attrs;
>    while (*attr_ptr)
>      if (get_attribute_namespace (*attr_ptr) == NULL_TREE)
>        {
> +       if (TREE_CODE (type) == FUNCTION_TYPE)
> +         {
> +           tree name = get_attribute_name (*attr_ptr);
> +           /* [[unsequenced]] and [[reproducible]] is fine on function
> +              types that aren't being defined.  */
> +           if (is_attribute_p ("unsequenced", name)
> +               || is_attribute_p ("reproducible", name))
> +             {
> +               attr_ptr = &TREE_CHAIN (*attr_ptr);
> +               continue;
> +             }
> +         }
>         pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored",
>                  get_attribute_name (*attr_ptr));
>         *attr_ptr = TREE_CHAIN (*attr_ptr);
> @@ -5386,7 +5435,7 @@ groktypename (struct c_type_name *type_n
>                          DEPRECATED_NORMAL);
>
>    /* Apply attributes.  */
> -  attrs = c_warn_type_attributes (attrs);
> +  attrs = c_warn_type_attributes (type, attrs);
>    decl_attributes (&type, attrs, 0);
>
>    return type;
> @@ -7196,7 +7245,7 @@ grokdeclarator (const struct c_declarato
>                 else if (inner_decl->kind == cdk_array)
>                   attr_flags |= (int) ATTR_FLAG_ARRAY_NEXT;
>               }
> -           attrs = c_warn_type_attributes (attrs);
> +           attrs = c_warn_type_attributes (type, attrs);
>             returned_attrs = decl_attributes (&type,
>                                               chainon (returned_attrs, attrs),
>                                               attr_flags);
> @@ -13433,7 +13482,8 @@ finish_declspecs (struct c_declspecs *sp
>   handle_postfix_attrs:
>    if (specs->type != NULL)
>      {
> -      specs->postfix_attrs = c_warn_type_attributes (specs->postfix_attrs);
> +      specs->postfix_attrs
> +       = c_warn_type_attributes (specs->type, specs->postfix_attrs);
>        decl_attributes (&specs->type, specs->postfix_attrs, 0);
>        specs->postfix_attrs = NULL_TREE;
>      }
> --- gcc/c/c-parser.cc.jj        2024-07-24 17:55:57.388097924 +0200
> +++ gcc/c/c-parser.cc   2024-07-30 11:22:32.915821271 +0200
> @@ -2677,8 +2677,9 @@ c_parser_declaration_or_fndef (c_parser
>                       /* Postfix [[]] attributes are valid with C23
>                          auto, although not with __auto_type, and
>                          modify the type given by the initializer.  */
> -                     specs->postfix_attrs =
> -                       c_warn_type_attributes (specs->postfix_attrs);
> +                     specs->postfix_attrs
> +                       = c_warn_type_attributes (specs->type,
> +                                                 specs->postfix_attrs);
>                       decl_attributes (&specs->type, specs->postfix_attrs, 0);
>                       specs->postfix_attrs = NULL_TREE;
>                     }
> --- gcc/c/c-tree.h.jj   2024-07-24 15:47:15.129478691 +0200
> +++ gcc/c/c-tree.h      2024-07-30 11:22:04.963197642 +0200
> @@ -680,7 +680,7 @@ extern tree c_builtin_function (tree);
>  extern tree c_builtin_function_ext_scope (tree);
>  extern tree c_simulate_builtin_function_decl (tree);
>  extern void c_warn_unused_attributes (tree);
> -extern tree c_warn_type_attributes (tree);
> +extern tree c_warn_type_attributes (tree, tree);
>  extern void shadow_tag (const struct c_declspecs *);
>  extern void shadow_tag_warned (const struct c_declspecs *, int);
>  extern tree start_enum (location_t, struct c_enum_contents *, tree, tree,
> --- gcc/testsuite/c-c++-common/attr-reproducible-1.c.jj 2024-07-30 
> 13:32:10.090796441 +0200
> +++ gcc/testsuite/c-c++-common/attr-reproducible-1.c    2024-07-30 
> 13:43:41.088667213 +0200
> @@ -0,0 +1,80 @@
> +/* Test gnu::reproducible attribute: valid uses.  */
> +/* { dg-do compile { target { c || c++11 } } } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +/* { dg-additional-options "-std=gnu23" { target c } } */
> +/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 2 
> "optimized" } } */
> +
> +int f1 () [[gnu::reproducible]];
> +int f2 () [[gnu::reproducible]], f3 (int) [[__gnu__::__reproducible__]];
> +int f4 (int, int *) [[gnu::reproducible]];
> +int f5 (int) [[gnu::reproducible]];
> +int f6 (int);
> +int (*fp1) (int) [[gnu::reproducible]] = f6;
> +typedef int ft1 (int) [[gnu::reproducible]];
> +typedef int ft2 (int);
> +#ifndef __cplusplus
> +extern __typeof (f6) [[gnu::reproducible]] f7;
> +extern ft2 [[__gnu__::__reproducible__]] f8;
> +#else
> +int f7 (int) [[gnu::reproducible, gnu::reproducible]];
> +int f8 (int) [[__gnu__::reproducible, gnu::__reproducible__]];
> +#endif
> +int f1 ();
> +int f9 (int);
> +int f9 (int) [[__gnu__::__reproducible__]];
> +extern int x;
> +
> +int
> +f10 (int w) [[gnu::reproducible]]
> +{
> +  return w + 42 + x;
> +}
> +
> +int
> +f11 (int *w, long long y[1], int z) [[__gnu__::__reproducible__]]
> +{
> +  w[0] = z + x;
> +  w[1] = z + x + 1;
> +  w[2] = z + x + 2;
> +  *y = z + x + 3;
> +  return z + 4 + f10 (-42);
> +}
> +
> +int
> +g ()
> +{
> +  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
> +  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
> +  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int e = fp1 (14) + fp1 (14);
> +  x++;
> +  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
> +  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
> +  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int j = fp1 (14) + fp1 (14);
> +  return a + b + c + d + e + f + g + h + i + j;
> +}
> +
> +int
> +h ()
> +{
> +  f3 (52);
> +  f3 (52);
> +  f3 (52);
> +  return 0;
> +}
> --- gcc/testsuite/c-c++-common/attr-reproducible-2.c.jj 2024-07-30 
> 13:58:38.433861263 +0200
> +++ gcc/testsuite/c-c++-common/attr-reproducible-2.c    2024-07-30 
> 14:26:47.293852999 +0200
> @@ -0,0 +1,74 @@
> +/* Test reproducible attribute: valid uses.  */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 2 
> "optimized" } } */
> +
> +__attribute__((reproducible)) int f1 (void);
> +__attribute__((__reproducible__)) int f2 (void), f3 (int);
> +int f4 (int, int *) __attribute__((reproducible));
> +int f5 (int) __attribute__((reproducible));
> +int f6 (int);
> +int (*fp1) (int) __attribute__((reproducible)) = f6;
> +typedef int ft1 (int) __attribute__((reproducible));
> +typedef int ft2 (int);
> +extern __typeof (f6) __attribute__((reproducible)) f7;
> +extern ft2 __attribute__((__reproducible__)) f8;
> +int f1 (void);
> +int f9 (int);
> +int f9 (int) __attribute__((__reproducible__));
> +extern int x;
> +
> +__attribute__((reproducible)) int
> +f10 (int w)
> +{
> +  return w + 42 + x;
> +}
> +
> +__attribute__((reproducible)) int
> +f11 (int *w, long long y[1], int z)
> +{
> +  w[0] = z + x;
> +  w[1] = z + x + 1;
> +  w[2] = z + x + 2;
> +  *y = z + x + 3;
> +  return z + 4 + f10 (-42);
> +}
> +
> +int
> +g (void)
> +{
> +  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
> +  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
> +  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int e = fp1 (14) + fp1 (14);
> +  x++;
> +  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
> +  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
> +  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int j = fp1 (14) + fp1 (14);
> +  return a + b + c + d + e + f + g + h + i + j;
> +}
> +
> +int
> +h (void)
> +{
> +  f3 (52);
> +  f3 (52);
> +  f3 (52);
> +  return 0;
> +}
> --- gcc/testsuite/c-c++-common/attr-unsequenced-1.c.jj  2024-07-30 
> 13:29:39.757782214 +0200
> +++ gcc/testsuite/c-c++-common/attr-unsequenced-1.c     2024-07-30 
> 13:44:47.992783260 +0200
> @@ -0,0 +1,87 @@
> +/* Test gnu::unsequenced attribute: valid uses.  */
> +/* { dg-do compile { target { c || c++11 } } } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +/* { dg-additional-options "-std=gnu23" { target c } } */
> +/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f12 \\\(\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f13 \\\(\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 1 
> "optimized" } } */
> +
> +[[gnu::unsequenced]] int f1 ();
> +[[gnu::unsequenced]] int f2 (), f3 (int);
> +int f4 (int, int *) [[gnu::unsequenced]];
> +int f5 (int) [[gnu::unsequenced]];
> +int f6 (int);
> +int (*fp1) (int) [[gnu::unsequenced]] = f6;
> +typedef int ft1 (int) [[gnu::unsequenced]];
> +typedef int ft2 (int);
> +#ifndef __cplusplus
> +extern __typeof (f6) [[gnu::unsequenced]] f7;
> +extern ft2 [[__gnu__::__unsequenced__]] f8;
> +#else
> +int f7 (int) [[gnu::unsequenced, gnu::unsequenced]];
> +int f8 (int) [[__gnu__::unsequenced, gnu::__unsequenced__]];
> +#endif
> +int f1 ();
> +int f9 (int);
> +int f9 (int) [[__gnu__::__unsequenced__]];
> +extern int x;
> +
> +int
> +f10 (int x) [[gnu::unsequenced]]
> +{
> +  return x + 42;
> +}
> +
> +int
> +f11 (int *x, long long y[1], int z) [[__gnu__::__unsequenced__]]
> +{
> +  x[0] = z;
> +  x[1] = z + 1;
> +  x[2] = z + 2;
> +  *y = z + 3;
> +  return z + 4 + f10 (-42);
> +}
> +
> +int f12 () [[gnu::unsequenced]];
> +int f12 () [[gnu::reproducible]];
> +int f13 () [[gnu::reproducible]];
> +int f13 () [[gnu::unsequenced]];
> +
> +int
> +g ()
> +{
> +  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + 
> f12 () + f13 ();
> +  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + 
> f12 () + f13 ();
> +  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int e = fp1 (14) + fp1 (14);
> +  x++;
> +  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + 
> f12 () + f13 ();
> +  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + 
> f12 () + f13 ();
> +  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int j = fp1 (14) + fp1 (14);
> +  return a + b + c + d + e + f + g + h + i + j;
> +}
> +
> +int
> +h ()
> +{
> +  f3 (52);
> +  f3 (52);
> +  f3 (52);
> +  return 0;
> +}
> --- gcc/testsuite/c-c++-common/attr-unsequenced-2.c.jj  2024-07-30 
> 13:45:58.221855370 +0200
> +++ gcc/testsuite/c-c++-common/attr-unsequenced-2.c     2024-07-30 
> 14:03:45.395843298 +0200
> @@ -0,0 +1,81 @@
> +/* Test unsequenced attribute: valid uses.  */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f12 \\\(\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f13 \\\(\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 1 
> "optimized" } } */
> +
> +__attribute__((unsequenced)) int f1 (void);
> +__attribute__((unsequenced)) int f2 (void), f3 (int);
> +int f4 (int, int *) __attribute__((unsequenced));
> +int f5 (int) __attribute__((unsequenced));
> +int f6 (int);
> +int (*fp1) (int) __attribute__((unsequenced)) = f6;
> +typedef int ft1 (int) __attribute__((unsequenced));
> +typedef int ft2 (int);
> +extern __typeof (f6) __attribute__((unsequenced)) f7;
> +extern ft2 __attribute__((__unsequenced__)) f8;
> +int f1 (void);
> +int f9 (int);
> +int f9 (int) __attribute__((__unsequenced__));
> +extern int x;
> +
> +__attribute__((unsequenced)) int
> +f10 (int x)
> +{
> +  return x + 42;
> +}
> +
> +__attribute__((__unsequenced__)) int
> +f11 (int *x, long long y[1], int z)
> +{
> +  x[0] = z;
> +  x[1] = z + 1;
> +  x[2] = z + 2;
> +  *y = z + 3;
> +  return z + 4 + f10 (-42);
> +}
> +
> +int f12 (void) __attribute__((unsequenced));
> +int f12 (void) __attribute__((reproducible));
> +int f13 (void) __attribute__((reproducible));
> +int f13 (void) __attribute__((unsequenced));
> +
> +int
> +g (void)
> +{
> +  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + 
> f12 () + f13 ();
> +  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + 
> f12 () + f13 ();
> +  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int e = fp1 (14) + fp1 (14);
> +  x++;
> +  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + 
> f12 () + f13 ();
> +  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + 
> f12 () + f13 ();
> +  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int j = fp1 (14) + fp1 (14);
> +  return a + b + c + d + e + f + g + h + i + j;
> +}
> +
> +int
> +h (void)
> +{
> +  f3 (52);
> +  f3 (52);
> +  f3 (52);
> +  return 0;
> +}
> --- gcc/testsuite/gcc.dg/c23-attr-reproducible-1.c.jj   2024-07-30 
> 10:39:09.790803085 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-1.c      2024-07-30 
> 13:19:09.908102024 +0200
> @@ -0,0 +1,74 @@
> +/* Test C23 reproducible attribute: valid uses.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c23 -pedantic-errors -O2 -fdump-tree-optimized" } */
> +/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 2 
> "optimized" } } */
> +
> +int f1 () [[reproducible]];
> +int f2 () [[reproducible]], f3 (int) [[__reproducible__]];
> +int f4 (int, int *restrict) [[reproducible]];
> +int f5 (int) [[reproducible]];
> +int f6 (int);
> +int (*fp1) (int) [[reproducible]] = f6;
> +typedef int ft1 (int) [[reproducible]];
> +typedef int ft2 (int);
> +extern typeof (f6) [[reproducible]] f7;
> +extern ft2 [[__reproducible__]] f8;
> +int f1 ();
> +int f9 (int);
> +int f9 (int) [[__reproducible__]];
> +extern int x;
> +
> +int
> +f10 (int w) [[reproducible]]
> +{
> +  return w + 42 + x;
> +}
> +
> +int
> +f11 (int *restrict w, long long y[restrict static 1], int z) 
> [[__reproducible__]]
> +{
> +  w[0] = z + x;
> +  w[1] = z + x + 1;
> +  w[2] = z + x + 2;
> +  *y = z + x + 3;
> +  return z + 4 + f10 (-42);
> +}
> +
> +int
> +g ()
> +{
> +  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
> +  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
> +  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int e = fp1 (14) + fp1 (14);
> +  x++;
> +  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
> +  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42);
> +  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int j = fp1 (14) + fp1 (14);
> +  return a + b + c + d + e + f + g + h + i + j;
> +}
> +
> +int
> +h ()
> +{
> +  f3 (52);
> +  f3 (52);
> +  f3 (52);
> +  return 0;
> +}
> --- gcc/testsuite/gcc.dg/c23-attr-reproducible-2.c.jj   2024-07-30 
> 10:39:12.632764961 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-2.c      2024-07-30 
> 13:09:35.803687498 +0200
> @@ -0,0 +1,47 @@
> +/* Test C23 reproducible attribute: invalid contexts.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c23 -pedantic-errors" } */
> +
> +/* This attribute is not valid in most cases on types other than
> +   type specifiers with function type or function declarators.  */
> +
> +[[reproducible]]; /* { dg-error "ignored" } */
> +
> +int [[reproducible]] var; /* { dg-error "ignored" } */
> +
> +int array_with_dep_type[2] [[reproducible]]; /* { dg-error "ignored" } */
> +
> +[[reproducible]] int fn1 (); /* { dg-error "standard 'reproducible' 
> attribute can only be applied to function declarators or type specifiers with 
> function type" } */
> +
> +[[reproducible]] int fn2 (), fn3 (); /* { dg-error "standard 'reproducible' 
> attribute can only be applied to function declarators or type specifiers with 
> function type" } */
> +
> +int var2 [[reproducible]]; /* { dg-warning "'reproducible' attribute only 
> applies to function types" } */
> +
> +int fn4 [[reproducible]] (); /* { dg-error "standard 'reproducible' 
> attribute can only be applied to function declarators or type specifiers with 
> function type" } */
> +
> +int [[reproducible]] fn5 (); /* { dg-error "ignored" } */
> +
> +int z = sizeof (int [[__reproducible__]]); /* { dg-error "ignored" } */
> +
> +/* This is valid, but not really useful, as it can't return results
> +   in return type nor has any pointer arguments to store results into.  */
> +void
> +fn6 (int x, double y) [[reproducible]]
> +{ /* { dg-warning "reproducible' attribute on function type without pointer 
> arguments returning 'void'" } */
> +  y = x;
> +  (void) y;
> +}
> +
> +void
> +f (void)
> +{
> +  int a;
> +  [[reproducible]]; /* { dg-error "ignored" } */
> +  [[reproducible]] a = 1; /* { dg-error "ignored" } */
> +  [[reproducible]] label: ; /* { dg-warning "'reproducible' attribute only 
> applies to function types" } */
> +  switch (var)
> +    {
> +    [[reproducible]] case 1: ; /* { dg-warning "'reproducible' attribute 
> only applies to function types" } */
> +    [[reproducible]] default: ; /* { dg-warning "'reproducible' attribute 
> only applies to function types" } */
> +    }
> +}
> --- gcc/testsuite/gcc.dg/c23-attr-reproducible-3.c.jj   2024-07-30 
> 10:39:15.313728999 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-3.c      2024-07-30 
> 13:10:14.556175440 +0200
> @@ -0,0 +1,14 @@
> +/* Test C23 reproducible attribute: invalid syntax.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c23 -pedantic-errors" } */
> +
> +int a () [[reproducible()]]; /* { dg-error "'reproducible' attribute does 
> not take any arguments" } */
> +
> +int b () [[reproducible(0)]]; /* { dg-error "expected" } */
> +                             /* { dg-error "'reproducible' attribute does 
> not take any arguments" "" { target *-*-* } .-1 } */
> +
> +int c () [[reproducible("", 123)]]; /* { dg-error "expected" } */
> +                                   /* { dg-error "'reproducible' attribute 
> does not take any arguments" "" { target *-*-* } .-1 } */
> +
> +int d () [[reproducible((""))]]; /* { dg-error "expected" } */
> +                                /* { dg-error "'reproducible' attribute does 
> not take any arguments" "" { target *-*-* } .-1 } */
> --- gcc/testsuite/gcc.dg/c23-attr-reproducible-4.c.jj   2024-07-30 
> 10:39:18.168690708 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-4.c      2024-07-30 
> 13:10:26.005024168 +0200
> @@ -0,0 +1,12 @@
> +/* Test C23 reproducible attribute: duplicates (allowed after N2557).  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c23 -pedantic-errors" } */
> +
> +int a () [[reproducible, __reproducible__]];
> +int b () [[__reproducible__, reproducible]];
> +int c () [[reproducible, reproducible]];
> +int d () [[__reproducible__, __reproducible__]];
> +int d () [[reproducible]];
> +int d () [[__reproducible__]];
> +[[reproducible, reproducible]];
> +/* { dg-error "ignored" "ignored" { target *-*-* } .-1 } */
> --- gcc/testsuite/gcc.dg/c23-attr-reproducible-5.c.jj   2024-07-30 
> 14:44:26.750009364 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-5.c      2024-07-30 
> 14:54:04.312467597 +0200
> @@ -0,0 +1,44 @@
> +/* Test C23 reproducible attribute: composite type on ?:.  */
> +/* { dg-do run } */
> +/* { dg-options "-std=c23 -pedantic-errors" } */
> +/* { dg-additional-sources "c23-attr-reproducible-6.c" } */
> +
> +int f1 () [[reproducible]];
> +int f2 ();
> +int f3 ();
> +int (*fp1) () [[reproducible]] = f2;
> +int (*fp2) () [[reproducible]] = f3;
> +extern void abort ();
> +
> +int
> +foo (int x)
> +{
> +  return __builtin_has_attribute (*(x ? f1 : f3), reproducible);
> +}
> +
> +int
> +bar (int x)
> +{
> +  return __builtin_has_attribute (*(x ? fp1 : fp2), reproducible);
> +}
> +
> +int
> +baz (int x)
> +{
> +  return __builtin_has_attribute (*(x ? f3 : f1), reproducible);
> +}
> +
> +int
> +qux (int x)
> +{
> +  return __builtin_has_attribute (*(x ? fp2 : fp1), reproducible);
> +}
> +
> +int
> +main ()
> +{
> +  if (!foo (0) || !bar (0) || !baz (0) || !qux (0))
> +    abort ();
> +  if (!foo (1) || !bar (1) || !baz (1) || !qux (1))
> +    abort ();
> +}
> --- gcc/testsuite/gcc.dg/c23-attr-reproducible-6.c.jj   2024-07-30 
> 14:50:39.070147636 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-reproducible-6.c      2024-07-30 
> 14:51:28.339504281 +0200
> @@ -0,0 +1,21 @@
> +/* Test C23 reproducible attribute: composite type on ?:.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c23 -pedantic-errors" } */
> +
> +int
> +f1 () [[reproducible]]
> +{
> +  return 42;
> +}
> +
> +int
> +f2 ()
> +{
> +  return 43;
> +}
> +
> +int
> +f3 ()
> +{
> +  return 44;
> +}
> --- gcc/testsuite/gcc.dg/c23-attr-unsequenced-1.c.jj    2024-07-30 
> 10:39:09.790803085 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-1.c       2024-07-30 
> 13:07:48.585104231 +0200
> @@ -0,0 +1,81 @@
> +/* Test C23 unsequenced attribute: valid uses.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c23 -pedantic-errors -O2 -fdump-tree-optimized" } */
> +/* { dg-final { scan-tree-dump-times " f1 \\\(\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f2 \\\(\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f12 \\\(\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f13 \\\(\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f3 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f5 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f7 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f8 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f9 \\\(42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f3 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f5 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f7 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f8 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " f9 \\\(-42\\\);" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump " f3 \\\(52\\\);" "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " fp1\.\[0-9]*_\[0-9]* \\\(14\\\);" 1 
> "optimized" } } */
> +
> +int f1 () [[unsequenced]];
> +int f2 () [[unsequenced]], f3 (int) [[__unsequenced__]];
> +int f4 (int, int *restrict) [[unsequenced]];
> +int f5 (int) [[unsequenced]];
> +int f6 (int);
> +int (*fp1) (int) [[unsequenced]] = f6;
> +typedef int ft1 (int) [[unsequenced]];
> +typedef int ft2 (int);
> +extern typeof (f6) [[unsequenced]] f7;
> +extern ft2 [[__unsequenced__]] f8;
> +int f1 ();
> +int f9 (int);
> +int f9 (int) [[__unsequenced__]];
> +extern int x;
> +
> +int
> +f10 (int x) [[unsequenced]]
> +{
> +  return x + 42;
> +}
> +
> +int
> +f11 (int *restrict x, long long y[restrict static 1], int z) 
> [[__unsequenced__]]
> +{
> +  x[0] = z;
> +  x[1] = z + 1;
> +  x[2] = z + 2;
> +  *y = z + 3;
> +  return z + 4 + f10 (-42);
> +}
> +
> +int f12 () [[unsequenced]];
> +int f12 () [[reproducible]];
> +int f13 () [[reproducible]];
> +int f13 () [[unsequenced]];
> +
> +int
> +g ()
> +{
> +  int a = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + 
> f12 () + f13 ();
> +  int b = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + 
> f12 () + f13 ();
> +  int c = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int d = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int e = fp1 (14) + fp1 (14);
> +  x++;
> +  int f = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + 
> f12 () + f13 ();
> +  int g = f1 () + f2 () + f3 (42) + f5 (42) + f7 (42) + f8 (42) + f9 (42) + 
> f12 () + f13 ();
> +  int h = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int i = f3 (-42) + f5 (-42) + f7 (-42) + f8 (-42) + f9 (-42);
> +  int j = fp1 (14) + fp1 (14);
> +  return a + b + c + d + e + f + g + h + i + j;
> +}
> +
> +int
> +h ()
> +{
> +  f3 (52);
> +  f3 (52);
> +  f3 (52);
> +  return 0;
> +}
> --- gcc/testsuite/gcc.dg/c23-attr-unsequenced-2.c.jj    2024-07-30 
> 10:39:12.632764961 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-2.c       2024-07-30 
> 13:04:18.566879305 +0200
> @@ -0,0 +1,47 @@
> +/* Test C23 unsequenced attribute: invalid contexts.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c23 -pedantic-errors" } */
> +
> +/* This attribute is not valid in most cases on types other than
> +   type specifiers with function type or function declarators.  */
> +
> +[[unsequenced]]; /* { dg-error "ignored" } */
> +
> +int [[unsequenced]] var; /* { dg-error "ignored" } */
> +
> +int array_with_dep_type[2] [[unsequenced]]; /* { dg-error "ignored" } */
> +
> +[[unsequenced]] int fn1 (); /* { dg-error "standard 'unsequenced' attribute 
> can only be applied to function declarators or type specifiers with function 
> type" } */
> +
> +[[unsequenced]] int fn2 (), fn3 (); /* { dg-error "standard 'unsequenced' 
> attribute can only be applied to function declarators or type specifiers with 
> function type" } */
> +
> +int var2 [[unsequenced]]; /* { dg-warning "'unsequenced' attribute only 
> applies to function types" } */
> +
> +int fn4 [[unsequenced]] (); /* { dg-error "standard 'unsequenced' attribute 
> can only be applied to function declarators or type specifiers with function 
> type" } */
> +
> +int [[unsequenced]] fn5 (); /* { dg-error "ignored" } */
> +
> +int z = sizeof (int [[__unsequenced__]]); /* { dg-error "ignored" } */
> +
> +/* This is valid, but not really useful, as it can't return results
> +   in return type nor has any pointer arguments to store results into.  */
> +void
> +fn6 (int x, double y) [[unsequenced]]
> +{ /* { dg-warning "unsequenced' attribute on function type without pointer 
> arguments returning 'void'" } */
> +  y = x;
> +  (void) y;
> +}
> +
> +void
> +f (void)
> +{
> +  int a;
> +  [[unsequenced]]; /* { dg-error "ignored" } */
> +  [[unsequenced]] a = 1; /* { dg-error "ignored" } */
> +  [[unsequenced]] label: ; /* { dg-warning "'unsequenced' attribute only 
> applies to function types" } */
> +  switch (var)
> +    {
> +    [[unsequenced]] case 1: ; /* { dg-warning "'unsequenced' attribute only 
> applies to function types" } */
> +    [[unsequenced]] default: ; /* { dg-warning "'unsequenced' attribute only 
> applies to function types" } */
> +    }
> +}
> --- gcc/testsuite/gcc.dg/c23-attr-unsequenced-3.c.jj    2024-07-30 
> 10:39:15.313728999 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-3.c       2024-07-30 
> 12:59:47.146465073 +0200
> @@ -0,0 +1,14 @@
> +/* Test C23 unsequenced attribute: invalid syntax.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c23 -pedantic-errors" } */
> +
> +int a () [[unsequenced()]]; /* { dg-error "'unsequenced' attribute does not 
> take any arguments" } */
> +
> +int b () [[unsequenced(0)]]; /* { dg-error "expected" } */
> +                            /* { dg-error "'unsequenced' attribute does not 
> take any arguments" "" { target *-*-* } .-1 } */
> +
> +int c () [[unsequenced("", 123)]]; /* { dg-error "expected" } */
> +                                  /* { dg-error "'unsequenced' attribute 
> does not take any arguments" "" { target *-*-* } .-1 } */
> +
> +int d () [[unsequenced((""))]]; /* { dg-error "expected" } */
> +                               /* { dg-error "'unsequenced' attribute does 
> not take any arguments" "" { target *-*-* } .-1 } */
> --- gcc/testsuite/gcc.dg/c23-attr-unsequenced-4.c.jj    2024-07-30 
> 10:39:18.168690708 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-4.c       2024-07-30 
> 12:45:58.298379802 +0200
> @@ -0,0 +1,12 @@
> +/* Test C23 unsequenced attribute: duplicates (allowed after N2557).  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c23 -pedantic-errors" } */
> +
> +int a () [[unsequenced, __unsequenced__]];
> +int b () [[__unsequenced__, unsequenced]];
> +int c () [[unsequenced, unsequenced]];
> +int d () [[__unsequenced__, __unsequenced__]];
> +int d () [[unsequenced]];
> +int d () [[__unsequenced__]];
> +[[unsequenced, unsequenced]];
> +/* { dg-error "ignored" "ignored" { target *-*-* } .-1 } */
> --- gcc/testsuite/gcc.dg/c23-attr-unsequenced-5.c.jj    2024-07-30 
> 14:44:26.750009364 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-5.c       2024-07-30 
> 14:53:55.734579606 +0200
> @@ -0,0 +1,44 @@
> +/* Test C23 unsequenced attribute: composite type on ?:.  */
> +/* { dg-do run } */
> +/* { dg-options "-std=c23 -pedantic-errors" } */
> +/* { dg-additional-sources "c23-attr-unsequenced-6.c" } */
> +
> +int f1 () [[unsequenced]];
> +int f2 ();
> +int f3 ();
> +int (*fp1) () [[unsequenced]] = f2;
> +int (*fp2) () [[unsequenced]] = f3;
> +extern void abort ();
> +
> +int
> +foo (int x)
> +{
> +  return __builtin_has_attribute (*(x ? f1 : f3), unsequenced);
> +}
> +
> +int
> +bar (int x)
> +{
> +  return __builtin_has_attribute (*(x ? fp1 : fp2), unsequenced);
> +}
> +
> +int
> +baz (int x)
> +{
> +  return __builtin_has_attribute (*(x ? f3 : f1), unsequenced);
> +}
> +
> +int
> +qux (int x)
> +{
> +  return __builtin_has_attribute (*(x ? fp2 : fp1), unsequenced);
> +}
> +
> +int
> +main ()
> +{
> +  if (!foo (0) || !bar (0) || !baz (0) || !qux (0))
> +    abort ();
> +  if (!foo (1) || !bar (1) || !baz (1) || !qux (1))
> +    abort ();
> +}
> --- gcc/testsuite/gcc.dg/c23-attr-unsequenced-6.c.jj    2024-07-30 
> 14:50:39.070147636 +0200
> +++ gcc/testsuite/gcc.dg/c23-attr-unsequenced-6.c       2024-07-30 
> 14:52:50.354433341 +0200
> @@ -0,0 +1,21 @@
> +/* Test C23 unsequenced attribute: composite type on ?:.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c23 -pedantic-errors" } */
> +
> +int
> +f1 () [[unsequenced]]
> +{
> +  return 42;
> +}
> +
> +int
> +f2 ()
> +{
> +  return 43;
> +}
> +
> +int
> +f3 ()
> +{
> +  return 44;
> +}
> --- gcc/testsuite/gcc.dg/c23-has-c-attribute-2.c.jj     2023-11-09 
> 09:04:19.505530809 +0100
> +++ gcc/testsuite/gcc.dg/c23-has-c-attribute-2.c        2024-07-30 
> 09:52:19.525586926 +0200
> @@ -50,6 +50,22 @@
>  #error "bad result for ___Noreturn__"
>  #endif
>
> +#if __has_c_attribute (unsequenced) != 202311L
> +#error "bad result for unsequenced"
> +#endif
> +
> +#if __has_c_attribute (__unsequenced__) != 202311L
> +#error "bad result for __unsequenced__"
> +#endif
> +
> +#if __has_c_attribute (reproducible) != 202311L
> +#error "bad result for reproducible"
> +#endif
> +
> +#if __has_c_attribute (__reproducible__) != 202311L
> +#error "bad result for __reproducible__"
> +#endif
> +
>  /* Macros in the attribute name are expanded.  */
>  #define foo deprecated
>  #if __has_c_attribute (foo) != 202311L
>
>         Jakub
>

Reply via email to