c: Add support for unsequenced and reproducible attributes

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, 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.

2024-08-31  Jakub Jelinek  <jakub@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.
	(handle_unsequenced_attribute): New function.
	(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-5-aux.c: New file.
	* 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-5-aux.c: New file.
	* gcc.dg/c23-has-c-attribute-2.c: Add tests for unsequenced
	and reproducible attributes.
This commit is contained in:
Jakub Jelinek 2024-08-31 15:58:23 +02:00 committed by Jakub Jelinek
parent dc476e5f68
commit dd346b6138
25 changed files with 979 additions and 9 deletions

View file

@ -444,6 +444,14 @@ const struct attribute_spec c_common_gnu_attributes[] =
{ "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,53 @@ handle_pure_attribute (tree *node, tree name, tree ARG_UNUSED (args),
return NULL_TREE;
}
/* 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 any of the arguments have pointer or reference type, just
add the attribute alone. */
if (POINTER_TYPE_P (TREE_VALUE (argtype)))
return NULL_TREE;
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;
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;
}
/* 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. */

View file

@ -864,6 +864,8 @@ extern void check_function_format (const_tree, tree, int, tree *,
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 *);

View file

@ -445,7 +445,9 @@ c_common_has_attribute (cpp_reader *pfile, bool std_syntax)
|| 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)

View file

@ -4702,6 +4702,39 @@ handle_std_noreturn_attribute (tree *node, tree name, tree args,
}
}
/* 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_attributes[] =
{ "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_name, tree *expr,
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_declarator *declarator,
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);
@ -13444,7 +13493,8 @@ finish_declspecs (struct c_declspecs *specs)
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;
}

View file

@ -2677,8 +2677,9 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
/* 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;
}

View file

@ -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,

View file

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

View file

@ -4024,6 +4024,69 @@ diagnosed. Because a pure function cannot have any observable side
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 are similar
to functions with the @code{const} attribute, except that @code{const}
attribute also requires finiteness. 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 or references or references to pointers
or can store additional results through those pointers or references or
references to pointers.
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 finiteness. 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 or references to
pointers.
@cindex @code{retain} function attribute
@item retain
For ELF targets that support the GNU or FreeBSD OSABIs, this attribute

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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" } */
}
}

View file

@ -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 } */

View file

@ -0,0 +1,12 @@
/* Test C23 reproducible attribute: duplicates. */
/* { 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 } */

View file

@ -0,0 +1,21 @@
/* Auxiliary source for c23-attr-reproducible-5.c test. */
/* { dg-do compile } */
/* { dg-options "-std=c23 -pedantic-errors" } */
int
f1 () [[reproducible]]
{
return 42;
}
int
f2 ()
{
return 43;
}
int
f3 ()
{
return 44;
}

View file

@ -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-5-aux.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 ();
}

View file

@ -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;
}

View file

@ -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" } */
}
}

View file

@ -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 } */

View file

@ -0,0 +1,12 @@
/* Test C23 unsequenced attribute: duplicates. */
/* { 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 } */

View file

@ -0,0 +1,21 @@
/* Auxiliary source for c23-attr-unsequenced-5.c test. */
/* { dg-do compile } */
/* { dg-options "-std=c23 -pedantic-errors" } */
int
f1 () [[unsequenced]]
{
return 42;
}
int
f2 ()
{
return 43;
}
int
f3 ()
{
return 44;
}

View file

@ -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-5-aux.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 ();
}

View file

@ -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