public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* c23 type compatibility rules, v3
@ 2023-11-16 21:36 Martin Uecker
  2023-11-16 21:38 ` [PATCH 1/4] c23: tag compatibility rules for struct and unions Martin Uecker
                   ` (17 more replies)
  0 siblings, 18 replies; 53+ messages in thread
From: Martin Uecker @ 2023-11-16 21:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers


Joseph,

this is another revised series for the C23 rules for type
compatibility.

1/4 c23: tag compatibility rules for struct and unions
2/4 c23: tag compatibility rules for enums
3/4 c23: aliasing of compatible tagged types
4/4 c23: construct composite type for tagged types


The first two were revised to address the nesting (and
other) issues you pointed out.

For 3 and 4 I only changed c2x to c23 and moved some
tests around. 3 wasn't reviewed so far and 4 still
needs some more work from my side.


Bootstrapped and regression tested on x86_64.


Martin







^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH 1/4] c23: tag compatibility rules for struct and unions
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
@ 2023-11-16 21:38 ` Martin Uecker
  2023-11-23 23:17   ` Joseph Myers
  2023-11-16 21:38 ` [PATCH 2/4] c23: tag compatibility rules for enums Martin Uecker
                   ` (16 subsequent siblings)
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2023-11-16 21:38 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers



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/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/c/c-decl.cc                    | 62 ++++++++++++++++++++++++---
 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   | 25 +++++++++++
 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 | 19 +++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-3.c | 28 +++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-4.c | 31 ++++++++++++++
 16 files changed, 411 insertions(+), 16 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

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 64d3a941cb9..194dd595334 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2039,6 +2039,28 @@ locate_old_decl (tree decl)
 	    decl, TREE_TYPE (decl));
 }
 
+
+/* Subroutine of finish_struct.  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,
@@ -8782,6 +8807,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))
@@ -9576,6 +9609,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 c6f38ec94a0..7df4d65bf7a 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -756,6 +756,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 1dbb4471a88..dc8a16df272 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))
@@ -7031,7 +7048,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);
 
@@ -8401,6 +8418,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)
 	{
@@ -10486,7 +10510,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..ff40d07aef1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-5.c
@@ -0,0 +1,33 @@
+/* { dg-do run { target { ! "*-*-*" } } }
+ * { dg-options "-std=c23" }
+ */
+
+// 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..f8eb5698af5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-6.c
@@ -0,0 +1,25 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// (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/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..73725c79ebf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-2.c
@@ -0,0 +1,19 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=gnu23" }
+ */
+
+// 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/gnu23-tag-3.c b/gcc/testsuite/gcc.dg/gnu23-tag-3.c
new file mode 100644
index 00000000000..7ec121fe80e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-3.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/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; }
+}
+
+
-- 
2.39.2



^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH 2/4] c23: tag compatibility rules for enums
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
  2023-11-16 21:38 ` [PATCH 1/4] c23: tag compatibility rules for struct and unions Martin Uecker
@ 2023-11-16 21:38 ` Martin Uecker
  2023-11-23 23:26   ` Joseph Myers
  2023-11-16 21:39 ` [PATCH 3/4] c23: aliasing of compatible tagged types Martin Uecker
                   ` (15 subsequent siblings)
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2023-11-16 21:38 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers




Allow redefinition of enum types and enumerators.  Diagnose
nested redefinitions including redefinitions in the enum
specifier for enum types with fixed underlying type.

gcc/c:
	* c-tree.h (c_parser_enum_specifier): Add parameter.
	* c-decl.cc (start_enum): Allow redefinition.
	(finish_enum): Diagnose conflicts.
	(build_enumerator): Set context.
	(diagnose_mismatched_decls): Diagnose conflicting enumerators.
	(push_decl): Preserve context for enumerators.
	* c-parser.cc (c_parser_enum_specifier): Remember when
	seen is from an enum type which is not yet defined.

gcc/testsuide/:
	* gcc.dg/c23-tag-enum-1.c: New test.
	* gcc.dg/c23-tag-enum-2.c: New test.
	* gcc.dg/c23-tag-enum-3.c: New test.
	* gcc.dg/c23-tag-enum-4.c: New test.
	* gcc.dg/c23-tag-enum-5.c: New test.
---
 gcc/c/c-decl.cc                       | 65 +++++++++++++++++++++++----
 gcc/c/c-parser.cc                     |  5 ++-
 gcc/c/c-tree.h                        |  3 +-
 gcc/c/c-typeck.cc                     |  5 ++-
 gcc/testsuite/gcc.dg/c23-tag-enum-1.c | 56 +++++++++++++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-enum-2.c | 23 ++++++++++
 gcc/testsuite/gcc.dg/c23-tag-enum-3.c |  7 +++
 gcc/testsuite/gcc.dg/c23-tag-enum-4.c | 22 +++++++++
 gcc/testsuite/gcc.dg/c23-tag-enum-5.c | 18 ++++++++
 9 files changed, 192 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-4.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-5.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 194dd595334..e5d48c3fa56 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2114,9 +2114,24 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
      given scope.  */
   if (TREE_CODE (olddecl) == CONST_DECL)
     {
-      auto_diagnostic_group d;
-      error ("redeclaration of enumerator %q+D", newdecl);
-      locate_old_decl (olddecl);
+      if (flag_isoc23
+	  && TYPE_NAME (DECL_CONTEXT (newdecl))
+	  && DECL_CONTEXT (newdecl) != DECL_CONTEXT (olddecl)
+	  && TYPE_NAME (DECL_CONTEXT (newdecl)) == TYPE_NAME (DECL_CONTEXT (olddecl)))
+	{
+	  if (!simple_cst_equal (DECL_INITIAL (olddecl), DECL_INITIAL (newdecl)))
+	    {
+	      auto_diagnostic_group d;
+	      error ("conflicting redeclaration of enumerator %q+D", newdecl);
+	      locate_old_decl (olddecl);
+	    }
+	}
+      else
+	{
+	  auto_diagnostic_group d;
+	  error ("redeclaration of enumerator %q+D", newdecl);
+	  locate_old_decl (olddecl);
+	}
       return false;
     }
 
@@ -3277,8 +3292,11 @@ pushdecl (tree x)
 
   /* Must set DECL_CONTEXT for everything not at file scope or
      DECL_FILE_SCOPE_P won't work.  Local externs don't count
-     unless they have initializers (which generate code).  */
+     unless they have initializers (which generate code).  We
+     also exclude CONST_DECLs because enumerators will get the
+     type of the enum as context.  */
   if (current_function_decl
+      && TREE_CODE (x) != CONST_DECL
       && (!VAR_OR_FUNCTION_DECL_P (x)
 	  || DECL_INITIAL (x) || !TREE_PUBLIC (x)))
     DECL_CONTEXT (x) = current_function_decl;
@@ -9737,7 +9755,7 @@ layout_array_type (tree t)
 
 tree
 start_enum (location_t loc, struct c_enum_contents *the_enum, tree name,
-	    tree fixed_underlying_type)
+	    tree fixed_underlying_type, bool potential_nesting_p)
 {
   tree enumtype = NULL_TREE;
   location_t enumloc = UNKNOWN_LOCATION;
@@ -9749,9 +9767,26 @@ start_enum (location_t loc, struct c_enum_contents *the_enum, tree name,
   if (name != NULL_TREE)
     enumtype = lookup_tag (ENUMERAL_TYPE, name, true, &enumloc);
 
+  if (enumtype != NULL_TREE && TREE_CODE (enumtype) == ENUMERAL_TYPE)
+    {
+      /* If the type is currently being defined or if we have seen an
+	 incomplete version which is now complete, this is a nested
+	 redefinition.  The later happens if the redefinition occurs
+	 inside the enum specifier itself.  */
+      if (C_TYPE_BEING_DEFINED (enumtype)
+	  || (potential_nesting_p && TYPE_VALUES (enumtype) != NULL_TREE))
+	error_at (loc, "nested redefinition of %<enum %E%>", name);
+
+     /* For C23 we allow redefinitions.  We set to zero and check for
+	consistency later.  */
+      if (flag_isoc23 && TYPE_VALUES (enumtype) != NULL_TREE)
+	enumtype = NULL_TREE;
+    }
+
   if (enumtype == NULL_TREE || TREE_CODE (enumtype) != ENUMERAL_TYPE)
     {
       enumtype = make_node (ENUMERAL_TYPE);
+      TYPE_SIZE (enumtype) = NULL_TREE;
       pushtag (loc, name, enumtype);
       if (fixed_underlying_type != NULL_TREE)
 	{
@@ -9779,9 +9814,6 @@ start_enum (location_t loc, struct c_enum_contents *the_enum, tree name,
       DECL_SOURCE_LOCATION (TYPE_STUB_DECL (enumtype)) = loc;
     }
 
-  if (C_TYPE_BEING_DEFINED (enumtype))
-    error_at (loc, "nested redefinition of %<enum %E%>", name);
-
   C_TYPE_BEING_DEFINED (enumtype) = 1;
 
   if (TYPE_VALUES (enumtype) != NULL_TREE)
@@ -10011,6 +10043,20 @@ finish_enum (tree enumtype, tree values, tree attributes)
       && !in_sizeof && !in_typeof && !in_alignof)
     struct_parse_info->struct_types.safe_push (enumtype);
 
+  /* Check for consistency with previous definition */
+  if (flag_isoc23)
+    {
+      tree vistype = previous_tag (enumtype);
+      if (vistype
+	  && TREE_CODE (vistype) == TREE_CODE (enumtype)
+	  && !C_TYPE_BEING_DEFINED (vistype))
+	{
+	  TYPE_STUB_DECL (vistype) = TYPE_STUB_DECL (enumtype);
+	  if (!comptypes_same_p (enumtype, vistype))
+	    error("conflicting redefinition of enum %qT", enumtype);
+	}
+    }
+
   C_TYPE_BEING_DEFINED (enumtype) = 0;
 
   return enumtype;
@@ -10190,6 +10236,7 @@ build_enumerator (location_t decl_loc, location_t loc,
 
   decl = build_decl (decl_loc, CONST_DECL, name, TREE_TYPE (value));
   DECL_INITIAL (decl) = value;
+  DECL_CONTEXT (decl) = the_enum->enum_type;
   pushdecl (decl);
 
   return tree_cons (decl, value, NULL_TREE);
@@ -10206,7 +10253,7 @@ c_simulate_enum_decl (location_t loc, const char *name,
 
   struct c_enum_contents the_enum;
   tree enumtype = start_enum (loc, &the_enum, get_identifier (name),
-			      NULL_TREE);
+			      NULL_TREE, false);
 
   tree value_chain = NULL_TREE;
   string_int_pair *value;
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 703f9570dbc..ff30ba198ca 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -3667,6 +3667,7 @@ c_parser_enum_specifier (c_parser *parser)
 {
   struct c_typespec ret;
   bool have_std_attrs;
+  bool potential_nesting_p = false;
   tree std_attrs = NULL_TREE;
   tree attrs;
   tree ident = NULL_TREE;
@@ -3706,6 +3707,7 @@ c_parser_enum_specifier (c_parser *parser)
 	  if (!ENUM_FIXED_UNDERLYING_TYPE_P (ret.spec))
 	    error_at (enum_loc, "%<enum%> declared both with and without "
 		      "fixed underlying type");
+	  potential_nesting_p = NULL_TREE == TYPE_VALUES (ret.spec);
 	}
       else
 	{
@@ -3776,7 +3778,8 @@ c_parser_enum_specifier (c_parser *parser)
 	 forward order at the end.  */
       tree values;
       timevar_push (TV_PARSE_ENUM);
-      type = start_enum (enum_loc, &the_enum, ident, fixed_underlying_type);
+      type = start_enum (enum_loc, &the_enum, ident, fixed_underlying_type,
+			 potential_nesting_p);
       values = NULL_TREE;
       c_parser_consume_token (parser);
       while (true)
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 7df4d65bf7a..a5dd9a37944 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -682,7 +682,8 @@ extern void c_warn_unused_attributes (tree);
 extern tree c_warn_type_attributes (tree);
 extern void shadow_tag (const struct c_declspecs *);
 extern void shadow_tag_warned (const struct c_declspecs *, int);
-extern tree start_enum (location_t, struct c_enum_contents *, tree, tree);
+extern tree start_enum (location_t, struct c_enum_contents *, tree, tree,
+			bool potential_nesting_p);
 extern bool start_function (struct c_declspecs *, struct c_declarator *, tree);
 extern tree start_decl (struct c_declarator *, struct c_declspecs *, bool,
 			tree, bool = true, location_t * = NULL);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index dc8a16df272..8116c9b3e68 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1419,6 +1419,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
     {
     case ENUMERAL_TYPE:
       {
+	if (!comptypes (ENUM_UNDERLYING_TYPE (t1), ENUM_UNDERLYING_TYPE (t2)))
+	  return false;
+
 	/* Speed up the case where the type values are in the same order.  */
 	tree tv1 = TYPE_VALUES (t1);
 	tree tv2 = TYPE_VALUES (t2);
@@ -6948,7 +6951,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
       if (checktype != error_mark_node
 	  && TREE_CODE (checktype) == ENUMERAL_TYPE
 	  && TREE_CODE (type) == ENUMERAL_TYPE
-	  && TYPE_MAIN_VARIANT (checktype) != TYPE_MAIN_VARIANT (type))
+	  && !comptypes (TYPE_MAIN_VARIANT (checktype), TYPE_MAIN_VARIANT (type)))
        {
 	  gcc_rich_location loc (location);
 	  warning_at (&loc, OPT_Wenum_conversion,
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-1.c b/gcc/testsuite/gcc.dg/c23-tag-enum-1.c
new file mode 100644
index 00000000000..0b4829cdbe3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-1.c
@@ -0,0 +1,56 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// incompatible redeclarations, conflicing redefinitions
+
+
+enum aa { A = 1 } *a;
+enum bb { B = 1 } *b;
+
+void test(void)
+{
+  enum aa { A = 1 } *c = a;
+  enum bb { B = 2 } *d = b;	/* { dg-warning "incompatible pointer type" } */
+}
+
+enum cc { C = 1 };
+enum cc { D = 1 };		/* { dg-error "conflicting redefinition" } */	
+
+enum dd { E = 1 };
+enum dd { E = 2 };		/* { dg-error "conflicting redefinition" } */	
+				/* { dg-error "redeclaration of enumerator" "" { target *-*-* } .-1 } */	
+
+
+
+void test2(void)
+{
+  enum ee *a;
+  enum ee { F = 2 } *b;
+  b = a;
+}
+
+
+enum ff { G = 2 };
+enum gg { G = 2 };		/* { dg-error "redeclaration of enumerator" } */
+enum g2 { G = 3 };		/* { dg-error "redeclaration of enumerator" } */
+
+enum hh { H = 1, H = 1 };	/* { dg-error "redeclaration of enumerator" } */
+
+enum ss { K = 2 };
+enum ss { K = 2 };
+
+enum tt { R = 2 } TT;
+enum tt {
+	R = _Generic(&TT, enum tt*: 0, default: 2)
+};
+
+enum { U = 1 };
+enum { U = 1 };			/* { dg-error "redeclaration of enumerator" } */
+
+enum { V = 1 };
+enum { V = 2 };			/* { dg-error "redeclaration of enumerator" } */
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-2.c b/gcc/testsuite/gcc.dg/c23-tag-enum-2.c
new file mode 100644
index 00000000000..1ced39974f4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-2.c
@@ -0,0 +1,23 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// incomplete during construction
+
+enum A { B = 7 } y;
+enum A { B = 7 };
+
+enum A { B = _Generic(&y, enum A*: 1, default: 7) };
+
+void g(void)
+{
+	enum A { B = _Generic(&y, enum A*: 1, default: 7) };
+	_Static_assert(7 == B, "");
+}
+
+enum X { E = 1, F = 1 + 1 };
+enum X { F = 2, E = 1 };
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-3.c b/gcc/testsuite/gcc.dg/c23-tag-enum-3.c
new file mode 100644
index 00000000000..12218a5b911
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-3.c
@@ -0,0 +1,7 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+enum A { N = 0 * sizeof(enum A { M = 1 }) }; 	/* { dg-error "nested" } */
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-4.c b/gcc/testsuite/gcc.dg/c23-tag-enum-4.c
new file mode 100644
index 00000000000..f20dc55fc6c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-4.c
@@ -0,0 +1,22 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// fixed underlying types
+
+enum A : int { N = 1 } x1 = { };
+enum B : int { M = 1 } x2 = { };
+enum C { U = 1 } x3 = { };
+
+void f(void)
+{
+	enum A : int { N = 1 } y1 = x1;
+	enum B : short { M = 1 } y2;
+        y2 = x2;
+	enum B : short { M = 1 } y2b;
+	enum Bb : short { V = 1 } y2d = x2;
+	enum B : short { M = 1 } *y2e = &x2;	/* { dg-warning "incompatible" } */
+	enum B : short { M = 1 } y2c = x2;
+	enum C { U = 1 } y3 = x3;
+}
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-5.c b/gcc/testsuite/gcc.dg/c23-tag-enum-5.c
new file mode 100644
index 00000000000..22ce06fb80d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-5.c
@@ -0,0 +1,18 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" } */
+
+// test for nested redefinitions of enums
+
+void foo(void)
+{
+	enum e { A = 1 };
+	enum e { A = 1 					/* { dg-error "redeclaration" } */
+		+ 0 * sizeof(enum e { A = 1 }) };	/* { dg-error "nested redefinition" } */
+							
+}
+
+typedef __SIZE_TYPE__ size_t;
+enum f : typeof (sizeof (enum f : size_t { B })) { B };	/* { dg-error "nested redefinition" } */
+
+
+
-- 
2.39.2



^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH 3/4] c23: aliasing of compatible tagged types
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
  2023-11-16 21:38 ` [PATCH 1/4] c23: tag compatibility rules for struct and unions Martin Uecker
  2023-11-16 21:38 ` [PATCH 2/4] c23: tag compatibility rules for enums Martin Uecker
@ 2023-11-16 21:39 ` Martin Uecker
  2023-11-23 23:47   ` Joseph Myers
  2023-11-16 21:40 ` [PATCH 4/4] c23: construct composite type for " Martin Uecker
                   ` (14 subsequent siblings)
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2023-11-16 21:39 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers




Tell the backend which types are equivalent by setting
TYPE_CANONICAL to one struct in the set of equivalent
structs. Structs are considered equivalent by ignoring
all sizes of arrays nested in types below field level.

gcc/c:
	* c-decl.cc (c_struct_hasher): Hash stable for struct
	types.
	(c_struct_hasher::hash, c_struct_hasher::equal): New
	functions.
	(finish_struct): Set TYPE_CANONICAL to first struct in
	equivalence class.
	* c-objc-common.cc (c_get_alias_set): Let structs or
	unions with variable size alias anything.
	* c-tree.h (comptypes_equiv): New prototype.
	* c-typeck.cc (comptypes_equiv): New function.
	(comptypes_internal): Implement equivalence mode.
	(tagged_types_tu_compatible): Implement equivalence mode.

gcc/testsuite:
	* gcc.dg/c23-tag-2.c: Activate.
	* gcc.dg/c23-tag-6.c: Activate.
	* gcc.dg/c23-tag-alias-1.c: New test.
	* gcc.dg/c23-tag-alias-2.c: New test.
	* gcc.dg/c23-tag-alias-3.c: New test.
	* gcc.dg/c23-tag-alias-4.c: New test.
	* gcc.dg/c23-tag-alias-5.c: New test.
	* gcc.dg/c23-tag-alias-6.c: New test.
	* gcc.dg/c23-tag-alias-7.c: New test.
	* gcc.dg/c23-tag-alias-8.c: New test.
	* gcc.dg/gnu23-tag-alias-1.c: New test.
---
 gcc/c/c-decl.cc                          | 48 +++++++++++++
 gcc/c/c-objc-common.cc                   |  5 ++
 gcc/c/c-tree.h                           |  1 +
 gcc/c/c-typeck.cc                        | 31 ++++++++
 gcc/testsuite/gcc.dg/c23-tag-2.c         |  4 +-
 gcc/testsuite/gcc.dg/c23-tag-5.c         |  5 +-
 gcc/testsuite/gcc.dg/c23-tag-alias-1.c   | 48 +++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-2.c   | 73 +++++++++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-3.c   | 48 +++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-4.c   | 73 +++++++++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-5.c   | 30 ++++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-6.c   | 77 ++++++++++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-7.c   | 86 ++++++++++++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-8.c   | 90 ++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c | 33 +++++++++
 15 files changed, 648 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-4.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-5.c
 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-alias-8.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index e5d48c3fa56..d0a405087c3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -634,6 +634,36 @@ public:
   auto_vec<tree> typedefs_seen;
 };
 
+
+/* Hash table for structs and unions.  */
+struct c_struct_hasher : ggc_ptr_hash<tree_node>
+{
+  static hashval_t hash (tree t);
+  static bool equal (tree, tree);
+};
+
+/* Hash an RECORD OR UNION.  */
+hashval_t
+c_struct_hasher::hash (tree type)
+{
+  inchash::hash hstate;
+
+  hstate.add_int (TREE_CODE (type));
+  hstate.add_object (TYPE_NAME (type));
+
+  return hstate.end ();
+}
+
+/* Compare two RECORD or UNION types.  */
+bool
+c_struct_hasher::equal (tree t1,  tree t2)
+{
+  return comptypes_equiv_p (t1, t2);
+}
+
+/* All tagged typed so that TYPE_CANONICAL can be set correctly.  */
+static GTY (()) hash_table<c_struct_hasher> *c_struct_htab;
+
 /* Information for the struct or union currently being parsed, or
    NULL if not parsing a struct or union.  */
 static class c_struct_parse_info *struct_parse_info;
@@ -9646,6 +9676,24 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
 
   C_TYPE_BEING_DEFINED (t) = 0;
 
+  /* Set type canonical based on equivalence class.  */
+  if (flag_isoc23)
+    {
+      if (NULL == c_struct_htab)
+	c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61);
+
+      hashval_t hash = c_struct_hasher::hash (t);
+
+      tree *e = c_struct_htab->find_slot_with_hash (t, hash, INSERT);
+      if (*e)
+	TYPE_CANONICAL (t) = *e;
+      else
+	{
+	  TYPE_CANONICAL (t) = t;
+	  *e = t;
+	}
+    }
+
   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-objc-common.cc b/gcc/c/c-objc-common.cc
index c8f49aa2370..738afbad770 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -389,6 +389,11 @@ c_get_alias_set (tree t)
   if (TREE_CODE (t) == ENUMERAL_TYPE)
     return get_alias_set (ENUM_UNDERLYING_TYPE (t));
 
+  /* Structs with variable size can alias different incompatible
+     structs.  Let them alias anything.   */
+  if (RECORD_OR_UNION_TYPE_P (t) && C_TYPE_VARIABLE_SIZE (t))
+    return 0;
+
   return c_common_get_alias_set (t);
 }
 
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index a5dd9a37944..ece5b6a5d26 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -758,6 +758,7 @@ 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_equiv_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 8116c9b3e68..262b04c582f 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1063,6 +1063,7 @@ struct comptypes_data {
   bool different_types_p;
   bool warning_needed;
   bool anon_field;
+  bool equiv;
 
   const struct tagged_tu_seen_cache* cache;
 };
@@ -1123,6 +1124,21 @@ comptypes_check_different_types (tree type1, tree type2,
 
   return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
+
+
+/* Like comptypes, but if it returns nonzero for struct and union
+   types considered equivalent for aliasing purposes.  */
+
+int
+comptypes_equiv_p (tree type1, tree type2)
+{
+  struct comptypes_data data = { };
+  data.equiv = true;
+  bool ret = comptypes_internal (type1, type2, &data);
+
+  return ret;
+}
+
 \f
 /* Return true if TYPE1 and TYPE2 are compatible types for assignment
    or various other operations.  If they are compatible but a warning may
@@ -1250,6 +1266,9 @@ comptypes_internal (const_tree type1, const_tree type2,
 
 	if ((d1 == NULL_TREE) != (d2 == NULL_TREE))
 	  data->different_types_p = true;
+	/* ignore size mismatches */
+	if (data->equiv)
+	  return 1;
 	/* Sizes must match unless one is missing or variable.  */
 	if (d1 == NULL_TREE || d2 == NULL_TREE || d1 == d2)
 	  return true;
@@ -1467,6 +1486,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	if (list_length (TYPE_FIELDS (t1)) != list_length (TYPE_FIELDS (t2)))
 	  return false;
 
+	if (data->equiv && (C_TYPE_VARIABLE_SIZE (t1) || C_TYPE_VARIABLE_SIZE (t2)))
+	  return 0;
+
 	for (s1 = TYPE_FIELDS (t1), s2 = TYPE_FIELDS (t2);
 	     s1 && s2;
 	     s1 = DECL_CHAIN (s1), s2 = DECL_CHAIN (s2))
@@ -1486,6 +1508,15 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 		&& simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
 				     DECL_FIELD_BIT_OFFSET (s2)) != 1)
 	      return false;
+
+	    tree st1 = TYPE_SIZE (TREE_TYPE (s1));
+	    tree st2 = TYPE_SIZE (TREE_TYPE (s2));
+
+	    if (data->equiv
+		&& st1 && TREE_CODE (st1) == INTEGER_CST
+		&& st2 && TREE_CODE (st2) == INTEGER_CST
+		&& !tree_int_cst_equal (st1, st2))
+	     return 0;
 	  }
 	return true;
 
diff --git a/gcc/testsuite/gcc.dg/c23-tag-2.c b/gcc/testsuite/gcc.dg/c23-tag-2.c
index 5dd4a21e9df..e28c2b5eea2 100644
--- a/gcc/testsuite/gcc.dg/c23-tag-2.c
+++ b/gcc/testsuite/gcc.dg/c23-tag-2.c
@@ -1,5 +1,5 @@
-/* { dg-do compile { target { ! "*-*-*" } } }
- * { dg-options "-std=c23" }
+/* { dg-do compile }
+ * { dg-options "-std=c2x" }
  */
 
 // compatibility of structs in assignment
diff --git a/gcc/testsuite/gcc.dg/c23-tag-5.c b/gcc/testsuite/gcc.dg/c23-tag-5.c
index ff40d07aef1..95a04bf9b0e 100644
--- a/gcc/testsuite/gcc.dg/c23-tag-5.c
+++ b/gcc/testsuite/gcc.dg/c23-tag-5.c
@@ -1,5 +1,6 @@
-/* { dg-do run { target { ! "*-*-*" } } }
- * { dg-options "-std=c23" }
+/*
+ * { dg-do run }
+ * { dg-options "-std=c2x" }
  */
 
 // nesting and parameters
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-1.c b/gcc/testsuite/gcc.dg/c23-tag-alias-1.c
new file mode 100644
index 00000000000..6704ba9c8b4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-1.c
@@ -0,0 +1,48 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+
+struct foo { int x; };
+
+int test_foo(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+enum bar { A = 1, B = 3 };
+
+int test_bar(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (2 != test_foo(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (A == test_bar(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-2.c b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
new file mode 100644
index 00000000000..555c30a8501
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
@@ -0,0 +1,73 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+
+struct foo { int x; };
+
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+int test_foo2(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct fox { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+enum bar { A = 1, B = 3, C = 5, D = 9 };
+
+int test_bar1(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3, C = 6, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+int test_bar2(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum baX { A = 1, B = 3, C = 5, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (1 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	if (1 != test_foo2(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (A == test_bar1(&z, &z))
+		__builtin_abort();
+
+	if (A == test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-3.c b/gcc/testsuite/gcc.dg/c23-tag-alias-3.c
new file mode 100644
index 00000000000..122e8806af8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-3.c
@@ -0,0 +1,48 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c23 -flto -O2" }
+ */
+
+
+struct foo { int x; };
+
+int test_foo(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+enum bar { A = 1, B = 3 };
+
+int test_bar(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (2 != test_foo(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (A == test_bar(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-4.c b/gcc/testsuite/gcc.dg/c23-tag-alias-4.c
new file mode 100644
index 00000000000..d86a174f4a2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-4.c
@@ -0,0 +1,73 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c23 -flto -O2" }
+ */
+
+
+struct foo { int x; };
+
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+int test_foo2(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct fox { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+enum bar { A = 1, B = 3, C = 5, D = 9 };
+
+int test_bar1(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3, C = 6, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+int test_bar2(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum baX { A = 1, B = 3, C = 5, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (1 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	if (1 != test_foo2(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (A == test_bar1(&z, &z))
+		__builtin_abort();
+
+	if (A == test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-5.c b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c
new file mode 100644
index 00000000000..4e956720143
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c
@@ -0,0 +1,30 @@
+/* { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+// not sure this is wise, but this was already like thi sbefore
+
+typedef struct { int x; } foo_t;
+
+int test_foo(foo_t* a, void* b)
+{
+	a->x = 1;
+
+	struct { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+int main()
+{
+	foo_t y;
+
+	if (1 != test_foo(&y, &y))
+		__builtin_abort();
+
+	return 0;
+}
+
+
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..3f3e5f01473
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-6.c
@@ -0,0 +1,77 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+
+/* We check that we tolerate differences for
+ * optimization.
+ */
+
+
+struct bar0 { int x; int f[3]; int y; };
+
+int test_bar0(struct bar0* a, void* b)
+{
+	a->x = 1;
+
+	struct bar0 { int x; int f[4]; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+struct bar1 { int x; int (*f)[3]; };
+
+int test_bar1(struct bar1* a, void* b)
+{
+	a->x = 1;
+
+	struct bar1 { int x; int (*f)[3]; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar2 { int x; int (*f)[3]; };
+
+int test_bar2(struct bar2* a, void* b)
+{
+	a->x = 1;
+
+	struct bar2 { int x; int (*f)[4]; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	// control
+
+	struct bar0 z0;
+
+	if (1 != test_bar0(&z0, &z0))
+		__builtin_abort();
+
+	// this could be different
+	struct bar1 z1;
+
+	if (2 != test_bar1(&z1, &z1))
+		__builtin_abort();
+
+	struct bar2 z2;
+
+	if (2 != test_bar2(&z2, &z2))
+		__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..74cf5f212b3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-7.c
@@ -0,0 +1,86 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+
+
+struct bar { int x; int f[]; };
+
+int test_bar1(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+int test_bar3(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[1]; }* p = b;
+	struct bar* q = a;			/* { dg-warning "incompatible" } */
+	p->x = 2;
+
+	return a->x;
+}
+
+
+int test_bar4(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	int n = 3;
+	struct bar { int x; int f[n]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct foo { int x; int f[3]; };
+
+
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	int n = 3;
+	struct foo { int x; int f[n]; }* p = b;
+	struct foo* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar z;
+
+	if (2 != test_bar1(&z, &z))
+		__builtin_abort();
+
+#if 0
+	if (1 != test_bar3(&z, &z))
+		__builtin_abort();
+#endif
+	if (2 != test_bar4(&z, &z))
+		__builtin_abort();
+
+	struct foo y;
+
+	if (2 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-8.c b/gcc/testsuite/gcc.dg/c23-tag-alias-8.c
new file mode 100644
index 00000000000..7c2ced4de51
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-8.c
@@ -0,0 +1,90 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+
+/* We check that we tolerate differences for
+ * optimization.
+ */
+
+struct bar1 { int x; enum A1 { X1 = 1 } f; };
+
+int test_bar1(struct bar1* a, void* b)
+{
+	a->x = 1;
+
+	struct bar1 { int x; enum A1 { X1 = 2 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar2 { int x; enum A2 { X2 = 1 } f; };
+
+int test_bar2(struct bar2* a, void* b)
+{
+	a->x = 1;
+
+	struct bar2 { int x; enum B2 { X2 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+struct bar3 { int x; enum A3 { X3 = 1 } f; };
+
+int test_bar3(struct bar3* a, void* b)
+{
+	a->x = 1;
+
+	struct bar3 { int x; enum A3 { Y3 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar4 { int x; enum { Z4 = 1 } f; };
+
+int test_bar4(struct bar4* a, void* b)
+{
+	a->x = 1;
+
+	struct bar4 { int x; enum { Z4 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar1 z1;
+
+	if (1 != test_bar1(&z1, &z1))
+		__builtin_abort();
+
+	struct bar2 z2;
+
+	if (1 != test_bar2(&z2, &z2))
+		__builtin_abort();
+
+	struct bar3 z3;
+
+	if (1 != test_bar3(&z3, &z3))
+		__builtin_abort();
+
+	struct bar4 z4;
+
+	if (1 != test_bar4(&z4, &z4))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c
new file mode 100644
index 00000000000..9bccc651afb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c
@@ -0,0 +1,33 @@
+/*
+ * { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+
+
+struct bar { int x; int f[]; };
+
+int test_bar2(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[0]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar z;
+
+	if (2 != test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
-- 
2.39.2



^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH 4/4] c23: construct composite type for tagged types
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (2 preceding siblings ...)
  2023-11-16 21:39 ` [PATCH 3/4] c23: aliasing of compatible tagged types Martin Uecker
