]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c: fix checking for a tag for variably modified tagged types [PR119612]
authorMartin Uecker <uecker@tugraz.at>
Fri, 4 Apr 2025 19:01:48 +0000 (21:01 +0200)
committerMartin Uecker <uecker@gcc.gnu.org>
Tue, 8 Apr 2025 12:27:36 +0000 (14:27 +0200)
The checking assertion added for PR118765 did not take into account
that add_decl_expr can change TYPE_NAME to a TYPE_DECL with no name
for certain cases of variably modified types.  This also implies that we
might sometimes not reliably detect the absence of a tag when only
considering TYPE_NAME.  This patch introduces a new helper function
c_type_tag to reliable compute the tag for a tagged types and uses it
for code where the switch to C23 may cause regressions.

PR c/119612

gcc/c/ChangeLog:
* c-tree.h (c_type_tag): Add prototype.
* c-typeck.cc (c_type_tag): New function.
(tagged_types_tu_compatible_p, composite_type_internal): Use
c_type_tag.
* c-decl.cc (c_struct_hasher::hash, previous_tag): Use c_type_tag.

gcc/testsuite/ChangeLog:
* gcc.dg/gnu23-tag-6.c: New test.
* gcc.dg/pr119612.c: New test.

gcc/c/c-decl.cc
gcc/c/c-tree.h
gcc/c/c-typeck.cc
gcc/testsuite/gcc.dg/gnu23-tag-6.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/pr119612.c [new file with mode: 0644]

index c778c7febfa0308b9fea571a8ff0e12175a48c5f..8c420f2297626313cb45b8087630c27cfb8fe8f1 100644 (file)
@@ -659,7 +659,8 @@ c_struct_hasher::hash (tree type)
   inchash::hash hstate;
 
   hstate.add_int (TREE_CODE (type));
-  hstate.add_object (TYPE_NAME (type));
+  tree tag = c_type_tag (type);
+  hstate.add_object (tag);
 
   return hstate.end ();
 }
@@ -2073,7 +2074,7 @@ static tree
 previous_tag (tree type)
 {
   struct c_binding *b = NULL;
-  tree name = TYPE_NAME (type);
+  tree name = c_type_tag (type);
 
   if (name)
     b = I_TAG_BINDING (name);
index 743ec5cbae618efbc7896be02876ae6d24a332b6..2098120de2971ed61f66e91dfd0cbd30347e98f5 100644 (file)
@@ -795,6 +795,7 @@ c_type_unspecified_p (tree t)
 }
 
 extern bool char_type_p (tree);
+extern tree c_type_tag (const_tree t);
 extern tree c_objc_common_truthvalue_conversion (location_t, tree,
                                                 tree = integer_type_node);
 extern tree require_complete_type (location_t, tree);
index 19e79b554dca792cbb623381c1a96726de55382d..3870e8a155877a3d1bf637b064908fa2fd6f6d8f 100644 (file)
@@ -577,7 +577,7 @@ c_build_functype_attribute_variant (tree ntype, tree otype, tree attrs)
 }
 
 /* Given a type which could be a typedef name, make sure to return the
-   original type.  */
+   original type.  See set_underlying_type. */
 static const_tree
 c_type_original (const_tree t)
 {
@@ -591,6 +591,26 @@ c_type_original (const_tree t)
   return t;
 }
 
+/* Return the tag for a tagged type.  */
+tree
+c_type_tag (const_tree t)
+{
+  gcc_assert (RECORD_OR_UNION_TYPE_P (t) || TREE_CODE (t) == ENUMERAL_TYPE);
+  const_tree orig = c_type_original (t);
+  tree name = TYPE_NAME (orig);
+  if (!name)
+    return NULL_TREE;
+  if (TREE_CODE (name) == TYPE_DECL)
+    {
+      /* A TYPE_DECL added by add_decl_expr.  */
+      gcc_checking_assert (!DECL_NAME (name));
+      return NULL_TREE;
+    }
+  gcc_checking_assert (TREE_CODE (name) == IDENTIFIER_NODE);
+  return name;
+}
+
+
 \f
 /* Return the composite type of two compatible types.
 
@@ -744,7 +764,7 @@ composite_type_internal (tree t1, tree t2, struct composite_cache* cache)
       if (flag_isoc23 && !comptypes_same_p (t1, t2))
        {
          /* Go to the original type to get the right tag.  */
-         tree tag = TYPE_NAME (c_type_original (const_cast<tree> (t1)));
+         tree tag = c_type_tag (t1);
 
          gcc_checking_assert (COMPLETE_TYPE_P (t1) && COMPLETE_TYPE_P (t2));
          gcc_checking_assert (!tag || comptypes (t1, t2));
@@ -1807,17 +1827,7 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 {
   tree s1, s2;
 
-  /* We have to verify that the tags of the types are the same.  This
-     is harder than it looks because this may be a typedef, so we have
-     to go look at the original type.  */
-  t1 = c_type_original (t1);
-  t2 = c_type_original (t2);
-  gcc_checking_assert (!TYPE_NAME (t1)
-                      || TREE_CODE (TYPE_NAME (t1)) == IDENTIFIER_NODE);
-  gcc_checking_assert (!TYPE_NAME (t2)
-                      || TREE_CODE (TYPE_NAME (t2)) == IDENTIFIER_NODE);
-
-  if (TYPE_NAME (t1) != TYPE_NAME (t2))
+  if (c_type_tag (t1) != c_type_tag (t2))
     return false;
 
   /* When forming equivalence classes for TYPE_CANONICAL in C23, we treat
@@ -1828,7 +1838,7 @@ tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 
   /* Different types without tag are incompatible except as an anonymous
      field or when forming equivalence classes for TYPE_CANONICAL.  */
-  if (!data->anon_field && !data->equiv && NULL_TREE == TYPE_NAME (t1))
+  if (!data->anon_field && !data->equiv && NULL_TREE == c_type_tag (t1))
     return false;
 
   if (!data->anon_field && TYPE_STUB_DECL (t1) != TYPE_STUB_DECL (t2))
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-6.c b/gcc/testsuite/gcc.dg/gnu23-tag-6.c
new file mode 100644 (file)
index 0000000..b28c7b3
--- /dev/null
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-std=gnu23" } */
+
+int f(int n)
+{
+       struct foo { struct { char (*x)[n]; }; } a;
+       {
+               struct foo { struct { char (*x)[n]; }; } b = a;
+       }
+}
+
+int g(int n)
+{
+       struct foo { struct { char (*x)[n]; }; } a;
+       {
+               struct foo { struct { char (*x)[n]; }; } *b = &a;
+       }
+}
+
+int h(int n)
+{
+       struct foo { struct { struct bar { char (*x)[n]; }* p; }; } a;
+       {
+               struct foo { struct { struct bar { char (*x)[n]; }* p; }; } *b = &a;
+       }
+}
+
diff --git a/gcc/testsuite/gcc.dg/pr119612.c b/gcc/testsuite/gcc.dg/pr119612.c
new file mode 100644 (file)
index 0000000..82bc5d9
--- /dev/null
@@ -0,0 +1,9 @@
+/* PR c/119612
+ * { dg-do compile }
+ * { dg-options "-std=gnu23" }
+ * */
+
+int n = 3;
+void a(struct { char (*p)[n]; } *);    /* { dg-warning "anonymous struct" } */
+void b(struct { char (*p)[n]; } *);    /* { dg-warning "anonymous struct" } */
+