diff mbox series

[V5,C,1/4] c23: tag compatibility rules for struct and unions

Message ID 0ddfc2244740988d57b41c021b899f28f781c381.camel@tugraz.at
State New
Headers show
Series [V5,C,1/4] c23: tag compatibility rules for struct and unions | expand

Commit Message

Martin Uecker Dec. 17, 2023, 5:41 p.m. UTC
Here is the revised series.  The first three patches only
have changes in the tests as well as the return value
changes.   The fourth patch was now also revised,
with changes and tests to make sure that the composite
type works correctly for bit-fields, anonymous structs/unions,
alignment, packed structs, attributes, aliasing, etc. 
It now calls finish_struct to reuse the existing code for
setting up the struct.


Bootstrapped and regression tested on x86_64.




Implement redeclaration and compatibility rules for
structures and unions in C23.

gcc/c/:
	* c-decl.cc (previous_tag): New function.
	(parser_xref_tag): Find earlier definition.
	(get_parm_info): Turn off warning for C23.
	(start_struct): Allow redefinitons.
	(finish_struct): Diagnose conflicts.
	* c-tree.h (comptypes_same_p): Add prototype.
	* c-typeck.cc (comptypes_same_p): New function
	(comptypes_internal): Activate comparison of tagged types.
	(convert_for_assignment): Ignore qualifiers.
	(digest_init): Add error.
	(initialized_elementwise_p): Allow compatible types.

gcc/testsuite/:
	* gcc.dg/c23-enum-7.c: Remove warning.
	* gcc.dg/c23-tag-1.c: New test.
	* gcc.dg/c23-tag-2.c: New deactivated test.
	* gcc.dg/c23-tag-3.c: New test.
	* gcc.dg/c23-tag-4.c: New test.
	* gcc.dg/c23-tag-5.c: New deactivated test.
	* gcc.dg/c23-tag-6.c: New test.
	* gcc.dg/c23-tag-7.c: New test.
	* gcc.dg/c23-tag-8.c: New test.
	* gcc.dg/gnu23-tag-1.c: New test.
	* gcc.dg/gnu23-tag-2.c: New test.
	* gcc.dg/gnu23-tag-3.c: New test.
	* gcc.dg/gnu23-tag-4.c: New test.
	* gcc.dg/pr112488-2.c: Remove warning.
---
 gcc/c/c-decl.cc                    | 72 +++++++++++++++++++++++++++---
 gcc/c/c-tree.h                     |  1 +
 gcc/c/c-typeck.cc                  | 38 +++++++++++++---
 gcc/testsuite/gcc.dg/c23-enum-7.c  |  6 +--
 gcc/testsuite/gcc.dg/c23-tag-1.c   | 67 +++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-2.c   | 43 ++++++++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-3.c   | 16 +++++++
 gcc/testsuite/gcc.dg/c23-tag-4.c   | 26 +++++++++++
 gcc/testsuite/gcc.dg/c23-tag-5.c   | 33 ++++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-6.c   | 58 ++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-7.c   | 12 +++++
 gcc/testsuite/gcc.dg/c23-tag-8.c   | 10 +++++
 gcc/testsuite/gcc.dg/gnu23-tag-1.c | 10 +++++
 gcc/testsuite/gcc.dg/gnu23-tag-2.c | 18 ++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-3.c | 28 ++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-4.c | 31 +++++++++++++
 gcc/testsuite/gcc.dg/pr112488-2.c  |  2 +-
 17 files changed, 454 insertions(+), 17 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-4.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-5.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-6.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-7.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-8.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-1.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-2.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-3.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-4.c

Comments

Joseph Myers Dec. 19, 2023, 9:48 p.m. UTC | #1
On Sun, 17 Dec 2023, Martin Uecker wrote:

> Here is the revised series.  The first three patches only
> have changes in the tests as well as the return value
> changes.   The fourth patch was now also revised,
> with changes and tests to make sure that the composite
> type works correctly for bit-fields, anonymous structs/unions,
> alignment, packed structs, attributes, aliasing, etc. 
> It now calls finish_struct to reuse the existing code for
> setting up the struct.
> 
> 
> Bootstrapped and regression tested on x86_64.
> 
> 
> 
> 
> Implement redeclaration and compatibility rules for
> structures and unions in C23.

Patch 1 is OK.
diff mbox series

Patch

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 039a66fef09..0e6b4a5248b 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2037,6 +2037,28 @@  locate_old_decl (tree decl)
 	    decl, TREE_TYPE (decl));
 }
 
+
+/* Helper function.  For a tagged type, it finds the declaration
+   for a visible tag declared in the the same scope if such a
+   declaration exists.  */
+static tree
+previous_tag (tree type)
+{
+  struct c_binding *b = NULL;
+  tree name = TYPE_NAME (type);
+
+  if (name)
+    b = I_TAG_BINDING (name);
+
+  if (b)
+    b = b->shadowed;
+
+  if (b && B_IN_CURRENT_SCOPE (b))
+    return b->decl;
+
+  return NULL_TREE;
+}
+
 /* Subroutine of duplicate_decls.  Compare NEWDECL to OLDDECL.
    Returns true if the caller should proceed to merge the two, false
    if OLDDECL should simply be discarded.  As a side effect, issues
@@ -8573,11 +8595,14 @@  get_parm_info (bool ellipsis, tree expr)
 	  if (TREE_CODE (decl) != UNION_TYPE || b->id != NULL_TREE)
 	    {
 	      if (b->id)
-		/* The %s will be one of 'struct', 'union', or 'enum'.  */
-		warning_at (b->locus, 0,
-			    "%<%s %E%> declared inside parameter list"
-			    " will not be visible outside of this definition or"
-			    " declaration", keyword, b->id);
+		{
+		  /* The %s will be one of 'struct', 'union', or 'enum'.  */
+		  if (!flag_isoc23)
+		    warning_at (b->locus, 0,
+				"%<%s %E%> declared inside parameter list"
+				" will not be visible outside of this definition or"
+				" declaration", keyword, b->id);
+		}
 	      else
 		/* The %s will be one of 'struct', 'union', or 'enum'.  */
 		warning_at (b->locus, 0,
@@ -8668,6 +8693,16 @@  parser_xref_tag (location_t loc, enum tree_code code, tree name,
      present, only a definition in the current scope is relevant.  */
 
   ref = lookup_tag (code, name, has_enum_type_specifier, &refloc);
+
+  /* If the visble type is still being defined, see if there is
+     an earlier definition (which may be complete).  */
+  if (flag_isoc23 && ref && C_TYPE_BEING_DEFINED (ref))
+    {
+      tree vis = previous_tag (ref);
+      if (vis)
+	ref = vis;
+    }
+
   /* If this is the right type of tag, return what we found.
      (This reference will be shadowed by shadow_tag later if appropriate.)
      If this is the wrong type of tag, do not return it.  If it was the
@@ -8782,6 +8817,14 @@  start_struct (location_t loc, enum tree_code code, tree name,
 
   if (name != NULL_TREE)
     ref = lookup_tag (code, name, true, &refloc);
+
+  /* For C23, even if we already have a completed definition,
+     we do not use it. We will check for consistency later.
+     If we are in a nested redefinition the type is not
+     complete. We will then detect this below.  */
+  if (flag_isoc23 && ref && TYPE_SIZE (ref))
+    ref = NULL_TREE;
+
   if (ref && TREE_CODE (ref) == code)
     {
       if (TYPE_STUB_DECL (ref))
@@ -9581,6 +9624,25 @@  finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
       warning_at (loc, 0, "union cannot be made transparent");
     }
 
+  /* Check for consistency with previous definition.  */
+  if (flag_isoc23)
+    {
+      tree vistype = previous_tag (t);
+      if (vistype
+	  && TREE_CODE (vistype) == TREE_CODE (t)
+	  && !C_TYPE_BEING_DEFINED (vistype))
+	{
+	  TYPE_STUB_DECL (vistype) = TYPE_STUB_DECL (t);
+	  if (c_type_variably_modified_p (t))
+	    error ("redefinition of struct or union %qT with variably "
+		   "modified type", t);
+	  else if (!comptypes_same_p (t, vistype))
+	    error ("redefinition of struct or union %qT", t);
+	}
+    }
+
+  C_TYPE_BEING_DEFINED (t) = 0;
+
   tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t));
   for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
     {
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index b325723a734..e53948f21f3 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -757,6 +757,7 @@  extern tree c_objc_common_truthvalue_conversion (location_t, tree);
 extern tree require_complete_type (location_t, tree);
 extern bool same_translation_unit_p (const_tree, const_tree);
 extern int comptypes (tree, tree);
+extern bool comptypes_same_p (tree, tree);
 extern int comptypes_check_different_types (tree, tree, bool *);
 extern int comptypes_check_enum_int (tree, tree, bool *);
 extern bool c_mark_addressable (tree, bool = false);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 022e3c6386b..0d514ec3570 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1080,6 +1080,23 @@  comptypes (tree type1, tree type2)
   return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 
+
+/* Like comptypes, but it returns non-zero only for identical
+   types.  */
+
+bool
+comptypes_same_p (tree type1, tree type2)
+{
+  struct comptypes_data data = { };
+  bool ret = comptypes_internal (type1, type2, &data);
+
+  if (data.different_types_p)
+    return false;
+
+  return ret;
+}
+
+
 /* Like comptypes, but if it returns non-zero because enum and int are
    compatible, it sets *ENUM_AND_INT_P to true.  */
 
@@ -1266,11 +1283,11 @@  comptypes_internal (const_tree type1, const_tree type2,
     case ENUMERAL_TYPE:
     case RECORD_TYPE:
     case UNION_TYPE:
-      if (false)
-	{
-	  return tagged_types_tu_compatible_p (t1, t2, data);
-	}
-      return false;
+
+      if (!flag_isoc23)
+	return false;
+
+      return tagged_types_tu_compatible_p (t1, t2, data);
 
     case VECTOR_TYPE:
       return known_eq (TYPE_VECTOR_SUBPARTS (t1), TYPE_VECTOR_SUBPARTS (t2))
@@ -7089,7 +7106,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
   /* Aggregates in different TUs might need conversion.  */
   if ((codel == RECORD_TYPE || codel == UNION_TYPE)
       && codel == coder
-      && comptypes (type, rhstype))
+      && comptypes (TYPE_MAIN_VARIANT (type), TYPE_MAIN_VARIANT (rhstype)))
     return convert_and_check (expr_loc != UNKNOWN_LOCATION
 			      ? expr_loc : location, type, rhs);
 
@@ -8461,6 +8478,13 @@  digest_init (location_t init_loc, tree type, tree init, tree origtype,
 	   conversion.  */
 	inside_init = convert (type, inside_init);
 
+      if ((code == RECORD_TYPE || code == UNION_TYPE)
+	  && !comptypes (TYPE_MAIN_VARIANT (type), TYPE_MAIN_VARIANT (TREE_TYPE (inside_init))))
+	{
+	  error_init (init_loc, "invalid initializer");
+	  return error_mark_node;
+	}
+
       if (require_constant
 	  && TREE_CODE (inside_init) == COMPOUND_LITERAL_EXPR)
 	{
@@ -10546,7 +10570,7 @@  initialize_elementwise_p (tree type, tree value)
     return !VECTOR_TYPE_P (value_type);
 
   if (AGGREGATE_TYPE_P (type))
-    return type != TYPE_MAIN_VARIANT (value_type);
+    return !comptypes (type, TYPE_MAIN_VARIANT (value_type));
 
   return false;
 }
diff --git a/gcc/testsuite/gcc.dg/c23-enum-7.c b/gcc/testsuite/gcc.dg/c23-enum-7.c
index c9ef0882b41..ff8e145c2a8 100644
--- a/gcc/testsuite/gcc.dg/c23-enum-7.c
+++ b/gcc/testsuite/gcc.dg/c23-enum-7.c
@@ -26,17 +26,15 @@  enum e13 : short x13; /* { dg-error "'enum' underlying type may not be specified
 enum e14 : short f14 (); /* { dg-error "'enum' underlying type may not be specified here" } */
 typeof (enum e15 : long) x15; /* { dg-error "'enum' underlying type may not be specified here" } */
 int f16 (enum e16 : char p); /* { dg-error "'enum' underlying type may not be specified here" } */
-/* { dg-warning "will not be visible outside of this definition or declaration" "warning" { target *-*-* } .-1 } */
 int f17 (enum e17 : char); /* { dg-error "'enum' underlying type may not be specified here" } */
-/* { dg-warning "will not be visible outside of this definition or declaration" "warning" { target *-*-* } .-1 } */
 struct s18 { enum e18 : int x; }; /* { dg-error "'enum' underlying type may not be specified here" } */
 
 /* But those are OK if the enum content is defined.  */
 enum e19 : short { E19 } x19;
 enum e20 : long { E20 } f20 ();
 typeof (enum e21 : long { E21 }) x21;
-int f22 (enum e22 : long long { E22 } p); /* { dg-warning "will not be visible outside of this definition or declaration" } */
-int f23 (enum e23 : long long { E23 } p); /* { dg-warning "will not be visible outside of this definition or declaration" } */
+int f22 (enum e22 : long long { E22 } p);
+int f23 (enum e23 : long long { E23 } p);
 struct s24 { enum e24 : int { E24 } x; };
 
 /* Incompatible kinds of tags in the same scope are errors.  */
diff --git a/gcc/testsuite/gcc.dg/c23-tag-1.c b/gcc/testsuite/gcc.dg/c23-tag-1.c
new file mode 100644
index 00000000000..4a6207ec626
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-1.c
@@ -0,0 +1,67 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// allowed and forbidden redefinitions of the same struct/union in the same scope
+
+typedef struct p { int a; } pd_t;
+typedef struct p { int a; } pd_t;
+
+typedef struct bar { int x; } X;
+typedef struct bar { float x; } Y; /* { dg-error "redefinition of struct or union" } */
+
+void test(void)
+{
+	struct foo { int x; };
+	struct foo { float x; }; /* { dg-error "redefinition of struct or union" } */
+}
+
+struct aa { int a; };
+
+void f(void)
+{
+	typedef struct aa A;
+	struct bb { struct aa a; } x;
+	struct aa { int a; };
+	typedef struct aa A;		/* { dg-error "redefinition" } */
+	struct bb { struct aa a; } y; /* { dg-error "redefinition of struct or union" } */
+	(void)x; (void)y;
+}
+
+
+
+void h(void)
+{
+	struct a2 { int a; };
+	{
+		typedef struct a2 A;
+		struct b2 { struct a2 a; } x;
+		struct a2 { int a; };
+		typedef struct a2 A;		/* { dg-error "redefinition" } */
+		struct b2 { struct a2 a; } y; /* { dg-error "redefinition of struct or union" } */
+		(void)x; (void)y;
+	}
+}
+
+
+union cc { int x; float y; } z;
+union cc { int x; float y; } z1;
+union cc { float y; int x; } z2;	/* { dg-error "redefinition of struct or union" } */
+
+void g(void)
+{
+	struct s { int a; };
+	struct s { int a; } x0;
+	struct p { struct s c; } y1 = { x0 };
+	struct p { struct s { int a; } c; } y = { x0 };
+}
+
+struct q { struct { int a; }; }; 
+struct q { struct { int a; }; }; 
+struct q { int a; };  /* { dg-error "redefinition of struct or union" } */
+
+struct r { int a; char b[]; };
+struct r { int a; char b[]; };
+struct r { int a; char b[1]; }; /* { dg-error "redefinition of struct or union" } */
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-2.c b/gcc/testsuite/gcc.dg/c23-tag-2.c
new file mode 100644
index 00000000000..5dd4a21e9df
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-2.c
@@ -0,0 +1,43 @@ 
+/* { dg-do compile { target { ! "*-*-*" } } }
+ * { dg-options "-std=c23" }
+ */
+
+// compatibility of structs in assignment
+
+typedef struct p { int a; } pd_t;
+
+void test1(void)
+{
+  pd_t y0;
+  struct p { int a; } x;
+  y0 = x;
+}
+
+void test2(void)
+{
+  struct p { int a; } x;
+  struct p y0 = x;
+}
+
+void test3(void)
+{
+  struct p { int a; } x;
+  pd_t y0 = x;
+}
+
+typedef struct p { int a; } p2_t;
+
+void test4(void)
+{
+  p2_t x;
+  pd_t y0 = x;
+}
+
+void test5(void)
+{
+  struct q { int a; } a;
+  struct q { int a; } b;
+  a = b;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-3.c b/gcc/testsuite/gcc.dg/c23-tag-3.c
new file mode 100644
index 00000000000..4847e783c82
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-3.c
@@ -0,0 +1,16 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// conflicting types via linkage
+
+extern struct foo { int x; } x;
+extern struct bar { float x; } y;
+
+void test(void)
+{
+  extern struct foo { int x; } x;
+  extern struct bar { int x; } y;	/* { dg-error "conflicting types" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-4.c b/gcc/testsuite/gcc.dg/c23-tag-4.c
new file mode 100644
index 00000000000..8083c43f607
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-4.c
@@ -0,0 +1,26 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// conflicting types for anonymous structs / unions
+
+extern struct { int x; } a;
+extern struct { int x; } a;	/* { dg-error "conflicting types" } */
+
+extern union { int x; } b;
+extern union { int x; } b;	/* { dg-error "conflicting types" } */
+
+typedef struct { int x; } u;
+typedef struct { int x; } v;
+
+u c;
+v c;				/* { dg-error "conflicting types" } */
+
+typedef union { int x; } q;
+typedef union { int x; } r;
+
+q d;
+r d;				/* { dg-error "conflicting types" } */
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-5.c b/gcc/testsuite/gcc.dg/c23-tag-5.c
new file mode 100644
index 00000000000..ff7bbd662aa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-5.c
@@ -0,0 +1,33 @@ 
+/* { dg-do run { target { ! "*-*-*" } } }
+ * { dg-options "-std=c23 -fpermissive" }
+ */
+
+// nesting and parameters
+
+#define product_type(T, A, B) \
+struct product_ ## T { A a ; B b ; }
+#define sum_type(T, A, B) \
+struct sum_ ## T { _Bool flag ; union { A a ; B b ; }; }
+
+float foo1(product_type(iSfd_, int, sum_type(fd, float, double)) x)
+{
+	return x.b.a;
+}
+
+static void test1(void)
+{
+	product_type(iSfd_, int, sum_type(fd, float, double)) y = { 3, { 1, { .a = 1. } } };
+	product_type(iSfd_, int, sum_type(fd, float, double)) z = y;
+	product_type(iSfd_, int, sum_type(fd, float, double)) *zp = &y;
+	float a = foo1(y);
+	product_type(iSid_, int, sum_type(id, int, double)) *wp = &y; /* { dg-warning "incompatible pointer type" } */
+	float b = foo1(y);
+	product_type(iSid_, int, sum_type(id, int, double)) w = *wp;
+	(void)a; (void)b; (void)z; (void)zp; (void)w; (void)wp;
+}
+
+int main()
+{
+	test1();
+}
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-6.c b/gcc/testsuite/gcc.dg/c23-tag-6.c
new file mode 100644
index 00000000000..1b65ed3e35d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-6.c
@@ -0,0 +1,58 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// (in-)completeness
+
+struct foo {
+	char x[10];
+} x;
+
+// complete, same type
+
+struct foo {
+	_Static_assert(_Generic(&x, struct foo*: 1, default: 0));
+	char x[_Generic(&x, struct foo*: 10, default: 1)];
+	_Static_assert(_Generic(0, struct foo: 0, default: 1));
+};
+
+// incomplete, same type
+
+struct bar* p;
+struct bar {
+	_Static_assert(_Generic(p, struct bar*: 1, default: 0));
+	char x[_Generic(p, struct bar*: 10, default: 1)];
+	_Static_assert(_Generic(0, struct bar: 0, default: 1));	/* { dg-error "incomplete type" } */
+};
+
+struct bar {
+	char x[10];
+};
+
+struct h *hp;
+
+void f(void)
+{
+	// again incomplete, different type
+
+	struct foo { 
+		char x[_Generic(&x, struct foo*: 1, default: 10)]; 
+		_Static_assert(_Generic(0, struct foo: 0, default: 1));	/* { dg-error "incomplete type" } */
+	};
+
+	struct foo z;
+	_Static_assert(10 == sizeof(z.x), "");
+
+	// still incomplete, different type
+
+	struct h { 
+		char x[_Generic(hp, struct h*: 1, default: 10)]; 
+		_Static_assert(_Generic(0, struct h: 0, default: 1));	/* { dg-error "incomplete type" } */
+	};
+
+	struct h y;
+	_Static_assert(10 == sizeof(y.x), "");
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-7.c b/gcc/testsuite/gcc.dg/c23-tag-7.c
new file mode 100644
index 00000000000..dd3b5988e24
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-7.c
@@ -0,0 +1,12 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// recursive declarations
+
+extern struct bar { struct bar* p; int x; } b;
+extern struct bar { struct bar* p; int x; } b;
+
+struct foo { struct foo { struct foo* p; int x; }* p; int x; } a;	/* { dg-error "nested" } */
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-8.c b/gcc/testsuite/gcc.dg/c23-tag-8.c
new file mode 100644
index 00000000000..8b3b5ef5dfe
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-8.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile }
+   { dg-options "-std=c23" } */
+
+void foo(void)
+{
+	struct bar { struct bar* next; };
+	struct bar { struct bar* next; };
+	struct bar { struct bar { struct bar* next; }* next; };	/* { dg-error "nested" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-1.c b/gcc/testsuite/gcc.dg/gnu23-tag-1.c
new file mode 100644
index 00000000000..3c0303d4c3f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-1.c
@@ -0,0 +1,10 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=gnu23" }
+ */
+
+struct r { int a; char b[]; };
+struct r { int a; char b[0]; };	/* allowed GNU extension */
+struct r { int a; char b[1]; }; /* { dg-error "redefinition of struct or union" } */
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-2.c b/gcc/testsuite/gcc.dg/gnu23-tag-2.c
new file mode 100644
index 00000000000..1888641402f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-2.c
@@ -0,0 +1,18 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=gnu23" }
+ */
+
+// conflicting attributes
+
+extern struct [[gnu::transaction_safe]] foo { int x; } x;
+extern struct [[gnu::unused]] foo2 { int x; } x2;
+extern struct [[gnu::may_alias]] foo3 { int x; } x3;
+
+void test()
+{
+  extern struct foo { int x; } x;		/* { dg-error "conflicting types" } */
+  extern struct foo2 { int x; } x2;
+  extern struct foo3 { int x; } x3;
+}
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-3.c b/gcc/testsuite/gcc.dg/gnu23-tag-3.c
new file mode 100644
index 00000000000..8919144ef3a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-3.c
@@ -0,0 +1,28 @@ 
+/* 
+ * { dg-do compile } 
+ * { dg-options "-Wno-vla -std=gnu23" } 
+ */
+
+// arrays in structs
+
+void foo(int n, int m)
+{
+	struct f { int b; int a[n]; };
+	struct f { int b; int a[n]; };	/* { dg-error "redefinition of struct or union" } */
+	struct f { int b; int a[m]; };	/* { dg-error "redefinition of struct or union" } */
+	struct f { int b; int a[5]; };	/* { dg-error "redefinition of struct or union" } */
+	struct f { int b; int a[]; };	/* { dg-error "redefinition of struct or union" } */
+
+	struct g { int a[n]; int b; };
+	struct g { int a[n]; int b; };	/* { dg-error "redefinition of struct or union" } */
+	struct g { int a[m]; int b; };	/* { dg-error "redefinition of struct or union" } */
+	struct g { int a[4]; int b; };	/* { dg-error "redefinition of struct or union" } */
+
+	struct h { int (*a)[n]; int b; };
+	struct h { int (*a)[n]; int b; };	/* { dg-error "redefinition of struct or union" } */
+	struct h { int (*a)[m]; int b; };	/* { dg-error "redefinition of struct or union" } */
+	struct h { int (*a)[4]; int b; };	/* { dg-error "redefinition of struct or union" } */
+	struct h { int (*a)[]; int b; };	/* { dg-error "redefinition of struct or union" } */
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-4.c b/gcc/testsuite/gcc.dg/gnu23-tag-4.c
new file mode 100644
index 00000000000..8db81c7b87d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-4.c
@@ -0,0 +1,31 @@ 
+/* { dg-do compile }
+ * { dg-options "-std=gnu23" } */
+
+// structs with variably modified types
+
+void bar(int n, int m)
+{
+	struct f { int b; int a[n]; } *x;
+	{ struct f { int b; int a[n]; } *x2 = x; }
+	{ struct f { int b; int a[m]; } *x2 = x; }
+	{ struct f { int b; int a[5]; } *x2 = x; }
+	{ struct f { int b; int a[0]; }  *x2 = x; }
+	{ struct f { int b; int a[]; }  *x2 = x; }
+
+	struct g { int a[n]; int b; } *y;
+	{ struct g { int a[n]; int b; } *y2 = y; }
+	{ struct g { int a[m]; int b; } *y2 = y; }
+	{ struct g { int a[4]; int b; } *y2 = y; }
+
+	struct h { int b; int a[5]; } *w;
+	{ struct h { int b; int a[5]; } *w2 = w; }
+	{ struct h { int b; int a[n]; } *w2 = w; }
+	{ struct h { int b; int a[m]; } *w2 = w; }
+
+	struct i { int b; int (*a)(int c[n]); } *u;
+	{ struct i { int b; int (*a)(int c[4]); } *u2 = u; }
+	{ struct i { int b; int (*a)(int c[]); } *u2 = u; }
+	{ struct i { int b; int (*a)(int c[*]); } *u2 = u; }
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/pr112488-2.c b/gcc/testsuite/gcc.dg/pr112488-2.c
index 3f0fc43eeb6..b5e5fbd3f2b 100644
--- a/gcc/testsuite/gcc.dg/pr112488-2.c
+++ b/gcc/testsuite/gcc.dg/pr112488-2.c
@@ -3,7 +3,7 @@ 
 
 extern void abort(void);
 
-int test(int *n, struct T { char a[*n], b[*n]; }*) {	/* { dg-warning "declared inside parameter list" } */
+int test(int *n, struct T { char a[*n], b[*n]; }*) {
   return sizeof(struct T) - sizeof(struct T);
 }