@ 2023-11-16 21:40 ` Martin Uecker
  2023-11-27 13:16 ` [V4] [C PATCH 1/4] c23: tag compatibility rules for struct and unions Martin Uecker
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 53+ messages in thread
From: Martin Uecker @ 2023-11-16 21:40 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers






Support for constructing composite type 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.

gcc/testsuite:
	* 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/c/c-typeck.cc                          | 114 +++++++++++++++++----
 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 |  17 +++
 gcc/testsuite/gcc.dg/c23-tag-composite-4.c |  21 ++++
 5 files changed, 176 insertions(+), 18 deletions(-)
 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

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 262b04c582f..2255fb66bb2 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,61 @@ 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 (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);
+	  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);
+
+	      gcc_assert (DECL_NAME (a) == DECL_NAME (b));
+	      gcc_assert (comptypes (ta, tb));
+
+	      tree f = build_decl (input_location, FIELD_DECL, DECL_NAME (a),
+				   composite_type_internal (ta, tb, cache));
+
+	      DECL_FIELD_CONTEXT (f) = n;
+	      DECL_CHAIN (f) = fields;
+	      fields = f;
+	    }
+
+	  TYPE_NAME (n) = TYPE_NAME (t1);
+	  TYPE_FIELDS (n) = nreverse (fields);
+	  TYPE_ATTRIBUTES (n) = attributes;
+	  layout_type (n);
+	  n = build_type_attribute_variant (n, attributes);
+	  n = qualify_type (n, t1);
+
+	  gcc_checking_assert (comptypes (n, t1));
+	  gcc_checking_assert (comptypes (n, t2));
+
+	  return n;
+	}
+      /* FALLTHRU */
+    case ENUMERAL_TYPE:
       if (attributes != NULL)
 	{
 	  /* Try harder not to create a new aggregate type.  */
@@ -520,7 +581,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 +627,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 +657,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 +666,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 +679,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 +688,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 +710,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
@@ -5528,6 +5601,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-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..bc689478d8d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-composite-3.c
@@ -0,0 +1,17 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// bit-fields
+
+extern struct foo { int x:3; } x;
+struct foo { int x:3; } y;
+
+void f(void)
+{
+	extern typeof(*(1 ? &x : &y)) x;
+	&x.x;					/* { dg-error "bit-field" } */
+}
+
+struct foo { int x:2; };			/* { 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;
+}
+
-- 
2.39.2





^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/4] c23: tag compatibility rules for struct and unions
  2023-11-16 21:38 ` [PATCH 1/4] c23: tag compatibility rules for struct and unions Martin Uecker
@ 2023-11-23 23:17   ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2023-11-23 23:17 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Thu, 16 Nov 2023, Martin Uecker wrote:

> 	* c-decl.cc (previous_tag): New function.
> 	(get_parm_info): Turn off warning for C2X.

C23 now, not C2X.

> 	(comptypes_internal): Activate comparison of tagged
> 	types (convert_for_assignment): Ingore qualifiers.

This still has the "Ingore" typo.  Also, add '.' and start a new line 
before "(convert_for_assignment)".

> +  /* Check for consistency with previous definition */

Missing '.' at end of comment.

OK with those fixes.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 2/4] c23: tag compatibility rules for enums
  2023-11-16 21:38 ` [PATCH 2/4] c23: tag compatibility rules for enums Martin Uecker
@ 2023-11-23 23:26   ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2023-11-23 23:26 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Thu, 16 Nov 2023, Martin Uecker wrote:

> +enum tt { R = 2 } TT;
> +enum tt {
> +	R = _Generic(&TT, enum tt*: 0, default: 2)
> +};

> +// incomplete during construction
> +
> +enum A { B = 7 } y;
> +enum A { B = 7 };
> +
> +enum A { B = _Generic(&y, enum A*: 1, default: 7) };

I don't follow the basis for these examples.

The multiple definitions are in the same scope, so declare the same type.  
The type "is incomplete until immediately after the closing brace of the 
list defining the content for the first time and complete thereafter.", 
which should include being complete inside the redefinition (so avoiding 
the problems discussed in earlier drafts of the proposal when a type went 
from complete to incomplete and there were corresponding issues with type 
compatibility questions inside the redefinition).  So I'd expect the "enum 
tt*" and "enum A*" cases in _Generic to match (which would make these 
definitions invalid because of the enumeration constant getting a 
different type).

> +void g(void)
> +{
> +	enum A { B = _Generic(&y, enum A*: 1, default: 7) };

In this case with an inner scope, however, "enum A" refers to the new type 
which indeed is incomplete at this point - though that also means this 
isn't an ISO C example but one using a GNU C extension ("A type specifier 
of the form enum identifier without an enumerator list shall only appear 
after the type it specifies is complete.") and so should correspondingly 
be in a gnu23-* test.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 3/4] c23: aliasing of compatible tagged types
  2023-11-16 21:39 ` [PATCH 3/4] c23: aliasing of compatible tagged types Martin Uecker
@ 2023-11-23 23:47   ` Joseph Myers
  2023-11-26 22:48     ` Martin Uecker
  0 siblings, 1 reply; 53+ messages in thread
From: Joseph Myers @ 2023-11-23 23:47 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Thu, 16 Nov 2023, Martin Uecker wrote:

> Tell the backend which types are equivalent by setting
> TYPE_CANONICAL to one struct in the set of equivalent
> structs. Structs are considered equivalent by ignoring
> all sizes of arrays nested in types below field level.

Is TYPE_CANONICAL *only* used for alias analysis?  It's not obvious to me 
that setting TYPE_CANONICAL to a type that's definitely not equivalent for 
other purposes is necessarily safe.

I also think more rationale is needed for ignoring sizes like this.  Is it 
intended for e.g. making structs with flexible array members 
alias-compatible with similar structs with a fixed-size array?

> @@ -1250,6 +1266,9 @@ comptypes_internal (const_tree type1, const_tree type2,
>  
>  	if ((d1 == NULL_TREE) != (d2 == NULL_TREE))
>  	  data->different_types_p = true;
> +	/* ignore size mismatches */
> +	if (data->equiv)
> +	  return 1;

Should start comment with capital letter, end with '.'.

> diff --git a/gcc/testsuite/gcc.dg/c23-tag-2.c b/gcc/testsuite/gcc.dg/c23-tag-2.c
> index 5dd4a21e9df..e28c2b5eea2 100644
> --- a/gcc/testsuite/gcc.dg/c23-tag-2.c
> +++ b/gcc/testsuite/gcc.dg/c23-tag-2.c
> @@ -1,5 +1,5 @@
> -/* { dg-do compile { target { ! "*-*-*" } } }
> - * { dg-options "-std=c23" }
> +/* { dg-do compile }
> + * { dg-options "-std=c2x" }
>   */
>  
>  // compatibility of structs in assignment
> diff --git a/gcc/testsuite/gcc.dg/c23-tag-5.c b/gcc/testsuite/gcc.dg/c23-tag-5.c
> index ff40d07aef1..95a04bf9b0e 100644
> --- a/gcc/testsuite/gcc.dg/c23-tag-5.c
> +++ b/gcc/testsuite/gcc.dg/c23-tag-5.c
> @@ -1,5 +1,6 @@
> -/* { dg-do run { target { ! "*-*-*" } } }
> - * { dg-options "-std=c23" }
> +/*
> + * { dg-do run }
> + * { dg-options "-std=c2x" }

These tests should not be changed to use -std=c2x.

> diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-2.c b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
> new file mode 100644
> index 00000000000..555c30a8501
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
> @@ -0,0 +1,73 @@
> +/*
> + * { dg-do run }
> + * { dg-options "-std=c23 -O2" }
> + */
> +
> +
> +struct foo { int x; };
> +
> +int test_foo1(struct foo* a, void* b)
> +{
> +	a->x = 1;
> +
> +	struct foo { int x; int y; }* p = b;
> +	p->x = 2;
> +
> +	return a->x;
> +}

> +int main()
> +{
> +	struct foo y;
> +
> +	if (1 != test_foo1(&y, &y))
> +		__builtin_abort();

This test appears to be testing various invalid cases - testing that the 
compiler does not consider aliasing to occur in those cases (even though 
in fact there is aliasing).

If that's the intent of this test, it definitely needs commenting.  The 
test would also need to (be a gnu23-* test and) use appropriate attributes 
to disable interprocedural analysis, since it would be entirely valid for 
the compiler in this test to inline test_foo1, see that p->x in fact 
points to the same location as a->x despite the incompatible types, and 
have the function return 2.

The same applies to c23-tag-alias-4.c and c23-tag-alias-5.c.

> diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-5.c b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c
> new file mode 100644
> index 00000000000..4e956720143
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c
> @@ -0,0 +1,30 @@
> +/* { dg-do run }
> + * { dg-options "-std=c23 -O2" }
> + */
> +
> +// not sure this is wise, but this was already like thi sbefore

"this before"

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 3/4] c23: aliasing of compatible tagged types
  2023-11-23 23:47   ` Joseph Myers
@ 2023-11-26 22:48     ` Martin Uecker
  2023-11-27  7:46       ` Richard Biener
  2023-11-28  1:00       ` Joseph Myers
  0 siblings, 2 replies; 53+ messages in thread
From: Martin Uecker @ 2023-11-26 22:48 UTC (permalink / raw)
  To: Joseph Myers; +Cc: gcc-patches, Richard Biener


Thanks Joseph, I will sent an updated series tomorrow. 

Richard, maybe you could look at what I wrote below
about my use of TYPE_CANONICAL ?  Does this make sense?


Am Donnerstag, dem 23.11.2023 um 23:47 +0000 schrieb Joseph Myers:
> On Thu, 16 Nov 2023, Martin Uecker wrote:
> 
> > Tell the backend which types are equivalent by setting
> > TYPE_CANONICAL to one struct in the set of equivalent
> > structs. Structs are considered equivalent by ignoring
> > all sizes of arrays nested in types below field level.
> 
> Is TYPE_CANONICAL *only* used for alias analysis?  It's not obvious to me 
> that setting TYPE_CANONICAL to a type that's definitely not equivalent for 
> other purposes is necessarily safe.

My understand is that it is used for aliasing analysis and also
checking of conversions.  TYPE_CANONICAL must be consistent with
the idea the middle-end has about type conversions.  But as long
as we do not give the same TYPE_CANONICAL to types the middle-end
thinks must be incompatible using its own type checking machinery,
it should be safe even for types the C standard thinks must be
incompatible for some reason.

> I also think more rationale is needed for ignoring sizes like this.  Is it 
> intended for e.g. making structs with flexible array members 
> alias-compatible with similar structs with a fixed-size array?

The main reason are pointers to arrays:

struct foo { int (*x)[]; }
struct foo { int (*x)[2]; };
struct foo { int (*x)[1]; };

So at least when putting it in terms of equivalence classes,
one has no choice than making those types equivalent. So
all those would get the same TYPE_CANONICAL. The middle-end 
does not care about the different pointer types (in
useless_type_conversion_p or
gimple_canonical_types_compatible_p).


Martin




> 
> > @@ -1250,6 +1266,9 @@ comptypes_internal (const_tree type1, const_tree type2,
> >  
> >  	if ((d1 == NULL_TREE) != (d2 == NULL_TREE))
> >  	  data->different_types_p = true;
> > +	/* ignore size mismatches */
> > +	if (data->equiv)
> > +	  return 1;
> 
> Should start comment with capital letter, end with '.'.
> 
> > diff --git a/gcc/testsuite/gcc.dg/c23-tag-2.c b/gcc/testsuite/gcc.dg/c23-tag-2.c
> > index 5dd4a21e9df..e28c2b5eea2 100644
> > --- a/gcc/testsuite/gcc.dg/c23-tag-2.c
> > +++ b/gcc/testsuite/gcc.dg/c23-tag-2.c
> > @@ -1,5 +1,5 @@
> > -/* { dg-do compile { target { ! "*-*-*" } } }
> > - * { dg-options "-std=c23" }
> > +/* { dg-do compile }
> > + * { dg-options "-std=c2x" }
> >   */
> >  
> >  // compatibility of structs in assignment
> > diff --git a/gcc/testsuite/gcc.dg/c23-tag-5.c b/gcc/testsuite/gcc.dg/c23-tag-5.c
> > index ff40d07aef1..95a04bf9b0e 100644
> > --- a/gcc/testsuite/gcc.dg/c23-tag-5.c
> > +++ b/gcc/testsuite/gcc.dg/c23-tag-5.c
> > @@ -1,5 +1,6 @@
> > -/* { dg-do run { target { ! "*-*-*" } } }
> > - * { dg-options "-std=c23" }
> > +/*
> > + * { dg-do run }
> > + * { dg-options "-std=c2x" }
> 
> These tests should not be changed to use -std=c2x.
> 
> > diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-2.c b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
> > new file mode 100644
> > index 00000000000..555c30a8501
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
> > @@ -0,0 +1,73 @@
> > +/*
> > + * { dg-do run }
> > + * { dg-options "-std=c23 -O2" }
> > + */
> > +
> > +
> > +struct foo { int x; };
> > +
> > +int test_foo1(struct foo* a, void* b)
> > +{
> > +	a->x = 1;
> > +
> > +	struct foo { int x; int y; }* p = b;
> > +	p->x = 2;
> > +
> > +	return a->x;
> > +}
> 
> > +int main()
> > +{
> > +	struct foo y;
> > +
> > +	if (1 != test_foo1(&y, &y))
> > +		__builtin_abort();
> 
> This test appears to be testing various invalid cases - testing that the 
> compiler does not consider aliasing to occur in those cases (even though 
> in fact there is aliasing).
> 
> If that's the intent of this test, it definitely needs commenting.  The 
> test would also need to (be a gnu23-* test and) use appropriate attributes 
> to disable interprocedural analysis, since it would be entirely valid for 
> the compiler in this test to inline test_foo1, see that p->x in fact 
> points to the same location as a->x despite the incompatible types, and 
> have the function return 2.
> 
> The same applies to c23-tag-alias-4.c and c23-tag-alias-5.c.
> 
> > diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-5.c b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c
> > new file mode 100644
> > index 00000000000..4e956720143
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c
> > @@ -0,0 +1,30 @@
> > +/* { dg-do run }
> > + * { dg-options "-std=c23 -O2" }
> > + */
> > +
> > +// not sure this is wise, but this was already like thi sbefore
> 
> "this before"
> 


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 3/4] c23: aliasing of compatible tagged types
  2023-11-26 22:48     ` Martin Uecker
@ 2023-11-27  7:46       ` Richard Biener
  2023-11-28  1:00       ` Joseph Myers
  1 sibling, 0 replies; 53+ messages in thread
From: Richard Biener @ 2023-11-27  7:46 UTC (permalink / raw)
  To: Martin Uecker; +Cc: Joseph Myers, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 6010 bytes --]

On Sun, 26 Nov 2023, Martin Uecker wrote:

> 
> Thanks Joseph, I will sent an updated series tomorrow. 
> 
> Richard, maybe you could look at what I wrote below
> about my use of TYPE_CANONICAL ?  Does this make sense?
> 
> 
> Am Donnerstag, dem 23.11.2023 um 23:47 +0000 schrieb Joseph Myers:
> > On Thu, 16 Nov 2023, Martin Uecker wrote:
> > 
> > > Tell the backend which types are equivalent by setting
> > > TYPE_CANONICAL to one struct in the set of equivalent
> > > structs. Structs are considered equivalent by ignoring
> > > all sizes of arrays nested in types below field level.
> > 
> > Is TYPE_CANONICAL *only* used for alias analysis?  It's not obvious to me 
> > that setting TYPE_CANONICAL to a type that's definitely not equivalent for 
> > other purposes is necessarily safe.
> 
> My understand is that it is used for aliasing analysis and also
> checking of conversions.  TYPE_CANONICAL must be consistent with
> the idea the middle-end has about type conversions.  But as long
> as we do not give the same TYPE_CANONICAL to types the middle-end
> thinks must be incompatible using its own type checking machinery,
> it should be safe even for types the C standard thinks must be
> incompatible for some reason.

TYPE_CANONICAL is used to record structurally equivalent types
that should be in a common class for TBAA.  GIMPLE allows direct
assignment of structurally equivalent types in aggregate copies
and with LTO we are forming conservatively larger such groups
to make cross-language TBAA possible.

That means when the language standards says two types are compatible
(and for example valid to assign to each other) then they have to
have the same canonical type (or the frontend has to insert conversions).

It's also the only means besides the type variant chain to assign the
same alias set to a class of types when LTO is in effect (the exception
is when the langhook assigns alias-set zero which is honored).

> > I also think more rationale is needed for ignoring sizes like this.  Is it 
> > intended for e.g. making structs with flexible array members 
> > alias-compatible with similar structs with a fixed-size array?
> 
> The main reason are pointers to arrays:
> 
> struct foo { int (*x)[]; }
> struct foo { int (*x)[2]; };
> struct foo { int (*x)[1]; };
> 
> So at least when putting it in terms of equivalence classes,
> one has no choice than making those types equivalent. So
> all those would get the same TYPE_CANONICAL. The middle-end 
> does not care about the different pointer types (in
> useless_type_conversion_p or
> gimple_canonical_types_compatible_p).

Indeed LTO would put those into the same equivalence class.

Richard.

> 
> Martin
> 
> 
> 
> 
> > 
> > > @@ -1250,6 +1266,9 @@ comptypes_internal (const_tree type1, const_tree type2,
> > >  
> > >  	if ((d1 == NULL_TREE) != (d2 == NULL_TREE))
> > >  	  data->different_types_p = true;
> > > +	/* ignore size mismatches */
> > > +	if (data->equiv)
> > > +	  return 1;
> > 
> > Should start comment with capital letter, end with '.'.
> > 
> > > diff --git a/gcc/testsuite/gcc.dg/c23-tag-2.c b/gcc/testsuite/gcc.dg/c23-tag-2.c
> > > index 5dd4a21e9df..e28c2b5eea2 100644
> > > --- a/gcc/testsuite/gcc.dg/c23-tag-2.c
> > > +++ b/gcc/testsuite/gcc.dg/c23-tag-2.c
> > > @@ -1,5 +1,5 @@
> > > -/* { dg-do compile { target { ! "*-*-*" } } }
> > > - * { dg-options "-std=c23" }
> > > +/* { dg-do compile }
> > > + * { dg-options "-std=c2x" }
> > >   */
> > >  
> > >  // compatibility of structs in assignment
> > > diff --git a/gcc/testsuite/gcc.dg/c23-tag-5.c b/gcc/testsuite/gcc.dg/c23-tag-5.c
> > > index ff40d07aef1..95a04bf9b0e 100644
> > > --- a/gcc/testsuite/gcc.dg/c23-tag-5.c
> > > +++ b/gcc/testsuite/gcc.dg/c23-tag-5.c
> > > @@ -1,5 +1,6 @@
> > > -/* { dg-do run { target { ! "*-*-*" } } }
> > > - * { dg-options "-std=c23" }
> > > +/*
> > > + * { dg-do run }
> > > + * { dg-options "-std=c2x" }
> > 
> > These tests should not be changed to use -std=c2x.
> > 
> > > diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-2.c b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
> > > new file mode 100644
> > > index 00000000000..555c30a8501
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
> > > @@ -0,0 +1,73 @@
> > > +/*
> > > + * { dg-do run }
> > > + * { dg-options "-std=c23 -O2" }
> > > + */
> > > +
> > > +
> > > +struct foo { int x; };
> > > +
> > > +int test_foo1(struct foo* a, void* b)
> > > +{
> > > +	a->x = 1;
> > > +
> > > +	struct foo { int x; int y; }* p = b;
> > > +	p->x = 2;
> > > +
> > > +	return a->x;
> > > +}
> > 
> > > +int main()
> > > +{
> > > +	struct foo y;
> > > +
> > > +	if (1 != test_foo1(&y, &y))
> > > +		__builtin_abort();
> > 
> > This test appears to be testing various invalid cases - testing that the 
> > compiler does not consider aliasing to occur in those cases (even though 
> > in fact there is aliasing).
> > 
> > If that's the intent of this test, it definitely needs commenting.  The 
> > test would also need to (be a gnu23-* test and) use appropriate attributes 
> > to disable interprocedural analysis, since it would be entirely valid for 
> > the compiler in this test to inline test_foo1, see that p->x in fact 
> > points to the same location as a->x despite the incompatible types, and 
> > have the function return 2.
> > 
> > The same applies to c23-tag-alias-4.c and c23-tag-alias-5.c.
> > 
> > > diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-5.c b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c
> > > new file mode 100644
> > > index 00000000000..4e956720143
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c
> > > @@ -0,0 +1,30 @@
> > > +/* { dg-do run }
> > > + * { dg-options "-std=c23 -O2" }
> > > + */
> > > +
> > > +// not sure this is wise, but this was already like thi sbefore
> > 
> > "this before"
> > 
> 
> 

-- 
Richard Biener <rguenther@suse.de>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [V4] [C PATCH 1/4] c23: tag compatibility rules for struct and unions
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (3 preceding siblings ...)
  2023-11-16 21:40 ` [PATCH 4/4] c23: construct composite type for " Martin Uecker
@ 2023-11-27 13:16 ` Martin Uecker
  2023-12-14 20:53   ` Joseph Myers
  2023-11-27 13:16 ` [V4] [PATCH 2/4] c23: tag compatibility rules for enums Martin Uecker
                   ` (12 subsequent siblings)
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2023-11-27 13:16 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers


Note that there is an additional change in parser_xref_tag
to address the issue regarding completeness in redefinition
which affects also structs / unions.  The test c23-tag-6.c
was changed accordingly.


c23: tag compatibility rules for struct and unions

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/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 | 19 ++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-3.c | 28 ++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-4.c | 31 +++++++++++++
 16 files changed, 454 insertions(+), 16 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

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 64d3a941cb9..ebe1708b977 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2039,6 +2039,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))
@@ -9576,6 +9619,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 c6f38ec94a0..7df4d65bf7a 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -756,6 +756,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 1dbb4471a88..dc8a16df272 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))
@@ -7031,7 +7048,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);
 
@@ -8401,6 +8418,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)
 	{
@@ -10486,7 +10510,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..ff40d07aef1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-5.c
@@ -0,0 +1,33 @@
+/* { dg-do run { target { ! "*-*-*" } } }
+ * { dg-options "-std=c23" }
+ */
+
+// 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..73725c79ebf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-2.c
@@ -0,0 +1,19 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=gnu23" }
+ */
+
+// 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/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; }
+}
+
+
-- 
2.39.2



^ permalink raw reply	[flat|nested] 53+ messages in thread

