https://gcc.gnu.org/g:e2acc3d38e800f8dd1f45f5ae6bf2ee090044bcf
commit r16-6750-ge2acc3d38e800f8dd1f45f5ae6bf2ee090044bcf Author: Martin Uecker <[email protected]> Date: Tue Jan 13 19:09:53 2026 +0100 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. (type_lists_compatible_p): Remove dead code for NULL arguments. gcc/testsuite/ChangeLog: * gcc.dg/pr123309.c: New test. * gcc.dg/union-composite-type.c: New test. Diff: --- gcc/c/c-typeck.cc | 148 ++++++++++------------------ gcc/testsuite/gcc.dg/pr123309.c | 13 +++ gcc/testsuite/gcc.dg/union-composite-type.c | 8 ++ 3 files changed, 74 insertions(+), 95 deletions(-) diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index a762f3c47d1c..af7059813532 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -630,6 +630,34 @@ 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) || TYPE_NAME (u) == NULL_TREE) + || 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 +717,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 +933,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 +958,30 @@ 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)); - - /* 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) + if (p1 == void_list_node) { - TREE_VALUE (n) = TREE_VALUE (p1); - goto parm_done; + *endp = void_list_node; + break; } + tree mv1 = remove_qualifiers (TREE_VALUE (p1)); + tree mv2 = remove_qualifiers (TREE_VALUE (p2)); - /* 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: ; + gcc_assert (mv1); + gcc_assert (mv2); + + 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); @@ -2158,24 +2128,12 @@ type_lists_compatible_p (const_tree args1, const_tree args2, tree a2 = TREE_VALUE (args2); tree mv1 = remove_qualifiers (a1); tree mv2 = remove_qualifiers (a2); - /* A null pointer instead of a type - means there is supposed to be an argument - but nothing is specified about what type it has. - So match anything that self-promotes. */ - if ((a1 == NULL_TREE) != (a2 == NULL_TREE)) - data->different_types_p = true; - if (a1 == NULL_TREE) - { - if (c_type_promotes_to (a2) != a2) - return false; - } - else if (a2 == NULL_TREE) - { - if (c_type_promotes_to (a1) != a1) - return false; - } + + gcc_assert (mv2); + gcc_assert (mv2); + /* If one of the lists has an error marker, ignore this arg. */ - else if (TREE_CODE (a1) == ERROR_MARK + if (TREE_CODE (a1) == ERROR_MARK || TREE_CODE (a2) == ERROR_MARK) ; else if (!comptypes_internal (mv1, mv2, data)) diff --git a/gcc/testsuite/gcc.dg/pr123309.c b/gcc/testsuite/gcc.dg/pr123309.c new file mode 100644 index 000000000000..8ddc66c19df6 --- /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; +} + diff --git a/gcc/testsuite/gcc.dg/union-composite-type.c b/gcc/testsuite/gcc.dg/union-composite-type.c new file mode 100644 index 000000000000..b9a8cb7a19ea --- /dev/null +++ b/gcc/testsuite/gcc.dg/union-composite-type.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-Wpedantic" } */ + +/* An old extension */ + +int f(union { int a; }); +int f(int a); /* { dg-warning "not truly compatible" } */ +
