[C] Avoid aka types that just add tags
authorRichard Sandiford <richard.sandiford@arm.com>
Tue, 1 Oct 2019 08:56:25 +0000 (08:56 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Tue, 1 Oct 2019 08:56:25 +0000 (08:56 +0000)
diag-aka-1.c tests that:

  struct T { int i; } T;
  void *a;
  T *t = a;

produces:

  request for implicit conversion from 'void *' to 'T *' {aka 'struct T *'} ...

But printing an aka for the tag seems a bit redundant when the tag name
is the same as the typedef name.  It's probably not going to be telling
the user anything they don't already know, and can be distracting if "T"
rather than "struct T" is the preferred choice for an exported interface.
This is even more true if the tag is anonymous; e.g.:

  struct { int i; } T;
  void *a;
  T *t = a;

gives:

  request for implicit conversion from 'void *' to 'T *' {aka 'struct <anonymous> *'}

Rather than just drop the test above, the patch instead tests for:

  struct T { int i; } *T;

where seeing the tag definitely helps.

2019-10-01  Richard Sandiford  <richard.sandiford@arm.com>

gcc/c/
* c-objc-common.c (useful_aka_type_p): New function.
(print_type): Use it to decide whether an aka type is worth printing.

gcc/testsuite/
* gcc.dg/diag-aka-1.c (T): Turn into a pointer typedef.
(foo): Update accordingly.
* gcc.dg/diag-aka-4.c: New test.

From-SVN: r276395

gcc/c/ChangeLog
gcc/c/c-objc-common.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/diag-aka-1.c
gcc/testsuite/gcc.dg/diag-aka-4.c [new file with mode: 0644]

index fa0bbc83a1eb6d9fbbdb41defa88693d178b2e3d..3156e35f39becb95615d436d4424efae07b68e16 100644 (file)
@@ -1,3 +1,8 @@
+2019-10-01  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * c-objc-common.c (useful_aka_type_p): New function.
+       (print_type): Use it to decide whether an aka type is worth printing.
+
 2019-09-27  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/88203
index 2b76737a74a58b0df49086ae1fcebeb517652857..e1f3b2ee436ccfc53905d1ff361b3ba3b6f2fef7 100644 (file)
@@ -62,6 +62,73 @@ c_objc_common_init (void)
   return c_common_init ();
 }
 
+/* Return true if it's worth saying that TYPE1 is also known as TYPE2.  */
+
+static bool
+useful_aka_type_p (tree type1, tree type2)
+{
+  if (type1 == type2)
+    return false;
+
+  if (type1 == error_mark_node || type2 == error_mark_node)
+    return false;
+
+  if (TREE_CODE (type1) != TREE_CODE (type2))
+    return true;
+
+  if (typedef_variant_p (type1))
+    {
+      /* Saying that "foo" is also known as "struct foo" or
+        "struct <anonymous>" is unlikely to be useful, since users of
+        structure-like types would already know that they're structures.
+        The same applies to unions and enums; in general, printing the
+        tag is only useful if it has a different name.  */
+      tree_code code = TREE_CODE (type2);
+      tree id2 = TYPE_IDENTIFIER (type2);
+      if ((code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE)
+         && (!id2 || TYPE_IDENTIFIER (type1) == id2))
+       return false;
+
+      return true;
+    }
+  else
+    {
+      switch (TREE_CODE (type1))
+       {
+       case POINTER_TYPE:
+       case REFERENCE_TYPE:
+         return useful_aka_type_p (TREE_TYPE (type1), TREE_TYPE (type2));
+
+       case ARRAY_TYPE:
+         return (useful_aka_type_p (TYPE_DOMAIN (type1), TYPE_DOMAIN (type2))
+                 || useful_aka_type_p (TREE_TYPE (type1), TREE_TYPE (type2)));
+
+       case FUNCTION_TYPE:
+         {
+           tree args1 = TYPE_ARG_TYPES (type1);
+           tree args2 = TYPE_ARG_TYPES (type2);
+           while (args1 != args2)
+             {
+               /* Although this shouldn't happen, it seems to wrong to assert
+                  for it in a diagnostic routine.  */
+               if (!args1 || args1 == void_type_node)
+                 return true;
+               if (!args2 || args2 == void_type_node)
+                 return true;
+               if (useful_aka_type_p (TREE_VALUE (args1), TREE_VALUE (args2)))
+                 return true;
+               args1 = TREE_CHAIN (args1);
+               args2 = TREE_CHAIN (args2);
+             }
+           return useful_aka_type_p (TREE_TYPE (type1), TREE_TYPE (type2));
+         }
+
+       default:
+         return true;
+       }
+    }
+}
+
 /* Print T to CPP.  */
 
 static void
@@ -83,7 +150,7 @@ print_type (c_pretty_printer *cpp, tree t, bool *quoted)
      stripped version.  But sometimes the stripped version looks
      exactly the same, so we don't want it after all.  To avoid
      printing it in that case, we play ugly obstack games.  */