* [V4] [PATCH 2/4] c23: tag compatibility rules for enums
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (4 preceding siblings ...)
  2023-11-27 13:16 ` [V4] [C PATCH 1/4] c23: tag compatibility rules for struct and unions Martin Uecker
@ 2023-11-27 13:16 ` Martin Uecker
  2023-12-14 20:58   ` Joseph Myers
  2023-11-27 13:16 ` [V4] [PATCH 3/4] c23: aliasing of compatible tagged types Martin Uecker
                   ` (11 subsequent siblings)
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2023-11-27 13:16 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers


(only tests were changed)


c23: tag compatibility rules for enums

Allow redefinition of enum types and enumerators.  Diagnose
nested redefinitions including redefinitions in the enum
specifier for enum types with fixed underlying type.

gcc/c:
	* c-tree.h (c_parser_enum_specifier): Add parameter.
	* c-decl.cc (start_enum): Allow redefinition.
	(finish_enum): Diagnose conflicts.
	(build_enumerator): Set context.
	(diagnose_mismatched_decls): Diagnose conflicting enumerators.
	(push_decl): Preserve context for enumerators.
	* c-parser.cc (c_parser_enum_specifier): Remember when
	seen is from an enum type which is not yet defined.

gcc/testsuide/:
	* gcc.dg/c23-tag-enum-1.c: New test.
	* gcc.dg/c23-tag-enum-2.c: New test.
	* gcc.dg/c23-tag-enum-3.c: New test.
	* gcc.dg/c23-tag-enum-4.c: New test.
	* gcc.dg/c23-tag-enum-5.c: New test.
	* gcc.dg/gnu23-tag-enum-1.c: Mew test.
---
 gcc/c/c-decl.cc                         | 65 +++++++++++++++++++++----
 gcc/c/c-parser.cc                       |  5 +-
 gcc/c/c-tree.h                          |  3 +-
 gcc/c/c-typeck.cc                       |  5 +-
 gcc/testsuite/gcc.dg/c23-tag-enum-1.c   | 56 +++++++++++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-enum-2.c   | 17 +++++++
 gcc/testsuite/gcc.dg/c23-tag-enum-3.c   |  7 +++
 gcc/testsuite/gcc.dg/c23-tag-enum-4.c   | 22 +++++++++
 gcc/testsuite/gcc.dg/c23-tag-enum-5.c   | 18 +++++++
 gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c | 19 ++++++++
 10 files changed, 205 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-4.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-5.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index ebe1708b977..bcc09ba479e 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2114,9 +2114,24 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
      given scope.  */
   if (TREE_CODE (olddecl) == CONST_DECL)
     {
-      auto_diagnostic_group d;
-      error ("redeclaration of enumerator %q+D", newdecl);
-      locate_old_decl (olddecl);
+      if (flag_isoc23
+	  && TYPE_NAME (DECL_CONTEXT (newdecl))
+	  && DECL_CONTEXT (newdecl) != DECL_CONTEXT (olddecl)
+	  && TYPE_NAME (DECL_CONTEXT (newdecl)) == TYPE_NAME (DECL_CONTEXT (olddecl)))
+	{
+	  if (!simple_cst_equal (DECL_INITIAL (olddecl), DECL_INITIAL (newdecl)))
+	    {
+	      auto_diagnostic_group d;
+	      error ("conflicting redeclaration of enumerator %q+D", newdecl);
+	      locate_old_decl (olddecl);
+	    }
+	}
+      else
+	{
+	  auto_diagnostic_group d;
+	  error ("redeclaration of enumerator %q+D", newdecl);
+	  locate_old_decl (olddecl);
+	}
       return false;
     }
 
@@ -3277,8 +3292,11 @@ pushdecl (tree x)
 
   /* Must set DECL_CONTEXT for everything not at file scope or
      DECL_FILE_SCOPE_P won't work.  Local externs don't count
-     unless they have initializers (which generate code).  */
+     unless they have initializers (which generate code).  We
+     also exclude CONST_DECLs because enumerators will get the
+     type of the enum as context.  */
   if (current_function_decl
+      && TREE_CODE (x) != CONST_DECL
       && (!VAR_OR_FUNCTION_DECL_P (x)
 	  || DECL_INITIAL (x) || !TREE_PUBLIC (x)))
     DECL_CONTEXT (x) = current_function_decl;
@@ -9747,7 +9765,7 @@ layout_array_type (tree t)
 
 tree
 start_enum (location_t loc, struct c_enum_contents *the_enum, tree name,
-	    tree fixed_underlying_type)
+	    tree fixed_underlying_type, bool potential_nesting_p)
 {
   tree enumtype = NULL_TREE;
   location_t enumloc = UNKNOWN_LOCATION;
@@ -9759,9 +9777,26 @@ start_enum (location_t loc, struct c_enum_contents *the_enum, tree name,
   if (name != NULL_TREE)
     enumtype = lookup_tag (ENUMERAL_TYPE, name, true, &enumloc);
 
+  if (enumtype != NULL_TREE && TREE_CODE (enumtype) == ENUMERAL_TYPE)
+    {
+      /* If the type is currently being defined or if we have seen an
+	 incomplete version which is now complete, this is a nested
+	 redefinition.  The later happens if the redefinition occurs
+	 inside the enum specifier itself.  */
+      if (C_TYPE_BEING_DEFINED (enumtype)
+	  || (potential_nesting_p && TYPE_VALUES (enumtype) != NULL_TREE))
+	error_at (loc, "nested redefinition of %<enum %E%>", name);
+
+      /* For C23 we allow redefinitions.  We set to zero and check for
+	 consistency later.  */
+      if (flag_isoc23 && TYPE_VALUES (enumtype) != NULL_TREE)
+	enumtype = NULL_TREE;
+    }
+
   if (enumtype == NULL_TREE || TREE_CODE (enumtype) != ENUMERAL_TYPE)
     {
       enumtype = make_node (ENUMERAL_TYPE);
+      TYPE_SIZE (enumtype) = NULL_TREE;
       pushtag (loc, name, enumtype);
       if (fixed_underlying_type != NULL_TREE)
 	{
@@ -9789,9 +9824,6 @@ start_enum (location_t loc, struct c_enum_contents *the_enum, tree name,
       DECL_SOURCE_LOCATION (TYPE_STUB_DECL (enumtype)) = loc;
     }
 
-  if (C_TYPE_BEING_DEFINED (enumtype))
-    error_at (loc, "nested redefinition of %<enum %E%>", name);
-
   C_TYPE_BEING_DEFINED (enumtype) = 1;
 
   if (TYPE_VALUES (enumtype) != NULL_TREE)
@@ -10021,6 +10053,20 @@ finish_enum (tree enumtype, tree values, tree attributes)
       && !in_sizeof && !in_typeof && !in_alignof)
     struct_parse_info->struct_types.safe_push (enumtype);
 
+  /* Check for consistency with previous definition */
+  if (flag_isoc23)
+    {
+      tree vistype = previous_tag (enumtype);
+      if (vistype
+	  && TREE_CODE (vistype) == TREE_CODE (enumtype)
+	  && !C_TYPE_BEING_DEFINED (vistype))
+	{
+	  TYPE_STUB_DECL (vistype) = TYPE_STUB_DECL (enumtype);
+	  if (!comptypes_same_p (enumtype, vistype))
+	    error("conflicting redefinition of enum %qT", enumtype);
+	}
+    }
+
   C_TYPE_BEING_DEFINED (enumtype) = 0;
 
   return enumtype;
@@ -10200,6 +10246,7 @@ build_enumerator (location_t decl_loc, location_t loc,
 
   decl = build_decl (decl_loc, CONST_DECL, name, TREE_TYPE (value));
   DECL_INITIAL (decl) = value;
+  DECL_CONTEXT (decl) = the_enum->enum_type;
   pushdecl (decl);
 
   return tree_cons (decl, value, NULL_TREE);
@@ -10216,7 +10263,7 @@ c_simulate_enum_decl (location_t loc, const char *name,
 
   struct c_enum_contents the_enum;
   tree enumtype = start_enum (loc, &the_enum, get_identifier (name),
-			      NULL_TREE);
+			      NULL_TREE, false);
 
   tree value_chain = NULL_TREE;
   string_int_pair *value;
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 703f9570dbc..ff30ba198ca 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -3667,6 +3667,7 @@ c_parser_enum_specifier (c_parser *parser)
 {
   struct c_typespec ret;
   bool have_std_attrs;
+  bool potential_nesting_p = false;
   tree std_attrs = NULL_TREE;
   tree attrs;
   tree ident = NULL_TREE;
@@ -3706,6 +3707,7 @@ c_parser_enum_specifier (c_parser *parser)
 	  if (!ENUM_FIXED_UNDERLYING_TYPE_P (ret.spec))
 	    error_at (enum_loc, "%<enum%> declared both with and without "
 		      "fixed underlying type");
+	  potential_nesting_p = NULL_TREE == TYPE_VALUES (ret.spec);
 	}
       else
 	{
@@ -3776,7 +3778,8 @@ c_parser_enum_specifier (c_parser *parser)
 	 forward order at the end.  */
       tree values;
       timevar_push (TV_PARSE_ENUM);
-      type = start_enum (enum_loc, &the_enum, ident, fixed_underlying_type);
+      type = start_enum (enum_loc, &the_enum, ident, fixed_underlying_type,
+			 potential_nesting_p);
       values = NULL_TREE;
       c_parser_consume_token (parser);
       while (true)
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 7df4d65bf7a..a5dd9a37944 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -682,7 +682,8 @@ extern void c_warn_unused_attributes (tree);
 extern tree c_warn_type_attributes (tree);
 extern void shadow_tag (const struct c_declspecs *);
 extern void shadow_tag_warned (const struct c_declspecs *, int);
-extern tree start_enum (location_t, struct c_enum_contents *, tree, tree);
+extern tree start_enum (location_t, struct c_enum_contents *, tree, tree,
+			bool potential_nesting_p);
 extern bool start_function (struct c_declspecs *, struct c_declarator *, tree);
 extern tree start_decl (struct c_declarator *, struct c_declspecs *, bool,
 			tree, bool = true, location_t * = NULL);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index dc8a16df272..8116c9b3e68 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1419,6 +1419,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
     {
     case ENUMERAL_TYPE:
       {
+	if (!comptypes (ENUM_UNDERLYING_TYPE (t1), ENUM_UNDERLYING_TYPE (t2)))
+	  return false;
+
 	/* Speed up the case where the type values are in the same order.  */
 	tree tv1 = TYPE_VALUES (t1);
 	tree tv2 = TYPE_VALUES (t2);
@@ -6948,7 +6951,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
       if (checktype != error_mark_node
 	  && TREE_CODE (checktype) == ENUMERAL_TYPE
 	  && TREE_CODE (type) == ENUMERAL_TYPE
-	  && TYPE_MAIN_VARIANT (checktype) != TYPE_MAIN_VARIANT (type))
+	  && !comptypes (TYPE_MAIN_VARIANT (checktype), TYPE_MAIN_VARIANT (type)))
        {
 	  gcc_rich_location loc (location);
 	  warning_at (&loc, OPT_Wenum_conversion,
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-1.c b/gcc/testsuite/gcc.dg/c23-tag-enum-1.c
new file mode 100644
index 00000000000..58933cfac24
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-1.c
@@ -0,0 +1,56 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// incompatible redeclarations, conflicing redefinitions
+
+
+enum aa { A = 1 } *a;
+enum bb { B = 1 } *b;
+
+void test(void)
+{
+  enum aa { A = 1 } *c = a;
+  enum bb { B = 2 } *d = b;	/* { dg-warning "incompatible pointer type" } */
+}
+
+enum cc { C = 1 };
+enum cc { D = 1 };		/* { dg-error "conflicting redefinition" } */	
+
+enum dd { E = 1 };
+enum dd { E = 2 };		/* { dg-error "conflicting redefinition" } */	
+				/* { dg-error "redeclaration of enumerator" "" { target *-*-* } .-1 } */	
+
+
+
+void test2(void)
+{
+  enum ee *a;
+  enum ee { F = 2 } *b;
+  b = a;
+}
+
+
+enum ff { G = 2 };
+enum gg { G = 2 };		/* { dg-error "redeclaration of enumerator" } */
+enum g2 { G = 3 };		/* { dg-error "redeclaration of enumerator" } */
+
+enum hh { H = 1, H = 1 };	/* { dg-error "redeclaration of enumerator" } */
+
+enum ss { K = 2 };
+enum ss { K = 2 };
+
+enum tt { R = 2 } TT;
+enum tt {
+	R = _Generic(&TT, enum tt*: 2, default: 0)
+};
+
+enum { U = 1 };
+enum { U = 1 };			/* { dg-error "redeclaration of enumerator" } */
+
+enum { V = 1 };
+enum { V = 2 };			/* { dg-error "redeclaration of enumerator" } */
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-2.c b/gcc/testsuite/gcc.dg/c23-tag-enum-2.c
new file mode 100644
index 00000000000..2bf7255b844
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-2.c
@@ -0,0 +1,17 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+enum A { B = 7 } y;
+
+// complete during later construction
+
+enum A { B = _Generic(&y, enum A*: 7, default: 1) };
+
+
+enum X { E = 1, F = 1 + 1 };
+enum X { F = 2, E = 1 };
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-3.c b/gcc/testsuite/gcc.dg/c23-tag-enum-3.c
new file mode 100644
index 00000000000..12218a5b911
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-3.c
@@ -0,0 +1,7 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+enum A { N = 0 * sizeof(enum A { M = 1 }) }; 	/* { dg-error "nested" } */
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-4.c b/gcc/testsuite/gcc.dg/c23-tag-enum-4.c
new file mode 100644
index 00000000000..f20dc55fc6c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-4.c
@@ -0,0 +1,22 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// fixed underlying types
+
+enum A : int { N = 1 } x1 = { };
+enum B : int { M = 1 } x2 = { };
+enum C { U = 1 } x3 = { };
+
+void f(void)
+{
+	enum A : int { N = 1 } y1 = x1;
+	enum B : short { M = 1 } y2;
+        y2 = x2;
+	enum B : short { M = 1 } y2b;
+	enum Bb : short { V = 1 } y2d = x2;
+	enum B : short { M = 1 } *y2e = &x2;	/* { dg-warning "incompatible" } */
+	enum B : short { M = 1 } y2c = x2;
+	enum C { U = 1 } y3 = x3;
+}
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-5.c b/gcc/testsuite/gcc.dg/c23-tag-enum-5.c
new file mode 100644
index 00000000000..22ce06fb80d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-5.c
@@ -0,0 +1,18 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" } */
+
+// test for nested redefinitions of enums
+
+void foo(void)
+{
+	enum e { A = 1 };
+	enum e { A = 1 					/* { dg-error "redeclaration" } */
+		+ 0 * sizeof(enum e { A = 1 }) };	/* { dg-error "nested redefinition" } */
+							
+}
+
+typedef __SIZE_TYPE__ size_t;
+enum f : typeof (sizeof (enum f : size_t { B })) { B };	/* { dg-error "nested redefinition" } */
+
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c b/gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c
new file mode 100644
index 00000000000..6b346ed35a0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c
@@ -0,0 +1,19 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=gnu23" }
+ */
+
+
+enum A { B = 7 } y;
+
+void g(void)
+{
+	// incomplete during construction
+	// this is a GNU extension because enum A is used
+	// before the type is completed.
+
+	enum A { B = _Generic(&y, enum A*: 1, default: 7) };
+	_Static_assert(7 == B, "");
+}
+
+
-- 
2.39.2



^ permalink raw reply	[flat|nested] 53+ messages in thread

* [V4] [PATCH 3/4] c23: aliasing of compatible tagged types
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (5 preceding siblings ...)
  2023-11-27 13:16 ` [V4] [PATCH 2/4] c23: tag compatibility rules for enums Martin Uecker
@ 2023-11-27 13:16 ` Martin Uecker
  2023-12-14 21:10   ` Joseph Myers
  2023-11-27 13:16 ` [V4] [PATCH 4/4] c23: construct composite type for " Martin Uecker
                   ` (10 subsequent siblings)
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2023-11-27 13:16 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers


(this mostly got an extended description and more
comments, also tests were reorganized)



c23: aliasing of compatible tagged types

Tell the backend which types are equivalent by setting
TYPE_CANONICAL to one struct in the set of equivalent
structs.  Structs are considered equivalent by ignoring
all sizes of arrays nested in types below field level.

The following two structs are incompatible and lvalues
with these types can be assumed not to alias:

 struct foo { int a[3]; };
 struct foo { int a[4]; };

The following two structs are also incompatible, but
will get the same TYPE_CANONICAL and it is then not
exploited that lvalues with those types can not alias:

 struct bar { int (*p)[3]; };
 struct bar { int (*p)[4]; };

The reason is that both are compatible to

 struct bar { int (*p)[]; };

and therefore are in the same equivalence class.  For
the same reason all enums with the same underyling type
are in the same equivalence class.  Tests are added
for the expected aliasing behavior with optimization.

gcc/c:
	* c-decl.cc (c_struct_hasher): Hash stable for struct
	types.
	(c_struct_hasher::hash, c_struct_hasher::equal): New
	functions.
	(finish_struct): Set TYPE_CANONICAL to first struct in
	equivalence class.
	* c-objc-common.cc (c_get_alias_set): Let structs or
	unions with variable size alias anything.
	* c-tree.h (comptypes_equiv): New prototype.
	* c-typeck.cc (comptypes_equiv): New function.
	(comptypes_internal): Implement equivalence mode.
	(tagged_types_tu_compatible): Implement equivalence mode.

gcc/testsuite:
	* gcc.dg/c23-tag-2.c: Activate.
	* gcc.dg/c23-tag-6.c: Activate.
	* gcc.dg/c23-tag-alias-1.c: New test.
	* gcc.dg/c23-tag-alias-2.c: New test.
	* gcc.dg/gnu23-tag-alias-1.c: New test.
	* gcc.dg/gnu23-tag-alias-2.c: New test.
	* gcc.dg/gnu23-tag-alias-3.c: New test.
	* gcc.dg/gnu23-tag-alias-4.c: New test.
	* gcc.dg/gnu23-tag-alias-5.c: New test.
	* gcc.dg/gnu23-tag-alias-6.c: New test.
	* gcc.dg/gnu23-tag-alias-7.c: New test.
---
 gcc/c/c-decl.cc                          |  51 ++++++++++-
 gcc/c/c-objc-common.cc                   |   5 ++
 gcc/c/c-tree.h                           |   1 +
 gcc/c/c-typeck.cc                        |  31 +++++++
 gcc/testsuite/gcc.dg/c23-tag-2.c         |   2 +-
 gcc/testsuite/gcc.dg/c23-tag-5.c         |   2 +-
 gcc/testsuite/gcc.dg/c23-tag-alias-1.c   |  49 +++++++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-2.c   |  50 +++++++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-3.c   |  32 +++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-4.c   |  54 ++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c |  33 +++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c |  85 ++++++++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c |  83 ++++++++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c |  36 ++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c | 107 +++++++++++++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c |  60 +++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c |  93 ++++++++++++++++++++
 17 files changed, 771 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-4.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index bcc09ba479e..68cba131704 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -634,6 +634,36 @@ public:
   auto_vec<tree> typedefs_seen;
 };
 
+
+/* Hash table for structs and unions.  */
+struct c_struct_hasher : ggc_ptr_hash<tree_node>
+{
+  static hashval_t hash (tree t);
+  static bool equal (tree, tree);
+};
+
+/* Hash an RECORD OR UNION.  */
+hashval_t
+c_struct_hasher::hash (tree type)
+{
+  inchash::hash hstate;
+
+  hstate.add_int (TREE_CODE (type));
+  hstate.add_object (TYPE_NAME (type));
+
+  return hstate.end ();
+}
+
+/* Compare two RECORD or UNION types.  */
+bool
+c_struct_hasher::equal (tree t1,  tree t2)
+{
+  return comptypes_equiv_p (t1, t2);
+}
+
+/* All tagged typed so that TYPE_CANONICAL can be set correctly.  */
+static GTY (()) hash_table<c_struct_hasher> *c_struct_htab;
+
 /* Information for the struct or union currently being parsed, or
    NULL if not parsing a struct or union.  */
 static class c_struct_parse_info *struct_parse_info;
@@ -8713,7 +8743,8 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name,
   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).  */
+     an earlier definition (which may be complete).  We do not
+     have to loop because nested redefinitions are not allowed.  */
   if (flag_isoc23 && ref && C_TYPE_BEING_DEFINED (ref))
     {
       tree vis = previous_tag (ref);
@@ -9656,6 +9687,24 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
 
   C_TYPE_BEING_DEFINED (t) = 0;
 
+  /* Set type canonical based on equivalence class.  */
+  if (flag_isoc23)
+    {
+      if (NULL == c_struct_htab)
+	c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61);
+
+      hashval_t hash = c_struct_hasher::hash (t);
+
+      tree *e = c_struct_htab->find_slot_with_hash (t, hash, INSERT);
+      if (*e)
+	TYPE_CANONICAL (t) = *e;
+      else
+	{
+	  TYPE_CANONICAL (t) = t;
+	  *e = t;
+	}
+    }
+
   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-objc-common.cc b/gcc/c/c-objc-common.cc
index c8f49aa2370..738afbad770 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -389,6 +389,11 @@ c_get_alias_set (tree t)
   if (TREE_CODE (t) == ENUMERAL_TYPE)
     return get_alias_set (ENUM_UNDERLYING_TYPE (t));
 
+  /* Structs with variable size can alias different incompatible
+     structs.  Let them alias anything.   */
+  if (RECORD_OR_UNION_TYPE_P (t) && C_TYPE_VARIABLE_SIZE (t))
+    return 0;
+
   return c_common_get_alias_set (t);
 }
 
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index a5dd9a37944..ece5b6a5d26 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -758,6 +758,7 @@ 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_equiv_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 8116c9b3e68..00eb65dbcce 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1063,6 +1063,7 @@ struct comptypes_data {
   bool different_types_p;
   bool warning_needed;
   bool anon_field;
+  bool equiv;
 
   const struct tagged_tu_seen_cache* cache;
 };
@@ -1123,6 +1124,21 @@ comptypes_check_different_types (tree type1, tree type2,
 
   return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
+
+
+/* Like comptypes, but if it returns nonzero for struct and union
+   types considered equivalent for aliasing purposes.  */
+
+int
+comptypes_equiv_p (tree type1, tree type2)
+{
+  struct comptypes_data data = { };
+  data.equiv = true;
+  bool ret = comptypes_internal (type1, type2, &data);
+
+  return ret;
+}
+
 \f
 /* Return true if TYPE1 and TYPE2 are compatible types for assignment
    or various other operations.  If they are compatible but a warning may
@@ -1250,6 +1266,9 @@ comptypes_internal (const_tree type1, const_tree type2,
 
 	if ((d1 == NULL_TREE) != (d2 == NULL_TREE))
 	  data->different_types_p = true;
+	/* Ignore size mismatches.  */
+	if (data->equiv)
+	  return 1;
 	/* Sizes must match unless one is missing or variable.  */
 	if (d1 == NULL_TREE || d2 == NULL_TREE || d1 == d2)
 	  return true;
@@ -1467,6 +1486,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	if (list_length (TYPE_FIELDS (t1)) != list_length (TYPE_FIELDS (t2)))
 	  return false;
 
+	if (data->equiv && (C_TYPE_VARIABLE_SIZE (t1) || C_TYPE_VARIABLE_SIZE (t2)))
+	  return 0;
+
 	for (s1 = TYPE_FIELDS (t1), s2 = TYPE_FIELDS (t2);
 	     s1 && s2;
 	     s1 = DECL_CHAIN (s1), s2 = DECL_CHAIN (s2))
@@ -1486,6 +1508,15 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 		&& simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
 				     DECL_FIELD_BIT_OFFSET (s2)) != 1)
 	      return false;
+
+	    tree st1 = TYPE_SIZE (TREE_TYPE (s1));
+	    tree st2 = TYPE_SIZE (TREE_TYPE (s2));
+
+	    if (data->equiv
+		&& st1 && TREE_CODE (st1) == INTEGER_CST
+		&& st2 && TREE_CODE (st2) == INTEGER_CST
+		&& !tree_int_cst_equal (st1, st2))
+	     return 0;
 	  }
 	return true;
 
diff --git a/gcc/testsuite/gcc.dg/c23-tag-2.c b/gcc/testsuite/gcc.dg/c23-tag-2.c
index 5dd4a21e9df..444605a93c6 100644
--- a/gcc/testsuite/gcc.dg/c23-tag-2.c
+++ b/gcc/testsuite/gcc.dg/c23-tag-2.c
@@ -1,4 +1,4 @@
-/* { dg-do compile { target { ! "*-*-*" } } }
+/* { dg-do compile }
  * { dg-options "-std=c23" }
  */
 
