Support for constructing composite types for structs and unions
in C23.
gcc/c:
* c-typeck.cc (composite_type_internal): Adapted from
composite_type to support structs and unions.
(composite_type): New wrapper function.
(build_conditional_operator): Return composite type.
* c-decl.cc (finish_struct): Allow NULL for
enclosing_struct_parse_info.
gcc/testsuite:
* gcc.dg/c23-tag-alias-6.c: New test.
* gcc.dg/c23-tag-composite-1.c: New test.
* gcc.dg/c23-tag-composite-2.c: New test.
* gcc.dg/c23-tag-composite-3.c: New test.
* gcc.dg/c23-tag-composite-4.c: New test.
* gcc.dg/c23-tag-composite-5.c: New test.
* gcc.dg/c23-tag-composite-6.c: New test.
* gcc.dg/c23-tag-composite-7.c: New test.
* gcc.dg/c23-tag-composite-8.c: New test.
* gcc.dg/c23-tag-composite-9.c: New test.
* gcc.dg/gnu23-tag-composite-1.c: New test.
* gcc.dg/gnu23-tag-composite-2.c: New test.
* gcc.dg/gnu23-tag-composite-3.c: New test.
* gcc.dg/gnu23-tag-composite-4.c: New test.
---
gcc/c/c-decl.cc | 21 +--
gcc/c/c-typeck.cc | 137 ++++++++++++++++---
gcc/testsuite/gcc.dg/c23-tag-alias-6.c | 32 +++++
gcc/testsuite/gcc.dg/c23-tag-composite-1.c | 26 ++++
gcc/testsuite/gcc.dg/c23-tag-composite-2.c | 16 +++
gcc/testsuite/gcc.dg/c23-tag-composite-3.c | 50 +++++++
gcc/testsuite/gcc.dg/c23-tag-composite-4.c | 21 +++
gcc/testsuite/gcc.dg/c23-tag-composite-5.c | 25 ++++
gcc/testsuite/gcc.dg/c23-tag-composite-6.c | 18 +++
gcc/testsuite/gcc.dg/c23-tag-composite-7.c | 20 +++
gcc/testsuite/gcc.dg/c23-tag-composite-8.c | 15 ++
gcc/testsuite/gcc.dg/c23-tag-composite-9.c | 19 +++
gcc/testsuite/gcc.dg/gnu23-tag-composite-1.c | 45 ++++++
gcc/testsuite/gcc.dg/gnu23-tag-composite-2.c | 30 ++++
gcc/testsuite/gcc.dg/gnu23-tag-composite-3.c | 24 ++++
gcc/testsuite/gcc.dg/gnu23-tag-composite-4.c | 28 ++++
16 files changed, 500 insertions(+), 27 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-6.c
create mode 100644 gcc/testsuite/gcc.dg/c23-tag-composite-1.c
create mode 100644 gcc/testsuite/gcc.dg/c23-tag-composite-2.c
create mode 100644 gcc/testsuite/gcc.dg/c23-tag-composite-3.c
create mode 100644 gcc/testsuite/gcc.dg/c23-tag-composite-4.c
create mode 100644 gcc/testsuite/gcc.dg/c23-tag-composite-5.c
create mode 100644 gcc/testsuite/gcc.dg/c23-tag-composite-6.c
create mode 100644 gcc/testsuite/gcc.dg/c23-tag-composite-7.c
create mode 100644 gcc/testsuite/gcc.dg/c23-tag-composite-8.c
create mode 100644 gcc/testsuite/gcc.dg/c23-tag-composite-9.c
create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-composite-1.c
create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-composite-2.c
create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-composite-3.c
create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-composite-4.c
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 6639ec35e5f..b72738ea04a 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -9674,7 +9674,7 @@ finish_struct (location_t loc, tree t, tree fieldlist,
tree attributes,
}
/* Check for consistency with previous definition. */
- if (flag_isoc23)
+ if (flag_isoc23 && NULL != enclosing_struct_parse_info)
{
tree vistype = previous_tag (t);
if (vistype
@@ -9744,16 +9744,19 @@ finish_struct (location_t loc, tree t, tree fieldlist,
tree attributes,
if (warn_cxx_compat)
warn_cxx_compat_finish_struct (fieldlist, TREE_CODE (t), loc);
- delete struct_parse_info;
+ if (NULL != enclosing_struct_parse_info)
+ {
+ delete struct_parse_info;
- struct_parse_info = enclosing_struct_parse_info;
+ struct_parse_info = enclosing_struct_parse_info;
- /* If this struct is defined inside a struct, add it to
- struct_types. */
- if (warn_cxx_compat
- && struct_parse_info != NULL
- && !in_sizeof && !in_typeof && !in_alignof)
- struct_parse_info->struct_types.safe_push (t);
+ /* If this struct is defined inside a struct, add it to
+ struct_types. */
+ if (warn_cxx_compat
+ && struct_parse_info != NULL
+ && !in_sizeof && !in_typeof && !in_alignof)
+ struct_parse_info->struct_types.safe_push (t);
+ }
return t;
}
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 4d3079156ba..ac31eba6e46 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -381,8 +381,15 @@ build_functype_attribute_variant (tree ntype, tree otype,
tree attrs)
nonzero; if that isn't so, this may crash. In particular, we
assume that qualifiers match. */
+struct composite_cache {
+ tree t1;
+ tree t2;
+ tree composite;
+ struct composite_cache* next;
+};
+
tree
-composite_type (tree t1, tree t2)
+composite_type_internal (tree t1, tree t2, struct composite_cache* cache)
{
enum tree_code code1;
enum tree_code code2;
@@ -427,7 +434,8 @@ composite_type (tree t1, tree t2)
{
tree pointed_to_1 = TREE_TYPE (t1);
tree pointed_to_2 = TREE_TYPE (t2);
- tree target = composite_type (pointed_to_1, pointed_to_2);
+ tree target = composite_type_internal (pointed_to_1,
+ pointed_to_2, cache);
t1 = build_pointer_type_for_mode (target, TYPE_MODE (t1), false);
t1 = build_type_attribute_variant (t1, attributes);
return qualify_type (t1, t2);
@@ -435,7 +443,8 @@ composite_type (tree t1, tree t2)
case ARRAY_TYPE:
{
- tree elt = composite_type (TREE_TYPE (t1), TREE_TYPE (t2));
+ tree elt = composite_type_internal (TREE_TYPE (t1), TREE_TYPE (t2),
+ cache);
int quals;
tree unqual_elt;
tree d1 = TYPE_DOMAIN (t1);
@@ -503,9 +512,84 @@ composite_type (tree t1, tree t2)
return build_type_attribute_variant (t1, attributes);
}
- case ENUMERAL_TYPE:
case RECORD_TYPE:
case UNION_TYPE:
+ if (flag_isoc23 && !comptypes_same_p (t1, t2))
+ {
+ gcc_checking_assert (COMPLETE_TYPE_P (t1) && COMPLETE_TYPE_P (t2));
+ gcc_checking_assert (!TYPE_NAME (t1) || comptypes (t1, t2));
+
+ /* If a composite type for these two types is already under
+ construction, return it. */
+
+ for (struct composite_cache *c = cache; c != NULL; c = c->next)
+ if (c->t1 == t1 && c->t2 == t2)
+ return c->composite;
+
+ /* Otherwise, create a new type node and link it into the cache. */
+
+ tree n = make_node (code1);
+ TYPE_NAME (n) = TYPE_NAME (t1);
+
+ struct composite_cache cache2 = { t1, t2, n, cache };
+ cache = &cache2;
+
+ tree f1 = TYPE_FIELDS (t1);
+ tree f2 = TYPE_FIELDS (t2);
+ tree fields = NULL_TREE;
+
+ for (tree a = f1, b = f2; a && b;
+ a = DECL_CHAIN (a), b = DECL_CHAIN (b))
+ {
+ tree ta = TREE_TYPE (a);
+ tree tb = TREE_TYPE (b);
+
+ if (DECL_C_BIT_FIELD (a))
+ {
+ ta = DECL_BIT_FIELD_TYPE (a);
+ tb = DECL_BIT_FIELD_TYPE (b);
+ }
+
+ gcc_assert (DECL_NAME (a) == DECL_NAME (b));
+ gcc_checking_assert (!DECL_NAME (a) || comptypes (ta, tb));
+
+ tree f = build_decl (input_location, FIELD_DECL, DECL_NAME (a),
+ composite_type_internal (ta, tb, cache));
+
+ DECL_PACKED (f) = DECL_PACKED (a);
+ SET_DECL_ALIGN (f, DECL_ALIGN (a));
+ DECL_ATTRIBUTES (f) = DECL_ATTRIBUTES (a);
+
+ finish_decl (f, input_location, NULL, NULL, NULL);
+
+ if (DECL_C_BIT_FIELD (a))
+ {
+ /* This will be processed by finish_struct. */
+ SET_DECL_C_BIT_FIELD (f);
+ DECL_INITIAL (f) = build_int_cst (integer_type_node,
+ tree_to_uhwi (DECL_SIZE
(a)));
+ }
+
+ DECL_CHAIN (f) = fields;
+ fields = f;
+ }
+
+ fields = nreverse (fields);
+
+ /* Setup the struct/union type. Because we inherit all variably
+ modified components, we can ignore the size expression. */
+ tree expr = NULL_TREE;
+ n = finish_struct(input_location, n, fields, attributes, NULL, &expr);
+
+ n = qualify_type (n, t1);
+
+ gcc_checking_assert (!TYPE_NAME (n) || comptypes (n, t1));
+ gcc_checking_assert (!TYPE_NAME (n) || comptypes (n, t2));
+
+ return n;
+ }
+ /* FALLTHRU */
+ case ENUMERAL_TYPE:
if (attributes != NULL)
{
/* Try harder not to create a new aggregate type. */
@@ -520,7 +604,8 @@ composite_type (tree t1, tree t2)
/* Function types: prefer the one that specified arg types.
If both do, merge the arg types. Also merge the return types. */
{
- tree valtype = composite_type (TREE_TYPE (t1), TREE_TYPE (t2));
+ tree valtype = composite_type_internal (TREE_TYPE (t1),
+ TREE_TYPE (t2), cache);
tree p1 = TYPE_ARG_TYPES (t1);
tree p2 = TYPE_ARG_TYPES (t2);
int len;
@@ -565,6 +650,16 @@ composite_type (tree t1, tree t2)
for (; p1 && p1 != void_list_node;
p1 = TREE_CHAIN (p1), p2 = TREE_CHAIN (p2), n = TREE_CHAIN (n))
{
+ tree mv1 = TREE_VALUE (p1);
+ if (mv1 && mv1 != error_mark_node
+ && TREE_CODE (mv1) != ARRAY_TYPE)
+ mv1 = TYPE_MAIN_VARIANT (mv1);
+
+ tree mv2 = TREE_VALUE (p2);
+ if (mv2 && mv2 != error_mark_node
+ && TREE_CODE (mv2) != ARRAY_TYPE)
+ mv2 = TYPE_MAIN_VARIANT (mv2);
+
/* A null type means arg type is not specified.
Take whatever the other function type has. */
if (TREE_VALUE (p1) == NULL_TREE)
@@ -585,10 +680,6 @@ composite_type (tree t1, tree t2)
&& TREE_VALUE (p1) != TREE_VALUE (p2))
{
tree memb;
- tree mv2 = TREE_VALUE (p2);
- if (mv2 && mv2 != error_mark_node
- && TREE_CODE (mv2) != ARRAY_TYPE)
- mv2 = TYPE_MAIN_VARIANT (mv2);
for (memb = TYPE_FIELDS (TREE_VALUE (p1));
memb; memb = DECL_CHAIN (memb))
{
@@ -598,8 +689,9 @@ composite_type (tree t1, tree t2)
mv3 = TYPE_MAIN_VARIANT (mv3);
if (comptypes (mv3, mv2))
{
- TREE_VALUE (n) = composite_type (TREE_TYPE (memb),
- TREE_VALUE (p2));
+ TREE_VALUE (n) = composite_type_internal (TREE_TYPE
(memb),
+ TREE_VALUE
(p2),
+ cache);
pedwarn (input_location, OPT_Wpedantic,
"function types not truly compatible in ISO
C");
goto parm_done;
@@ -610,10 +702,6 @@ composite_type (tree t1, tree t2)
&& TREE_VALUE (p2) != TREE_VALUE (p1))
{
tree memb;
- tree mv1 = TREE_VALUE (p1);
- if (mv1 && mv1 != error_mark_node
- && TREE_CODE (mv1) != ARRAY_TYPE)
- mv1 = TYPE_MAIN_VARIANT (mv1);
for (memb = TYPE_FIELDS (TREE_VALUE (p2));
memb; memb = DECL_CHAIN (memb))
{
@@ -623,15 +711,17 @@ composite_type (tree t1, tree t2)
mv3 = TYPE_MAIN_VARIANT (mv3);
if (comptypes (mv3, mv1))
{
- TREE_VALUE (n) = composite_type (TREE_TYPE (memb),
- TREE_VALUE (p1));
+ TREE_VALUE (n)
+ = composite_type_internal (TREE_TYPE (memb),
+ TREE_VALUE (p1),
+ cache);
pedwarn (input_location, OPT_Wpedantic,
"function types not truly compatible in ISO
C");
goto parm_done;
}
}
}
- TREE_VALUE (n) = composite_type (TREE_VALUE (p1), TREE_VALUE (p2));
+ TREE_VALUE (n) = composite_type_internal (mv1, mv2, cache);
parm_done: ;
}
@@ -643,7 +733,13 @@ composite_type (tree t1, tree t2)
default:
return build_type_attribute_variant (t1, attributes);
}
+}
+tree
+composite_type (tree t1, tree t2)
+{
+ struct composite_cache cache = { };
+ return composite_type_internal (t1, t2, &cache);
}
/* Return the type of a conditional expression between pointers to
@@ -5566,6 +5662,11 @@ build_conditional_expr (location_t colon_loc, tree
ifexp, bool ifexp_bcp,
result_type = type2;
else if (code1 == POINTER_TYPE && code2 == NULLPTR_TYPE)
result_type = type1;
+ else if (RECORD_OR_UNION_TYPE_P (type1) && RECORD_OR_UNION_TYPE_P (type2)
+ && comptypes (TYPE_MAIN_VARIANT (type1),
+ TYPE_MAIN_VARIANT (type2)))
+ result_type = composite_type (TYPE_MAIN_VARIANT (type1),
+ TYPE_MAIN_VARIANT (type2));
if (!result_type)
{
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-6.c
b/gcc/testsuite/gcc.dg/c23-tag-alias-6.c
new file mode 100644
index 00000000000..586965f3eac
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-6.c
@@ -0,0 +1,32 @@
+/* { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+
+/* These tests check that a composite type for a struct
+ * can alias the original definition. */
+
+struct foo { int (*y)[]; int x; } s;
+
+int test_foo(struct foo* a, void* b)
+{
+ a->x = 1;
+
+ struct foo { int (*y)[1]; int x; } t;
+ typeof(*(1 ? &s: &t)) *p = b;
+ p->x = 2;
+
+ return a->x;
+}
+
+
+int main()
+{
+ struct foo y;
+
+ if (2 != test_foo(&y, &y))
+ __builtin_abort();
+
+ return 0;
+}
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-composite-1.c
b/gcc/testsuite/gcc.dg/c23-tag-composite-1.c
new file mode 100644
index 00000000000..d79c8eefc91
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-composite-1.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c23" } */
+
+void b(void)
+{
+ int n = 3;
+
+ extern struct f { char (*x)[3]; char (*y)[]; } q;
+ { extern struct f { char (*x)[]; char (*y)[4]; } q;
+ _Static_assert(3 == sizeof(*q.x), "");
+ _Static_assert(4 == sizeof(*q.y), "");
+ }
+ { extern struct f { char (*x)[2]; char (*y)[]; } q; (void)q; } /* {
dg-error "conflicting" } */
+
+ { struct f { char (*x)[n]; char (*y)[3]; }* qp = &q; (void)*qp; }
+ (void)q;
+
+ static struct g { int a; char buf[n]; } *p; (void)p;
+ { static struct g { int a; char buf[3]; } *p; (void)p; }
+
+ static struct h { int a; void (*s)(char buf[n]); } *t; (void)t;
+ { static struct h { int a; void (*s)(char buf[3]); } *t; (void)t; }
+}
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-composite-2.c
b/gcc/testsuite/gcc.dg/c23-tag-composite-2.c
new file mode 100644
index 00000000000..0b06c573e87
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-composite-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c23" } */
+
+
+struct foo { int (*(*i)(void))[]; } x;
+
+
+void f(void)
+{
+ const struct foo { int (*(*i)())[3]; } y;
+ _Static_assert(3 * sizeof(int) == sizeof(*((1 ? &x : &y)->i())), "");
+}
+
+void g(struct foo { int x; } a);
+void g(const struct foo { int x; } a);
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-composite-3.c
b/gcc/testsuite/gcc.dg/c23-tag-composite-3.c
new file mode 100644
index 00000000000..2c1c699440d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-composite-3.c
@@ -0,0 +1,50 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// bit-fields
+
+extern struct foo { int x:3; } x;
+struct foo { int x:3; } y;
+
+void f()
+{
+ extern typeof(*(1 ? &x : &y)) x;
+ &x.x; /* { dg-error "bit-field" } */
+}
+
+
+void g()
+{
+ struct foo { int x:3; } z;
+ extern typeof(*(1 ? &x : &z)) x;
+ &x.x; /* { dg-error "bit-field" } */
+}
+
+struct foo { int x:2; }; /* { dg-error "redefinition" }
*/
+
+extern struct bar { int x:3; } a;
+
+void h()
+{
+ struct bar { signed int x:3; } b;
+ extern typeof(*(1 ? &a : &b)) a;
+ &a.x; /* { dg-error "bit-field" } */
+}
+
+void i()
+{
+ struct bar { unsigned int x:3; } c;
+ (1 ? &a : &c); /* { dg-error "mismatch" } */
+}
+
+struct bar { unsigned int x:3; } d; /* { dg-error "redefinition" }
*/
+struct bar { signed int x:3; } e; /* { dg-error "redefinition" }
*/
+
+
+struct bas { int x:2; int :0; int z:1; };
+struct bas { int x:2; int :0; int z:1; };
+struct bas { int x:2; int :1; int z:1; }; /* { dg-error "redefinition" }
*/
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-composite-4.c
b/gcc/testsuite/gcc.dg/c23-tag-composite-4.c
new file mode 100644
index 00000000000..2cc4b067c05
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-composite-4.c
@@ -0,0 +1,21 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// conditional operator
+
+void f(void)
+{
+ struct foo { int x; } a;
+ struct foo { int x; } b;
+ 1 ? a : b;
+}
+
+struct bar { int x; } a;
+
+void g(void)
+{
+ struct bar { int x; } b;
+ 1 ? a : b;
+}
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-composite-5.c
b/gcc/testsuite/gcc.dg/c23-tag-composite-5.c
new file mode 100644
index 00000000000..3e4820742f8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-composite-5.c
@@ -0,0 +1,25 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// anonymous structs / unions
+
+extern struct foo { int (*x)[]; struct { int y; }; } a;
+extern struct foo { int (*x)[]; struct { int y; }; } a;
+extern struct bar { int (*x)[]; union { int y; }; } b;
+extern struct bar { int (*x)[]; union { int y; }; } b;
+
+void f(void)
+{
+ struct foo { int (*x)[1]; struct { int y; }; } c;
+ extern typeof(*(1 ? &a : &c)) a;
+ a.y;
+
+ struct bar { int (*x)[1]; union { int y; }; } d;
+ extern typeof(*(1 ? &b : &d)) b;
+ b.y;
+}
+
+
+struct foo { int (*x)[]; union { int y; }; }; /* { dg-error
"redefinition" } */
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-composite-6.c
b/gcc/testsuite/gcc.dg/c23-tag-composite-6.c
new file mode 100644
index 00000000000..999bec60e5d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-composite-6.c
@@ -0,0 +1,18 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// alignment
+
+extern struct foo { char x; alignas(int) char y; } a;
+extern struct foo { char x; alignas(int) char y; } a;
+
+void f()
+{
+ extern struct foo { char x; alignas(int) char y; } b;
+ extern typeof(*(1 ? &a : &b)) a;
+ static_assert(alignof(a.y) == alignof(int));
+}
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-composite-7.c
b/gcc/testsuite/gcc.dg/c23-tag-composite-7.c
new file mode 100644
index 00000000000..a0976191f21
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-composite-7.c
@@ -0,0 +1,20 @@
+/* { dg-do run }
+ * { dg-options "-std=c23" }
+ */
+
+// bit-fields
+
+struct foo { char (*y)[]; unsigned x:3; } x;
+
+int main()
+{
+ struct foo { char (*y)[1]; unsigned x:3; } y;
+
+ typeof(*(1 ? &x : &y)) a;
+ a.x = 8; /* { dg-warning "changes value" } */
+
+ if (a.x)
+ __builtin_abort();
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-composite-8.c
b/gcc/testsuite/gcc.dg/c23-tag-composite-8.c
new file mode 100644
index 00000000000..5c61119f9dd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-composite-8.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c23" } */
+
+// adapted from PR c/11428.
+
+struct s { int m : 1; char (*y)[]; } s;
+
+int
+foo (void *q)
+{
+ struct s { int m : 1; char (*y)[1]; } t;
+ typeof(1 ? &s : &t) p = q;
+ return !p->m;
+}
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-composite-9.c
b/gcc/testsuite/gcc.dg/c23-tag-composite-9.c
new file mode 100644
index 00000000000..46300eef9b6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-composite-9.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c23 -Wc++-compat" } */
+
+// test that DECL_BIT_FIELD_TYPE is set correctly
+
+enum e { A, B, C };
+struct s { enum e m : 3; char (*y)[]; } s = { };
+
+void f(enum e);
+
+void foo ()
+{
+ struct s { enum e m : 3; char (*y)[1]; } t = { };
+ f(s.m);
+ f(t.m);
+ typeof(*(1 ? &s : &t)) u = { };
+ f(u.m); // should not warn
+}
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-composite-1.c
b/gcc/testsuite/gcc.dg/gnu23-tag-composite-1.c
new file mode 100644
index 00000000000..7ffaa8ad8af
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-composite-1.c
@@ -0,0 +1,45 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// packed structs
+
+struct foo {
+ char a;
+ int b [[gnu::packed]];
+ char d;
+ int c [[gnu::packed]];
+};
+
+struct foo {
+ char a;
+ int b [[gnu::packed]];
+ char d;
+ int c [[gnu::packed]];
+};
+
+extern struct foo x;
+
+void g()
+{
+ struct foo {
+ char a;
+ int b [[gnu::packed]];
+ char d;
+ int c [[gnu::packed]];
+ };
+
+ extern struct foo y;
+ extern typeof(*(1 ? &x : &y)) x;
+}
+
+void h()
+{
+ struct foo {
+ char a;
+ int b;
+ char d;
+ int c;
+ }* z = &x; /* { dg-error "incompatible" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-composite-2.c
b/gcc/testsuite/gcc.dg/gnu23-tag-composite-2.c
new file mode 100644
index 00000000000..61f2feef183
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-composite-2.c
@@ -0,0 +1,30 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// attributes
+
+struct [[gnu::designated_init]] buf { char x; };
+
+struct buf s = { 0 }; /* { dg-warning "positional" }
*/
+
+void j()
+{
+ struct buf { char x; } t = { 0 };
+ typeof(*(1 ? &s : &t)) u = { 0 }; /* { dg-warning "positional" }
*/
+ typeof(*(1 ? &t : &s)) v = { 0 }; /* { dg-warning "positional" }
*/
+}
+
+
+struct bar { struct buf y; };
+extern struct bar a;
+struct bar a = { { 0 } }; /* { dg-warning "positional" }
*/
+
+void k()
+{
+ struct buf { char x; } t = { 0 };
+ struct bar { struct buf y; } b;
+ extern typeof(*(1 ? &a : &b)) a;
+ typeof(*(1 ? &a : &b)) c = { { 0 } }; /* { dg-warning "positional" }
*/
+}
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-composite-3.c
b/gcc/testsuite/gcc.dg/gnu23-tag-composite-3.c
new file mode 100644
index 00000000000..f69e9ee1379
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-composite-3.c
@@ -0,0 +1,24 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -std=gnu23" } */
+
+// struct with variably-modified member
+
+struct s { char (*y)[]; } s;
+
+int
+foo ()
+{
+ int n = 10;
+ struct s { char (*y)[n]; } t;
+ typeof(*(1 ? &s : &t)) u;
+ return sizeof(*u.y);
+}
+
+int main()
+{
+ if (10 != foo())
+ __builtin_abort();
+
+ return 0;
+}
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-composite-4.c
b/gcc/testsuite/gcc.dg/gnu23-tag-composite-4.c
new file mode 100644
index 00000000000..f3cb7369d4a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-composite-4.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -std=gnu23" } */
+
+// struct with variable size
+
+
+
+int
+foo ()
+{
+ int n = 10;
+ struct s { char buf[n]; } s;
+ {
+ int m = 10;
+ struct s { char buf[m]; } t;
+ typeof(*(1 ? &s : &t)) u;
+ return sizeof(u.buf);
+ }
+}
+
+int main()
+{
+ if (10 != foo())
+ __builtin_abort();
+
+ return 0;
+}
+
--
2.39.2