diff mbox series

[C,3/6] c23: tag compatibility rules for struct and unions

Message ID 00f5c725f1e5234a8f5f396c393d4d09159c6eae.camel@tugraz.at
State New
Headers show
Series [C,1/6] c: reorganize recursive type checking | expand

Commit Message

Martin Uecker Aug. 26, 2023, 4:23 p.m. UTC
Implement redeclaration and compatibility rules for
structures and unions in C23.

gcc/c/:
	* c-decl.cc (previous_tag): New function.
	(get_parm_info): Turn off warning for C2X.
	(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): Ingore qualifiers.
	(digest_init): Add error.
	(initialized_elementwise_p): Allow compatible types.

gcc/testsuite/:
	* gcc.dg/c2x-enum-7.c: Remove warning.
	* gcc.dg/c2x-tag-1.c: New test.
	* gcc.dg/c2x-tag-2.c: New test.
	* gcc.dg/c2x-tag-3.c: New test.
	* gcc.dg/c2x-tag-4.c: New test.
	* gcc.dg/c2x-tag-5.c: New test.
	* gcc.dg/c2x-tag-6.c: New test.
	* gcc.dg/c2x-tag-7.c: New test.
	* gcc.dg/c2x-tag-8.c: New test.
	* gcc.dg/c2x-tag-9.c: New test.
	* gcc.dg/c2x-tag-10.c: New test.
---
 gcc/c/c-decl.cc                   | 56 ++++++++++++++++++++++---
 gcc/c/c-tree.h                    |  1 +
 gcc/c/c-typeck.cc                 | 38 +++++++++++++----
 gcc/testsuite/gcc.dg/c2x-enum-7.c |  6 +--
 gcc/testsuite/gcc.dg/c2x-tag-1.c  | 68 +++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-10.c | 31 ++++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-2.c  | 43 +++++++++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-3.c  | 16 ++++++++
 gcc/testsuite/gcc.dg/c2x-tag-4.c  | 19 +++++++++
 gcc/testsuite/gcc.dg/c2x-tag-5.c  | 26 ++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-6.c  | 34 ++++++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-7.c  | 28 +++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-8.c  | 25 ++++++++++++
 gcc/testsuite/gcc.dg/c2x-tag-9.c  | 12 ++++++
 14 files changed, 387 insertions(+), 16 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-10.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-4.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-5.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-6.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-7.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-8.c
 create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-9.c

Comments

Joseph Myers Nov. 7, 2023, 11:18 p.m. UTC | #1
On Sat, 26 Aug 2023, Martin Uecker via Gcc-patches wrote:

> 	types (convert_for_assignment): Ingore qualifiers.

"Ignore".

> @@ -1993,6 +1993,24 @@ locate_old_decl (tree decl)
>  	    decl, TREE_TYPE (decl));
>  }
>  
> +static tree
> +previous_tag (tree type)

This function needs a comment documenting its semantics.

> @@ -8651,6 +8672,12 @@ start_struct (location_t loc, enum tree_code code, tree name,
>  
>    if (name != NULL_TREE)
>      ref = lookup_tag (code, name, true, &refloc);
> +
> +  /* For C2X, even if we already have a completed definition,
> +     we do not use it. We will check for consistency later.  */
> +  if (flag_isoc2x && ref && TYPE_SIZE (ref))
> +    ref = NULL_TREE;
> +
>    if (ref && TREE_CODE (ref) == code)
>      {
>        if (TYPE_STUB_DECL (ref))

This comes before the check for nested redefinitions (which are still 
invalid) - so meaning that, if ref is set to NULL_TREE here, the check 
for nested redefinitions won't apply.

You have a testcase for nested redefinitions in a slightly different case 
(where the struct's first definition hasn't finished when the nested 
definition is encountered).  But what about the case where: first, the 
struct gets defined; then, in the same scope, it gets redefined, with the 
redefinition containing a nested redefinition?  I don't see anything here 
to detect that case of nested redefinitions

For enums, note that nested redefinitions include cases where the nesting 
is inside an enum type specifier (currently diagnosed by GCC following an 
ordinary redefinition path, not one for nested definitions).

typedef __SIZE_TYPE__ size_t;
enum e : typeof (sizeof (enum e : size_t { A })) { A };

is invalid because the definitions of enum e are nested, so should be 
diagnosed, and there should be a test that it is.

> @@ -8315,6 +8332,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 %qT %qT", type, TREE_TYPE (inside_init));
> +	  return error_mark_node;
> +	}

I'd expect some words between the two type names, or explaining how they 
relate to the initialization, rather than just two type names in 
succession with no explanation of what's the type of the initializer and 
what's the type of the object being initialized.

> diff --git a/gcc/testsuite/gcc.dg/c2x-tag-1.c b/gcc/testsuite/gcc.dg/c2x-tag-1.c

> +struct r { int a; char b[0]; };

I tend to think tests such as this, involving GNU extensions ([0] arrays), 
should go in gnu23-* tests not c23-* ones.

(I'm currently testing the final C2X -> C23 patch, that renames existing 
tests.  The next revision of this patch series will need updating for the 
renaming in both file names and file contents.)

> +++ b/gcc/testsuite/gcc.dg/c2x-tag-10.c

This is definitely a GNU extensions test (VLAs in structures).

> +++ b/gcc/testsuite/gcc.dg/c2x-tag-4.c

Another GNU extensions test (GNU attributes).

> diff --git a/gcc/testsuite/gcc.dg/c2x-tag-7.c b/gcc/testsuite/gcc.dg/c2x-tag-7.c

Another GNU extensions test (VLAs in structures).
diff mbox series

Patch

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 1f9eb44dbaa..c5c6a853fa9 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -1993,6 +1993,24 @@  locate_old_decl (tree decl)
 	    decl, TREE_TYPE (decl));
 }
 