-  if (TYPE_CANONICAL (t) && t != TYPE_CANONICAL (t))
+  if (TYPE_CANONICAL (t) && useful_aka_type_p (t, TYPE_CANONICAL (t)))
     {
       c_pretty_printer cpp2;
       /* Print the stripped version into a temporary printer.  */
index af7f6a25a029715cc4f2aabb1b573b312952df9a..891b2bf5e4be14b70b73312280d87acaff4fe10d 100644 (file)
@@ -1,3 +1,9 @@
+2019-10-01  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * gcc.dg/diag-aka-1.c (T): Turn into a pointer typedef.
+       (foo): Update accordingly.
+       * gcc.dg/diag-aka-4.c: New test.
+
 2019-10-01  Richard Sandiford  <richard.sandiford@arm.com>
 
        * gcc.dg/diag-aka-3.c: New test.
index fde4ca7c74312e75931e52e1849b8254bde2cc21..3383c1c263bf1dfd57ab941c7766fc4525fd9ef2 100644 (file)
@@ -2,7 +2,7 @@
 /* { dg-options "-Wc++-compat" } */
 
 typedef struct A { int i; } B;
-typedef struct T { int i; } T;
+typedef struct T { int i; } *T; /* { dg-warning "using 'T' as both a typedef and a tag is invalid" } */
 typedef const float TFA;
 typedef TFA TFB;
 typedef TFB TFC;
@@ -24,6 +24,6 @@ bar (B *b, int *i)
 int
 foo (void *a)
 {
-  T *t = a; /* { dg-warning "request for implicit conversion from 'void \\*' to 'T \\*' {aka 'struct T \\*'} not" } */
+  T t = a; /* { dg-warning "request for implicit conversion from 'void \\*' to 'T' {aka 'struct T \\*'} not" } */
   return t->i;
 }
diff --git a/gcc/testsuite/gcc.dg/diag-aka-4.c b/gcc/testsuite/gcc.dg/diag-aka-4.c
new file mode 100644 (file)
index 0000000..cf98dd9
--- /dev/null
@@ -0,0 +1,72 @@
+typedef struct struct_wrapper { int i; } struct_wrapper;
+typedef struct { int i; } anon_struct_wrapper;
+
+typedef union union_wrapper { int i; } union_wrapper;
+typedef union { int i; } anon_union_wrapper;
+
+typedef enum enum_wrapper { A, B } enum_wrapper;
+typedef enum { C, D } anon_enum_wrapper;
+
+void test_struct_wrapper (struct_wrapper y, int x)
+{
+  struct_wrapper *ptr = &x; /* { dg-error {initialization of 'struct_wrapper \*' from incompatible pointer type 'int \*'} } */
+  const struct_wrapper *const_ptr = &x; /* { dg-error {initialization of 'const struct_wrapper \*' from incompatible pointer type 'int \*'} } */
+  volatile struct_wrapper *volatile_ptr = &x; /* { dg-error {initialization of 'volatile struct_wrapper \*' from incompatible pointer type 'int \*'} } */
+  struct_wrapper (*aptr)[10] = &x; /* { dg-error {initialization of 'struct_wrapper \(\*\)\[10\]' from incompatible pointer type 'int \*'} } */
+  struct_wrapper (*f1)(int) = &x; /* { dg-error {initialization of 'struct_wrapper \(\*\)\(int\)' from incompatible pointer type 'int \*'} } */
+  int (*f2)(struct_wrapper) = &x; /* { dg-error {initialization of 'int \(\*\)\(struct_wrapper\)' from incompatible pointer type 'int \*'} } */
+  y = x; /* { dg-error {incompatible types when assigning to type 'struct_wrapper' from type 'int'} } */
+}
+
+void test_anon_struct_wrapper (anon_struct_wrapper y, int x)
+{
+  anon_struct_wrapper *ptr = &x; /* { dg-error {initialization of 'anon_struct_wrapper \*' from incompatible pointer type 'int \*'} } */
+  const anon_struct_wrapper *const_ptr = &x; /* { dg-error {initialization of 'const anon_struct_wrapper \*' from incompatible pointer type 'int \*'} } */
+  volatile anon_struct_wrapper *volatile_ptr = &x; /* { dg-error {initialization of 'volatile anon_struct_wrapper \*' from incompatible pointer type 'int \*'} } */
+  anon_struct_wrapper (*aptr)[10] = &x; /* { dg-error {initialization of 'anon_struct_wrapper \(\*\)\[10\]' from incompatible pointer type 'int \*'} } */
+  anon_struct_wrapper (*f1)(int) = &x; /* { dg-error {initialization of 'anon_struct_wrapper \(\*\)\(int\)' from incompatible pointer type 'int \*'} } */
+  int (*f2)(anon_struct_wrapper) = &x; /* { dg-error {initialization of 'int \(\*\)\(anon_struct_wrapper\)' from incompatible pointer type 'int \*'} } */
+  y = x; /* { dg-error {incompatible types when assigning to type 'anon_struct_wrapper' from type 'int'} } */
+}
+
+void test_union_wrapper (union_wrapper y, int x)
+{
+  union_wrapper *ptr = &x; /* { dg-error {initialization of 'union_wrapper \*' from incompatible pointer type 'int \*'} } */
+  const union_wrapper *const_ptr = &x; /* { dg-error {initialization of 'const union_wrapper \*' from incompatible pointer type 'int \*'} } */
+  volatile union_wrapper *volatile_ptr = &x; /* { dg-error {initialization of 'volatile union_wrapper \*' from incompatible pointer type 'int \*'} } */
+  union_wrapper (*aptr)[10] = &x; /* { dg-error {initialization of 'union_wrapper \(\*\)\[10\]' from incompatible pointer type 'int \*'} } */
+  union_wrapper (*f1)(int) = &x; /* { dg-error {initialization of 'union_wrapper \(\*\)\(int\)' from incompatible pointer type 'int \*'} } */
+  int (*f2)(union_wrapper) = &x; /* { dg-error {initialization of 'int \(\*\)\(union_wrapper\)' from incompatible pointer type 'int \*'} } */
+  y = x; /* { dg-error {incompatible types when assigning to type 'union_wrapper' from type 'int'} } */
+}
+
+void test_anon_union_wrapper (anon_union_wrapper y, int x)
+{
+  anon_union_wrapper *ptr = &x; /* { dg-error {initialization of 'anon_union_wrapper \*' from incompatible pointer type 'int \*'} } */
+  const anon_union_wrapper *const_ptr = &x; /* { dg-error {initialization of 'const anon_union_wrapper \*' from incompatible pointer type 'int \*'} } */
+  volatile anon_union_wrapper *volatile_ptr = &x; /* { dg-error {initialization of 'volatile anon_union_wrapper \*' from incompatible pointer type 'int \*'} } */
+  anon_union_wrapper (*aptr)[10] = &x; /* { dg-error {initialization of 'anon_union_wrapper \(\*\)\[10\]' from incompatible pointer type 'int \*'} } */
+  anon_union_wrapper (*f1)(int) = &x; /* { dg-error {initialization of 'anon_union_wrapper \(\*\)\(int\)' from incompatible pointer type 'int \*'} } */
+  int (*f2)(anon_union_wrapper) = &x; /* { dg-error {initialization of 'int \(\*\)\(anon_union_wrapper\)' from incompatible pointer type 'int \*'} } */
+  y = x; /* { dg-error {incompatible types when assigning to type 'anon_union_wrapper' from type 'int'} } */
+}
+
+void test_enum_wrapper (enum_wrapper y, int x)
+{
+  enum_wrapper *ptr = &x; /* { dg-error {initialization of 'enum_wrapper \*' from incompatible pointer type 'int \*'} } */
+  const enum_wrapper *const_ptr = &x; /* { dg-error {initialization of 'const enum_wrapper \*' from incompatible pointer type 'int \*'} } */
+  volatile enum_wrapper *volatile_ptr = &x; /* { dg-error {initialization of 'volatile enum_wrapper \*' from incompatible pointer type 'int \*'} } */
+  enum_wrapper (*aptr)[10] = &x; /* { dg-error {initialization of 'enum_wrapper \(\*\)\[10\]' from incompatible pointer type 'int \*'} } */
+  enum_wrapper (*f1)(int) = &x; /* { dg-error {initialization of 'enum_wrapper \(\*\)\(int\)' from incompatible pointer type 'int \*'} } */
+  int (*f2)(enum_wrapper) = &x; /* { dg-error {initialization of 'int \(\*\)\(enum_wrapper\)' from incompatible pointer type 'int \*'} } */
+}
+
+void test_anon_enum_wrapper (anon_enum_wrapper y, int x)
+{
+  anon_enum_wrapper *ptr = &x; /* { dg-error {initialization of 'anon_enum_wrapper \*' from incompatible pointer type 'int \*'} } */
+  const anon_enum_wrapper *const_ptr = &x; /* { dg-error {initialization of 'const anon_enum_wrapper \*' from incompatible pointer type 'int \*'} } */
+  volatile anon_enum_wrapper *volatile_ptr = &x; /* { dg-error {initialization of 'volatile anon_enum_wrapper \*' from incompatible pointer type 'int \*'} } */
+  anon_enum_wrapper (*aptr)[10] = &x; /* { dg-error {initialization of 'anon_enum_wrapper \(\*\)\[10\]' from incompatible pointer type 'int \*'} } */
+  anon_enum_wrapper (*f1)(int) = &x; /* { dg-error {initialization of 'anon_enum_wrapper \(\*\)\(int\)' from incompatible pointer type 'int \*'} } */
+  int (*f2)(anon_enum_wrapper) = &x; /* { dg-error {initialization of 'int \(\*\)\(anon_enum_wrapper\)' from incompatible pointer type 'int \*'} } */
+}