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

commit r16-7872-gd5c50c75f05c783e4b2dd8599f6d16f13961e5f1
Author: Martin Uecker <[email protected]>
Date:   Fri Feb 20 17:19:10 2026 +0100

    c: Fix wrong code related to TBAA for components of structure types 2/2 
[PR122572]
    
    Given the following two types, the C FE assigns the same
    TYPE_CANONICAL to both struct bar, because it treats pointer to
    tagged types with the same type as compatible (in this context).
    
    struct foo { int y; };
    struct bar { struct foo *c; }
    
    struct foo { long y; };
    struct bar { struct foo *c; }
    
    get_alias_set records the components of aggregate types, but only
    considers the components of the canonical version.  To prevent
    miscompilation, we create a modified canonical type where we
    change such pointers to void pointers.
    
            PR c/122572
    
    gcc/c/ChangeLog:
            * c-decl.cc (finish_struct): Add distinct canonical type.
            * c-tree.h (c_type_canonical): Prototype for new function.
            * c-typeck.cc (c_type_canonical): New function.
            (ptr_to_tagged_member): New function.
    
    gcc/testsuite/ChangeLog:
            * gcc.dg/pr123356-2.c: New test.
            * gcc.dg/struct-alias-2.c: New test.

Diff:
---
 gcc/c/c-decl.cc                       |   4 +-
 gcc/c/c-tree.h                        |   1 +
 gcc/c/c-typeck.cc                     |  72 +++++++++++++++++-
 gcc/testsuite/gcc.dg/pr123356-2.c     |  69 ++++++++++++++++++
 gcc/testsuite/gcc.dg/struct-alias-2.c | 133 ++++++++++++++++++++++++++++++++++
 5 files changed, 275 insertions(+), 4 deletions(-)

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 80eebc359084..10b4d77daee4 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -9995,10 +9995,10 @@ finish_struct (location_t loc, tree t, tree fieldlist, 
tree attributes,
 
       tree *e = c_struct_htab->find_slot_with_hash (t, hash, INSERT);
       if (*e)
-       TYPE_CANONICAL (t) = *e;
+       TYPE_CANONICAL (t) = TYPE_CANONICAL (*e);
       else
        {
-         TYPE_CANONICAL (t) = t;
+         TYPE_CANONICAL (t) = c_type_canonical (t);
          *e = t;
        }
       c_update_type_canonical (t);
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 167f50e900c7..30c602b731f8 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -931,6 +931,7 @@ extern tree c_build_function_call_vec (location_t, const 
vec<location_t>&,
                                       vec<tree, va_gc> *);
 extern tree c_omp_clause_copy_ctor (tree, tree, tree);
 extern tree c_reconstruct_complex_type (tree, tree);
+extern tree c_type_canonical (tree);
 extern tree c_build_type_attribute_variant (tree ntype, tree attrs);
 extern tree c_build_pointer_type (tree type);
 extern tree c_build_array_type (tree type, tree domain);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 5b6b2b1d6e08..051773a0cd4d 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -543,8 +543,6 @@ c_reconstruct_complex_type (tree type, tree bottom)
 
       gcc_checking_assert (C_TYPE_VARIABLE_SIZE (type)
                           == C_TYPE_VARIABLE_SIZE (outer));
-      gcc_checking_assert (C_TYPE_VARIABLY_MODIFIED (outer)
-                          == C_TYPE_VARIABLY_MODIFIED (type));
     }
   else if (TREE_CODE (type) == FUNCTION_TYPE)
     {
@@ -562,6 +560,76 @@ c_reconstruct_complex_type (tree type, tree bottom)
                                              TYPE_QUALS (type));
 }
 
