https://gcc.gnu.org/g:d2cfe8a73b3c4195a25cde28e1641ef36ebb08c1

commit r15-934-gd2cfe8a73b3c4195a25cde28e1641ef36ebb08c1
Author: Martin Uecker <uec...@tugraz.at>
Date:   Fri May 24 12:35:27 2024 +0200

    C23: allow aliasing for types derived from structs with variable size
    
    Previously, we set the aliasing set of structures with variable size
    
    struct foo { int x[n]; char b; };
    
    to zero. The reason is that such types can be compatible to diffrent
    structure types which are incompatible.
    
    struct foo { int x[2]; char b; };
    struct foo { int x[3]; char b; };
    
    But it is not enough to set the aliasing set to zero, because derived
    types would then still end up in different equivalence classes even
    though they might be compatible.  Instead those types should be set
    to structural equivalency.  We also add checking assertions that
    ensure that TYPE_CANONICAL is set correctly for all tagged types.
    
    gcc/c/
            * c-decl.cc (finish_struct): Do not set TYPE_CANONICAL for
            structure or unions with variable size.
            * c-objc-common.cc (c_get_alias_set): Do not set alias set to zero.
            * c-typeck.cc (comptypes_verify): New function.
            (comptypes,comptypes_same_p,comptypes_check_enum_int): Add 
assertion.
            (comptypes_equiv_p): Add assertion that ensures that compatible
            types have the same equivalence class.
            (tagged_types_tu_compatible_p): Remove now unneeded special case.
    
    gcc/testsuite/
            * gcc.dg/gnu23-tag-alias-8.c: New test.

Diff:
---
 gcc/c/c-decl.cc                          |  2 +-
 gcc/c/c-objc-common.cc                   |  5 -----
 gcc/c/c-typeck.cc                        | 37 +++++++++++++++++++++++++++++---
 gcc/testsuite/gcc.dg/gnu23-tag-alias-8.c | 24 +++++++++++++++++++++
 4 files changed, 59 insertions(+), 9 deletions(-)

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 6e6606c9570..9f7d55c0b10 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -9749,7 +9749,7 @@ 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 (flag_isoc23 && !C_TYPE_VARIABLE_SIZE (t))
     {
       if (c_struct_htab == NULL)
        c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61);
diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
index 283f6a8ae26..738e899a2a9 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -420,11 +420,6 @@ c_var_mod_p (tree x, tree fn ATTRIBUTE_UNUSED)
 alias_set_type
 c_get_alias_set (tree 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-typeck.cc b/gcc/c/c-typeck.cc
index 09b2c265a46..48934802148 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1167,6 +1167,28 @@ common_type (tree t1, tree t2)
   return c_common_type (t1, t2);
 }
 
+
+
+/* Helper function for comptypes.  For two compatible types, return 1
+   if they pass consistency checks.  In particular we test that
+   TYPE_CANONICAL is set correctly, i.e. the two types can alias.  */
+
+static bool
+comptypes_verify (tree type1, tree type2)
+{
+  if (TYPE_CANONICAL (type1) != TYPE_CANONICAL (type2)
+      && !TYPE_STRUCTURAL_EQUALITY_P (type1)
+      && !TYPE_STRUCTURAL_EQUALITY_P (type2))
+    {
+      /* FIXME: check other types. */
+      if (RECORD_OR_UNION_TYPE_P (type1)
+         || TREE_CODE (type1) == ENUMERAL_TYPE
+         || TREE_CODE (type2) == ENUMERAL_TYPE)
+       return false;
+    }
+  return true;
+}
+
 struct comptypes_data {
   bool enum_and_int_p;
   bool different_types_p;
@@ -1188,6 +1210,8 @@ comptypes (tree type1, tree type2)
   struct comptypes_data data = { };
   bool ret = comptypes_internal (type1, type2, &data);
 
+  gcc_checking_assert (!ret || comptypes_verify (type1, type2));
+
   return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 
@@ -1201,6 +1225,8 @@ comptypes_same_p (tree type1, tree type2)
   struct comptypes_data data = { };
   bool ret = comptypes_internal (type1, type2, &data);
 
+  gcc_checking_assert (!ret || comptypes_verify (type1, type2));
+
   if (data.different_types_p)
     return false;
 
@@ -1218,6 +1244,8 @@ comptypes_check_enum_int (tree type1, tree type2, bool 
*enum_and_int_p)
   bool ret = comptypes_internal (type1, type2, &data);
   *enum_and_int_p = data.enum_and_int_p;
 
+  gcc_checking_assert (!ret || comptypes_verify (type1, type2));
+
   return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 
@@ -1232,6 +1260,8 @@ comptypes_check_different_types (tree type1, tree type2,
   bool ret = comptypes_internal (type1, type2, &data);
   *different_types_p = data.different_types_p;
 
+  gcc_checking_assert (!ret || comptypes_verify (type1, type2));
+
   return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
 
@@ -1274,6 +1304,10 @@ comptypes_equiv_p (tree type1, tree type2)
   data.equiv = true;
   bool ret = comptypes_internal (type1, type2, &data);
 
+  /* check that different equivance classes are assigned only
+     to types that are not compatible.  */
+  gcc_checking_assert (ret || !comptypes (type1, type2));
+
   return ret;
 }
 
@@ -1629,9 +1663,6 @@ 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))
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-8.c 
b/gcc/testsuite/gcc.dg/gnu23-tag-alias-8.c
new file mode 100644
index 00000000000..11f2787521e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-8.c
@@ -0,0 +1,24 @@
+/* { dg-do run } */
+/* { dg-options "-std=gnu23 -O2" } */
+
+typedef struct E { int a[2]; int b; } *A;
+
+void* foo(void* a, void *b, void *c, void *d)
+{
+       *(A**)a = c;
+
+       int N = 2;
+       typedef struct E { int a[N]; int b; } *B;
+       *(B**)b = d;
+
+       return *(A**)a;
+}
+
+int main()
+{
+       A *a, b, c;
+       if (&c != (A*)foo(&a, &a, &b, &c))
+               __builtin_abort();
+}
+
+

Reply via email to