+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
@@ -8442,11 +8460,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_isoc2x)
+		    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,
@@ -8651,6 +8672,12 @@  start_struct (location_t loc, enum tree_code code, tree name,
 
   if (name != NULL_TREE)
     ref = lookup_tag (code, name, true, &refloc);
+
+  /* For C2X, even if we already have a completed definition,
+     we do not use it. We will check for consistency later.  */
+  if (flag_isoc2x && ref && TYPE_SIZE (ref))
+    ref = NULL_TREE;
+
   if (ref && TREE_CODE (ref) == code)
     {
       if (TYPE_STUB_DECL (ref))
@@ -9439,6 +9466,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_isoc2x)
+    {
+      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 7c5234e80fd..511fd9ee0e5 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -747,6 +747,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 41ef05f005c..802c727d9d3 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1058,6 +1058,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.  */
 
@@ -1243,11 +1260,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_isoc2x)
+	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))
@@ -6978,7 +6995,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);
 
@@ -8315,6 +8332,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 %qT %qT", type, TREE_TYPE (inside_init));
+	  return error_mark_node;
+	}
+
       if (require_constant
 	  && TREE_CODE (inside_init) == COMPOUND_LITERAL_EXPR)
 	{
@@ -10399,7 +10423,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/c2x-enum-7.c b/gcc/testsuite/gcc.dg/c2x-enum-7.c
index 08bae31d82c..d4ddcc821dc 100644
--- a/gcc/testsuite/gcc.dg/c2x-enum-7.c
+++ b/gcc/testsuite/gcc.dg/c2x-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/c2x-tag-1.c b/gcc/testsuite/gcc.dg/c2x-tag-1.c
new file mode 100644
index 00000000000..0cda7aa0c34
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-1.c
@@ -0,0 +1,68 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// 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[0]; };
+struct r { int a; char b[1]; }; /* { dg-error "redefinition of struct or union" } */
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-10.c b/gcc/testsuite/gcc.dg/c2x-tag-10.c
new file mode 100644
index 00000000000..39abcb2db60
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-10.c
@@ -0,0 +1,31 @@ 
+/* { dg-do compile }
+ * { dg-options "-std=c2x" } */
+
+// 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/c2x-tag-2.c b/gcc/testsuite/gcc.dg/c2x-tag-2.c
new file mode 100644
index 00000000000..a68392e1fab
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-2.c
@@ -0,0 +1,43 @@ 
+/* { dg-do { compile xfail { *-*-* } } }
+ * { dg-options "-std=c2x" }
+ */
+
+// 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/c2x-tag-3.c b/gcc/testsuite/gcc.dg/c2x-tag-3.c
new file mode 100644
index 00000000000..bafb08ca11d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-3.c
@@ -0,0 +1,16 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// 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/c2x-tag-4.c b/gcc/testsuite/gcc.dg/c2x-tag-4.c
new file mode 100644
index 00000000000..b7c793c2dce
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-4.c
@@ -0,0 +1,19 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// conflicting attributes
+
+extern struct __attribute__(( transaction_safe )) foo { int x; } x;
+extern struct __attribute__(( unused )) foo2 { int x; } x2;
+extern struct __attribute__(( may_alias )) foo3 { int x; } x3;
+
+void test(void)
+{
+  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/c2x-tag-5.c b/gcc/testsuite/gcc.dg/c2x-tag-5.c
new file mode 100644
index 00000000000..b597d6403d2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-5.c
@@ -0,0 +1,26 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// 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/c2x-tag-6.c b/gcc/testsuite/gcc.dg/c2x-tag-6.c
new file mode 100644
index 00000000000..bf7cfb342d4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-6.c
@@ -0,0 +1,34 @@ 
+/*
+ * { dg-do { run xfail { "*-*-*" } } }
+ * { dg-options "-std=c2x" }
+ */
+
+// 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/c2x-tag-7.c b/gcc/testsuite/gcc.dg/c2x-tag-7.c
new file mode 100644
index 00000000000..7ec121fe80e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-7.c
@@ -0,0 +1,28 @@ 
+/* 
+ * { dg-do compile } 
+ * { dg-options "-Wno-vla -std=gnu2x" } 
+ */
+
+// 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/c2x-tag-8.c b/gcc/testsuite/gcc.dg/c2x-tag-8.c
new file mode 100644
index 00000000000..d1f503f23ba
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-8.c
@@ -0,0 +1,25 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// (in-)completeness
+
+struct foo {
+	char x[10];
+} x;
+
+struct foo {
+	_Static_assert(_Generic(&x, struct foo*: 0, default: 1));
+	char x[_Generic(&x, struct foo*: 1, default: 10)];
+};
+
+void f(void)
+{
+	struct foo { char x[_Generic(&x, struct foo*: 1, default: 10)]; };
+
+	struct foo z;
+	_Static_assert(10 == sizeof(z.x), "");
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c2x-tag-9.c b/gcc/testsuite/gcc.dg/c2x-tag-9.c
new file mode 100644
index 00000000000..fdbae7baf46
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-tag-9.c
@@ -0,0 +1,12 @@ 
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c2x" }
+ */
+
+// 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" } */
+