Bootstrapped and regression tested on x86_64.


    c: fix checking ICE related to transparent unions and atomic [PR123309]
    
    When matching function arguments in composite_type_internal and one
    type comes from a transparent union, it is possible to end up with
    atomic and non-atomic types because this case is not handled correctly.
    The type matching logic is rewritten in a cleaner way to use helper
    functions and to not walk the argument lists three times.  With this
    change, a checking assertion can be added to test for matching qualifiers
    for pointers. (In general, this assumption is still violated for
    function return types.)
    
            PR c/123309
    
    gcc/c/ChangeLog:
            * c-typeck.cc (transparent_union_replacement): New function.
            (composite_type_internal): Rewrite logic.
    
    gcc/testsuite/ChangeLog:
            * pr123309.c: New test.

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index dd9f33c979f..9a0433df67f 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -630,6 +630,33 @@ remove_qualifiers (tree t)
         : TYPE_MAIN_VARIANT (t);
 }
 
+
+/* Helper function for composite_type_internal.  Find a compatible type
+   in a (transparent) union U compatible to T.  If found, return the
+   type of the corresponding member.  Otherwise, return the union type U.  */
+static tree
+transparent_union_replacement (tree u, tree t)
+{
+  if (u == error_mark_node || t == error_mark_node
+      || TREE_CODE (u) != UNION_TYPE || !TYPE_TRANSPARENT_AGGR (u)
+      || comptypes (u, t))
+    return u;
+
+  for (tree memb = TYPE_FIELDS (u); memb; memb = DECL_CHAIN (memb))
+    {
+      tree m = remove_qualifiers (TREE_TYPE (memb));
+      if (comptypes (m, t))
+       {
+         pedwarn (input_location, OPT_Wpedantic,
+                   "function types not truly compatible in ISO C");
+         return m;
+       }
+    }
+
+  return u;
+}
+
+
 
 /* Return the composite type of two compatible types.
 
@@ -689,6 +716,7 @@ composite_type_internal (tree t1, tree t2, tree cond,
     case POINTER_TYPE:
       /* For two pointers, do this recursively on the target type.  */
       {
+       gcc_checking_assert (TYPE_QUALS (t1) == TYPE_QUALS (t2));
        tree target = composite_type_internal (TREE_TYPE (t1), TREE_TYPE (t2),
                                               cond, cache);
        tree n = c_build_pointer_type_for_mode (target, TYPE_MODE (t1), false);
@@ -904,9 +932,6 @@ composite_type_internal (tree t1, tree t2, tree cond,
                                                cond, cache);
        tree p1 = TYPE_ARG_TYPES (t1);
        tree p2 = TYPE_ARG_TYPES (t2);
-       int len;
-       tree newargs, n;
-       int i;
 
        /* Save space: see if the result is identical to one of the args.  */
        if (valtype == TREE_TYPE (t1) && !TYPE_ARG_TYPES (t2))
@@ -932,86 +957,34 @@ composite_type_internal (tree t1, tree t2, tree cond,
 
        /* If both args specify argument types, we must merge the two
           lists, argument by argument.  */
+       tree newargs = NULL_TREE;
+       tree *endp = &newargs;
 
-       for (len = 0, newargs = p1;
-            newargs && newargs != void_list_node;
-            len++, newargs = TREE_CHAIN (newargs))
-         ;
-
-       for (i = 0; i < len; i++)
-         newargs = tree_cons (NULL_TREE, NULL_TREE, newargs);
-
-       n = newargs;
-
-       for (; p1 && p1 != void_list_node;
-            p1 = TREE_CHAIN (p1), p2 = TREE_CHAIN (p2), n = TREE_CHAIN (n))
+       for (; p1; p1 = TREE_CHAIN (p1), p2 = TREE_CHAIN (p2))
          {
-            tree mv1 = remove_qualifiers (TREE_VALUE (p1));
-            tree mv2 = remove_qualifiers (TREE_VALUE (p2));
+           if (p1 == void_list_node)
+             {
+               *endp = void_list_node;
+               break;
+             }
+           tree mv1 = remove_qualifiers (TREE_VALUE (p1));
+           tree mv2 = remove_qualifiers (TREE_VALUE (p2));
 
            /* A null type means arg type is not specified.
               Take whatever the other function type has.  */
-           if (TREE_VALUE (p1) == NULL_TREE)
-             {
-               TREE_VALUE (n) = TREE_VALUE (p2);
-               goto parm_done;
-             }
-           if (TREE_VALUE (p2) == NULL_TREE)
-             {
-               TREE_VALUE (n) = TREE_VALUE (p1);
-               goto parm_done;
-             }
+           if (mv1 == NULL_TREE)
+             mv2 = NULL_TREE;
+           else if (mv2 == NULL_TREE)
+             mv1 = NULL_TREE;
 
-           /* Given  wait (union {union wait *u; int *i} *)
-              and  wait (union wait *),
-              prefer  union wait *  as type of parm.  */
-           if (TREE_CODE (TREE_VALUE (p1)) == UNION_TYPE
-               && TREE_VALUE (p1) != TREE_VALUE (p2))
-             {
-               tree memb;
-               for (memb = TYPE_FIELDS (TREE_VALUE (p1));
-                    memb; memb = DECL_CHAIN (memb))
-                 {
-                   tree mv3 = TREE_TYPE (memb);
-                   if (mv3 && mv3 != error_mark_node
-                       && TREE_CODE (mv3) != ARRAY_TYPE)
-                     mv3 = TYPE_MAIN_VARIANT (mv3);
-                   if (comptypes (mv3, mv2))
-                     {
-                       TREE_VALUE (n) = composite_type_internal (TREE_TYPE 
(memb),
-                                                                 TREE_VALUE 
(p2),
-                                                                 cond, cache);
-                       pedwarn (input_location, OPT_Wpedantic,
-                                "function types not truly compatible in ISO 
C");
-                       goto parm_done;
-                     }
-                 }
-             }
-           if (TREE_CODE (TREE_VALUE (p2)) == UNION_TYPE
-               && TREE_VALUE (p2) != TREE_VALUE (p1))
-             {
-               tree memb;
-               for (memb = TYPE_FIELDS (TREE_VALUE (p2));
-                    memb; memb = DECL_CHAIN (memb))
-                 {
-                   tree mv3 = TREE_TYPE (memb);
-                   if (mv3 && mv3 != error_mark_node
-                       && TREE_CODE (mv3) != ARRAY_TYPE)
-                     mv3 = TYPE_MAIN_VARIANT (mv3);
-                   if (comptypes (mv3, mv1))
-                     {
-                       TREE_VALUE (n)
-                               = composite_type_internal (TREE_TYPE (memb),
-                                                          TREE_VALUE (p1),
-                                                          cond, cache);
-                       pedwarn (input_location, OPT_Wpedantic,
-                                "function types not truly compatible in ISO 
C");
-                       goto parm_done;
-                     }
-                 }
-             }
-           TREE_VALUE (n) = composite_type_internal (mv1, mv2, cond, cache);
-         parm_done: ;
+           mv1 = transparent_union_replacement (mv1, mv2);
+           mv2 = transparent_union_replacement (mv2, mv1);
+
+           *endp = tree_cons (NULL_TREE,
+                              composite_type_internal (mv1, mv2, cond, cache),
+                              NULL_TREE);
+
+           endp = &TREE_CHAIN (*endp);
          }
 
        t1 = c_build_function_type (valtype, newargs);
diff --git a/gcc/testsuite/gcc.dg/pr123309.c b/gcc/testsuite/gcc.dg/pr123309.c
new file mode 100644
index 00000000000..8ddc66c19df
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr123309.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c11" } */
+
+typedef union {
+  const struct sockaddr *_Atomic __sockaddr__;
+} __CONST_SOCKADDR_ARG __attribute__((transparent_union));
+extern int sendto (__CONST_SOCKADDR_ARG __addr);
+
+int sendto(const struct sockaddr *_Atomic to)
+{
+       const struct sockaddr * _Atomic *p = &to;
+}
+

Reply via email to