diff --git a/gcc/testsuite/gcc.dg/c23-tag-5.c b/gcc/testsuite/gcc.dg/c23-tag-5.c
index ff40d07aef1..bd56e1bda1a 100644
--- a/gcc/testsuite/gcc.dg/c23-tag-5.c
+++ b/gcc/testsuite/gcc.dg/c23-tag-5.c
@@ -1,4 +1,4 @@
-/* { dg-do run { target { ! "*-*-*" } } }
+/* { dg-do run }
  * { dg-options "-std=c23" }
  */
 
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-1.c b/gcc/testsuite/gcc.dg/c23-tag-alias-1.c
new file mode 100644
index 00000000000..c92f942af88
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-1.c
@@ -0,0 +1,49 @@
+/* { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+
+/* These tests check that redefinitions of tagged
+   types can alias the original definitions.  */
+
+struct foo { int x; };
+
+int test_foo(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+enum bar { A = 1, B = 3 };
+
+int test_bar(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (2 != test_foo(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (B != test_bar(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-2.c b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
new file mode 100644
index 00000000000..64ff67d8552
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
@@ -0,0 +1,50 @@
+/* { dg-do run }
+ * { dg-options "-std=c23 -flto -O2" }
+ */
+
+/* These tests check that compatible definitions of
+   tagged types can alias the original definitions
+   with LTO.  */
+
+struct foo { int x; };
+
+int test_foo(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+enum bar { A = 1, B = 3 };
+
+int test_bar(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (2 != test_foo(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (B != test_bar(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-3.c b/gcc/testsuite/gcc.dg/c23-tag-alias-3.c
new file mode 100644
index 00000000000..b9fe6f3b407
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-3.c
@@ -0,0 +1,32 @@
+/* { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+/* These tests check that definitions of enums with 
+ * the same underlying type can alias, even when
+ * they are not compatible.  */
+
+enum bar : long { A = 1, B = 3 };
+
+int test_bar(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum foo : long { C = 2, D = 4 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	enum bar z;
+
+	if (B != test_bar(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-4.c b/gcc/testsuite/gcc.dg/c23-tag-alias-4.c
new file mode 100644
index 00000000000..53272b7e457
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-4.c
@@ -0,0 +1,54 @@
+/* { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+
+/* Here we check that structs with flexible array
+ * members can alias a compatible redefinition.  */
+
+struct bar { int x; int f[]; };
+
+int test_bar1(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+/* The structs are incompatible so can be assumed not to
+ * alias, but this is not exploited.  So do not check for 
+ * this below but check the warning about incompatibility.  */
+
+int test_bar3(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[1]; }* p = b;
+	struct bar* q = a;			/* { dg-warning "incompatible" } */
+	p->x = 2;
+
+	return a->x;
+}
+
+
+int main()
+{
+	struct bar z;
+
+	if (2 != test_bar1(&z, &z))
+        	__builtin_abort();
+
+	// allow both results here
+	int r = test_bar3(&z, &z);
+	if ((r != 2) && (r != 1))
+        	__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c
new file mode 100644
index 00000000000..c51417f831a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c
@@ -0,0 +1,33 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+/* Check that structs with flexible array member can alias.  */
+
+struct bar { int x; int f[]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[0]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar z;
+
+	if (2 != test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c
new file mode 100644
index 00000000000..c09c3ca40a6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c
@@ -0,0 +1,85 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+
+/* These tests check that incompatible definitions of
+   tagged types can be assumed not to alias and that
+   this is exploited during optimization.  */
+
+struct foo { int x; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo2(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct fox { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+/* While these tests check that incompatible enums can still
+ * alias, although this is not required.   */
+
+enum bar { A = 1, B = 3, C = 5, D = 9 };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar1(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3, C = 6, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum baX { A = 1, B = 3, C = 5, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (1 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	if (1 != test_foo2(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (B != test_bar1(&z, &z))
+		__builtin_abort();
+
+	if (B != test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c
new file mode 100644
index 00000000000..a07a1e6fa38
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c
@@ -0,0 +1,83 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -flto -O2" }
+ */
+
+/* These tests check that incompatible definitions of
+   tagged types can be assumed not to alias and that
+   this is exploited during optimization with LTO.  */
+
+struct foo { int x; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo2(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct fox { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+/* While tese tests check that incompatible definitions
+ * of enums can alias.  */
+
+enum bar { A = 1, B = 3, C = 5, D = 9 };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar1(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3, C = 6, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum baX { A = 1, B = 3, C = 5, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (1 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	if (1 != test_foo2(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (B != test_bar1(&z, &z))
+		__builtin_abort();
+
+	if (B != test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c
new file mode 100644
index 00000000000..1ea3a883d0c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c
@@ -0,0 +1,36 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+/* This test checks that an incompatible definition of
+ * a tagged type without tag can be assumed not to alias.  
+ * and that this is exploited during optimization.  */
+
+
+// not sure this is wise, but this was already like this before
+
+typedef struct { int x; } foo_t;
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo(foo_t* a, void* b)
+{
+	a->x = 1;
+
+	struct { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+int main()
+{
+	foo_t y;
+
+	if (1 != test_foo(&y, &y))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c
new file mode 100644
index 00000000000..5a83397bd6d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c
@@ -0,0 +1,107 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+/* This test checks that different field offsets imply
+ * that the types can be assumed not to alias
+ * and that this is exploited during optimization.  */
+
+
+struct bar0 { int x; int f[3]; int y; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar0(struct bar0* a, void* b)
+{
+	a->x = 1;
+
+	struct bar0 { int x; int f[4]; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+/* While these tests check that different structs with different
+ * sizes in arrays pointed to by field members can alias,
+ * even though the types are incompatible.  */
+
+
+struct bar1 { int x; int (*f)[3]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar1(struct bar1* a, void* b)
+{
+	a->x = 1;
+
+	struct bar1 { int x; int (*f)[3]; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar2 { int x; int (*f)[3]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(struct bar2* a, void* b)
+{
+	a->x = 1;
+
+	struct bar2 { int x; int (*f)[4]; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+/* This test checks that different structs with pointers to
+ * different compatible arrays types can alias.  */
+
+
+struct bar3 { int x; int (*f)[3]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar3(struct bar3* a, void* b)
+{
+	a->x = 1;
+
+	struct bar3 { int x; int (*f)[]; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+
+int main()
+{
+	// control
+
+	struct bar0 z0;
+
+	if (1 != test_bar0(&z0, &z0))
+		__builtin_abort();
+
+	// this could be different
+	struct bar1 z1;
+
+	if (2 != test_bar1(&z1, &z1))
+		__builtin_abort();
+
+	struct bar2 z2;
+
+	if (2 != test_bar2(&z2, &z2))
+		__builtin_abort();
+
+	struct bar3 z3;
+
+	if (2 != test_bar3(&z3, &z3))
+		__builtin_abort();
+
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c
new file mode 100644
index 00000000000..78d2abf2ed9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c
@@ -0,0 +1,60 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+
+
+/* Here we check that struct with a variable size (GNU extension)
+ * can alias a struct with a flexible array member or a struct with a
+ * fixed size array as last element.  */
+
+struct bar { int x; int f[]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar4(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	int n = 3;
+	struct bar { int x; int f[n]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct foo { int x; int f[3]; };
+
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	int n = 3;
+	struct foo { int x; int f[n]; }* p = b;
+	struct foo* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar z;
+
+	if (2 != test_bar4(&z, &z))
+		__builtin_abort();
+
+	struct foo y;
+
+	if (2 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c
new file mode 100644
index 00000000000..d3fc4bd57e1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c
@@ -0,0 +1,93 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+
+/* We check that the incompatible enums as fields lead to
+ * incompatible types that can be assumed not to alias
+ * and that this is exploited during optimization.  */
+
+struct bar1 { int x; enum A1 { X1 = 1 } f; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar1(struct bar1* a, void* b)
+{
+	a->x = 1;
+
+	struct bar1 { int x; enum A1 { X1 = 2 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar2 { int x; enum A2 { X2 = 1 } f; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(struct bar2* a, void* b)
+{
+	a->x = 1;
+
+	struct bar2 { int x; enum B2 { X2 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+struct bar3 { int x; enum A3 { X3 = 1 } f; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar3(struct bar3* a, void* b)
+{
+	a->x = 1;
+
+	struct bar3 { int x; enum A3 { Y3 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar4 { int x; enum { Z4 = 1 } f; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar4(struct bar4* a, void* b)
+{
+	a->x = 1;
+
+	struct bar4 { int x; enum { Z4 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar1 z1;
+
+	if (1 != test_bar1(&z1, &z1))
+		__builtin_abort();
+
+	struct bar2 z2;
+
+	if (1 != test_bar2(&z2, &z2))
+		__builtin_abort();
+
+	struct bar3 z3;
+
+	if (1 != test_bar3(&z3, &z3))
+		__builtin_abort();
+
+	struct bar4 z4;
+
+	if (1 != test_bar4(&z4, &z4))
+		__builtin_abort();
+
+	return 0;
+}
+
+
-- 
2.39.2



^ permalink raw reply	[flat|nested] 53+ messages in thread

* [V4] [PATCH 4/4] c23: construct composite type for tagged types
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (6 preceding siblings ...)
  2023-11-27 13:16 ` [V4] [PATCH 3/4] c23: aliasing of compatible tagged types Martin Uecker
@ 2023-11-27 13:16 ` Martin Uecker
  2023-12-17 17:41 ` [V5] [C PATCH 1/4] c23: tag compatibility rules for struct and unions Martin Uecker
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 53+ messages in thread
From: Martin Uecker @ 2023-11-27 13:16 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers


(this patch was still not updated and needs more work, so
only included now for completeness) 


c23: construct composite type for tagged types

Support for constructing composite type 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.

gcc/testsuite:
	* 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/c/c-typeck.cc                          | 114 +++++++++++++++++----
 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 |  17 +++
 gcc/testsuite/gcc.dg/c23-tag-composite-4.c |  21 ++++
 5 files changed, 176 insertions(+), 18 deletions(-)
 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

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 00eb65dbcce..7901368c9fd 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,61 @@ 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 (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);
+	  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);
+
+	      gcc_assert (DECL_NAME (a) == DECL_NAME (b));
+	      gcc_assert (comptypes (ta, tb));
+
+	      tree f = build_decl (input_location, FIELD_DECL, DECL_NAME (a),
+				   composite_type_internal (ta, tb, cache));
+
+	      DECL_FIELD_CONTEXT (f) = n;
+	      DECL_CHAIN (f) = fields;
+	      fields = f;
+	    }
+
+	  TYPE_NAME (n) = TYPE_NAME (t1);
+	  TYPE_FIELDS (n) = nreverse (fields);
+	  TYPE_ATTRIBUTES (n) = attributes;
+	  layout_type (n);
+	  n = build_type_attribute_variant (n, attributes);
+	  n = qualify_type (n, t1);
+
+	  gcc_checking_assert (comptypes (n, t1));
+	  gcc_checking_assert (comptypes (n, t2));
+
+	  return n;
+	}
+      /* FALLTHRU */
+    case ENUMERAL_TYPE:
       if (attributes != NULL)
 	{
 	  /* Try harder not to create a new aggregate type.  */
@@ -520,7 +581,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 +627,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 +657,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 +666,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 +679,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 +688,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 +710,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
@@ -5528,6 +5601,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-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..bc689478d8d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-composite-3.c
@@ -0,0 +1,17 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// bit-fields
+
+extern struct foo { int x:3; } x;
+struct foo { int x:3; } y;
+
+void f(void)
+{
+	extern typeof(*(1 ? &x : &y)) x;
+	&x.x;					/* { dg-error "bit-field" } */
+}
+
+struct foo { int x:2; };			/* { 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;
+}
+
-- 
2.39.2



^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 3/4] c23: aliasing of compatible tagged types
  2023-11-26 22:48     ` Martin Uecker
  2023-11-27  7:46       ` Richard Biener
@ 2023-11-28  1:00       ` Joseph Myers
  2023-11-28  6:49         ` Martin Uecker
  2023-11-28 10:47         ` Richard Biener
  1 sibling, 2 replies; 53+ messages in thread
From: Joseph Myers @ 2023-11-28  1:00 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches, Richard Biener

[-- Attachment #1: Type: text/plain, Size: 2030 bytes --]

On Sun, 26 Nov 2023, Martin Uecker wrote:

> My understand is that it is used for aliasing analysis and also
> checking of conversions.  TYPE_CANONICAL must be consistent with
> the idea the middle-end has about type conversions.  But as long
> as we do not give the same TYPE_CANONICAL to types the middle-end
> thinks must be incompatible using its own type checking machinery,
> it should be safe even for types the C standard thinks must be
> incompatible for some reason.

And presumably also for types that definitely can't be assigned because 
they have incompatible layout through the use of different array sizes - 
since the front end won't generate such assignments, it would never matter 
whether the middle end considers them valid without conversion or not?

> > I also think more rationale is needed for ignoring sizes like this.  Is it 
> > intended for e.g. making structs with flexible array members 
> > alias-compatible with similar structs with a fixed-size array?
> 
> The main reason are pointers to arrays:
> 
> struct foo { int (*x)[]; }
> struct foo { int (*x)[2]; };
> struct foo { int (*x)[1]; };

Thanks for the explanation.

I guess the cases involving flexible array members actually show up a bug 
in the standard rather than any kind of issue with this patch - the 
standard allows one structure ending with a flexible array member, and 
another ending with a fixed-size array, to be compatible (and in different 
translation units allowed that even before C23), but there is also clear 
text in the standard showing it's not intended to require the layout to be 
consistent (the fixed-size and flexible arrays might have different 
offsets), and what you'd actually get with an assignment or conditional 
expression mixing such structures certainly isn't clearly specified.  
Maybe the right resolution for that issue with the standard would be to 
make that particular case incompatible, but it would need to be raised as 
an issue after C23 is out.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 3/4] c23: aliasing of compatible tagged types
  2023-11-28  1:00       ` Joseph Myers
@ 2023-11-28  6:49         ` Martin Uecker
  2023-11-28 10:47         ` Richard Biener
  1 sibling, 0 replies; 53+ messages in thread
From: Martin Uecker @ 2023-11-28  6:49 UTC (permalink / raw)
  To: Joseph Myers; +Cc: gcc-patches, Richard Biener

Am Dienstag, dem 28.11.2023 um 01:00 +0000 schrieb Joseph Myers:
> On Sun, 26 Nov 2023, Martin Uecker wrote:
> 
> > My understand is that it is used for aliasing analysis and also
> > checking of conversions.  TYPE_CANONICAL must be consistent with
> > the idea the middle-end has about type conversions.  But as long
> > as we do not give the same TYPE_CANONICAL to types the middle-end
> > thinks must be incompatible using its own type checking machinery,
> > it should be safe even for types the C standard thinks must be
> > incompatible for some reason.
> 
> And presumably also for types that definitely can't be assigned because 
> they have incompatible layout through the use of different array sizes - 
> since the front end won't generate such assignments, it would never matter 
> whether the middle end considers them valid without conversion or not?

Yes, for checking assignments we use the stricter language
semantics so we should never generate assignments for
structs with different fields offsets or sizes. (I will
check this again).

> > > I also think more rationale is needed for ignoring sizes like this.  Is it 
> > > intended for e.g. making structs with flexible array members 
> > > alias-compatible with similar structs with a fixed-size array?
> > 
> > The main reason are pointers to arrays:
> > 
> > struct foo { int (*x)[]; }
> > struct foo { int (*x)[2]; };
> > struct foo { int (*x)[1]; };
> 
> Thanks for the explanation.
> 
> I guess the cases involving flexible array members actually show up a bug 
> in the standard rather than any kind of issue with this patch - the 
> standard allows one structure ending with a flexible array member, and 
> another ending with a fixed-size array, to be compatible (and in different 
> translation units allowed that even before C23), but there is also clear 
> text in the standard showing it's not intended to require the layout to be 
> consistent (the fixed-size and flexible arrays might have different 
> offsets), and what you'd actually get with an assignment or conditional 
> expression mixing such structures certainly isn't clearly specified.  

I agree, unfortunately it seems not well specified and we somehow missed 
that this now becomes more important.

> Maybe the right resolution for that issue with the standard would be to 
> make that particular case incompatible, but it would need to be raised as 
> an issue after C23 is out.

I think FAM may need further consideration from the standard
point of view for also other reasons.

My other issues I have now run into are structs with variably size
which I think do not work properly with LTO already today:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112716



Martin

> 





^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 3/4] c23: aliasing of compatible tagged types
  2023-11-28  1:00       ` Joseph Myers
  2023-11-28  6:49         ` Martin Uecker
@ 2023-11-28 10:47         ` Richard Biener
  2023-11-28 11:51           ` Martin Uecker
  1 sibling, 1 reply; 53+ messages in thread
From: Richard Biener @ 2023-11-28 10:47 UTC (permalink / raw)
  To: Joseph Myers; +Cc: Martin Uecker, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2674 bytes --]

On Tue, 28 Nov 2023, Joseph Myers wrote:

> On Sun, 26 Nov 2023, Martin Uecker wrote:
> 
> > My understand is that it is used for aliasing analysis and also
> > checking of conversions.  TYPE_CANONICAL must be consistent with
> > the idea the middle-end has about type conversions.  But as long
> > as we do not give the same TYPE_CANONICAL to types the middle-end
> > thinks must be incompatible using its own type checking machinery,
> > it should be safe even for types the C standard thinks must be
> > incompatible for some reason.
> 
> And presumably also for types that definitely can't be assigned because 
> they have incompatible layout through the use of different array sizes - 
> since the front end won't generate such assignments, it would never matter 
> whether the middle end considers them valid without conversion or not?

I guess so.  The middle-end considers an aggregate copy to have the
size of the LHS, so any mismatch there (in size) is the frontends
fault and will likely result in code generation with problems.

> > > I also think more rationale is needed for ignoring sizes like this.  Is it 
> > > intended for e.g. making structs with flexible array members 
> > > alias-compatible with similar structs with a fixed-size array?
> > 
> > The main reason are pointers to arrays:
> > 
> > struct foo { int (*x)[]; }
> > struct foo { int (*x)[2]; };
> > struct foo { int (*x)[1]; };
> 
> Thanks for the explanation.
> 
> I guess the cases involving flexible array members actually show up a bug 
> in the standard rather than any kind of issue with this patch - the 
> standard allows one structure ending with a flexible array member, and 
> another ending with a fixed-size array, to be compatible (and in different 
> translation units allowed that even before C23), but there is also clear 
> text in the standard showing it's not intended to require the layout to be 
> consistent (the fixed-size and flexible arrays might have different 
> offsets), and what you'd actually get with an assignment or conditional 
> expression mixing such structures certainly isn't clearly specified.  
> Maybe the right resolution for that issue with the standard would be to 
> make that particular case incompatible, but it would need to be raised as 
> an issue after C23 is out.

I think from a middle-end perspective it's desirable that accessing
either of the foo with size 2 and 1 through a pointer of the type
of the foo with unknown size is permissible from a TBAA view.  But
it's not clear to me that accessing the foo with array size 2 via
an lvalue of the type of foo with array size 1 is required to be
supported (from a TBAA view).

Richard.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 3/4] c23: aliasing of compatible tagged types
  2023-11-28 10:47         ` Richard Biener
@ 2023-11-28 11:51           ` Martin Uecker
  0 siblings, 0 replies; 53+ messages in thread
From: Martin Uecker @ 2023-11-28 11:51 UTC (permalink / raw)
  To: Richard Biener, Joseph Myers; +Cc: gcc-patches

Am Dienstag, dem 28.11.2023 um 10:47 +0000 schrieb Richard Biener:
> On Tue, 28 Nov 2023, Joseph Myers wrote:
> 
> > On Sun, 26 Nov 2023, Martin Uecker wrote:
> > 
> 

> > > > I also think more rationale is needed for ignoring sizes like this.  Is it 
> > > > intended for e.g. making structs with flexible array members 
> > > > alias-compatible with similar structs with a fixed-size array?
> > > 
> > > The main reason are pointers to arrays:
> > > 
> > > struct foo { int (*x)[]; }
> > > struct foo { int (*x)[2]; };
> > > struct foo { int (*x)[1]; };
> > 
> > Thanks for the explanation.
> > 
> > I guess the cases involving flexible array members actually show up a bug 
> > in the standard rather than any kind of issue with this patch - the 
> > standard allows one structure ending with a flexible array member, and 
> > another ending with a fixed-size array, to be compatible (and in different 
> > translation units allowed that even before C23), but there is also clear 
> > text in the standard showing it's not intended to require the layout to be 
> > consistent (the fixed-size and flexible arrays might have different 
> > offsets), and what you'd actually get with an assignment or conditional 
> > expression mixing such structures certainly isn't clearly specified.  
> > Maybe the right resolution for that issue with the standard would be to 
> > make that particular case incompatible, but it would need to be raised as 
> > an issue after C23 is out.
> 
> I think from a middle-end perspective it's desirable that accessing
> either of the foo with size 2 and 1 through a pointer of the type
> of the foo with unknown size is permissible from a TBAA view.  But
> it's not clear to me that accessing the foo with array size 2 via
> an lvalue of the type of foo with array size 1 is required to be
> supported (from a TBAA view).

This is the same from the C language side.  In principle, accesses 
to an object with effective type foo [2] using an lvalue of type 
foo [1] or vice versa are undefined behavior.

So a model based on equivalence classes loses some information. 

Martin


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [V4] [C PATCH 1/4] c23: tag compatibility rules for struct and unions
  2023-11-27 13:16 ` [V4] [C PATCH 1/4] c23: tag compatibility rules for struct and unions Martin Uecker
@ 2023-12-14 20:53   ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2023-12-14 20:53 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Mon, 27 Nov 2023, Martin Uecker wrote:

> Note that there is an additional change in parser_xref_tag
> to address the issue regarding completeness in redefinition
> which affects also structs / unions.  The test c23-tag-6.c
> was changed accordingly.
> 
> 
> c23: tag compatibility rules for struct and unions
> 
> Implement redeclaration and compatibility rules for
> structures and unions in C23.

This patch is OK.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [V4] [PATCH 2/4] c23: tag compatibility rules for enums
  2023-11-27 13:16 ` [V4] [PATCH 2/4] c23: tag compatibility rules for enums Martin Uecker
@ 2023-12-14 20:58   ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2023-12-14 20:58 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Mon, 27 Nov 2023, Martin Uecker wrote:

> +	enum B : short { M = 1 } *y2e = &x2;	/* { dg-warning "incompatible" } */

This probably now needs to be dg-error because 
-Wincompatible-pointer-types is now an error by default.  OK with that 
changed as needed (you may also need such a change in patch 1, so retest 
both patches against current master before applying).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [V4] [PATCH 3/4] c23: aliasing of compatible tagged types
  2023-11-27 13:16 ` [V4] [PATCH 3/4] c23: aliasing of compatible tagged types Martin Uecker
@ 2023-12-14 21:10   ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2023-12-14 21:10 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Mon, 27 Nov 2023, Martin Uecker wrote:

> diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
> index a5dd9a37944..ece5b6a5d26 100644
> --- a/gcc/c/c-tree.h
> +++ b/gcc/c/c-tree.h
> @@ -758,6 +758,7 @@ 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_equiv_p (tree, tree);

This function should return bool.

> @@ -1250,6 +1266,9 @@ comptypes_internal (const_tree type1, const_tree type2,
>  
>  	if ((d1 == NULL_TREE) != (d2 == NULL_TREE))
>  	  data->different_types_p = true;
> +	/* Ignore size mismatches.  */
> +	if (data->equiv)
> +	  return 1;
>  	/* Sizes must match unless one is missing or variable.  */
>  	if (d1 == NULL_TREE || d2 == NULL_TREE || d1 == d2)
>  	  return true;
> @@ -1467,6 +1486,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
>  	if (list_length (TYPE_FIELDS (t1)) != list_length (TYPE_FIELDS (t2)))
>  	  return false;
>  
> +	if (data->equiv && (C_TYPE_VARIABLE_SIZE (t1) || C_TYPE_VARIABLE_SIZE (t2)))
> +	  return 0;
> +
>  	for (s1 = TYPE_FIELDS (t1), s2 = TYPE_FIELDS (t2);
>  	     s1 && s2;
>  	     s1 = DECL_CHAIN (s1), s2 = DECL_CHAIN (s2))
> @@ -1486,6 +1508,15 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
>  		&& simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
>  				     DECL_FIELD_BIT_OFFSET (s2)) != 1)
>  	      return false;
> +
> +	    tree st1 = TYPE_SIZE (TREE_TYPE (s1));
> +	    tree st2 = TYPE_SIZE (TREE_TYPE (s2));
> +
> +	    if (data->equiv
> +		&& st1 && TREE_CODE (st1) == INTEGER_CST
> +		&& st2 && TREE_CODE (st2) == INTEGER_CST
> +		&& !tree_int_cst_equal (st1, st2))
> +	     return 0;

And these functions do return bool, so you should use true and false 
instead of 1 and 0.

> +/* The structs are incompatible so can be assumed not to
> + * alias, but this is not exploited.  So do not check for 
> + * this below but check the warning about incompatibility.  */
> +
> +int test_bar3(struct bar* a, void* b)
> +{
> +	a->x = 1;
> +
> +	struct bar { int x; int f[1]; }* p = b;
> +	struct bar* q = a;			/* { dg-warning "incompatible" } */

I expect you'll now need -fpermissive or 
-Wno-error=incompatible-pointer-types (this is an execution test so you 
need to stop this being an error, but see below).

> +	// allow both results here
> +	int r = test_bar3(&z, &z);
> +	if ((r != 2) && (r != 1))
> +        	__builtin_abort();

I don't think you should really be executing this call at all (aliasing 
not allowed means undefined behavior at runtime); better to put this in a 
separate compile-only test (which would also avoid the need for 
-fpermissive or -Wno-error=incompatible-pointer-types because once it's no 
longer an execution test, having an error is OK).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [V5] [C PATCH 1/4] c23: tag compatibility rules for struct and unions
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (7 preceding siblings ...)
  2023-11-27 13:16 ` [V4] [PATCH 4/4] c23: construct composite type for " Martin Uecker
@ 2023-12-17 17:41 ` Martin Uecker
  2023-12-19 21:48   ` Joseph Myers
  2023-12-17 17:42 ` [V5] [C PATCH 2/4] c23: tag compatibility rules for enums Martin Uecker
                   ` (8 subsequent siblings)
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2023-12-17 17:41 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers


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

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);
 }
 
-- 
2.39.2



^ permalink raw reply	[flat|nested] 53+ messages in thread

* [V5] [C PATCH 2/4] c23: tag compatibility rules for enums
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (8 preceding siblings ...)
  2023-12-17 17:41 ` [V5] [C PATCH 1/4] c23: tag compatibility rules for struct and unions Martin Uecker
@ 2023-12-17 17:42 ` Martin Uecker
  2023-12-19 21:50   ` Joseph Myers
  2023-12-17 17:42 ` [V5] [C PATCH 3/4] c23: aliasing of compatible tagged types Martin Uecker
                   ` (7 subsequent siblings)
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2023-12-17 17:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers



Allow redefinition of enum types and enumerators.  Diagnose
nested redefinitions including redefinitions in the enum
specifier for enum types with fixed underlying type.

gcc/c:
	* c-tree.h (c_parser_enum_specifier): Add parameter.
	* c-decl.cc (start_enum): Allow redefinition.
	(finish_enum): Diagnose conflicts.
	(build_enumerator): Set context.
	(diagnose_mismatched_decls): Diagnose conflicting enumerators.
	(push_decl): Preserve context for enumerators.
	* c-parser.cc (c_parser_enum_specifier): Remember when
	seen is from an enum type which is not yet defined.

gcc/testsuide/:
	* gcc.dg/c23-tag-enum-1.c: New test.
	* gcc.dg/c23-tag-enum-2.c: New test.
	* gcc.dg/c23-tag-enum-3.c: New test.
	* gcc.dg/c23-tag-enum-4.c: New test.
	* gcc.dg/c23-tag-enum-5.c: New test.
	* gcc.dg/gnu23-tag-enum-1.c: Mew test.
---
 gcc/c/c-decl.cc                         | 65 +++++++++++++++++++++----
 gcc/c/c-parser.cc                       |  5 +-
 gcc/c/c-tree.h                          |  3 +-
 gcc/c/c-typeck.cc                       |  5 +-
 gcc/testsuite/gcc.dg/c23-tag-enum-1.c   | 56 +++++++++++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-enum-2.c   | 17 +++++++
 gcc/testsuite/gcc.dg/c23-tag-enum-3.c   |  7 +++
 gcc/testsuite/gcc.dg/c23-tag-enum-4.c   | 22 +++++++++
 gcc/testsuite/gcc.dg/c23-tag-enum-5.c   | 18 +++++++
 gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c | 19 ++++++++
 10 files changed, 205 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-4.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-enum-5.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 0e6b4a5248b..26188aa225e 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2112,9 +2112,24 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
      given scope.  */
   if (TREE_CODE (olddecl) == CONST_DECL)
     {
-      auto_diagnostic_group d;
-      error ("redeclaration of enumerator %q+D", newdecl);
-      locate_old_decl (olddecl);
+      if (flag_isoc23
+	  && TYPE_NAME (DECL_CONTEXT (newdecl))
+	  && DECL_CONTEXT (newdecl) != DECL_CONTEXT (olddecl)
+	  && TYPE_NAME (DECL_CONTEXT (newdecl)) == TYPE_NAME (DECL_CONTEXT (olddecl)))
+	{
+	  if (!simple_cst_equal (DECL_INITIAL (olddecl), DECL_INITIAL (newdecl)))
+	    {
+	      auto_diagnostic_group d;
+	      error ("conflicting redeclaration of enumerator %q+D", newdecl);
+	      locate_old_decl (olddecl);
+	    }
+	}
+      else
+	{
+	  auto_diagnostic_group d;
+	  error ("redeclaration of enumerator %q+D", newdecl);
+	  locate_old_decl (olddecl);
+	}
       return false;
     }
 
@@ -3275,8 +3290,11 @@ pushdecl (tree x)
 
   /* Must set DECL_CONTEXT for everything not at file scope or
      DECL_FILE_SCOPE_P won't work.  Local externs don't count
-     unless they have initializers (which generate code).  */
+     unless they have initializers (which generate code).  We
+     also exclude CONST_DECLs because enumerators will get the
+     type of the enum as context.  */
   if (current_function_decl
+      && TREE_CODE (x) != CONST_DECL
       && (!VAR_OR_FUNCTION_DECL_P (x)
 	  || DECL_INITIAL (x) || !TREE_PUBLIC (x)))
     DECL_CONTEXT (x) = current_function_decl;
@@ -9759,7 +9777,7 @@ layout_array_type (tree t)
 
 tree
 start_enum (location_t loc, struct c_enum_contents *the_enum, tree name,
-	    tree fixed_underlying_type)
+	    tree fixed_underlying_type, bool potential_nesting_p)
 {
   tree enumtype = NULL_TREE;
   location_t enumloc = UNKNOWN_LOCATION;
@@ -9771,9 +9789,26 @@ start_enum (location_t loc, struct c_enum_contents *the_enum, tree name,
   if (name != NULL_TREE)
     enumtype = lookup_tag (ENUMERAL_TYPE, name, true, &enumloc);
 
+  if (enumtype != NULL_TREE && TREE_CODE (enumtype) == ENUMERAL_TYPE)
+    {
+      /* If the type is currently being defined or if we have seen an
+	 incomplete version which is now complete, this is a nested
+	 redefinition.  The later happens if the redefinition occurs
+	 inside the enum specifier itself.  */
+      if (C_TYPE_BEING_DEFINED (enumtype)
+	  || (potential_nesting_p && TYPE_VALUES (enumtype) != NULL_TREE))
+	error_at (loc, "nested redefinition of %<enum %E%>", name);
+
+      /* For C23 we allow redefinitions.  We set to zero and check for
+	 consistency later.  */
+      if (flag_isoc23 && TYPE_VALUES (enumtype) != NULL_TREE)
+	enumtype = NULL_TREE;
+    }
+
   if (enumtype == NULL_TREE || TREE_CODE (enumtype) != ENUMERAL_TYPE)
     {
       enumtype = make_node (ENUMERAL_TYPE);
+      TYPE_SIZE (enumtype) = NULL_TREE;
       pushtag (loc, name, enumtype);
       if (fixed_underlying_type != NULL_TREE)
 	{
@@ -9801,9 +9836,6 @@ start_enum (location_t loc, struct c_enum_contents *the_enum, tree name,
       DECL_SOURCE_LOCATION (TYPE_STUB_DECL (enumtype)) = loc;
     }
 
-  if (C_TYPE_BEING_DEFINED (enumtype))
-    error_at (loc, "nested redefinition of %<enum %E%>", name);
-
   C_TYPE_BEING_DEFINED (enumtype) = 1;
 
   if (TYPE_VALUES (enumtype) != NULL_TREE)
@@ -10033,6 +10065,20 @@ finish_enum (tree enumtype, tree values, tree attributes)
       && !in_sizeof && !in_typeof && !in_alignof)
     struct_parse_info->struct_types.safe_push (enumtype);
 
+  /* Check for consistency with previous definition */
+  if (flag_isoc23)
+    {
+      tree vistype = previous_tag (enumtype);
+      if (vistype
+	  && TREE_CODE (vistype) == TREE_CODE (enumtype)
+	  && !C_TYPE_BEING_DEFINED (vistype))
+	{
+	  TYPE_STUB_DECL (vistype) = TYPE_STUB_DECL (enumtype);
+	  if (!comptypes_same_p (enumtype, vistype))
+	    error("conflicting redefinition of enum %qT", enumtype);
+	}
+    }
+
   C_TYPE_BEING_DEFINED (enumtype) = 0;
 
   return enumtype;
@@ -10212,6 +10258,7 @@ build_enumerator (location_t decl_loc, location_t loc,
 
   decl = build_decl (decl_loc, CONST_DECL, name, TREE_TYPE (value));
   DECL_INITIAL (decl) = value;
+  DECL_CONTEXT (decl) = the_enum->enum_type;
   pushdecl (decl);
 
   return tree_cons (decl, value, NULL_TREE);
@@ -10228,7 +10275,7 @@ c_simulate_enum_decl (location_t loc, const char *name,
 
   struct c_enum_contents the_enum;
   tree enumtype = start_enum (loc, &the_enum, get_identifier (name),
-			      NULL_TREE);
+			      NULL_TREE, false);
 
   tree value_chain = NULL_TREE;
   string_int_pair *value;
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index a7dc096011f..5e1b56665d2 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -3667,6 +3667,7 @@ c_parser_enum_specifier (c_parser *parser)
 {
   struct c_typespec ret;
   bool have_std_attrs;
+  bool potential_nesting_p = false;
   tree std_attrs = NULL_TREE;
   tree attrs;
   tree ident = NULL_TREE;
@@ -3706,6 +3707,7 @@ c_parser_enum_specifier (c_parser *parser)
 	  if (!ENUM_FIXED_UNDERLYING_TYPE_P (ret.spec))
 	    error_at (enum_loc, "%<enum%> declared both with and without "
 		      "fixed underlying type");
+	  potential_nesting_p = NULL_TREE == TYPE_VALUES (ret.spec);
 	}
       else
 	{
@@ -3776,7 +3778,8 @@ c_parser_enum_specifier (c_parser *parser)
 	 forward order at the end.  */
       tree values;
       timevar_push (TV_PARSE_ENUM);
-      type = start_enum (enum_loc, &the_enum, ident, fixed_underlying_type);
+      type = start_enum (enum_loc, &the_enum, ident, fixed_underlying_type,
+			 potential_nesting_p);
       values = NULL_TREE;
       c_parser_consume_token (parser);
       while (true)
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index e53948f21f3..54f1353ad34 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -683,7 +683,8 @@ extern void c_warn_unused_attributes (tree);
 extern tree c_warn_type_attributes (tree);
 extern void shadow_tag (const struct c_declspecs *);
 extern void shadow_tag_warned (const struct c_declspecs *, int);
-extern tree start_enum (location_t, struct c_enum_contents *, tree, tree);
+extern tree start_enum (location_t, struct c_enum_contents *, tree, tree,
+			bool potential_nesting_p);
 extern bool start_function (struct c_declspecs *, struct c_declarator *, tree);
 extern tree start_decl (struct c_declarator *, struct c_declspecs *, bool,
 			tree, bool = true, location_t * = NULL);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 0d514ec3570..0a9e8980c79 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1419,6 +1419,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
     {
     case ENUMERAL_TYPE:
       {
+	if (!comptypes (ENUM_UNDERLYING_TYPE (t1), ENUM_UNDERLYING_TYPE (t2)))
+	  return false;
+
 	/* Speed up the case where the type values are in the same order.  */
 	tree tv1 = TYPE_VALUES (t1);
 	tree tv2 = TYPE_VALUES (t2);
@@ -7006,7 +7009,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
       if (checktype != error_mark_node
 	  && TREE_CODE (checktype) == ENUMERAL_TYPE
 	  && TREE_CODE (type) == ENUMERAL_TYPE
-	  && TYPE_MAIN_VARIANT (checktype) != TYPE_MAIN_VARIANT (type))
+	  && !comptypes (TYPE_MAIN_VARIANT (checktype), TYPE_MAIN_VARIANT (type)))
        {
 	  gcc_rich_location loc (location);
 	  warning_at (&loc, OPT_Wenum_conversion,
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-1.c b/gcc/testsuite/gcc.dg/c23-tag-enum-1.c
new file mode 100644
index 00000000000..a81a5afc456
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-1.c
@@ -0,0 +1,56 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// incompatible redeclarations, conflicing redefinitions
+
+
+enum aa { A = 1 } *a;
+enum bb { B = 1 } *b;
+
+void test(void)
+{
+  enum aa { A = 1 } *c = a;
+  enum bb { B = 2 } *d = b;	/* { dg-error "incompatible pointer type" } */
+}
+
+enum cc { C = 1 };
+enum cc { D = 1 };		/* { dg-error "conflicting redefinition" } */	
+
+enum dd { E = 1 };
+enum dd { E = 2 };		/* { dg-error "conflicting redefinition" } */	
+				/* { dg-error "redeclaration of enumerator" "" { target *-*-* } .-1 } */	
+
+
+
+void test2(void)
+{
+  enum ee *a;
+  enum ee { F = 2 } *b;
+  b = a;
+}
+
+
+enum ff { G = 2 };
+enum gg { G = 2 };		/* { dg-error "redeclaration of enumerator" } */
+enum g2 { G = 3 };		/* { dg-error "redeclaration of enumerator" } */
+
+enum hh { H = 1, H = 1 };	/* { dg-error "redeclaration of enumerator" } */
+
+enum ss { K = 2 };
+enum ss { K = 2 };
+
+enum tt { R = 2 } TT;
+enum tt {
+	R = _Generic(&TT, enum tt*: 2, default: 0)
+};
+
+enum { U = 1 };
+enum { U = 1 };			/* { dg-error "redeclaration of enumerator" } */
+
+enum { V = 1 };
+enum { V = 2 };			/* { dg-error "redeclaration of enumerator" } */
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-2.c b/gcc/testsuite/gcc.dg/c23-tag-enum-2.c
new file mode 100644
index 00000000000..2bf7255b844
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-2.c
@@ -0,0 +1,17 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+enum A { B = 7 } y;
+
+// complete during later construction
+
+enum A { B = _Generic(&y, enum A*: 7, default: 1) };
+
+
+enum X { E = 1, F = 1 + 1 };
+enum X { F = 2, E = 1 };
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-3.c b/gcc/testsuite/gcc.dg/c23-tag-enum-3.c
new file mode 100644
index 00000000000..12218a5b911
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-3.c
@@ -0,0 +1,7 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+enum A { N = 0 * sizeof(enum A { M = 1 }) }; 	/* { dg-error "nested" } */
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-4.c b/gcc/testsuite/gcc.dg/c23-tag-enum-4.c
new file mode 100644
index 00000000000..e6bc167a2f7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-4.c
@@ -0,0 +1,22 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" }
+ */
+
+// fixed underlying types
+
+enum A : int { N = 1 } x1 = { };
+enum B : int { M = 1 } x2 = { };
+enum C { U = 1 } x3 = { };
+
+void f(void)
+{
+	enum A : int { N = 1 } y1 = x1;
+	enum B : short { M = 1 } y2;
+        y2 = x2;
+	enum B : short { M = 1 } y2b;
+	enum Bb : short { V = 1 } y2d = x2;
+	enum B : short { M = 1 } *y2e = &x2;	/* { dg-error "incompatible" } */
+	enum B : short { M = 1 } y2c = x2;
+	enum C { U = 1 } y3 = x3;
+}
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-5.c b/gcc/testsuite/gcc.dg/c23-tag-enum-5.c
new file mode 100644
index 00000000000..22ce06fb80d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-enum-5.c
@@ -0,0 +1,18 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23" } */
+
+// test for nested redefinitions of enums
+
+void foo(void)
+{
+	enum e { A = 1 };
+	enum e { A = 1 					/* { dg-error "redeclaration" } */
+		+ 0 * sizeof(enum e { A = 1 }) };	/* { dg-error "nested redefinition" } */
+							
+}
+
+typedef __SIZE_TYPE__ size_t;
+enum f : typeof (sizeof (enum f : size_t { B })) { B };	/* { dg-error "nested redefinition" } */
+
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c b/gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c
new file mode 100644
index 00000000000..6b346ed35a0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-enum-1.c
@@ -0,0 +1,19 @@
+/*
+ * { dg-do compile }
+ * { dg-options "-std=gnu23" }
+ */
+
+
+enum A { B = 7 } y;
+
+void g(void)
+{
+	// incomplete during construction
+	// this is a GNU extension because enum A is used
+	// before the type is completed.
+
+	enum A { B = _Generic(&y, enum A*: 1, default: 7) };
+	_Static_assert(7 == B, "");
+}
+
+
-- 
2.39.2



^ permalink raw reply	[flat|nested] 53+ messages in thread

* [V5] [C PATCH 3/4] c23: aliasing of compatible tagged types
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (9 preceding siblings ...)
  2023-12-17 17:42 ` [V5] [C PATCH 2/4] c23: tag compatibility rules for enums Martin Uecker
@ 2023-12-17 17:42 ` Martin Uecker
  2023-12-19 22:02   ` Joseph Myers
  2023-12-17 17:42 ` [V5] [C PATCH 4/4] c23: construct composite type for " Martin Uecker
                   ` (6 subsequent siblings)
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2023-12-17 17:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers



Tell the backend which types are equivalent by setting
TYPE_CANONICAL to one struct in the set of equivalent
structs.  Structs are considered equivalent by ignoring
all sizes of arrays nested in types below field level.

The following two structs are incompatible and lvalues
with these types can be assumed not to alias:

 struct foo { int a[3]; };
 struct foo { int a[4]; };

The following two structs are also incompatible, but
will get the same TYPE_CANONICAL and it is then not
exploited that lvalues with those types can not alias:

 struct bar { int (*p)[3]; };
 struct bar { int (*p)[4]; };

The reason is that both are compatible to

 struct bar { int (*p)[]; };

and therefore are in the same equivalence class.  For
the same reason all enums with the same underyling type
are in the same equivalence class.  Tests are added
for the expected aliasing behavior with optimization.

gcc/c:
	* c-decl.cc (c_struct_hasher): Hash stable for struct
	types.
	(c_struct_hasher::hash, c_struct_hasher::equal): New
	functions.
	(finish_struct): Set TYPE_CANONICAL to first struct in
	equivalence class.
	* c-objc-common.cc (c_get_alias_set): Let structs or
	unions with variable size alias anything.
	* c-tree.h (comptypes_equiv): New prototype.
	* c-typeck.cc (comptypes_equiv): New function.
	(comptypes_internal): Implement equivalence mode.
	(tagged_types_tu_compatible): Implement equivalence mode.

gcc/testsuite:
	* gcc.dg/c23-tag-2.c: Activate.
	* gcc.dg/c23-tag-5.c: Activate.
	* gcc.dg/c23-tag-alias-1.c: New test.
	* gcc.dg/c23-tag-alias-2.c: New test.
	* gcc.dg/c23-tag-alias-3.c: New test.
	* gcc.dg/c23-tag-alias-4.c: New test.
	* gcc.dg/c23-tag-alias-5.c: New test.
	* gcc.dg/gnu23-tag-alias-1.c: New test.
	* gcc.dg/gnu23-tag-alias-2.c: New test.
	* gcc.dg/gnu23-tag-alias-3.c: New test.
	* gcc.dg/gnu23-tag-alias-4.c: New test.
	* gcc.dg/gnu23-tag-alias-5.c: New test.
	* gcc.dg/gnu23-tag-alias-6.c: New test.
	* gcc.dg/gnu23-tag-alias-7.c: New test.
---
 gcc/c/c-decl.cc                          |  51 ++++++++++-
 gcc/c/c-objc-common.cc                   |   5 ++
 gcc/c/c-tree.h                           |   1 +
 gcc/c/c-typeck.cc                        |  31 +++++++
 gcc/testsuite/gcc.dg/c23-tag-2.c         |   2 +-
 gcc/testsuite/gcc.dg/c23-tag-5.c         |   2 +-
 gcc/testsuite/gcc.dg/c23-tag-alias-1.c   |  49 +++++++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-2.c   |  50 +++++++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-3.c   |  32 +++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-4.c   |  32 +++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-5.c   |  36 ++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c |  33 +++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c |  85 ++++++++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c |  83 ++++++++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c |  36 ++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c | 107 +++++++++++++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c |  60 +++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c |  93 ++++++++++++++++++++
 18 files changed, 785 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-4.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-5.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 26188aa225e..6639ec35e5f 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -634,6 +634,36 @@ public:
   auto_vec<tree> typedefs_seen;
 };
 
+
+/* Hash table for structs and unions.  */
+struct c_struct_hasher : ggc_ptr_hash<tree_node>
+{
+  static hashval_t hash (tree t);
+  static bool equal (tree, tree);
+};
+
+/* Hash an RECORD OR UNION.  */
+hashval_t
+c_struct_hasher::hash (tree type)
+{
+  inchash::hash hstate;
+
+  hstate.add_int (TREE_CODE (type));
+  hstate.add_object (TYPE_NAME (type));
+
+  return hstate.end ();
+}
+
+/* Compare two RECORD or UNION types.  */
+bool
+c_struct_hasher::equal (tree t1,  tree t2)
+{
+  return comptypes_equiv_p (t1, t2);
+}
+
+/* All tagged typed so that TYPE_CANONICAL can be set correctly.  */
+static GTY (()) hash_table<c_struct_hasher> *c_struct_htab;
+
 /* Information for the struct or union currently being parsed, or
    NULL if not parsing a struct or union.  */
 static class c_struct_parse_info *struct_parse_info;
@@ -8713,7 +8743,8 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name,
   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).  */
+     an earlier definition (which may be complete).  We do not
+     have to loop because nested redefinitions are not allowed.  */
   if (flag_isoc23 && ref && C_TYPE_BEING_DEFINED (ref))
     {
       tree vis = previous_tag (ref);
@@ -9661,6 +9692,24 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
 
   C_TYPE_BEING_DEFINED (t) = 0;
 
+  /* Set type canonical based on equivalence class.  */
+  if (flag_isoc23)
+    {
+      if (NULL == c_struct_htab)
+	c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61);
+
+      hashval_t hash = c_struct_hasher::hash (t);
+
+      tree *e = c_struct_htab->find_slot_with_hash (t, hash, INSERT);
+      if (*e)
+	TYPE_CANONICAL (t) = *e;
+      else
+	{
+	  TYPE_CANONICAL (t) = t;
+	  *e = t;
+	}
+    }
+
   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-objc-common.cc b/gcc/c/c-objc-common.cc
index 53eda7fa707..a3946275b23 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -422,6 +422,11 @@ c_get_alias_set (tree t)
   if (TREE_CODE (t) == ENUMERAL_TYPE)
     return get_alias_set (ENUM_UNDERLYING_TYPE (t));
 
+  /* Structs with variable size can alias different incompatible
+     structs.  Let them alias anything.   */
+  if (RECORD_OR_UNION_TYPE_P (t) && C_TYPE_VARIABLE_SIZE (t))
+    return 0;
+
   return c_common_get_alias_set (t);
 }
 
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 54f1353ad34..02a09e53b05 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -759,6 +759,7 @@ 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 bool comptypes_equiv_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 0a9e8980c79..4d3079156ba 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1063,6 +1063,7 @@ struct comptypes_data {
   bool different_types_p;
   bool warning_needed;
   bool anon_field;
+  bool equiv;
 
   const struct tagged_tu_seen_cache* cache;
 };
@@ -1123,6 +1124,21 @@ comptypes_check_different_types (tree type1, tree type2,
 
   return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
+
+
+/* Like comptypes, but if it returns nonzero for struct and union
+   types considered equivalent for aliasing purposes.  */
+
+bool
+comptypes_equiv_p (tree type1, tree type2)
+{
+  struct comptypes_data data = { };
+  data.equiv = true;
+  bool ret = comptypes_internal (type1, type2, &data);
+
+  return ret;
+}
+
 \f
 /* Return true if TYPE1 and TYPE2 are compatible types for assignment
    or various other operations.  If they are compatible but a warning may
@@ -1250,6 +1266,9 @@ comptypes_internal (const_tree type1, const_tree type2,
 
 	if ((d1 == NULL_TREE) != (d2 == NULL_TREE))
 	  data->different_types_p = true;
+	/* Ignore size mismatches.  */
+	if (data->equiv)
+	  return true;
 	/* Sizes must match unless one is missing or variable.  */
 	if (d1 == NULL_TREE || d2 == NULL_TREE || d1 == d2)
 	  return true;
@@ -1467,6 +1486,9 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	if (list_length (TYPE_FIELDS (t1)) != list_length (TYPE_FIELDS (t2)))
 	  return false;
 
+	if (data->equiv && (C_TYPE_VARIABLE_SIZE (t1) || C_TYPE_VARIABLE_SIZE (t2)))
+	  return false;
+
 	for (s1 = TYPE_FIELDS (t1), s2 = TYPE_FIELDS (t2);
 	     s1 && s2;
 	     s1 = DECL_CHAIN (s1), s2 = DECL_CHAIN (s2))
@@ -1486,6 +1508,15 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 		&& simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
 				     DECL_FIELD_BIT_OFFSET (s2)) != 1)
 	      return false;
+
+	    tree st1 = TYPE_SIZE (TREE_TYPE (s1));
+	    tree st2 = TYPE_SIZE (TREE_TYPE (s2));
+
+	    if (data->equiv
+		&& st1 && TREE_CODE (st1) == INTEGER_CST
+		&& st2 && TREE_CODE (st2) == INTEGER_CST
+		&& !tree_int_cst_equal (st1, st2))
+	     return false;
 	  }
 	return true;
 
diff --git a/gcc/testsuite/gcc.dg/c23-tag-2.c b/gcc/testsuite/gcc.dg/c23-tag-2.c
index 5dd4a21e9df..444605a93c6 100644
--- a/gcc/testsuite/gcc.dg/c23-tag-2.c
+++ b/gcc/testsuite/gcc.dg/c23-tag-2.c
@@ -1,4 +1,4 @@
-/* { dg-do compile { target { ! "*-*-*" } } }
+/* { dg-do compile }
  * { dg-options "-std=c23" }
  */
 
diff --git a/gcc/testsuite/gcc.dg/c23-tag-5.c b/gcc/testsuite/gcc.dg/c23-tag-5.c
index ff7bbd662aa..90111266e06 100644
--- a/gcc/testsuite/gcc.dg/c23-tag-5.c
+++ b/gcc/testsuite/gcc.dg/c23-tag-5.c
@@ -1,4 +1,4 @@
-/* { dg-do run { target { ! "*-*-*" } } }
+/* { dg-do run }
  * { dg-options "-std=c23 -fpermissive" }
  */
 
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-1.c b/gcc/testsuite/gcc.dg/c23-tag-alias-1.c
new file mode 100644
index 00000000000..c92f942af88
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-1.c
@@ -0,0 +1,49 @@
+/* { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+
+/* These tests check that redefinitions of tagged
+   types can alias the original definitions.  */
+
+struct foo { int x; };
+
+int test_foo(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+enum bar { A = 1, B = 3 };
+
+int test_bar(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (2 != test_foo(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (B != test_bar(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-2.c b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
new file mode 100644
index 00000000000..64ff67d8552
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
@@ -0,0 +1,50 @@
+/* { dg-do run }
+ * { dg-options "-std=c23 -flto -O2" }
+ */
+
+/* These tests check that compatible definitions of
+   tagged types can alias the original definitions
+   with LTO.  */
+
+struct foo { int x; };
+
+int test_foo(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+enum bar { A = 1, B = 3 };
+
+int test_bar(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (2 != test_foo(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (B != test_bar(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-3.c b/gcc/testsuite/gcc.dg/c23-tag-alias-3.c
new file mode 100644
index 00000000000..b9fe6f3b407
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-3.c
@@ -0,0 +1,32 @@
+/* { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+/* These tests check that definitions of enums with 
+ * the same underlying type can alias, even when
+ * they are not compatible.  */
+
+enum bar : long { A = 1, B = 3 };
+
+int test_bar(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum foo : long { C = 2, D = 4 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	enum bar z;
+
+	if (B != test_bar(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-4.c b/gcc/testsuite/gcc.dg/c23-tag-alias-4.c
new file mode 100644
index 00000000000..1d43d0d754d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-4.c
@@ -0,0 +1,32 @@
+/* { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+
+/* Here we check that structs with flexible array
+ * members can alias a compatible redefinition.  */
+
+struct bar { int x; int f[]; };
+
+int test_bar1(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+int main()
+{
+	struct bar z;
+
+	if (2 != test_bar1(&z, &z))
+        	__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-5.c b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c
new file mode 100644
index 00000000000..f5cfad161a5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c
@@ -0,0 +1,36 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+/* The structs are incompatible so can be assumed not to
+ * alias, but this is not exploited.  So do not check for 
+ * this below but check the error about incompatibility.  */
+
+struct bar { int x; int f[]; };
+
+int test_bar3(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[1]; }* p = b;
+	struct bar* q = a;			/* { dg-error "incompatible" } */
+	p->x = 2;
+
+	return a->x;
+}
+
+
+int main()
+{
+	struct bar z;
+
+	// allow both results here
+	int r = test_bar3(&z, &z);
+
+	// UB but could be expected to return 1 with optimization
+	// exploiting the UB (not done at time of writing) or 2
+
+	return r;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c
new file mode 100644
index 00000000000..c51417f831a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c
@@ -0,0 +1,33 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+/* Check that structs with flexible array member can alias.  */
+
+struct bar { int x; int f[]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[0]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar z;
+
+	if (2 != test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c
new file mode 100644
index 00000000000..c09c3ca40a6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c
@@ -0,0 +1,85 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+
+/* These tests check that incompatible definitions of
+   tagged types can be assumed not to alias and that
+   this is exploited during optimization.  */
+
+struct foo { int x; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo2(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct fox { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+/* While these tests check that incompatible enums can still
+ * alias, although this is not required.   */
+
+enum bar { A = 1, B = 3, C = 5, D = 9 };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar1(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3, C = 6, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum baX { A = 1, B = 3, C = 5, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (1 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	if (1 != test_foo2(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (B != test_bar1(&z, &z))
+		__builtin_abort();
+
+	if (B != test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c
new file mode 100644
index 00000000000..a07a1e6fa38
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c
@@ -0,0 +1,83 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -flto -O2" }
+ */
+
+/* These tests check that incompatible definitions of
+   tagged types can be assumed not to alias and that
+   this is exploited during optimization with LTO.  */
+
+struct foo { int x; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo2(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct fox { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+/* While tese tests check that incompatible definitions
+ * of enums can alias.  */
+
+enum bar { A = 1, B = 3, C = 5, D = 9 };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar1(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3, C = 6, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum baX { A = 1, B = 3, C = 5, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (1 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	if (1 != test_foo2(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (B != test_bar1(&z, &z))
+		__builtin_abort();
+
+	if (B != test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c
new file mode 100644
index 00000000000..1ea3a883d0c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c
@@ -0,0 +1,36 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+/* This test checks that an incompatible definition of
+ * a tagged type without tag can be assumed not to alias.  
+ * and that this is exploited during optimization.  */
+
+
+// not sure this is wise, but this was already like this before
+
+typedef struct { int x; } foo_t;
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo(foo_t* a, void* b)
+{
+	a->x = 1;
+
+	struct { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+int main()
+{
+	foo_t y;
+
+	if (1 != test_foo(&y, &y))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c
new file mode 100644
index 00000000000..5a83397bd6d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c
@@ -0,0 +1,107 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+/* This test checks that different field offsets imply
+ * that the types can be assumed not to alias
+ * and that this is exploited during optimization.  */
+
+
+struct bar0 { int x; int f[3]; int y; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar0(struct bar0* a, void* b)
+{
+	a->x = 1;
+
+	struct bar0 { int x; int f[4]; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+/* While these tests check that different structs with different
+ * sizes in arrays pointed to by field members can alias,
+ * even though the types are incompatible.  */
+
+
+struct bar1 { int x; int (*f)[3]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar1(struct bar1* a, void* b)
+{
+	a->x = 1;
+
+	struct bar1 { int x; int (*f)[3]; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar2 { int x; int (*f)[3]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(struct bar2* a, void* b)
+{
+	a->x = 1;
+
+	struct bar2 { int x; int (*f)[4]; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+/* This test checks that different structs with pointers to
+ * different compatible arrays types can alias.  */
+
+
+struct bar3 { int x; int (*f)[3]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar3(struct bar3* a, void* b)
+{
+	a->x = 1;
+
+	struct bar3 { int x; int (*f)[]; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+
+int main()
+{
+	// control
+
+	struct bar0 z0;
+
+	if (1 != test_bar0(&z0, &z0))
+		__builtin_abort();
+
+	// this could be different
+	struct bar1 z1;
+
+	if (2 != test_bar1(&z1, &z1))
+		__builtin_abort();
+
+	struct bar2 z2;
+
+	if (2 != test_bar2(&z2, &z2))
+		__builtin_abort();
+
+	struct bar3 z3;
+
+	if (2 != test_bar3(&z3, &z3))
+		__builtin_abort();
+
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c
new file mode 100644
index 00000000000..78d2abf2ed9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c
@@ -0,0 +1,60 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+
+
+/* Here we check that struct with a variable size (GNU extension)
+ * can alias a struct with a flexible array member or a struct with a
+ * fixed size array as last element.  */
+
+struct bar { int x; int f[]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar4(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	int n = 3;
+	struct bar { int x; int f[n]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct foo { int x; int f[3]; };
+
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	int n = 3;
+	struct foo { int x; int f[n]; }* p = b;
+	struct foo* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar z;
+
+	if (2 != test_bar4(&z, &z))
+		__builtin_abort();
+
+	struct foo y;
+
+	if (2 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c
new file mode 100644
index 00000000000..d3fc4bd57e1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c
@@ -0,0 +1,93 @@
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+
+/* We check that the incompatible enums as fields lead to
+ * incompatible types that can be assumed not to alias
+ * and that this is exploited during optimization.  */
+
+struct bar1 { int x; enum A1 { X1 = 1 } f; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar1(struct bar1* a, void* b)
+{
+	a->x = 1;
+
+	struct bar1 { int x; enum A1 { X1 = 2 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar2 { int x; enum A2 { X2 = 1 } f; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(struct bar2* a, void* b)
+{
+	a->x = 1;
+
+	struct bar2 { int x; enum B2 { X2 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+struct bar3 { int x; enum A3 { X3 = 1 } f; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar3(struct bar3* a, void* b)
+{
+	a->x = 1;
+
+	struct bar3 { int x; enum A3 { Y3 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar4 { int x; enum { Z4 = 1 } f; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar4(struct bar4* a, void* b)
+{
+	a->x = 1;
+
+	struct bar4 { int x; enum { Z4 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar1 z1;
+
+	if (1 != test_bar1(&z1, &z1))
+		__builtin_abort();
+
+	struct bar2 z2;
+
+	if (1 != test_bar2(&z2, &z2))
+		__builtin_abort();
+
+	struct bar3 z3;
+
+	if (1 != test_bar3(&z3, &z3))
+		__builtin_abort();
+
+	struct bar4 z4;
+
+	if (1 != test_bar4(&z4, &z4))
+		__builtin_abort();
+
+	return 0;
+}
+
+
-- 
2.39.2



^ permalink raw reply	[flat|nested] 53+ messages in thread

* [V5] [C PATCH 4/4] c23: construct composite type for tagged types
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (10 preceding siblings ...)
  2023-12-17 17:42 ` [V5] [C PATCH 3/4] c23: aliasing of compatible tagged types Martin Uecker
@ 2023-12-17 17:42 ` Martin Uecker
  2023-12-19 22:25   ` Joseph Myers
  2023-12-21 21:47 ` [V6] " Martin Uecker
                   ` (5 subsequent siblings)
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2023-12-17 17:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers



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



^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [V5] [C PATCH 1/4] c23: tag compatibility rules for struct and unions
  2023-12-17 17:41 ` [V5] [C PATCH 1/4] c23: tag compatibility rules for struct and unions Martin Uecker
@ 2023-12-19 21:48   ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2023-12-19 21:48 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 689 bytes --]

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.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [V5] [C PATCH 2/4] c23: tag compatibility rules for enums
  2023-12-17 17:42 ` [V5] [C PATCH 2/4] c23: tag compatibility rules for enums Martin Uecker
@ 2023-12-19 21:50   ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2023-12-19 21:50 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Sun, 17 Dec 2023, Martin Uecker wrote:

> diff --git a/gcc/testsuite/gcc.dg/c23-tag-enum-1.c b/gcc/testsuite/gcc.dg/c23-tag-enum-1.c
> new file mode 100644
> index 00000000000..a81a5afc456
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c23-tag-enum-1.c

> +void test2(void)
> +{
> +  enum ee *a;
> +  enum ee { F = 2 } *b;

There's no prior declaration of enum ee, so this is using the GNU 
extension of forward enum declarations, and should move to a gnu23-* test.

Patch 2 is OK with that change.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [V5] [C PATCH 3/4] c23: aliasing of compatible tagged types
  2023-12-17 17:42 ` [V5] [C PATCH 3/4] c23: aliasing of compatible tagged types Martin Uecker
@ 2023-12-19 22:02   ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2023-12-19 22:02 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Sun, 17 Dec 2023, Martin Uecker wrote:

> +/* While tese tests check that incompatible definitions
> + * of enums can alias.  */

s/tese/these/

Patch 3 is OK with that fix.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [V5] [C PATCH 4/4] c23: construct composite type for tagged types
  2023-12-17 17:42 ` [V5] [C PATCH 4/4] c23: construct composite type for " Martin Uecker
@ 2023-12-19 22:25   ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2023-12-19 22:25 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Sun, 17 Dec 2023, Martin Uecker wrote:

> +	      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);

grokdeclarator, as called for the original declarations of fields, also 
has logic to set DECL_NONADDRESSABLE_P (for bit-fields), DECL_PADDING_P 
(for unnamed bit-fields) and C_DECL_VARIABLE_SIZE.  I don't see anything 
that would cause these to be set appropriately for a composite type.  (The 
first two are only used in the middle-end, which might make it harder to 
test that they have the right values.)

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [V6] c23: construct composite type for tagged types
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (11 preceding siblings ...)
  2023-12-17 17:42 ` [V5] [C PATCH 4/4] c23: construct composite type for " Martin Uecker
@ 2023-12-21 21:47 ` Martin Uecker
  2023-12-22 16:27   ` Joseph Myers
  2023-12-27 19:23 ` [C PATCH] C: Fix type compatibility for structs with variable sized fields Martin Uecker
                   ` (4 subsequent siblings)
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2023-12-21 21:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers


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

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
+	}
+}
+
-- 
2.39.2



^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [V6] c23: construct composite type for tagged types
  2023-12-21 21:47 ` [V6] " Martin Uecker
@ 2023-12-22 16:27   ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2023-12-22 16:27 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

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.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [C PATCH] C: Fix type compatibility for structs with variable sized fields.
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (12 preceding siblings ...)
  2023-12-21 21:47 ` [V6] " Martin Uecker
@ 2023-12-27 19:23 ` Martin Uecker
  2023-12-29 15:57   ` Joseph Myers
  2024-01-27 16:10 ` Fix ICE with -g and -std=c23 when forming composite types [PR113438] Martin Uecker
                   ` (3 subsequent siblings)
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2023-12-27 19:23 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers, Jiang, Haochen


This patch hopefully fixes the test failure we see with gnu23-tag-4.c.
It does for me locally with -march=native (which otherwise reproduces
the problem).

Bootstrapped and regession tested on x86_64


C: Fix type compatibility for structs with variable sized fields.

This fixes the test gcc.dg/gnu23-tag-4.c introduced by commit 23fee88f
which fails for -march=... because the DECL_FIELD_BIT_OFFSET are set
inconsistently for types with and without variable-sized field.  This
is fixed by testing for DECL_ALIGN instead.  The code is further
simplified by removing some unnecessary conditions, i.e. anon_field is
set unconditionaly and all fields are assumed to be DECL_FIELDs.

gcc/c:
	* c-typeck.c (tagged_types_tu_compatible_p): Revise.

gcc/testsuite:
	* gcc.dg./c23-tag-9.c: New test.
---
 gcc/c/c-typeck.cc                | 19 ++++++++-----------
 gcc/testsuite/gcc.dg/c23-tag-9.c |  8 ++++++++
 2 files changed, 16 insertions(+), 11 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-9.c

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 2d9139d09d2..84ddda1ebab 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1511,8 +1511,6 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
   if (!data->anon_field && TYPE_STUB_DECL (t1) != TYPE_STUB_DECL (t2))
     data->different_types_p = true;
 
-  data->anon_field = false;
-
   /* Incomplete types are incompatible inside a TU.  */
   if (TYPE_SIZE (t1) == NULL || TYPE_SIZE (t2) == NULL)
     return false;
@@ -1592,22 +1590,21 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	     s1 && s2;
 	     s1 = DECL_CHAIN (s1), s2 = DECL_CHAIN (s2))
 	  {
-	    if (TREE_CODE (s1) != TREE_CODE (s2)
-		|| DECL_NAME (s1) != DECL_NAME (s2))
+	    gcc_assert (TREE_CODE (s1) == FIELD_DECL);
+	    gcc_assert (TREE_CODE (s2) == FIELD_DECL);
+
+	    if (DECL_NAME (s1) != DECL_NAME (s2))
+	      return false;
+
+	    if (DECL_ALIGN (s1) != DECL_ALIGN (s2))
 	      return false;
 
-	    if (!DECL_NAME (s1) && RECORD_OR_UNION_TYPE_P (TREE_TYPE (s1)))
-	      data->anon_field = true;
+	    data->anon_field = !DECL_NAME (s1);
 
 	    data->cache = &entry;
 	    if (!comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2), data))
 	      return false;
 
-	    if (TREE_CODE (s1) == FIELD_DECL
-		&& simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
-				     DECL_FIELD_BIT_OFFSET (s2)) != 1)
-	      return false;
-
 	    tree st1 = TYPE_SIZE (TREE_TYPE (s1));
 	    tree st2 = TYPE_SIZE (TREE_TYPE (s2));
 
diff --git a/gcc/testsuite/gcc.dg/c23-tag-9.c b/gcc/testsuite/gcc.dg/c23-tag-9.c
new file mode 100644
index 00000000000..1d32560ec23
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-9.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c23" } */
+
+struct foo { int x; } x;
+struct foo { alignas(128) int x; } y;	/* { dg-error "redefinition" } */
+static_assert(alignof(y) == 128);
+
+
-- 
2.39.2




^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [C PATCH] C: Fix type compatibility for structs with variable sized fields.
  2023-12-27 19:23 ` [C PATCH] C: Fix type compatibility for structs with variable sized fields Martin Uecker
@ 2023-12-29 15:57   ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2023-12-29 15:57 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches, Jiang, Haochen

On Wed, 27 Dec 2023, Martin Uecker wrote:

> This patch hopefully fixes the test failure we see with gnu23-tag-4.c.
> It does for me locally with -march=native (which otherwise reproduces
> the problem).
> 
> Bootstrapped and regession tested on x86_64
> 
> 
> C: Fix type compatibility for structs with variable sized fields.
> 
> This fixes the test gcc.dg/gnu23-tag-4.c introduced by commit 23fee88f
> which fails for -march=... because the DECL_FIELD_BIT_OFFSET are set
> inconsistently for types with and without variable-sized field.  This
> is fixed by testing for DECL_ALIGN instead.  The code is further
> simplified by removing some unnecessary conditions, i.e. anon_field is
> set unconditionaly and all fields are assumed to be DECL_FIELDs.
> 
> gcc/c:
> 	* c-typeck.c (tagged_types_tu_compatible_p): Revise.
> 
> gcc/testsuite:
> 	* gcc.dg./c23-tag-9.c: New test.

OK.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Fix ICE with -g and -std=c23 when forming composite types [PR113438]
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (13 preceding siblings ...)
  2023-12-27 19:23 ` [C PATCH] C: Fix type compatibility for structs with variable sized fields Martin Uecker
@ 2024-01-27 16:10 ` Martin Uecker
  2024-01-29 20:27   ` Joseph Myers
  2024-04-02 18:54 ` [C PATCH] Fix ICE with -g and -std=c23 related to incomplete types [PR114361] Martin Uecker
                   ` (2 subsequent siblings)
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2024-01-27 16:10 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers


Debug output ICEs when we do not set TYPE_STUB_DECL, fix this.


    Fix ICE with -g and -std=c23 when forming composite types [PR113438]
    
    Set TYPE_STUB_DECL to an artificial decl when creating a new structure
    as a composite type.
    
            PR c/113438
    
    gcc/c/
            * c-typeck.cc (composite_type_internal): Set TYPE_STUB_DECL.
    
    gcc/testsuite/
            * gcc.dg/pr113438.c: New test.

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 66c6abc9f07..cfa3b7ab10f 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -585,6 +585,11 @@ composite_type_internal (tree t1, tree t2, struct composite_cache* cache)
 	  /* Setup the struct/union type.  Because we inherit all variably
 	     modified components, we can ignore the size expression.  */
 	  tree expr = NULL_TREE;
+
+	  /* Set TYPE_STUB_DECL for debugging symbols.  */
+	  TYPE_STUB_DECL (n) = pushdecl (build_decl (input_location, TYPE_DECL,
+						     NULL_TREE, n));
+
 	  n = finish_struct(input_location, n, fields, attributes, NULL, &expr);
 
 	  n = qualify_type (n, t1);
diff --git a/gcc/testsuite/gcc.dg/pr113438.c b/gcc/testsuite/gcc.dg/pr113438.c
new file mode 100644
index 00000000000..5612ee4fa38
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr113438.c
@@ -0,0 +1,7 @@
+/* PR113438
+ * { dg-do compile }
+ * { dg-options "-std=c23 -g" } */
+
+void g(struct foo { int x; } a);
+void g(struct foo { int x; } a);
+


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: Fix ICE with -g and -std=c23 when forming composite types [PR113438]
  2024-01-27 16:10 ` Fix ICE with -g and -std=c23 when forming composite types [PR113438] Martin Uecker
@ 2024-01-29 20:27   ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2024-01-29 20:27 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Sat, 27 Jan 2024, Martin Uecker wrote:

> Debug output ICEs when we do not set TYPE_STUB_DECL, fix this.
> 
> 
>     Fix ICE with -g and -std=c23 when forming composite types [PR113438]
>     
>     Set TYPE_STUB_DECL to an artificial decl when creating a new structure
>     as a composite type.
>     
>             PR c/113438
>     
>     gcc/c/
>             * c-typeck.cc (composite_type_internal): Set TYPE_STUB_DECL.
>     
>     gcc/testsuite/
>             * gcc.dg/pr113438.c: New test.

OK.

-- 
Joseph S. Myers
josmyers@redhat.com


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [C PATCH] Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (14 preceding siblings ...)
  2024-01-27 16:10 ` Fix ICE with -g and -std=c23 when forming composite types [PR113438] Martin Uecker
@ 2024-04-02 18:54 ` Martin Uecker
  2024-04-02 20:31   ` Joseph Myers
  2024-04-02 19:02 ` [C PATCH] fix aliasing for structures/unions with incomplete types Martin Uecker
  2024-04-14 12:30 ` [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361] Martin Uecker
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2024-04-02 18:54 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers



I did not copy TYPE_CANONICAL to incomplete variants
when they are completed.



Bootstrapped and regession tested on x86_64



Fix ICE with -g and -std=c23 related to incomplete types [PR114361]

We did not copy TYPE_CANONICAL to the incomplete variants when
completing a structure.

            PR c/114361

    gcc/c/
            * c-decl.c (finish_struct): Set TYPE_CANONICAL when completing
	    strucute types.

    gcc/testsuite/
            * gcc.dg/pr114361.c: New test.
            * gcc.dg/c23-tag-incomplete-1.c: New test.
            * gcc.dg/c23-tag-incomplete-2.c: New test.
---
 gcc/c/c-decl.cc                             |  1 +
 gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c | 14 ++++++++++++++
 gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c | 13 +++++++++++++
 gcc/testsuite/gcc.dg/pr114361.c             | 11 +++++++++++
 4 files changed, 39 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
 create mode 100644 gcc/testsuite/gcc.dg/pr114361.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index c747abe9f4e..f2083b9d96f 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -9722,6 +9722,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
       C_TYPE_VARIABLE_SIZE (x) = C_TYPE_VARIABLE_SIZE (t);
       C_TYPE_VARIABLY_MODIFIED (x) = C_TYPE_VARIABLY_MODIFIED (t);
       C_TYPE_INCOMPLETE_VARS (x) = NULL_TREE;
+      TYPE_CANONICAL (x) = TYPE_CANONICAL (t);
     }
 
   /* Update type location to the one of the definition, instead of e.g.
diff --git a/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c b/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
new file mode 100644
index 00000000000..82d652569e9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
@@ -0,0 +1,14 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23 -g" } */
+
+struct a;
+typedef struct a b;
+
+void g() {
+    struct a { b* x; };
+}
+
+struct a { b* x; };
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c b/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
new file mode 100644
index 00000000000..bc47a04ece5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23 -g" } */
+
+struct a;
+typedef struct a b;
+
+void f() {
+	extern struct a { b* x; } t;
+}
+
+extern struct a { b* x; } t;
+
+
diff --git a/gcc/testsuite/gcc.dg/pr114361.c b/gcc/testsuite/gcc.dg/pr114361.c
new file mode 100644
index 00000000000..0f3feb53566
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr114361.c
@@ -0,0 +1,11 @@
+/* PR c/114361 */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu23 -g" } */
+
+void f()
+{
+    typedef struct foo bar;
+    typedef __typeof( ({ (struct foo { bar *x; }){ }; }) ) wuz;
+    struct foo { wuz *x; };
+}
+
-- 
2.39.2



^ permalink raw reply	[flat|nested] 53+ messages in thread

* [C PATCH] fix aliasing for structures/unions with incomplete types
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (15 preceding siblings ...)
  2024-04-02 18:54 ` [C PATCH] Fix ICE with -g and -std=c23 related to incomplete types [PR114361] Martin Uecker
@ 2024-04-02 19:02 ` Martin Uecker
  2024-04-02 20:42   ` Joseph Myers
  2024-04-14 12:30 ` [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361] Martin Uecker
  17 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2024-04-02 19:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers



While fixing the other issue, I realized that the way the
equivalence classes are computed for TYPE_CANONICAL did
not take into account that completion of struct types
also affectes compatibility of types that contain pointers
to them.  So the algorithm must be more conservative
creating bigger equivalence classes.



Bootstrapped and regession tested on x86_64



[C23]fix aliasing for structures/unions with incomplete types

When incomplete structure/union types are completed later, compatibility
of struct types that contain pointers to such types changes.  When forming
equivalence classes for TYPE_CANONICAL, we therefor need to be conservative
and treat all structs with the same tag which are pointer targets as
equivalent.

gcc/c/
	* c-typeck.cc (comptypes_internal): Add flag to track
	whether a struct is the target of a pointer.
	(tagged_types_tu_compatible): When forming equivalence
	classes, treat pointed-to structs as equivalent.

gcc/testsuite/
	* gcc.dg/c23-tag-incomplate-alias-1.c: New test.
---
 gcc/c/c-typeck.cc                             | 11 ++++++
 .../gcc.dg/c23-tag-incomplete-alias-1.c       | 34 +++++++++++++++++++
 2 files changed, 45 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-incomplete-alias-1.c

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index ddeab1e2a8a..b86450580ad 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1170,6 +1170,7 @@ struct comptypes_data {
   bool different_types_p;
   bool warning_needed;
   bool anon_field;
+  bool pointedto;
   bool equiv;
 
   const struct tagged_tu_seen_cache* cache;
@@ -1355,6 +1356,7 @@ comptypes_internal (const_tree type1, const_tree type2,
       /* Do not remove mode information.  */
       if (TYPE_MODE (t1) != TYPE_MODE (t2))
 	return false;
+      data->pointedto = true;
       return comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2), data);
 
     case FUNCTION_TYPE:
@@ -1513,6 +1515,14 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
   if (TYPE_NAME (t1) != TYPE_NAME (t2))
     return false;
 
+  /* When forming equivalence classes for TYPE_CANONICAL in C23, we
+     have to treat structs with the same tag as equivalent, when they
+     are targets of pointers inside other structs.  This is necessary
+     so that the relationship of types does not change when incomplete
+     types are completed.  */
+  if (data->equiv && data->pointedto)
+    return true;
+
   if (!data->anon_field && NULL_TREE == TYPE_NAME (t1))
     return false;
 
@@ -1608,6 +1618,7 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	      return false;
 
 	    data->anon_field = !DECL_NAME (s1);
+	    data->pointedto = false;
 
 	    data->cache = &entry;
 	    if (!comptypes_internal (TREE_TYPE (s1), TREE_TYPE (s2), data))
diff --git a/gcc/testsuite/gcc.dg/c23-tag-incomplete-alias-1.c b/gcc/testsuite/gcc.dg/c23-tag-incomplete-alias-1.c
new file mode 100644
index 00000000000..7fb6a8513b2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-incomplete-alias-1.c
@@ -0,0 +1,34 @@
+/* { dg-do run } 
+ * { dg-options "-std=c23 -O2" } */
+
+[[gnu::noinline]]
+void *alias(void *ap, void *b, void *x, void *y)
+{
+	struct foo { struct bar *f; } *a = ap;
+	struct bar { long x; };
+
+	a->f = x;
+
+	{
+		struct bar;
+		struct foo { struct bar *f; };
+		struct bar { long x; };
+
+		((struct foo*)b)->f = y;
+	}
+
+
+	return a->f;
+}
+
+int main()
+{
+	struct bar { long x; };
+	struct foo { struct bar *f; } a;
+	struct bar x, y;
+	if (&y != alias(&a, &a, &x, &y))
+		__builtin_abort();
+
+	return 0;
+}
+
-- 
2.39.2



^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [C PATCH] Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2024-04-02 18:54 ` [C PATCH] Fix ICE with -g and -std=c23 related to incomplete types [PR114361] Martin Uecker
@ 2024-04-02 20:31   ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2024-04-02 20:31 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Tue, 2 Apr 2024, Martin Uecker wrote:

> Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
> 
> We did not copy TYPE_CANONICAL to the incomplete variants when
> completing a structure.
> 
>             PR c/114361
> 
>     gcc/c/
>             * c-decl.c (finish_struct): Set TYPE_CANONICAL when completing
> 	    strucute types.
> 
>     gcc/testsuite/
>             * gcc.dg/pr114361.c: New test.
>             * gcc.dg/c23-tag-incomplete-1.c: New test.
>             * gcc.dg/c23-tag-incomplete-2.c: New test.

OK.

-- 
Joseph S. Myers
josmyers@redhat.com


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [C PATCH] fix aliasing for structures/unions with incomplete types
  2024-04-02 19:02 ` [C PATCH] fix aliasing for structures/unions with incomplete types Martin Uecker
@ 2024-04-02 20:42   ` Joseph Myers
  2024-04-02 21:22     ` Martin Uecker
  0 siblings, 1 reply; 53+ messages in thread
From: Joseph Myers @ 2024-04-02 20:42 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

On Tue, 2 Apr 2024, Martin Uecker wrote:

> [C23]fix aliasing for structures/unions with incomplete types
> 
> When incomplete structure/union types are completed later, compatibility
> of struct types that contain pointers to such types changes.  When forming
> equivalence classes for TYPE_CANONICAL, we therefor need to be conservative
> and treat all structs with the same tag which are pointer targets as
> equivalent.

I don't see how what it done is actually about "which are pointer 
targets".

> @@ -1355,6 +1356,7 @@ comptypes_internal (const_tree type1, const_tree type2,
>        /* Do not remove mode information.  */
>        if (TYPE_MODE (t1) != TYPE_MODE (t2))
>  	return false;
> +      data->pointedto = true;
>        return comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2), data);

This appears to be more like "which are the targets of pointers that *have 
just been compared in the present comptypes call*".  Not which are targets 
of some other pointers not involved in that call.

Maybe some such logic based only on pointers compared in the present call 
makes sense for some purposes, but it's not clear to me that either this 
or any similar approach is a good approach for TYPE_CANONICAL - couldn't 
that mean that two types are considered equivalent for TYPE_CANONICAL at 
one point in the translation unit, but no longer equivalent at some later 
point when the comparison takes place in the context of comparing two 
other pointer types?

-- 
Joseph S. Myers
josmyers@redhat.com


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [C PATCH] fix aliasing for structures/unions with incomplete types
  2024-04-02 20:42   ` Joseph Myers
@ 2024-04-02 21:22     ` Martin Uecker
  2024-04-03 15:33       ` Joseph Myers
  0 siblings, 1 reply; 53+ messages in thread
From: Martin Uecker @ 2024-04-02 21:22 UTC (permalink / raw)
  To: Joseph Myers; +Cc: gcc-patches

Am Dienstag, dem 02.04.2024 um 20:42 +0000 schrieb Joseph Myers:
> On Tue, 2 Apr 2024, Martin Uecker wrote:
> 
> > [C23]fix aliasing for structures/unions with incomplete types
> > 
> > When incomplete structure/union types are completed later, compatibility
> > of struct types that contain pointers to such types changes.  When forming
> > equivalence classes for TYPE_CANONICAL, we therefor need to be conservative
> > and treat all structs with the same tag which are pointer targets as
> > equivalent.
> 
> I don't see how what it done is actually about "which are pointer 
> targets".

Right, I see now that the description needs to be improved. This refers
only to targets of pointers included somewhere in the type we process
for purposes of determining the equivalence class of this type (but
not for other contexts).

> 
> > @@ -1355,6 +1356,7 @@ comptypes_internal (const_tree type1, const_tree type2,
> >        /* Do not remove mode information.  */
> >        if (TYPE_MODE (t1) != TYPE_MODE (t2))
> >  	return false;
> > +      data->pointedto = true;
> >        return comptypes_internal (TREE_TYPE (t1), TREE_TYPE (t2), data);
> 
> This appears to be more like "which are the targets of pointers that *have 
> just been compared in the present comptypes call*".  Not which are targets 
> of some other pointers not involved in that call.

Correct.
> 
> Maybe some such logic based only on pointers compared in the present call 
> makes sense for some purposes, but it's not clear to me that either this 
> or any similar approach is a good approach for TYPE_CANONICAL - couldn't 
> that mean that two types are considered equivalent for TYPE_CANONICAL at 
> one point in the translation unit, but no longer equivalent at some later 
> point when the comparison takes place in the context of comparing two 
> other pointer types?

They would always be considered equivalent when pointed to from another
struct (or indirectly from a type nested in this struct) for purposes
of determining the equivalence class of this struct.

When not a pointer target, i.e. when considering the struct type itself
for which we compute TYPE_CANONICAL or another struct type directly used
for a member, then the types are compared by recursing into them. Such 
types can never be incomplete at this point, so this is also stable property.

To summarize: for determining equivalence classes we always stop
the recursion after following pointers into other structs. We give
the same TYPE_CANONICAL to the following two structs foo:

struct foo { struct aa { int x; } *p; };
struct foo { struct aa { float x; } *p; };

while we give different TYPE_CANONICAL to

struct bar { struct aa { int x; } p; };
struct bar { struct aa { float x; } p; };

(not pointer).  The reason is that for the struct foo's there
could be a 

struct foo { struct aa *p; };

with incomplete type struct aa that later turns out to be compatible
with either of them. So we have to put them all into the same 
equivalence class.

(a potential alternative is to compute the classes only at the very end
when all types have stablized, but this would require much more changes
and another pass over all the types.)


Note that the TYPE_CANONICAL for the aa's is not affected in any case and
always computed based on *their* content  independent of whether they are
pointer targets or not.  (but this reminds me to double check what
happens with types that are never completed in a TU.).


I hope this explanation makes sense.


Martin




> 


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [C PATCH] fix aliasing for structures/unions with incomplete types
  2024-04-02 21:22     ` Martin Uecker
@ 2024-04-03 15:33       ` Joseph Myers
  0 siblings, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2024-04-03 15:33 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1131 bytes --]

On Tue, 2 Apr 2024, Martin Uecker wrote:

> Am Dienstag, dem 02.04.2024 um 20:42 +0000 schrieb Joseph Myers:
> > On Tue, 2 Apr 2024, Martin Uecker wrote:
> > 
> > > [C23]fix aliasing for structures/unions with incomplete types
> > > 
> > > When incomplete structure/union types are completed later, compatibility
> > > of struct types that contain pointers to such types changes.  When forming
> > > equivalence classes for TYPE_CANONICAL, we therefor need to be conservative
> > > and treat all structs with the same tag which are pointer targets as
> > > equivalent.
> > 
> > I don't see how what it done is actually about "which are pointer 
> > targets".
> 
> Right, I see now that the description needs to be improved. This refers
> only to targets of pointers included somewhere in the type we process
> for purposes of determining the equivalence class of this type (but
> not for other contexts).

A detailed self-contained description of the TYPE_CANONICAL / aliasing 
logic also needs to go somewhere in a comment in the source code rather 
than only in commit messages / emails.

-- 
Joseph S. Myers
josmyers@redhat.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
                   ` (16 preceding siblings ...)
  2024-04-02 19:02 ` [C PATCH] fix aliasing for structures/unions with incomplete types Martin Uecker
@ 2024-04-14 12:30 ` Martin Uecker
  2024-04-14 12:38   ` Martin Uecker
                     ` (2 more replies)
  17 siblings, 3 replies; 53+ messages in thread
From: Martin Uecker @ 2024-04-14 12:30 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers, Richard Biener, jakub


I had to revert the old patch because it broke LTO which lead
to PR114574.  We now set TYPE_STRUCTURAL_EQUALITY and properly
update TYPE_CANONICAL for such types and also for pointers
to such types via a new function c_update_type_canonical
(thanks to Jakob).


Bootstrapped and regession tested on x86_64.



Fix ICE with -g and -std=c23 related to incomplete types [PR114361]

We did not update TYPE_CANONICAL for incomplete variants when
completing a structure.  We now set TYPE_STRUCTURAL_EQUALITY for
incomplete structure and union types and then update TYPE_CANONICAL
later. See PR114574 for discussion.


2024-04-12  Martin Uecker  <uecker@tugraz.at>
	    Jakub Jelinek  <jakub@redhat.com>

	PR lto/114574
	PR c/114361
gcc/
	* ipa-free-lang-data.cc (fld_incomplete_type_of): Allow
	either of the types in the assert to have TYPE_STRUCTURAL_EQUALITY_P.
gcc/c/
	* c-decl.cc (shadow_tag_warned): For flag_isoc23 and code not
	ENUMERAL_TYPE use SET_TYPE_STRUCTURAL_EQUALITY.
	(parser_xref_tag): Likewise.
	(start_struct): For flag_isoc23 use SET_TYPE_STRUCTURAL_EQUALITY.
	(c_update_type_canonical): New function.
	(finish_struct): Put NULL as second == operand rather than first.
	Assert TYPE_STRUCTURAL_EQUALITY_P.  Call c_update_type_canonical.
	* c-typeck.cc (composite_type_internal): Use
	SET_TYPE_STRUCTURAL_EQUALITY.  Formatting fix.
gcc/testsuite/
	* gcc.dg/pr114574-1.c: New test.
	* gcc.dg/pr114574-2.c: New test.
	* gcc.dg/pr114361.c: New test.
	* gcc.dg/c23-tag-incomplete-1.c: New test.
	* gcc.dg/c23-tag-incomplete-2.c: New test.
---
 gcc/c/c-decl.cc                             | 47 ++++++++++++++++++++-
 gcc/c/c-typeck.cc                           |  4 +-
 gcc/ipa-free-lang-data.cc                   |  4 +-
 gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c | 14 ++++++
 gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c | 13 ++++++
 gcc/testsuite/gcc.dg/pr114361.c             | 11 +++++
 gcc/testsuite/gcc.dg/pr114574-1.c           | 10 +++++
 gcc/testsuite/gcc.dg/pr114574-2.c           | 10 +++++
 8 files changed, 110 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
 create mode 100644 gcc/testsuite/gcc.dg/pr114361.c
 create mode 100644 gcc/testsuite/gcc.dg/pr114574-1.c
 create mode 100644 gcc/testsuite/gcc.dg/pr114574-2.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 345090dae38..54917297b6a 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -5051,6 +5051,8 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned)
 	      if (t == NULL_TREE)
 		{
 		  t = make_node (code);
+		  if (flag_isoc23 && code != ENUMERAL_TYPE)
+		    SET_TYPE_STRUCTURAL_EQUALITY (t);
 		  pushtag (input_location, name, t);
 		}
 	    }
@@ -8809,6 +8811,8 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name,
      the forward-reference will be altered into a real type.  */
 
   ref = make_node (code);
+  if (flag_isoc23 && code != ENUMERAL_TYPE)
+    SET_TYPE_STRUCTURAL_EQUALITY (ref);
   if (code == ENUMERAL_TYPE)
     {
       /* Give the type a default layout like unsigned int
@@ -8910,6 +8914,8 @@ start_struct (location_t loc, enum tree_code code, tree name,
   if (ref == NULL_TREE || TREE_CODE (ref) != code)
     {
       ref = make_node (code);
+      if (flag_isoc23)
+	SET_TYPE_STRUCTURAL_EQUALITY (ref);
       pushtag (loc, name, ref);
     }
 
@@ -9347,6 +9353,43 @@ is_flexible_array_member_p (bool is_last_field,
   return false;
 }
 
+/* Recompute TYPE_CANONICAL for qualified versions of the type and
+   related pointer types after an aggregate type has been finalized.
+   Will not update array types, pointers to array types, function
+   types and other derived types created while the type was still
+   incomplete, those will remain TYPE_STRUCTURAL_EQUALITY_P.  */
+
+static void
+c_update_type_canonical (tree t)
+{
+  for (tree x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
+    {
+      if (x != t
+	  && TYPE_STRUCTURAL_EQUALITY_P (x)
+	  && check_qualified_type (x, t, TYPE_QUALS (x)))
+	{
+	  if (TYPE_CANONICAL (t) != t)
+	    TYPE_CANONICAL (x)
+	      = build_qualified_type (TYPE_CANONICAL (t), TYPE_QUALS (x));
+	  else
+	    TYPE_CANONICAL (x) = x;
+	}
+      else if (x != t)
+	continue;
+      for (tree p = TYPE_POINTER_TO (x); p; p = TYPE_NEXT_PTR_TO (p))
+	{
+	  if (!TYPE_STRUCTURAL_EQUALITY_P (p))
+	    continue;
+	  if (TYPE_CANONICAL (x) != x || TYPE_REF_CAN_ALIAS_ALL (p))
+	    TYPE_CANONICAL (p)
+	      = build_pointer_type_for_mode (TYPE_CANONICAL (x), TYPE_MODE (p),
+					     false);
+	  else
+	    TYPE_CANONICAL (p) = p;
+	  c_update_type_canonical (p);
+	}
+    }
+}
 
 /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T.
    LOC is the location of the RECORD_TYPE or UNION_TYPE's definition.
@@ -9695,11 +9738,12 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
   /* Set type canonical based on equivalence class.  */
   if (flag_isoc23)
     {
-      if (NULL == c_struct_htab)
+      if (c_struct_htab == NULL)
 	c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61);
 
       hashval_t hash = c_struct_hasher::hash (t);
 
+      gcc_checking_assert (TYPE_STRUCTURAL_EQUALITY_P (t));
       tree *e = c_struct_htab->find_slot_with_hash (t, hash, INSERT);
       if (*e)
 	TYPE_CANONICAL (t) = *e;
@@ -9708,6 +9752,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
 	  TYPE_CANONICAL (t) = t;
 	  *e = t;
 	}
+      c_update_type_canonical (t);
     }
 
   tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t));
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index ddeab1e2a8a..4567b114734 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -532,6 +532,7 @@ composite_type_internal (tree t1, tree t2, struct composite_cache* cache)
 	  /* Otherwise, create a new type node and link it into the cache.  */
 
 	  tree n = make_node (code1);
+	  SET_TYPE_STRUCTURAL_EQUALITY (n);
 	  TYPE_NAME (n) = TYPE_NAME (t1);
 
 	  struct composite_cache cache2 = { t1, t2, n, cache };
@@ -590,7 +591,8 @@ composite_type_internal (tree t1, tree t2, struct composite_cache* cache)
 	  TYPE_STUB_DECL (n) = pushdecl (build_decl (input_location, TYPE_DECL,
 						     NULL_TREE, n));
 
-	  n = finish_struct(input_location, n, fields, attributes, NULL, &expr);
+	  n = finish_struct (input_location, n, fields, attributes, NULL,
+			     &expr);
 
 	  n = qualify_type (n, t1);
 
diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc
index 16ed55e2e5a..6f75444e513 100644
--- a/gcc/ipa-free-lang-data.cc
+++ b/gcc/ipa-free-lang-data.cc
@@ -255,7 +255,9 @@ fld_incomplete_type_of (tree t, class free_lang_data_d *fld)
 	    first = build_reference_type_for_mode (t2, TYPE_MODE (t),
 						   TYPE_REF_CAN_ALIAS_ALL (t));
 	  gcc_assert (TYPE_CANONICAL (t2) != t2
-		      && TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t)));
+		      && (TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t))
+			  || TYPE_STRUCTURAL_EQUALITY_P (t2)
+			  || TYPE_STRUCTURAL_EQUALITY_P (TREE_TYPE (t))));
 	  if (!fld->pset.add (first))
 	    add_tree_to_fld_list (first, fld);
 	  return fld_type_variant (first, t, fld);
diff --git a/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c b/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
new file mode 100644
index 00000000000..82d652569e9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
@@ -0,0 +1,14 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23 -g" } */
+
+struct a;
+typedef struct a b;
+
+void g() {
+    struct a { b* x; };
+}
+
+struct a { b* x; };
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c b/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
new file mode 100644
index 00000000000..bc47a04ece5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23 -g" } */
+
+struct a;
+typedef struct a b;
+
+void f() {
+	extern struct a { b* x; } t;
+}
+
+extern struct a { b* x; } t;
+
+
diff --git a/gcc/testsuite/gcc.dg/pr114361.c b/gcc/testsuite/gcc.dg/pr114361.c
new file mode 100644
index 00000000000..0f3feb53566
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr114361.c
@@ -0,0 +1,11 @@
+/* PR c/114361 */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu23 -g" } */
+
+void f()
+{
+    typedef struct foo bar;
+    typedef __typeof( ({ (struct foo { bar *x; }){ }; }) ) wuz;
+    struct foo { wuz *x; };
+}
+
diff --git a/gcc/testsuite/gcc.dg/pr114574-1.c b/gcc/testsuite/gcc.dg/pr114574-1.c
new file mode 100644
index 00000000000..060dcdbe73e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr114574-1.c
@@ -0,0 +1,10 @@
+/* PR lto/114574
+ * { dg-do compile }
+ * { dg-options "-flto" } */
+
+const struct S * x;
+struct S {};
+void f(const struct S **);
+
+
+
diff --git a/gcc/testsuite/gcc.dg/pr114574-2.c b/gcc/testsuite/gcc.dg/pr114574-2.c
new file mode 100644
index 00000000000..723291e2211
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr114574-2.c
@@ -0,0 +1,10 @@
+/* PR lto/114574
+ * { dg-do compile }
+ * { dg-options "-flto -std=c23" } */
+
+const struct S * x;
+struct S {};
+void f(const struct S **);
+
+
+
-- 
2.39.2






^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2024-04-14 12:30 ` [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361] Martin Uecker
@ 2024-04-14 12:38   ` Martin Uecker
  2024-04-15  6:55   ` Richard Biener
  2024-04-15  7:03   ` [C PATCH, v2] " Jakub Jelinek
  2 siblings, 0 replies; 53+ messages in thread
From: Martin Uecker @ 2024-04-14 12:38 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers


(new email for Joseph)

Am Sonntag, dem 14.04.2024 um 14:30 +0200 schrieb Martin Uecker:
> I had to revert the old patch because it broke LTO which lead
> to PR114574.  We now set TYPE_STRUCTURAL_EQUALITY and properly
> update TYPE_CANONICAL for such types and also for pointers
> to such types via a new function c_update_type_canonical
> (thanks to Jakob).
> 
> 
> Bootstrapped and regession tested on x86_64.
> 
> 
> 
> Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
> 
> We did not update TYPE_CANONICAL for incomplete variants when
> completing a structure.  We now set TYPE_STRUCTURAL_EQUALITY for
> incomplete structure and union types and then update TYPE_CANONICAL
> later. See PR114574 for discussion.
> 
> 
> 2024-04-12  Martin Uecker  <uecker@tugraz.at>
> 	    Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR lto/114574
> 	PR c/114361
> gcc/
> 	* ipa-free-lang-data.cc (fld_incomplete_type_of): Allow
> 	either of the types in the assert to have TYPE_STRUCTURAL_EQUALITY_P.
> gcc/c/
> 	* c-decl.cc (shadow_tag_warned): For flag_isoc23 and code not
> 	ENUMERAL_TYPE use SET_TYPE_STRUCTURAL_EQUALITY.
> 	(parser_xref_tag): Likewise.
> 	(start_struct): For flag_isoc23 use SET_TYPE_STRUCTURAL_EQUALITY.
> 	(c_update_type_canonical): New function.
> 	(finish_struct): Put NULL as second == operand rather than first.
> 	Assert TYPE_STRUCTURAL_EQUALITY_P.  Call c_update_type_canonical.
> 	* c-typeck.cc (composite_type_internal): Use
> 	SET_TYPE_STRUCTURAL_EQUALITY.  Formatting fix.
> gcc/testsuite/
> 	* gcc.dg/pr114574-1.c: New test.
> 	* gcc.dg/pr114574-2.c: New test.
> 	* gcc.dg/pr114361.c: New test.
> 	* gcc.dg/c23-tag-incomplete-1.c: New test.
> 	* gcc.dg/c23-tag-incomplete-2.c: New test.
> ---
>  gcc/c/c-decl.cc                             | 47 ++++++++++++++++++++-
>  gcc/c/c-typeck.cc                           |  4 +-
>  gcc/ipa-free-lang-data.cc                   |  4 +-
>  gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c | 14 ++++++
>  gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c | 13 ++++++
>  gcc/testsuite/gcc.dg/pr114361.c             | 11 +++++
>  gcc/testsuite/gcc.dg/pr114574-1.c           | 10 +++++
>  gcc/testsuite/gcc.dg/pr114574-2.c           | 10 +++++
>  8 files changed, 110 insertions(+), 3 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/pr114361.c
>  create mode 100644 gcc/testsuite/gcc.dg/pr114574-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/pr114574-2.c
> 
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index 345090dae38..54917297b6a 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -5051,6 +5051,8 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned)
>  	      if (t == NULL_TREE)
>  		{
>  		  t = make_node (code);
> +		  if (flag_isoc23 && code != ENUMERAL_TYPE)
> +		    SET_TYPE_STRUCTURAL_EQUALITY (t);
>  		  pushtag (input_location, name, t);
>  		}
>  	    }
> @@ -8809,6 +8811,8 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name,
>       the forward-reference will be altered into a real type.  */
>  
>    ref = make_node (code);
> +  if (flag_isoc23 && code != ENUMERAL_TYPE)
> +    SET_TYPE_STRUCTURAL_EQUALITY (ref);
>    if (code == ENUMERAL_TYPE)
>      {
>        /* Give the type a default layout like unsigned int
> @@ -8910,6 +8914,8 @@ start_struct (location_t loc, enum tree_code code, tree name,
>    if (ref == NULL_TREE || TREE_CODE (ref) != code)
>      {
>        ref = make_node (code);
> +      if (flag_isoc23)
> +	SET_TYPE_STRUCTURAL_EQUALITY (ref);
>        pushtag (loc, name, ref);
>      }
>  
> @@ -9347,6 +9353,43 @@ is_flexible_array_member_p (bool is_last_field,
>    return false;
>  }
>  
> +/* Recompute TYPE_CANONICAL for qualified versions of the type and
> +   related pointer types after an aggregate type has been finalized.
> +   Will not update array types, pointers to array types, function
> +   types and other derived types created while the type was still
> +   incomplete, those will remain TYPE_STRUCTURAL_EQUALITY_P.  */
> +
> +static void
> +c_update_type_canonical (tree t)
> +{
> +  for (tree x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
> +    {
> +      if (x != t
> +	  && TYPE_STRUCTURAL_EQUALITY_P (x)
> +	  && check_qualified_type (x, t, TYPE_QUALS (x)))
> +	{
> +	  if (TYPE_CANONICAL (t) != t)
> +	    TYPE_CANONICAL (x)
> +	      = build_qualified_type (TYPE_CANONICAL (t), TYPE_QUALS (x));
> +	  else
> +	    TYPE_CANONICAL (x) = x;
> +	}
> +      else if (x != t)
> +	continue;
> +      for (tree p = TYPE_POINTER_TO (x); p; p = TYPE_NEXT_PTR_TO (p))
> +	{
> +	  if (!TYPE_STRUCTURAL_EQUALITY_P (p))
> +	    continue;
> +	  if (TYPE_CANONICAL (x) != x || TYPE_REF_CAN_ALIAS_ALL (p))
> +	    TYPE_CANONICAL (p)
> +	      = build_pointer_type_for_mode (TYPE_CANONICAL (x), TYPE_MODE (p),
> +					     false);
> +	  else
> +	    TYPE_CANONICAL (p) = p;
> +	  c_update_type_canonical (p);
> +	}
> +    }
> +}
>  
>  /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T.
>     LOC is the location of the RECORD_TYPE or UNION_TYPE's definition.
> @@ -9695,11 +9738,12 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
>    /* Set type canonical based on equivalence class.  */
>    if (flag_isoc23)
>      {
> -      if (NULL == c_struct_htab)
> +      if (c_struct_htab == NULL)
>  	c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61);
>  
>        hashval_t hash = c_struct_hasher::hash (t);
>  
> +      gcc_checking_assert (TYPE_STRUCTURAL_EQUALITY_P (t));
>        tree *e = c_struct_htab->find_slot_with_hash (t, hash, INSERT);
>        if (*e)
>  	TYPE_CANONICAL (t) = *e;
> @@ -9708,6 +9752,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
>  	  TYPE_CANONICAL (t) = t;
>  	  *e = t;
>  	}
> +      c_update_type_canonical (t);
>      }
>  
>    tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t));
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index ddeab1e2a8a..4567b114734 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -532,6 +532,7 @@ composite_type_internal (tree t1, tree t2, struct composite_cache* cache)
>  	  /* Otherwise, create a new type node and link it into the cache.  */
>  
>  	  tree n = make_node (code1);
> +	  SET_TYPE_STRUCTURAL_EQUALITY (n);
>  	  TYPE_NAME (n) = TYPE_NAME (t1);
>  
>  	  struct composite_cache cache2 = { t1, t2, n, cache };
> @@ -590,7 +591,8 @@ composite_type_internal (tree t1, tree t2, struct composite_cache* cache)
>  	  TYPE_STUB_DECL (n) = pushdecl (build_decl (input_location, TYPE_DECL,
>  						     NULL_TREE, n));
>  
> -	  n = finish_struct(input_location, n, fields, attributes, NULL, &expr);
> +	  n = finish_struct (input_location, n, fields, attributes, NULL,
> +			     &expr);
>  
>  	  n = qualify_type (n, t1);
>  
> diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc
> index 16ed55e2e5a..6f75444e513 100644
> --- a/gcc/ipa-free-lang-data.cc
> +++ b/gcc/ipa-free-lang-data.cc
> @@ -255,7 +255,9 @@ fld_incomplete_type_of (tree t, class free_lang_data_d *fld)
>  	    first = build_reference_type_for_mode (t2, TYPE_MODE (t),
>  						   TYPE_REF_CAN_ALIAS_ALL (t));
>  	  gcc_assert (TYPE_CANONICAL (t2) != t2
> -		      && TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t)));
> +		      && (TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t))
> +			  || TYPE_STRUCTURAL_EQUALITY_P (t2)
> +			  || TYPE_STRUCTURAL_EQUALITY_P (TREE_TYPE (t))));
>  	  if (!fld->pset.add (first))
>  	    add_tree_to_fld_list (first, fld);
>  	  return fld_type_variant (first, t, fld);
> diff --git a/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c b/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
> new file mode 100644
> index 00000000000..82d652569e9
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
> @@ -0,0 +1,14 @@
> +/* { dg-do compile }
> + * { dg-options "-std=c23 -g" } */
> +
> +struct a;
> +typedef struct a b;
> +
> +void g() {
> +    struct a { b* x; };
> +}
> +
> +struct a { b* x; };
> +
> +
> +
> diff --git a/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c b/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
> new file mode 100644
> index 00000000000..bc47a04ece5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
> @@ -0,0 +1,13 @@
> +/* { dg-do compile }
> + * { dg-options "-std=c23 -g" } */
> +
> +struct a;
> +typedef struct a b;
> +
> +void f() {
> +	extern struct a { b* x; } t;
> +}
> +
> +extern struct a { b* x; } t;
> +
> +
> diff --git a/gcc/testsuite/gcc.dg/pr114361.c b/gcc/testsuite/gcc.dg/pr114361.c
> new file mode 100644
> index 00000000000..0f3feb53566
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/pr114361.c
> @@ -0,0 +1,11 @@
> +/* PR c/114361 */
> +/* { dg-do compile } */
> +/* { dg-options "-std=gnu23 -g" } */
> +
> +void f()
> +{
> +    typedef struct foo bar;
> +    typedef __typeof( ({ (struct foo { bar *x; }){ }; }) ) wuz;
> +    struct foo { wuz *x; };
> +}
> +
> diff --git a/gcc/testsuite/gcc.dg/pr114574-1.c b/gcc/testsuite/gcc.dg/pr114574-1.c
> new file mode 100644
> index 00000000000..060dcdbe73e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/pr114574-1.c
> @@ -0,0 +1,10 @@
> +/* PR lto/114574
> + * { dg-do compile }
> + * { dg-options "-flto" } */
> +
> +const struct S * x;
> +struct S {};
> +void f(const struct S **);
> +
> +
> +
> diff --git a/gcc/testsuite/gcc.dg/pr114574-2.c b/gcc/testsuite/gcc.dg/pr114574-2.c
> new file mode 100644
> index 00000000000..723291e2211
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/pr114574-2.c
> @@ -0,0 +1,10 @@
> +/* PR lto/114574
> + * { dg-do compile }
> + * { dg-options "-flto -std=c23" } */
> +
> +const struct S * x;
> +struct S {};
> +void f(const struct S **);
> +
> +
> +


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2024-04-14 12:30 ` [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361] Martin Uecker
  2024-04-14 12:38   ` Martin Uecker
@ 2024-04-15  6:55   ` Richard Biener
  2024-04-15  7:38     ` Jakub Jelinek
  2024-04-15  7:03   ` [C PATCH, v2] " Jakub Jelinek
  2 siblings, 1 reply; 53+ messages in thread
From: Richard Biener @ 2024-04-15  6:55 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches, Joseph Myers, jakub

On Sun, 14 Apr 2024, Martin Uecker wrote:

> 
> I had to revert the old patch because it broke LTO which lead
> to PR114574.  We now set TYPE_STRUCTURAL_EQUALITY and properly
> update TYPE_CANONICAL for such types and also for pointers
> to such types via a new function c_update_type_canonical
> (thanks to Jakob).
> 
> 
> Bootstrapped and regession tested on x86_64.
> 
> 
> 
> Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
> 
> We did not update TYPE_CANONICAL for incomplete variants when
> completing a structure.  We now set TYPE_STRUCTURAL_EQUALITY for
> incomplete structure and union types and then update TYPE_CANONICAL
> later. See PR114574 for discussion.
> 
> 
> 2024-04-12  Martin Uecker  <uecker@tugraz.at>
> 	    Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR lto/114574
> 	PR c/114361
> gcc/
> 	* ipa-free-lang-data.cc (fld_incomplete_type_of): Allow
> 	either of the types in the assert to have TYPE_STRUCTURAL_EQUALITY_P.
> gcc/c/
> 	* c-decl.cc (shadow_tag_warned): For flag_isoc23 and code not
> 	ENUMERAL_TYPE use SET_TYPE_STRUCTURAL_EQUALITY.
> 	(parser_xref_tag): Likewise.
> 	(start_struct): For flag_isoc23 use SET_TYPE_STRUCTURAL_EQUALITY.
> 	(c_update_type_canonical): New function.
> 	(finish_struct): Put NULL as second == operand rather than first.
> 	Assert TYPE_STRUCTURAL_EQUALITY_P.  Call c_update_type_canonical.
> 	* c-typeck.cc (composite_type_internal): Use
> 	SET_TYPE_STRUCTURAL_EQUALITY.  Formatting fix.
> gcc/testsuite/
> 	* gcc.dg/pr114574-1.c: New test.
> 	* gcc.dg/pr114574-2.c: New test.
> 	* gcc.dg/pr114361.c: New test.
> 	* gcc.dg/c23-tag-incomplete-1.c: New test.
> 	* gcc.dg/c23-tag-incomplete-2.c: New test.
> ---
>  gcc/c/c-decl.cc                             | 47 ++++++++++++++++++++-
>  gcc/c/c-typeck.cc                           |  4 +-
>  gcc/ipa-free-lang-data.cc                   |  4 +-
>  gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c | 14 ++++++
>  gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c | 13 ++++++
>  gcc/testsuite/gcc.dg/pr114361.c             | 11 +++++
>  gcc/testsuite/gcc.dg/pr114574-1.c           | 10 +++++
>  gcc/testsuite/gcc.dg/pr114574-2.c           | 10 +++++
>  8 files changed, 110 insertions(+), 3 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/pr114361.c
>  create mode 100644 gcc/testsuite/gcc.dg/pr114574-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/pr114574-2.c
> 
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index 345090dae38..54917297b6a 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -5051,6 +5051,8 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned)
>  	      if (t == NULL_TREE)
>  		{
>  		  t = make_node (code);
> +		  if (flag_isoc23 && code != ENUMERAL_TYPE)
> +		    SET_TYPE_STRUCTURAL_EQUALITY (t);
>  		  pushtag (input_location, name, t);
>  		}
>  	    }
> @@ -8809,6 +8811,8 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name,
>       the forward-reference will be altered into a real type.  */
>  
>    ref = make_node (code);
> +  if (flag_isoc23 && code != ENUMERAL_TYPE)
> +    SET_TYPE_STRUCTURAL_EQUALITY (ref);
>    if (code == ENUMERAL_TYPE)
>      {
>        /* Give the type a default layout like unsigned int
> @@ -8910,6 +8914,8 @@ start_struct (location_t loc, enum tree_code code, tree name,
>    if (ref == NULL_TREE || TREE_CODE (ref) != code)
>      {
>        ref = make_node (code);
> +      if (flag_isoc23)
> +	SET_TYPE_STRUCTURAL_EQUALITY (ref);
>        pushtag (loc, name, ref);
>      }
>  
> @@ -9347,6 +9353,43 @@ is_flexible_array_member_p (bool is_last_field,
>    return false;
>  }
>  
> +/* Recompute TYPE_CANONICAL for qualified versions of the type and
> +   related pointer types after an aggregate type has been finalized.
> +   Will not update array types, pointers to array types, function
> +   types and other derived types created while the type was still
> +   incomplete, those will remain TYPE_STRUCTURAL_EQUALITY_P.  */
> +
> +static void
> +c_update_type_canonical (tree t)
> +{
> +  for (tree x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
> +    {
> +      if (x != t
> +	  && TYPE_STRUCTURAL_EQUALITY_P (x)
> +	  && check_qualified_type (x, t, TYPE_QUALS (x)))
> +	{
> +	  if (TYPE_CANONICAL (t) != t)
> +	    TYPE_CANONICAL (x)
> +	      = build_qualified_type (TYPE_CANONICAL (t), TYPE_QUALS (x));
> +	  else
> +	    TYPE_CANONICAL (x) = x;
> +	}
> +      else if (x != t)
> +	continue;
> +      for (tree p = TYPE_POINTER_TO (x); p; p = TYPE_NEXT_PTR_TO (p))
> +	{
> +	  if (!TYPE_STRUCTURAL_EQUALITY_P (p))
> +	    continue;
> +	  if (TYPE_CANONICAL (x) != x || TYPE_REF_CAN_ALIAS_ALL (p))
> +	    TYPE_CANONICAL (p)
> +	      = build_pointer_type_for_mode (TYPE_CANONICAL (x), TYPE_MODE (p),
> +					     false);
> +	  else
> +	    TYPE_CANONICAL (p) = p;
> +	  c_update_type_canonical (p);
> +	}
> +    }
> +}
>  
>  /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T.
>     LOC is the location of the RECORD_TYPE or UNION_TYPE's definition.
> @@ -9695,11 +9738,12 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
>    /* Set type canonical based on equivalence class.  */
>    if (flag_isoc23)
>      {
> -      if (NULL == c_struct_htab)
> +      if (c_struct_htab == NULL)
>  	c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61);
>  
>        hashval_t hash = c_struct_hasher::hash (t);
>  
> +      gcc_checking_assert (TYPE_STRUCTURAL_EQUALITY_P (t));
>        tree *e = c_struct_htab->find_slot_with_hash (t, hash, INSERT);
>        if (*e)
>  	TYPE_CANONICAL (t) = *e;
> @@ -9708,6 +9752,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
>  	  TYPE_CANONICAL (t) = t;
>  	  *e = t;
>  	}
> +      c_update_type_canonical (t);
>      }
>  
>    tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t));
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index ddeab1e2a8a..4567b114734 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -532,6 +532,7 @@ composite_type_internal (tree t1, tree t2, struct composite_cache* cache)
>  	  /* Otherwise, create a new type node and link it into the cache.  */
>  
>  	  tree n = make_node (code1);
> +	  SET_TYPE_STRUCTURAL_EQUALITY (n);
>  	  TYPE_NAME (n) = TYPE_NAME (t1);
>  
>  	  struct composite_cache cache2 = { t1, t2, n, cache };
> @@ -590,7 +591,8 @@ composite_type_internal (tree t1, tree t2, struct composite_cache* cache)
>  	  TYPE_STUB_DECL (n) = pushdecl (build_decl (input_location, TYPE_DECL,
>  						     NULL_TREE, n));
>  
> -	  n = finish_struct(input_location, n, fields, attributes, NULL, &expr);
> +	  n = finish_struct (input_location, n, fields, attributes, NULL,
> +			     &expr);
>  
>  	  n = qualify_type (n, t1);
>  
> diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc
> index 16ed55e2e5a..6f75444e513 100644
> --- a/gcc/ipa-free-lang-data.cc
> +++ b/gcc/ipa-free-lang-data.cc
> @@ -255,7 +255,9 @@ fld_incomplete_type_of (tree t, class free_lang_data_d *fld)
>  	    first = build_reference_type_for_mode (t2, TYPE_MODE (t),
>  						   TYPE_REF_CAN_ALIAS_ALL (t));
>  	  gcc_assert (TYPE_CANONICAL (t2) != t2
> -		      && TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t)));
> +		      && (TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t))
> +			  || TYPE_STRUCTURAL_EQUALITY_P (t2)
> +			  || TYPE_STRUCTURAL_EQUALITY_P (TREE_TYPE (t))));

I think we want the same canonical types for t2 and TREE_TYPE (t), why
doesn't fld_incomplete_type_of make it so?

Richard.

>  	  if (!fld->pset.add (first))
>  	    add_tree_to_fld_list (first, fld);
>  	  return fld_type_variant (first, t, fld);
> diff --git a/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c b/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
> new file mode 100644
> index 00000000000..82d652569e9
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
> @@ -0,0 +1,14 @@
> +/* { dg-do compile }
> + * { dg-options "-std=c23 -g" } */
> +
> +struct a;
> +typedef struct a b;
> +
> +void g() {
> +    struct a { b* x; };
> +}
> +
> +struct a { b* x; };
> +
> +
> +
> diff --git a/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c b/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
> new file mode 100644
> index 00000000000..bc47a04ece5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
> @@ -0,0 +1,13 @@
> +/* { dg-do compile }
> + * { dg-options "-std=c23 -g" } */
> +
> +struct a;
> +typedef struct a b;
> +
> +void f() {
> +	extern struct a { b* x; } t;
> +}
> +
> +extern struct a { b* x; } t;
> +
> +
> diff --git a/gcc/testsuite/gcc.dg/pr114361.c b/gcc/testsuite/gcc.dg/pr114361.c
> new file mode 100644
> index 00000000000..0f3feb53566
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/pr114361.c
> @@ -0,0 +1,11 @@
> +/* PR c/114361 */
> +/* { dg-do compile } */
> +/* { dg-options "-std=gnu23 -g" } */
> +
> +void f()
> +{
> +    typedef struct foo bar;
> +    typedef __typeof( ({ (struct foo { bar *x; }){ }; }) ) wuz;
> +    struct foo { wuz *x; };
> +}
> +
> diff --git a/gcc/testsuite/gcc.dg/pr114574-1.c b/gcc/testsuite/gcc.dg/pr114574-1.c
> new file mode 100644
> index 00000000000..060dcdbe73e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/pr114574-1.c
> @@ -0,0 +1,10 @@
> +/* PR lto/114574
> + * { dg-do compile }
> + * { dg-options "-flto" } */
> +
> +const struct S * x;
> +struct S {};
> +void f(const struct S **);
> +
> +
> +
> diff --git a/gcc/testsuite/gcc.dg/pr114574-2.c b/gcc/testsuite/gcc.dg/pr114574-2.c
> new file mode 100644
> index 00000000000..723291e2211
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/pr114574-2.c
> @@ -0,0 +1,10 @@
> +/* PR lto/114574
> + * { dg-do compile }
> + * { dg-options "-flto -std=c23" } */
> +
> +const struct S * x;
> +struct S {};
> +void f(const struct S **);
> +
> +
> +
> 

-- 
Richard Biener <rguenther@suse.de>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2024-04-14 12:30 ` [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361] Martin Uecker
  2024-04-14 12:38   ` Martin Uecker
  2024-04-15  6:55   ` Richard Biener
@ 2024-04-15  7:03   ` Jakub Jelinek
  2 siblings, 0 replies; 53+ messages in thread
From: Jakub Jelinek @ 2024-04-15  7:03 UTC (permalink / raw)
  To: Martin Uecker; +Cc: gcc-patches, Joseph Myers, Richard Biener

On Sun, Apr 14, 2024 at 02:30:27PM +0200, Martin Uecker wrote:
> 2024-04-12  Martin Uecker  <uecker@tugraz.at>
> 	    Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR lto/114574
> 	PR c/114361
> gcc/
> 	* ipa-free-lang-data.cc (fld_incomplete_type_of): Allow
> 	either of the types in the assert to have TYPE_STRUCTURAL_EQUALITY_P.
> gcc/c/
> 	* c-decl.cc (shadow_tag_warned): For flag_isoc23 and code not
> 	ENUMERAL_TYPE use SET_TYPE_STRUCTURAL_EQUALITY.
> 	(parser_xref_tag): Likewise.
> 	(start_struct): For flag_isoc23 use SET_TYPE_STRUCTURAL_EQUALITY.
> 	(c_update_type_canonical): New function.
> 	(finish_struct): Put NULL as second == operand rather than first.
> 	Assert TYPE_STRUCTURAL_EQUALITY_P.  Call c_update_type_canonical.
> 	* c-typeck.cc (composite_type_internal): Use
> 	SET_TYPE_STRUCTURAL_EQUALITY.  Formatting fix.
> gcc/testsuite/
> 	* gcc.dg/pr114574-1.c: New test.
> 	* gcc.dg/pr114574-2.c: New test.
> 	* gcc.dg/pr114361.c: New test.
> 	* gcc.dg/c23-tag-incomplete-1.c: New test.
> 	* gcc.dg/c23-tag-incomplete-2.c: New test.

Just a nit I haven't seen before (not a review, can't do that when
I've participated on the patch):

> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
> @@ -0,0 +1,14 @@
> +/* { dg-do compile }
> + * { dg-options "-std=c23 -g" } */
> +
> +struct a;
> +typedef struct a b;
> +
> +void g() {
> +    struct a { b* x; };
> +}
> +
> +struct a { b* x; };
> +
> +
> +

Please avoid the trailing whitespace in the testcases.  They should end with
a single newline, not 3-4 newlines.

	Jakub


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2024-04-15  6:55   ` Richard Biener
@ 2024-04-15  7:38     ` Jakub Jelinek
  2024-04-15  7:59       ` Jakub Jelinek
  0 siblings, 1 reply; 53+ messages in thread
From: Jakub Jelinek @ 2024-04-15  7:38 UTC (permalink / raw)
  To: Richard Biener; +Cc: Martin Uecker, gcc-patches, Joseph Myers

On Mon, Apr 15, 2024 at 08:55:56AM +0200, Richard Biener wrote:
> > diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc
> > index 16ed55e2e5a..6f75444e513 100644
> > --- a/gcc/ipa-free-lang-data.cc
> > +++ b/gcc/ipa-free-lang-data.cc
> > @@ -255,7 +255,9 @@ fld_incomplete_type_of (tree t, class free_lang_data_d *fld)
> >  	    first = build_reference_type_for_mode (t2, TYPE_MODE (t),
> >  						   TYPE_REF_CAN_ALIAS_ALL (t));
> >  	  gcc_assert (TYPE_CANONICAL (t2) != t2
> > -		      && TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t)));
> > +		      && (TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t))
> > +			  || TYPE_STRUCTURAL_EQUALITY_P (t2)
> > +			  || TYPE_STRUCTURAL_EQUALITY_P (TREE_TYPE (t))));
> 
> I think we want the same canonical types for t2 and TREE_TYPE (t), why
> doesn't fld_incomplete_type_of make it so?

I had this spot instrumented to log the different cases (before adding the
code to fix up also pointer types in c_update_type_canonical) and the only thing
that triggered was that the 2 TYPE_CANONICALs weren't equal if
TYPE_STRUCTURAL_EQUALITY_P (TREE_TYPE (t)), the other was just in case.
gcc.c-torture/compile/20021205-1.c
gcc.c-torture/compile/20040214-2.c
gcc.c-torture/compile/20060109-1.c
gcc.c-torture/compile/pr113623.c
gcc.c-torture/compile/pr46866.c
gcc.c-torture/compile/pta-1.c
gcc.c-torture/execute/pr33870-1.c
gcc.c-torture/execute/pr33870.c
gcc.dg/torture/pr57478.c
tests were affected in make check-gcc.
I thought it would be a clear consequence of the choice we've discussed on
IRC, that build_pointer_type_for_mode and other tree.cc functions which
lookup/create derived types don't try to fill in TYPE_CANONICAL for
types derived from something which initially had TYPE_STRUCTURAL_EQUALITY_P
but later changed to non-TYPE_STRUCTURAL_EQUALITY_P.  The patch updates
it solely for qualified types/related pointer types, but doesn't do that
for array types, pointer to array types, function types, ...
So, I think the assertion could still trigger if we have something like
-O2 -flto -std=c23
struct S;
typedef struct S *T;
typedef T U[10];
typedef U *V;
V foo (int x) { return 0; }
struct S { int s; };
(but doesn't, dunno what I'm missing; though here certainly V and U have
TYPE_STRUCTURAL_EQUALITY_P, even T has because it is a typedef, not
something actually normally returned by build_pointer_type).

	Jakub


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2024-04-15  7:38     ` Jakub Jelinek
@ 2024-04-15  7:59       ` Jakub Jelinek
  2024-04-15  8:02         ` Richard Biener
  0 siblings, 1 reply; 53+ messages in thread
From: Jakub Jelinek @ 2024-04-15  7:59 UTC (permalink / raw)
  To: Richard Biener, Martin Uecker, gcc-patches, Joseph S. Myers

On Mon, Apr 15, 2024 at 09:38:29AM +0200, Jakub Jelinek wrote:
> I had this spot instrumented to log the different cases (before adding the
> code to fix up also pointer types in c_update_type_canonical) and the only thing
> that triggered was that the 2 TYPE_CANONICALs weren't equal if
> TYPE_STRUCTURAL_EQUALITY_P (TREE_TYPE (t)), the other was just in case.
> gcc.c-torture/compile/20021205-1.c
> gcc.c-torture/compile/20040214-2.c
> gcc.c-torture/compile/20060109-1.c
> gcc.c-torture/compile/pr113623.c
> gcc.c-torture/compile/pr46866.c
> gcc.c-torture/compile/pta-1.c
> gcc.c-torture/execute/pr33870-1.c
> gcc.c-torture/execute/pr33870.c
> gcc.dg/torture/pr57478.c
> tests were affected in make check-gcc.
> I thought it would be a clear consequence of the choice we've discussed on
> IRC, that build_pointer_type_for_mode and other tree.cc functions which
> lookup/create derived types don't try to fill in TYPE_CANONICAL for
> types derived from something which initially had TYPE_STRUCTURAL_EQUALITY_P
> but later changed to non-TYPE_STRUCTURAL_EQUALITY_P.  The patch updates
> it solely for qualified types/related pointer types, but doesn't do that
> for array types, pointer to array types, function types, ...
> So, I think the assertion could still trigger if we have something like
> -O2 -flto -std=c23
> struct S;
> typedef struct S *T;
> typedef T U[10];
> typedef U *V;
> V foo (int x) { return 0; }
> struct S { int s; };
> (but doesn't, dunno what I'm missing; though here certainly V and U have
> TYPE_STRUCTURAL_EQUALITY_P, even T has because it is a typedef, not
> something actually normally returned by build_pointer_type).

Though, haven't managed to reproduce it with -O2 -flto -std=c23
struct S;
typedef struct S **V[10];
V **foo (int x) { return 0; }
struct S { int s; };
either.
So, maybe let's drop the ipa-free-lang-data.cc part?
Seems fld_incomplete_type_of uses fld_type_variant which should
copy over TYPE_CANONICAL.

	Jakub


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2024-04-15  7:59       ` Jakub Jelinek
@ 2024-04-15  8:02         ` Richard Biener
  2024-04-15  8:05           ` Jakub Jelinek
  0 siblings, 1 reply; 53+ messages in thread
From: Richard Biener @ 2024-04-15  8:02 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Martin Uecker, gcc-patches, Joseph S. Myers

On Mon, 15 Apr 2024, Jakub Jelinek wrote:

> On Mon, Apr 15, 2024 at 09:38:29AM +0200, Jakub Jelinek wrote:
> > I had this spot instrumented to log the different cases (before adding the
> > code to fix up also pointer types in c_update_type_canonical) and the only thing
> > that triggered was that the 2 TYPE_CANONICALs weren't equal if
> > TYPE_STRUCTURAL_EQUALITY_P (TREE_TYPE (t)), the other was just in case.
> > gcc.c-torture/compile/20021205-1.c
> > gcc.c-torture/compile/20040214-2.c
> > gcc.c-torture/compile/20060109-1.c
> > gcc.c-torture/compile/pr113623.c
> > gcc.c-torture/compile/pr46866.c
> > gcc.c-torture/compile/pta-1.c
> > gcc.c-torture/execute/pr33870-1.c
> > gcc.c-torture/execute/pr33870.c
> > gcc.dg/torture/pr57478.c
> > tests were affected in make check-gcc.
> > I thought it would be a clear consequence of the choice we've discussed on
> > IRC, that build_pointer_type_for_mode and other tree.cc functions which
> > lookup/create derived types don't try to fill in TYPE_CANONICAL for
> > types derived from something which initially had TYPE_STRUCTURAL_EQUALITY_P
> > but later changed to non-TYPE_STRUCTURAL_EQUALITY_P.  The patch updates
> > it solely for qualified types/related pointer types, but doesn't do that
> > for array types, pointer to array types, function types, ...
> > So, I think the assertion could still trigger if we have something like
> > -O2 -flto -std=c23
> > struct S;
> > typedef struct S *T;
> > typedef T U[10];
> > typedef U *V;
> > V foo (int x) { return 0; }
> > struct S { int s; };
> > (but doesn't, dunno what I'm missing; though here certainly V and U have
> > TYPE_STRUCTURAL_EQUALITY_P, even T has because it is a typedef, not
> > something actually normally returned by build_pointer_type).
> 
> Though, haven't managed to reproduce it with -O2 -flto -std=c23
> struct S;
> typedef struct S **V[10];
> V **foo (int x) { return 0; }
> struct S { int s; };
> either.
> So, maybe let's drop the ipa-free-lang-data.cc part?
> Seems fld_incomplete_type_of uses fld_type_variant which should
> copy over TYPE_CANONICAL.

If you have a testcase that still triggers it would be nice to see it.

Richard.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2024-04-15  8:02         ` Richard Biener
@ 2024-04-15  8:05           ` Jakub Jelinek
  2024-04-15 10:48             ` [PATCH] c, v3: " Jakub Jelinek
  0 siblings, 1 reply; 53+ messages in thread
From: Jakub Jelinek @ 2024-04-15  8:05 UTC (permalink / raw)
  To: Richard Biener; +Cc: Martin Uecker, gcc-patches, Joseph S. Myers

On Mon, Apr 15, 2024 at 10:02:25AM +0200, Richard Biener wrote:
> > Though, haven't managed to reproduce it with -O2 -flto -std=c23
> > struct S;
> > typedef struct S **V[10];
> > V **foo (int x) { return 0; }
> > struct S { int s; };
> > either.
> > So, maybe let's drop the ipa-free-lang-data.cc part?
> > Seems fld_incomplete_type_of uses fld_type_variant which should
> > copy over TYPE_CANONICAL.
> 
> If you have a testcase that still triggers it would be nice to see it.

I don't, that is why I'm now suggesting to just drop that hunk.

	Jakub


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH] c, v3: Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2024-04-15  8:05           ` Jakub Jelinek
@ 2024-04-15 10:48             ` Jakub Jelinek
  2024-04-15 11:33               ` Richard Biener
  2024-04-19 20:39               ` Joseph Myers
  0 siblings, 2 replies; 53+ messages in thread
From: Jakub Jelinek @ 2024-04-15 10:48 UTC (permalink / raw)
  To: Richard Biener, Martin Uecker, Joseph S. Myers; +Cc: gcc-patches

On Mon, Apr 15, 2024 at 10:05:58AM +0200, Jakub Jelinek wrote:
> On Mon, Apr 15, 2024 at 10:02:25AM +0200, Richard Biener wrote:
> > > Though, haven't managed to reproduce it with -O2 -flto -std=c23
> > > struct S;
> > > typedef struct S **V[10];
> > > V **foo (int x) { return 0; }
> > > struct S { int s; };
> > > either.
> > > So, maybe let's drop the ipa-free-lang-data.cc part?
> > > Seems fld_incomplete_type_of uses fld_type_variant which should
> > > copy over TYPE_CANONICAL.
> > 
> > If you have a testcase that still triggers it would be nice to see it.
> 
> I don't, that is why I'm now suggesting to just drop that hunk.

Actually no, I've just screwed up something in my testing.
One can reproduce it easily with -O2 -flto 20021205-1.c -std=c23
if the ipa-free-lang-data.cc hunk is removed.
This happens when fld_incomplete_type_of is called on a POINTER_TYPE
to RECORD_TYPE x, where the RECORD_TYPE x is not the TYPE_MAIN_VARIANT,
but another variant created by set_underlying_type.  The
c_update_type_canonical didn't touch TYPE_CANONICAL in those, I was too
afraid I don't know what TYPE_CANONICAL should be for all variant types,
so that TREE_TYPE (t) had TYPE_CANONICAL NULL.  But when we call
fld_incomplete_type_of on that TREE_TYPE (t), it sees it isn't
TYPE_MAIN_VARIANT, so calls
  return (fld_type_variant
          (fld_incomplete_type_of (TYPE_MAIN_VARIANT (t), fld), t, fld));
but TYPE_MAIN_VARIANT (t) has already TYPE_CANONICAL (TYPE_MAIN_VARIANT (t))
== TYPE_MAIN_VARIANT (t), that one has been completed on finish_struct.
And so we trigger the assertion, because
TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t))
is no longer true, the former is non-NULL, the latter is NULL.

But looking at all the build_variant_type_copy callers and the call itself,
the call itself sets TYPE_CANONICAL to the TYPE_CANONICAL of the type on
which it is called and the only caller I can find that changes
TYPE_CANONICAL sometimes is build_qualified_type.
So, I'd hope that normally all variant types of an aggregate type (or
pointer type) have the same TYPE_CANONICAL if they have the same TYPE_QUALS
and if they have it different, they have TYPE_CANONICAL of
build_qualified_type of the base TYPE_CANONICAL.

With the following updated patch (ipa-free-lang-data.cc hunk removed,
c_update_type_canonical function updated, plus removed trailing whitespace
from tests),
make check-gcc RUNTESTFLAGS="--target_board=unix/-std=gnu23 compile.exp='20021205-1.c 20040214-2.c 20060109-1.c pr113623.c pr46866.c pta-1.c' execute.exp='pr33870-1.c pr33870.c'"
no longer ICEs (have just expected FAILs on 20040214-2.c which isn't
compatible with C23) and make check-gcc -j32 doesn't regress compared
to the unpatched one.

Is this ok for trunk if it passes full bootstrap/regtest?

2024-04-15  Martin Uecker  <uecker@tugraz.at>
	    Jakub Jelinek  <jakub@redhat.com>

	PR lto/114574
	PR c/114361
gcc/c/
	* c-decl.cc (shadow_tag_warned): For flag_isoc23 and code not
	ENUMERAL_TYPE use SET_TYPE_STRUCTURAL_EQUALITY.
	(parser_xref_tag): Likewise.
	(start_struct): For flag_isoc23 use SET_TYPE_STRUCTURAL_EQUALITY.
	(c_update_type_canonical): New function.
	(finish_struct): Put NULL as second == operand rather than first.
	Assert TYPE_STRUCTURAL_EQUALITY_P.  Call c_update_type_canonical.
	* c-typeck.cc (composite_type_internal): Use
	SET_TYPE_STRUCTURAL_EQUALITY.  Formatting fix.
gcc/testsuite/
	* gcc.dg/pr114574-1.c: New test.
	* gcc.dg/pr114574-2.c: New test.
	* gcc.dg/pr114361.c: New test.
	* gcc.dg/c23-tag-incomplete-1.c: New test.
	* gcc.dg/c23-tag-incomplete-2.c: New test.

--- gcc/c/c-decl.cc.jj	2024-04-09 09:29:04.824520299 +0200
+++ gcc/c/c-decl.cc	2024-04-15 12:26:43.000790475 +0200
@@ -5051,6 +5051,8 @@ shadow_tag_warned (const struct c_declsp
 	      if (t == NULL_TREE)
 		{
 		  t = make_node (code);
+		  if (flag_isoc23 && code != ENUMERAL_TYPE)
+		    SET_TYPE_STRUCTURAL_EQUALITY (t);
 		  pushtag (input_location, name, t);
 		}
 	    }
@@ -8809,6 +8811,8 @@ parser_xref_tag (location_t loc, enum tr
      the forward-reference will be altered into a real type.  */
 
   ref = make_node (code);
+  if (flag_isoc23 && code != ENUMERAL_TYPE)
+    SET_TYPE_STRUCTURAL_EQUALITY (ref);
   if (code == ENUMERAL_TYPE)
     {
       /* Give the type a default layout like unsigned int
@@ -8910,6 +8914,8 @@ start_struct (location_t loc, enum tree_
   if (ref == NULL_TREE || TREE_CODE (ref) != code)
     {
       ref = make_node (code);
+      if (flag_isoc23)
+	SET_TYPE_STRUCTURAL_EQUALITY (ref);
       pushtag (loc, name, ref);
     }
 
@@ -9347,6 +9353,45 @@ is_flexible_array_member_p (bool is_last
   return false;
 }
 
+/* Recompute TYPE_CANONICAL for variants of the type including qualified
+   versions of the type and related pointer types after an aggregate type
+   has been finalized.
+   Will not update array types, pointers to array types, function
+   types and other derived types created while the type was still
+   incomplete, those will remain TYPE_STRUCTURAL_EQUALITY_P.  */
+
+static void
+c_update_type_canonical (tree t)
+{
+  for (tree x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
+    {
+      if (x != t && TYPE_STRUCTURAL_EQUALITY_P (x))
+	{
+	  if (TYPE_QUALS (x) == TYPE_QUALS (t))
+	    TYPE_CANONICAL (x) = TYPE_CANONICAL (t);
+	  else if (TYPE_CANONICAL (t) != t
+		   || check_qualified_type (x, t, TYPE_QUALS (x)))
+	    TYPE_CANONICAL (x)
+	      = build_qualified_type (TYPE_CANONICAL (t), TYPE_QUALS (x));
+	  else
+	    TYPE_CANONICAL (x) = x;
+	}
+      else if (x != t)
+	continue;
+      for (tree p = TYPE_POINTER_TO (x); p; p = TYPE_NEXT_PTR_TO (p))
+	{
+	  if (!TYPE_STRUCTURAL_EQUALITY_P (p))
+	    continue;
+	  if (TYPE_CANONICAL (x) != x || TYPE_REF_CAN_ALIAS_ALL (p))
+	    TYPE_CANONICAL (p)
+	      = build_pointer_type_for_mode (TYPE_CANONICAL (x), TYPE_MODE (p),
+					     false);
+	  else
+	    TYPE_CANONICAL (p) = p;
+	  c_update_type_canonical (p);
+	}
+    }
+}
 
 /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T.
    LOC is the location of the RECORD_TYPE or UNION_TYPE's definition.
@@ -9695,11 +9740,12 @@ finish_struct (location_t loc, tree t, t
   /* Set type canonical based on equivalence class.  */
   if (flag_isoc23)
     {
-      if (NULL == c_struct_htab)
+      if (c_struct_htab == NULL)
 	c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61);
 
       hashval_t hash = c_struct_hasher::hash (t);
 
+      gcc_checking_assert (TYPE_STRUCTURAL_EQUALITY_P (t));
       tree *e = c_struct_htab->find_slot_with_hash (t, hash, INSERT);
       if (*e)
 	TYPE_CANONICAL (t) = *e;
@@ -9708,6 +9754,7 @@ finish_struct (location_t loc, tree t, t
 	  TYPE_CANONICAL (t) = t;
 	  *e = t;
 	}
+      c_update_type_canonical (t);
     }
 
   tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t));
--- gcc/c/c-typeck.cc.jj	2024-02-08 11:17:37.035662909 +0100
+++ gcc/c/c-typeck.cc	2024-04-12 17:17:33.029950025 +0200
@@ -532,6 +532,7 @@ composite_type_internal (tree t1, tree t
 	  /* Otherwise, create a new type node and link it into the cache.  */
 
 	  tree n = make_node (code1);
+	  SET_TYPE_STRUCTURAL_EQUALITY (n);
 	  TYPE_NAME (n) = TYPE_NAME (t1);
 
 	  struct composite_cache cache2 = { t1, t2, n, cache };
@@ -590,7 +591,8 @@ composite_type_internal (tree t1, tree t
 	  TYPE_STUB_DECL (n) = pushdecl (build_decl (input_location, TYPE_DECL,
 						     NULL_TREE, n));
 
-	  n = finish_struct(input_location, n, fields, attributes, NULL, &expr);
+	  n = finish_struct (input_location, n, fields, attributes, NULL,
+			     &expr);
 
 	  n = qualify_type (n, t1);
 
--- gcc/testsuite/gcc.dg/pr114574-1.c.jj	2024-04-12 13:57:28.647782827 +0200
+++ gcc/testsuite/gcc.dg/pr114574-1.c	2024-04-12 13:57:28.647782827 +0200
@@ -0,0 +1,7 @@
+/* PR lto/114574
+ * { dg-do compile }
+ * { dg-options "-flto" } */
+
+const struct S * x;
+struct S {};
+void f(const struct S **);
--- gcc/testsuite/gcc.dg/pr114574-2.c.jj	2024-04-12 13:57:28.647782827 +0200
+++ gcc/testsuite/gcc.dg/pr114574-2.c	2024-04-12 13:57:28.647782827 +0200
@@ -0,0 +1,7 @@
+/* PR lto/114574
+ * { dg-do compile }
+ * { dg-options "-flto -std=c23" } */
+
+const struct S * x;
+struct S {};
+void f(const struct S **);
--- gcc/testsuite/gcc.dg/pr114361.c.jj	2024-04-12 13:57:28.684782327 +0200
+++ gcc/testsuite/gcc.dg/pr114361.c	2024-04-12 13:57:28.684782327 +0200
@@ -0,0 +1,10 @@
+/* PR c/114361 */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu23 -g" } */
+
+void f()
+{
+    typedef struct foo bar;
+    typedef __typeof( ({ (struct foo { bar *x; }){ }; }) ) wuz;
+    struct foo { wuz *x; };
+}
--- gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c.jj	2024-04-12 13:57:28.684782327 +0200
+++ gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c	2024-04-12 13:57:28.684782327 +0200
@@ -0,0 +1,11 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23 -g" } */
+
+struct a;
+typedef struct a b;
+
+void g() {
+    struct a { b* x; };
+}
+
+struct a { b* x; };
--- gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c.jj	2024-04-12 13:57:28.684782327 +0200
+++ gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c	2024-04-12 13:57:28.684782327 +0200
@@ -0,0 +1,11 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23 -g" } */
+
+struct a;
+typedef struct a b;
+
+void f() {
+	extern struct a { b* x; } t;
+}
+
+extern struct a { b* x; } t;


	Jakub


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH] c, v3: Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2024-04-15 10:48             ` [PATCH] c, v3: " Jakub Jelinek
@ 2024-04-15 11:33               ` Richard Biener
  2024-04-16  7:20                 ` Jakub Jelinek
  2024-04-19 20:39               ` Joseph Myers
  1 sibling, 1 reply; 53+ messages in thread
From: Richard Biener @ 2024-04-15 11:33 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Martin Uecker, Joseph S. Myers, gcc-patches

On Mon, 15 Apr 2024, Jakub Jelinek wrote:

> On Mon, Apr 15, 2024 at 10:05:58AM +0200, Jakub Jelinek wrote:
> > On Mon, Apr 15, 2024 at 10:02:25AM +0200, Richard Biener wrote:
> > > > Though, haven't managed to reproduce it with -O2 -flto -std=c23
> > > > struct S;
> > > > typedef struct S **V[10];
> > > > V **foo (int x) { return 0; }
> > > > struct S { int s; };
> > > > either.
> > > > So, maybe let's drop the ipa-free-lang-data.cc part?
> > > > Seems fld_incomplete_type_of uses fld_type_variant which should
> > > > copy over TYPE_CANONICAL.
> > > 
> > > If you have a testcase that still triggers it would be nice to see it.
> > 
> > I don't, that is why I'm now suggesting to just drop that hunk.
> 
> Actually no, I've just screwed up something in my testing.
> One can reproduce it easily with -O2 -flto 20021205-1.c -std=c23
> if the ipa-free-lang-data.cc hunk is removed.
> This happens when fld_incomplete_type_of is called on a POINTER_TYPE
> to RECORD_TYPE x, where the RECORD_TYPE x is not the TYPE_MAIN_VARIANT,
> but another variant created by set_underlying_type.  The
> c_update_type_canonical didn't touch TYPE_CANONICAL in those, I was too
> afraid I don't know what TYPE_CANONICAL should be for all variant types,
> so that TREE_TYPE (t) had TYPE_CANONICAL NULL.  But when we call
> fld_incomplete_type_of on that TREE_TYPE (t), it sees it isn't
> TYPE_MAIN_VARIANT, so calls
>   return (fld_type_variant
>           (fld_incomplete_type_of (TYPE_MAIN_VARIANT (t), fld), t, fld));
> but TYPE_MAIN_VARIANT (t) has already TYPE_CANONICAL (TYPE_MAIN_VARIANT (t))
> == TYPE_MAIN_VARIANT (t), that one has been completed on finish_struct.
> And so we trigger the assertion, because
> TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t))
> is no longer true, the former is non-NULL, the latter is NULL.
> 
> But looking at all the build_variant_type_copy callers and the call itself,
> the call itself sets TYPE_CANONICAL to the TYPE_CANONICAL of the type on
> which it is called and the only caller I can find that changes
> TYPE_CANONICAL sometimes is build_qualified_type.
> So, I'd hope that normally all variant types of an aggregate type (or
> pointer type) have the same TYPE_CANONICAL if they have the same TYPE_QUALS
> and if they have it different, they have TYPE_CANONICAL of
> build_qualified_type of the base TYPE_CANONICAL.

The middle-end assumes that TYPE_CANONICAL of all variant types are
the same, for TBAA purposes it immediately "puns" to
TYPE_CANONICAL (TYPE_MAIN_VARIANT (..)).  It also assumes that
the canonical type is not a variant type.  Note we never "honor"
TYPE_STRUCTURAL_EQUALITY_P on a variant type (because we don't look
at it, we only look at whether the main variant has
TYPE_STRUCTURAL_EQUALITY_P).

Thus, TYPE_CANONICAL of variant types in principle doesn't need to be
set (but not all places might go the extra step looking at the main
variant before accessing TYPE_CANONICAL).

Richard.

> With the following updated patch (ipa-free-lang-data.cc hunk removed,
> c_update_type_canonical function updated, plus removed trailing whitespace
> from tests),
> make check-gcc RUNTESTFLAGS="--target_board=unix/-std=gnu23 compile.exp='20021205-1.c 20040214-2.c 20060109-1.c pr113623.c pr46866.c pta-1.c' execute.exp='pr33870-1.c pr33870.c'"
> no longer ICEs (have just expected FAILs on 20040214-2.c which isn't
> compatible with C23) and make check-gcc -j32 doesn't regress compared
> to the unpatched one.
> 
> Is this ok for trunk if it passes full bootstrap/regtest?
> 
> 2024-04-15  Martin Uecker  <uecker@tugraz.at>
> 	    Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR lto/114574
> 	PR c/114361
> gcc/c/
> 	* c-decl.cc (shadow_tag_warned): For flag_isoc23 and code not
> 	ENUMERAL_TYPE use SET_TYPE_STRUCTURAL_EQUALITY.
> 	(parser_xref_tag): Likewise.
> 	(start_struct): For flag_isoc23 use SET_TYPE_STRUCTURAL_EQUALITY.
> 	(c_update_type_canonical): New function.
> 	(finish_struct): Put NULL as second == operand rather than first.
> 	Assert TYPE_STRUCTURAL_EQUALITY_P.  Call c_update_type_canonical.
> 	* c-typeck.cc (composite_type_internal): Use
> 	SET_TYPE_STRUCTURAL_EQUALITY.  Formatting fix.
> gcc/testsuite/
> 	* gcc.dg/pr114574-1.c: New test.
> 	* gcc.dg/pr114574-2.c: New test.
> 	* gcc.dg/pr114361.c: New test.
> 	* gcc.dg/c23-tag-incomplete-1.c: New test.
> 	* gcc.dg/c23-tag-incomplete-2.c: New test.
> 
> --- gcc/c/c-decl.cc.jj	2024-04-09 09:29:04.824520299 +0200
> +++ gcc/c/c-decl.cc	2024-04-15 12:26:43.000790475 +0200
> @@ -5051,6 +5051,8 @@ shadow_tag_warned (const struct c_declsp
>  	      if (t == NULL_TREE)
>  		{
>  		  t = make_node (code);
> +		  if (flag_isoc23 && code != ENUMERAL_TYPE)
> +		    SET_TYPE_STRUCTURAL_EQUALITY (t);
>  		  pushtag (input_location, name, t);
>  		}
>  	    }
> @@ -8809,6 +8811,8 @@ parser_xref_tag (location_t loc, enum tr
>       the forward-reference will be altered into a real type.  */
>  
>    ref = make_node (code);
> +  if (flag_isoc23 && code != ENUMERAL_TYPE)
> +    SET_TYPE_STRUCTURAL_EQUALITY (ref);
>    if (code == ENUMERAL_TYPE)
>      {
>        /* Give the type a default layout like unsigned int
> @@ -8910,6 +8914,8 @@ start_struct (location_t loc, enum tree_
>    if (ref == NULL_TREE || TREE_CODE (ref) != code)
>      {
>        ref = make_node (code);
> +      if (flag_isoc23)
> +	SET_TYPE_STRUCTURAL_EQUALITY (ref);
>        pushtag (loc, name, ref);
>      }
>  
> @@ -9347,6 +9353,45 @@ is_flexible_array_member_p (bool is_last
>    return false;
>  }
>  
> +/* Recompute TYPE_CANONICAL for variants of the type including qualified
> +   versions of the type and related pointer types after an aggregate type
> +   has been finalized.
> +   Will not update array types, pointers to array types, function
> +   types and other derived types created while the type was still
> +   incomplete, those will remain TYPE_STRUCTURAL_EQUALITY_P.  */
> +
> +static void
> +c_update_type_canonical (tree t)
> +{
> +  for (tree x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
> +    {
> +      if (x != t && TYPE_STRUCTURAL_EQUALITY_P (x))
> +	{
> +	  if (TYPE_QUALS (x) == TYPE_QUALS (t))
> +	    TYPE_CANONICAL (x) = TYPE_CANONICAL (t);
> +	  else if (TYPE_CANONICAL (t) != t
> +		   || check_qualified_type (x, t, TYPE_QUALS (x)))
> +	    TYPE_CANONICAL (x)
> +	      = build_qualified_type (TYPE_CANONICAL (t), TYPE_QUALS (x));
> +	  else
> +	    TYPE_CANONICAL (x) = x;
> +	}
> +      else if (x != t)
> +	continue;
> +      for (tree p = TYPE_POINTER_TO (x); p; p = TYPE_NEXT_PTR_TO (p))
> +	{
> +	  if (!TYPE_STRUCTURAL_EQUALITY_P (p))
> +	    continue;
> +	  if (TYPE_CANONICAL (x) != x || TYPE_REF_CAN_ALIAS_ALL (p))
> +	    TYPE_CANONICAL (p)
> +	      = build_pointer_type_for_mode (TYPE_CANONICAL (x), TYPE_MODE (p),
> +					     false);
> +	  else
> +	    TYPE_CANONICAL (p) = p;
> +	  c_update_type_canonical (p);
> +	}
> +    }
> +}
>  
>  /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T.
>     LOC is the location of the RECORD_TYPE or UNION_TYPE's definition.
> @@ -9695,11 +9740,12 @@ finish_struct (location_t loc, tree t, t
>    /* Set type canonical based on equivalence class.  */
>    if (flag_isoc23)
>      {
> -      if (NULL == c_struct_htab)
> +      if (c_struct_htab == NULL)
>  	c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61);
>  
>        hashval_t hash = c_struct_hasher::hash (t);
>  
> +      gcc_checking_assert (TYPE_STRUCTURAL_EQUALITY_P (t));
>        tree *e = c_struct_htab->find_slot_with_hash (t, hash, INSERT);
>        if (*e)
>  	TYPE_CANONICAL (t) = *e;
> @@ -9708,6 +9754,7 @@ finish_struct (location_t loc, tree t, t
>  	  TYPE_CANONICAL (t) = t;
>  	  *e = t;
>  	}
> +      c_update_type_canonical (t);
>      }
>  
>    tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t));
> --- gcc/c/c-typeck.cc.jj	2024-02-08 11:17:37.035662909 +0100
> +++ gcc/c/c-typeck.cc	2024-04-12 17:17:33.029950025 +0200
> @@ -532,6 +532,7 @@ composite_type_internal (tree t1, tree t
>  	  /* Otherwise, create a new type node and link it into the cache.  */
>  
>  	  tree n = make_node (code1);
> +	  SET_TYPE_STRUCTURAL_EQUALITY (n);
>  	  TYPE_NAME (n) = TYPE_NAME (t1);
>  
>  	  struct composite_cache cache2 = { t1, t2, n, cache };
> @@ -590,7 +591,8 @@ composite_type_internal (tree t1, tree t
>  	  TYPE_STUB_DECL (n) = pushdecl (build_decl (input_location, TYPE_DECL,
>  						     NULL_TREE, n));
>  
> -	  n = finish_struct(input_location, n, fields, attributes, NULL, &expr);
> +	  n = finish_struct (input_location, n, fields, attributes, NULL,
> +			     &expr);
>  
>  	  n = qualify_type (n, t1);
>  
> --- gcc/testsuite/gcc.dg/pr114574-1.c.jj	2024-04-12 13:57:28.647782827 +0200
> +++ gcc/testsuite/gcc.dg/pr114574-1.c	2024-04-12 13:57:28.647782827 +0200
> @@ -0,0 +1,7 @@
> +/* PR lto/114574
> + * { dg-do compile }
> + * { dg-options "-flto" } */
> +
> +const struct S * x;
> +struct S {};
> +void f(const struct S **);
> --- gcc/testsuite/gcc.dg/pr114574-2.c.jj	2024-04-12 13:57:28.647782827 +0200
> +++ gcc/testsuite/gcc.dg/pr114574-2.c	2024-04-12 13:57:28.647782827 +0200
> @@ -0,0 +1,7 @@
> +/* PR lto/114574
> + * { dg-do compile }
> + * { dg-options "-flto -std=c23" } */
> +
> +const struct S * x;
> +struct S {};
> +void f(const struct S **);
> --- gcc/testsuite/gcc.dg/pr114361.c.jj	2024-04-12 13:57:28.684782327 +0200
> +++ gcc/testsuite/gcc.dg/pr114361.c	2024-04-12 13:57:28.684782327 +0200
> @@ -0,0 +1,10 @@
> +/* PR c/114361 */
> +/* { dg-do compile } */
> +/* { dg-options "-std=gnu23 -g" } */
> +
> +void f()
> +{
> +    typedef struct foo bar;
> +    typedef __typeof( ({ (struct foo { bar *x; }){ }; }) ) wuz;
> +    struct foo { wuz *x; };
> +}
> --- gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c.jj	2024-04-12 13:57:28.684782327 +0200
> +++ gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c	2024-04-12 13:57:28.684782327 +0200
> @@ -0,0 +1,11 @@
> +/* { dg-do compile }
> + * { dg-options "-std=c23 -g" } */
> +
> +struct a;
> +typedef struct a b;
> +
> +void g() {
> +    struct a { b* x; };
> +}
> +
> +struct a { b* x; };
> --- gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c.jj	2024-04-12 13:57:28.684782327 +0200
> +++ gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c	2024-04-12 13:57:28.684782327 +0200
> @@ -0,0 +1,11 @@
> +/* { dg-do compile }
> + * { dg-options "-std=c23 -g" } */
> +
> +struct a;
> +typedef struct a b;
> +
> +void f() {
> +	extern struct a { b* x; } t;
> +}
> +
> +extern struct a { b* x; } t;
> 
> 
> 	Jakub
> 
> 

-- 
Richard Biener <rguenther@suse.de>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH] c, v3: Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2024-04-15 11:33               ` Richard Biener
@ 2024-04-16  7:20                 ` Jakub Jelinek
  0 siblings, 0 replies; 53+ messages in thread
From: Jakub Jelinek @ 2024-04-16  7:20 UTC (permalink / raw)
  To: Richard Biener; +Cc: Martin Uecker, Joseph S. Myers, gcc-patches

On Mon, Apr 15, 2024 at 01:33:18PM +0200, Richard Biener wrote:
> > But looking at all the build_variant_type_copy callers and the call itself,
> > the call itself sets TYPE_CANONICAL to the TYPE_CANONICAL of the type on
> > which it is called and the only caller I can find that changes
> > TYPE_CANONICAL sometimes is build_qualified_type.
> > So, I'd hope that normally all variant types of an aggregate type (or
> > pointer type) have the same TYPE_CANONICAL if they have the same TYPE_QUALS
> > and if they have it different, they have TYPE_CANONICAL of
> > build_qualified_type of the base TYPE_CANONICAL.
> 
> The middle-end assumes that TYPE_CANONICAL of all variant types are
> the same, for TBAA purposes it immediately "puns" to
> TYPE_CANONICAL (TYPE_MAIN_VARIANT (..)).  It also assumes that
> the canonical type is not a variant type.  Note we never "honor"
> TYPE_STRUCTURAL_EQUALITY_P on a variant type (because we don't look
> at it, we only look at whether the main variant has
> TYPE_STRUCTURAL_EQUALITY_P).
> 
> Thus, TYPE_CANONICAL of variant types in principle doesn't need to be
> set (but not all places might go the extra step looking at the main
> variant before accessing TYPE_CANONICAL).

The patch as posted passed bootstrap/regtest.

Even if the middle-end doesn't look at TYPE_CANONICAL on anything but
TYPE_MAIN_VARIANT, for the case of say POINTER_TYPE to TYPE_READONLY
qualified struct (like struct S const *) the pointer still has
TYPE_CANONICAL based on the qualified type rather than main variant,
so not recomputing TYPE_CANONICAL for the variant would have consequences
on TYPE_CANONICAL for types derived from those variant types.
So, if I have
struct S;
const struct S *p;
struct S { int s; };
const struct S q[10];
then if I don't recompute TYPE_CANONICAL for const struct S, both the
const struct S * TYPE_CANONICAL would remain TYPE_STRUCTURAL_EQUALITY_P,
but also the ARRAY_TYPE would have it as well, even when it has been
created only after finalizing the type.

	Jakub


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH] c, v3: Fix ICE with -g and -std=c23 related to incomplete types [PR114361]
  2024-04-15 10:48             ` [PATCH] c, v3: " Jakub Jelinek
  2024-04-15 11:33               ` Richard Biener
@ 2024-04-19 20:39               ` Joseph Myers
  1 sibling, 0 replies; 53+ messages in thread
From: Joseph Myers @ 2024-04-19 20:39 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, Martin Uecker, gcc-patches

On Mon, 15 Apr 2024, Jakub Jelinek wrote:

> 2024-04-15  Martin Uecker  <uecker@tugraz.at>
> 	    Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR lto/114574
> 	PR c/114361
> gcc/c/
> 	* c-decl.cc (shadow_tag_warned): For flag_isoc23 and code not
> 	ENUMERAL_TYPE use SET_TYPE_STRUCTURAL_EQUALITY.
> 	(parser_xref_tag): Likewise.
> 	(start_struct): For flag_isoc23 use SET_TYPE_STRUCTURAL_EQUALITY.
> 	(c_update_type_canonical): New function.
> 	(finish_struct): Put NULL as second == operand rather than first.
> 	Assert TYPE_STRUCTURAL_EQUALITY_P.  Call c_update_type_canonical.
> 	* c-typeck.cc (composite_type_internal): Use
> 	SET_TYPE_STRUCTURAL_EQUALITY.  Formatting fix.
> gcc/testsuite/
> 	* gcc.dg/pr114574-1.c: New test.
> 	* gcc.dg/pr114574-2.c: New test.
> 	* gcc.dg/pr114361.c: New test.
> 	* gcc.dg/c23-tag-incomplete-1.c: New test.
> 	* gcc.dg/c23-tag-incomplete-2.c: New test.

OK.

-- 
Joseph S. Myers
josmyers@redhat.com


^ permalink raw reply	[flat|nested] 53+ messages in thread

end of thread, other threads:[~2024-04-19 20:40 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-16 21:36 c23 type compatibility rules, v3 Martin Uecker
2023-11-16 21:38 ` [PATCH 1/4] c23: tag compatibility rules for struct and unions Martin Uecker
2023-11-23 23:17   ` Joseph Myers
2023-11-16 21:38 ` [PATCH 2/4] c23: tag compatibility rules for enums Martin Uecker
2023-11-23 23:26   ` Joseph Myers
2023-11-16 21:39 ` [PATCH 3/4] c23: aliasing of compatible tagged types Martin Uecker
2023-11-23 23:47   ` Joseph Myers
2023-11-26 22:48     ` Martin Uecker
2023-11-27  7:46       ` Richard Biener
2023-11-28  1:00       ` Joseph Myers
2023-11-28  6:49         ` Martin Uecker
2023-11-28 10:47         ` Richard Biener
2023-11-28 11:51           ` Martin Uecker
2023-11-16 21:40 ` [PATCH 4/4] c23: construct composite type for " Martin Uecker
2023-11-27 13:16 ` [V4] [C PATCH 1/4] c23: tag compatibility rules for struct and unions Martin Uecker
2023-12-14 20:53   ` Joseph Myers
2023-11-27 13:16 ` [V4] [PATCH 2/4] c23: tag compatibility rules for enums Martin Uecker
2023-12-14 20:58   ` Joseph Myers
2023-11-27 13:16 ` [V4] [PATCH 3/4] c23: aliasing of compatible tagged types Martin Uecker
2023-12-14 21:10   ` Joseph Myers
2023-11-27 13:16 ` [V4] [PATCH 4/4] c23: construct composite type for " Martin Uecker
2023-12-17 17:41 ` [V5] [C PATCH 1/4] c23: tag compatibility rules for struct and unions Martin Uecker
2023-12-19 21:48   ` Joseph Myers
2023-12-17 17:42 ` [V5] [C PATCH 2/4] c23: tag compatibility rules for enums Martin Uecker
2023-12-19 21:50   ` Joseph Myers
2023-12-17 17:42 ` [V5] [C PATCH 3/4] c23: aliasing of compatible tagged types Martin Uecker
2023-12-19 22:02   ` Joseph Myers
2023-12-17 17:42 ` [V5] [C PATCH 4/4] c23: construct composite type for " Martin Uecker
2023-12-19 22:25   ` Joseph Myers
2023-12-21 21:47 ` [V6] " Martin Uecker
2023-12-22 16:27   ` Joseph Myers
2023-12-27 19:23 ` [C PATCH] C: Fix type compatibility for structs with variable sized fields Martin Uecker
2023-12-29 15:57   ` Joseph Myers
2024-01-27 16:10 ` Fix ICE with -g and -std=c23 when forming composite types [PR113438] Martin Uecker
2024-01-29 20:27   ` Joseph Myers
2024-04-02 18:54 ` [C PATCH] Fix ICE with -g and -std=c23 related to incomplete types [PR114361] Martin Uecker
2024-04-02 20:31   ` Joseph Myers
2024-04-02 19:02 ` [C PATCH] fix aliasing for structures/unions with incomplete types Martin Uecker
2024-04-02 20:42   ` Joseph Myers
2024-04-02 21:22     ` Martin Uecker
2024-04-03 15:33       ` Joseph Myers
2024-04-14 12:30 ` [C PATCH, v2] Fix ICE with -g and -std=c23 related to incomplete types [PR114361] Martin Uecker
2024-04-14 12:38   ` Martin Uecker
2024-04-15  6:55   ` Richard Biener
2024-04-15  7:38     ` Jakub Jelinek
2024-04-15  7:59       ` Jakub Jelinek
2024-04-15  8:02         ` Richard Biener
2024-04-15  8:05           ` Jakub Jelinek
2024-04-15 10:48             ` [PATCH] c, v3: " Jakub Jelinek
2024-04-15 11:33               ` Richard Biener
2024-04-16  7:20                 ` Jakub Jelinek
2024-04-19 20:39               ` Joseph Myers
2024-04-15  7:03   ` [C PATCH, v2] " Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).