diff mbox series

[V6] c23: construct composite type for tagged types

Message ID 9346830c959b3d2fdc71bb174a6e81970b29e153.camel@tugraz.at
State New
Headers show
Series [V6] c23: construct composite type for tagged types | expand

Commit Message

Martin Uecker Dec. 21, 2023, 9:47 p.m. UTC
This version now sets  DECL_NONADDRESSABLE_P, DECL_PADDING_P 
and C_DECL_VARIABLE_SIZE and adds three new tests:
c23-tag-alias-7.c, c23-tag-composite-10.c, and 
gnu23-tag-composite-5.c.

Martin



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-alias-7.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/c23-tag-composite-10.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.dg/gnu23-tag-composite-5.c: New test.
---
 gcc/c/c-decl.cc                              |  21 +--
 gcc/c/c-typeck.cc                            | 140 ++++++++++++++++---
 gcc/testsuite/gcc.dg/c23-tag-alias-6.c       |  32 +++++
 gcc/testsuite/gcc.dg/c23-tag-alias-7.c       |  34 +++++
 gcc/testsuite/gcc.dg/c23-tag-composite-1.c   |  26 ++++
 gcc/testsuite/gcc.dg/c23-tag-composite-10.c  |  35 +++++
 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 ++++
 gcc/testsuite/gcc.dg/gnu23-tag-composite-5.c |  29 ++++
 19 files changed, 601 insertions(+), 27 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-6.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-7.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-composite-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-composite-10.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
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-composite-5.c

Comments

Joseph Myers Dec. 22, 2023, 4:27 p.m. UTC | #1
On Thu, 21 Dec 2023, Martin Uecker wrote:

> This version now sets  DECL_NONADDRESSABLE_P, DECL_PADDING_P 
> and C_DECL_VARIABLE_SIZE and adds three new tests:
> c23-tag-alias-7.c, c23-tag-composite-10.c, and 
> gnu23-tag-composite-5.c.

This version is OK.
diff mbox series

Patch

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 1c817297d1c..2d9139d09d2 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,87 @@  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 t = composite_type_internal (ta, tb, cache);
+	      tree f = build_decl (input_location, FIELD_DECL, DECL_NAME (a), t);
+
+	      DECL_PACKED (f) = DECL_PACKED (a);
+	      SET_DECL_ALIGN (f, DECL_ALIGN (a));
+	      DECL_ATTRIBUTES (f) = DECL_ATTRIBUTES (a);
+	      C_DECL_VARIABLE_SIZE (f) = C_TYPE_VARIABLE_SIZE (t);
+
+	      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_NONADDRESSABLE_P (f) = true;
+		  DECL_PADDING_P (f) = !DECL_NAME (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 +607,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 +653,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 +683,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 +692,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 +705,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 +714,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 +736,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 +5665,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-alias-7.c b/gcc/testsuite/gcc.dg/c23-tag-alias-7.c
new file mode 100644
index 00000000000..3316008e724
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-7.c
@@ -0,0 +1,34 @@ 
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+
+/* This test fails when the bitfield is not marked
+   nonaddressable in the composite type.  */
+
+struct foo { int x :3; } x;
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x :3; } y;
+	typeof(*(1 ? &x : &y)) *z = b;
+
+	z->x = 2;
+
+	return a->x;
+}
+
+int main()
+{
+	struct foo y;
+
+	if (2 != test_foo1(&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-10.c b/gcc/testsuite/gcc.dg/c23-tag-composite-10.c
new file mode 100644
index 00000000000..736ab2aa781
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-composite-10.c
@@ -0,0 +1,35 @@ 
+/* { dg-do run } */
+/* { dg-options "-std=c23" } */
+
+// test padding works correctly
+
+static struct fo { 
+	int a :1; 
+	long  :3; 
+	int b: 1;
+} x = { };
+
+static void foo(void* p)
+{
+	struct fo { 
+		int a :1; 
+		long  :3; 
+		int b: 1;
+	} y;
+
+	typeof(*(1 ? &x : &y))* z = p;
+	__builtin_clear_padding(z);
+}
+
+int main()
+{
+	struct fo *p = __builtin_malloc(sizeof *p);
+	__builtin_memset(p, 0xFFFF, sizeof *p);
+	foo(p);
+	p->a = 0;
+	p->b = 0;
+	if (0 != __builtin_memcmp(p, &x, sizeof *p))
+		__builtin_abort();
+	return 0;
+}
+
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;
+}
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-composite-5.c b/gcc/testsuite/gcc.dg/gnu23-tag-composite-5.c
new file mode 100644
index 00000000000..5b09aded369
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-composite-5.c
@@ -0,0 +1,29 @@ 
+/* { dg-do compile } 
+   { dg-options "-std=c23" } */
+
+// test that structs of variable size are detected correctly
+
+void f(int n)
+{
+	struct bar { char buf[n]; };
+	struct foo { struct bar y; } a;
+	{
+		struct bar { char buf[n]; };
+		struct foo { struct bar y; } b;
+
+		typeof((1 ? &a : &b)->y) c = { 0 };	/* { dg-error "variable-sized object may not be initialized" } */
+	}
+}
+
+void g(int n)
+{
+	struct bar { char buf[n]; };
+	struct foo { struct bar y; } a;
+	{
+		struct bar { char buf[2]; };
+		struct foo { struct bar y; } b;
+
+		typeof((1 ? &a : &b)->y) c = { 0 };	// composite type is not of variable size
+	}
+}
+