https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128595
Bug ID: 128595
Summary: convert_for_assignment transparent_union path strips
array types via TREE_TYPE without preserving element
qualifiers, silently dropping const/volatile on
pointer-to-array arguments
Product: gcc
Version: 16.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c
Assignee: unassigned at gcc dot gnu.org
Reporter: 220245569 at seu dot edu.cn
CC: 220255623 at seu dot edu.cn, jianhao.xu at seu dot edu.cn
Target Milestone: ---
Target: *-*-*
Reproducer
----------
$ cat trigger.c
typedef int (*arr_ptr)[4];
typedef const int (*carr_ptr)[4];
typedef union __attribute__((transparent_union)) {
arr_ptr p;
} U;
void take_u(U u);
void take_p(arr_ptr p);
void test(carr_ptr cp) {
take_u(cp);
take_p(cp);
}
Compile command / flags:
podman exec defuzz-toolchain-daemon /opt/gcc16/bin/x86_64-linux-gnu-gcc -O2
-Wall -Wextra -S -o /dev/null /tmp/t32.c
Observed output (GCC 16.2.0):
<stdin>: In function 'test':
<stdin>:13:12: warning: passing argument 1 of 'take_p' discards 'const'
qualifier from pointer target type [-Wdiscarded-array-qualifiers]
<stdin>:9:21: note: expected 'arr_ptr' {aka 'int (*)[4]'} but argument is of
type 'carr_ptr' {aka 'const int (*)[4]'}
(Whereas 'take_u(cp)' on line 12 compiles with ZERO warning or error, silently
converting 'const int (*)[4]' into mutable 'int (*)[4]'!)
Expected behavior:
Passing 'const int (*)[4]' or 'volatile int (*)[4]' to a transparent_union
whose only pointer member is 'int (*)[4]' must emit a -Wdiscarded-qualifiers /
-Wincompatible-pointer-types pedwarn just like passing it directly to 'int
(*)[4]'.
Root cause
----------
Source location: gcc/c/c-typeck.cc:8738-8780 (convert_for_assignment)
In convert_for_assignment (gcc/c/c-typeck.cc:8738-8780), when passing a pointer
argument to a __attribute__((transparent_union)) parameter, GCC checks whether
qualifiers were discarded on a marginally matching union member: lines
8758-8761 strip array types via 'while (TREE_CODE (ttl) == ARRAY_TYPE) ttl =
TREE_TYPE (ttl); while (TREE_CODE (ttr) == ARRAY_TYPE) ttr = TREE_TYPE (ttr);'
and then compare 'TYPE_QUALS_NO_ADDR_SPACE (ttr) & ~TYPE_QUALS_NO_ADDR_SPACE
(ttl)'. Wait—when the union member is 'int (*)[4]' and the passed argument is
'const int (*)[4]', why does it fail to warn? Because earlier in the
transparent_union member search loop (lines 8705-8730), comp_target_types
(memb_type, rhstype) returns 0 for 'int (*)[4]' vs 'const int (*)[4]' (since
array element qualifiers are on the element type, and TREE_TYPE(ttl) is an
ARRAY_TYPE whose own TYPE_QUALS may differ or marginal_memb selection at line
8722 tests TYPE_MAIN_VARIANT(TREE_TYPE(ttl)) ==
TYPE_MAIN_VARIANT(TREE_TYPE(ttr)), which is false for ARRAY_TYPE of qualified
vs unqualified elements because an array type's TYPE_MAIN_VARIANT in C retains
or differs in element qualification)!
Impact:
Silently strips const and volatile qualifiers from pointers to arrays when
passed through a transparent_union parameter.
Suggested Fix
-------------
In gcc/c/c-typeck.cc:8738-8780 (convert_for_assignment), check c_type_quals on
the stripped array element types (or use comp_target_types qualification check)
and emit OPT_Wdiscarded_qualifiers when qualifiers on array elements are
discarded.