+
+/* Helper function for c_canonical_type.  Check whether FIELD
+   contains a pointer to a structure or union with tag,
+   possibly nested in other type derivations.  */
+static bool
+ptr_to_tagged_member (tree field)
+{
+  gcc_assert (FIELD_DECL == TREE_CODE (field));
+  tree type = TREE_TYPE (field);
+  bool ptr_seen = false;
+  while (TREE_CODE (type) == ARRAY_TYPE
+        || TREE_CODE (type) == FUNCTION_TYPE
+        || TREE_CODE (type) == POINTER_TYPE)
+    {
+      if (TREE_CODE (type) == POINTER_TYPE)
+       ptr_seen = true;
+      type = TREE_TYPE (type);
+    }
+
+  if (ptr_seen
+      && RECORD_OR_UNION_TYPE_P (type)
+      && NULL_TREE != c_type_tag (type))
+    return true;
+
+  return false;
+}
+
+/* For a record or union type, make necessary adaptations so that the
+   type can be used as TYPE_CANONICAL.
+
+   If the TYPE contains a pointer (possibly nested in other type
+   derivations) to a structure or union as a member, create a copy
+   and change such pointers to void pointers.  Otherwise, the middle-end
+   gets confused when recording component aliases in the case where we
+   have formed equivalency classes that include types for which these
+   member pointers end up pointing to other structure or unions types
+   which have the same tag but are not compatible.  */
+tree
+c_type_canonical (tree type)
+{
+  gcc_assert (RECORD_OR_UNION_TYPE_P (type));
+
+  bool needs_mod = false;
+  for (tree x = TYPE_FIELDS (type); x; x = DECL_CHAIN (x))
+    {
+      if (!ptr_to_tagged_member (x))
+       continue;
+      needs_mod = true;
+      break;
+    }
+  if (!needs_mod)
+    return type;
+
+  /* Construct new version with such members replaced.  */
+  tree n = build_distinct_type_copy (type);
+  tree *fields = &TYPE_FIELDS (n);
+  for (tree x = TYPE_FIELDS (type); x; x = DECL_CHAIN (x))
+    {
+      tree f = copy_node (x);
+      if (ptr_to_tagged_member (x))
+       TREE_TYPE (f) = c_reconstruct_complex_type (TREE_TYPE (x),
+                                                   ptr_type_node);
+      *fields = f;
+      fields = &DECL_CHAIN (f);
+    }
+  TYPE_CANONICAL (n) = n;
+  return n;
+}
+
+
 /* If NTYPE is a type of a non-variadic function with a prototype
    and OTYPE is a type of a function without a prototype and ATTRS
    contains attribute format, diagnosess and removes it from ATTRS.
diff --git a/gcc/testsuite/gcc.dg/pr123356-2.c 
b/gcc/testsuite/gcc.dg/pr123356-2.c
new file mode 100644
index 000000000000..1f0b9c99b74b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr123356-2.c
@@ -0,0 +1,69 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+
+/* Based on the submitted test case but using types with
+ * tags and moving the second version into another scope.  */
+
+typedef struct foo
+{
+    long  coeffs;
+}
+fmpz_poly_struct;
+
+
+void f()
+{
+       typedef struct foo
+       {
+       } n_poly_struct;
+       typedef struct
+       {
+           n_poly_struct * coeffs;
+           long alloc;
+           long length;
+       } n_bpoly_struct;
+}
+
+
+typedef struct
+{
+    fmpz_poly_struct * coeffs;
+    long alloc;
+    long length;
+} fmpz_bpoly_struct;
+typedef fmpz_bpoly_struct fmpz_bpoly_t[];
+
+__attribute__((noinline))
+fmpz_poly_struct * fmpz_bpoly_swap_(fmpz_bpoly_t B, fmpz_bpoly_t Q)
+{
+    fmpz_bpoly_struct t = *B;
+    *B = *Q;
+    *Q = t;
+    return B->coeffs;
+}
+
+__attribute__((noinline,optimize("no-strict-aliasing")))
+fmpz_poly_struct * fmpz_bpoly_swap_2(fmpz_bpoly_t B, fmpz_bpoly_t Q)
+{
+    fmpz_bpoly_struct t = *B;
+    *B = *Q;
+    *Q = t;
+    return B->coeffs;
+}
+
+int main(){
+    fmpz_poly_struct B_coeffs = {0}, Q_coeffs = {0};
+    fmpz_bpoly_t B = {0};
+    fmpz_bpoly_t Q = {0};
+    B->coeffs = &B_coeffs;
+    Q->coeffs = &Q_coeffs;
+    if (fmpz_bpoly_swap_(B, Q) != &Q_coeffs)
+           __builtin_abort();
+    B->coeffs = &B_coeffs;
+    Q->coeffs = &Q_coeffs;
+    if (fmpz_bpoly_swap_2(B, Q) != &Q_coeffs)
+           __builtin_abort();
+    return 0;
+}
+
diff --git a/gcc/testsuite/gcc.dg/struct-alias-2.c 
b/gcc/testsuite/gcc.dg/struct-alias-2.c
new file mode 100644
index 000000000000..71bc1edc4653
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/struct-alias-2.c
@@ -0,0 +1,133 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -std=c23" } */
+
+
+/* Based on the submitted test case for PR123356 but using types with
+ * tags and moving the second version into another scope.  */
+
+struct foo { long x; };
+
+void f()
+{
+       struct foo { };
+       struct bar { struct foo *c; };
+       union baz { struct foo *c; };
+       struct arr { struct foo *c[1]; };
+       struct fun { struct foo (*c)(); };
+}
+
+
+void f1()
+{
+       struct foo { };
+       struct bar { struct foo *c; };
+       union baz { struct foo *c; };
+       struct arr { struct foo *c[1]; };
+       struct fun { struct foo (*c)(); };
+}
+
+struct bar { struct foo *c; };
+union baz { struct foo *c; };
+struct arr { struct foo *c[1]; };
+struct fun { struct foo (*c)(); };
+
+void f2()
+{
+       struct foo { int y; };
+       struct bar { struct foo *c; };
+       union baz { struct foo *c; };
+       struct arr { struct foo *c[1]; };
+       struct fun { struct foo (*c)(); };
+}
+
+__attribute__((noinline))
+struct foo * g1(struct bar *B, struct bar *Q)
+{
+    struct bar t = *B;
+    *B = *Q;
+    *Q = t;
+    return B->c;
+}
+
+__attribute__((noinline))
+struct foo * g2(union baz *B, union baz *Q)
+{
+    union baz t = *B;
+    *B = *Q;
+    *Q = t;
+    return B->c;
+}
+
+__attribute__((noinline))
+struct foo { long x; } * 
+       g3(struct bar { struct foo { long x; } *c; } *B,
+          struct bar { struct foo { long x; } *c; } *Q)
+{
+    struct bar t = *B;
+    *B = *Q;
+    *Q = t;
+    return B->c;
+}
+
+__attribute__((noinline))
+struct foo * g4(struct arr *B,
+               struct arr *Q)
+{
+    struct arr t = *B;
+    *B = *Q;
+    *Q = t;
+    return B->c[0];
+}
+
+__attribute__((noinline))
+struct foo (*g5(struct fun *B,
+               struct fun *Q))()
+{
+    struct fun t = *B;
+    *B = *Q;
+    *Q = t;
+    return B->c;
+}
+
+struct foo Bd() { };
+struct foo Qd() { };
+
+int main()
+{
+    struct foo Bc = { };
+    struct foo Qc = { };
+
+    struct bar B = { &Bc };
+    struct bar Q = { &Qc };
+
+    if (g1(&B, &Q) != &Qc)
+           __builtin_abort();
+
+    union baz Bu = { &Bc };
+    union baz Qu = { &Qc };
+
+    if (g2(&Bu, &Qu) != &Qc)
+           __builtin_abort();
+
+    struct bar B2 = { &Bc };
+    struct bar Q2 = { &Qc };
+
+    if (g3(&B2, &Q2) != &Qc)
+           __builtin_abort();
+
+    struct arr Ba = { &Bc };
+    struct arr Qa = { &Qc };
+
+    if (g4(&Ba, &Qa) != &Qc)
+           __builtin_abort();
+#if 0
+    // PR114959
+    struct fun Bf = { &Bd };
+    struct fun Qf = { &Qd };
+
+    if (g5(&Bf, &Qf) != &Qd)
+           __builtin_abort();
+#endif
+    return 0;
+}
+

Reply via email to