It's a test-case reduced from qemu: $ cat intelvf.i struct a { int b; int d[0]; } e; long int f; int h(); void j(struct a *l) { int i; for (i = 0; l; i++) l->d[i] = 0; } int m() { int c = ({ unsigned *k = (unsigned *)f; int g = h(k); g; }); if (c) j(&e); return 0; } $ gcc -c -O2 -Werror=array-bounds intelvf.i intelvf.i: In function ‘m’: intelvf.i:11:9: error: array subscript i is outside array bounds of ‘int[0]’ [-Werror=array-bounds] 11 | l->d[i] = 0; | ~~~~^~~ intelvf.i:3:7: note: while referencing ‘d’ 3 | int d[0]; | ^ intelvf.i:4:3: note: defined here ‘e’ 4 | } e; | ^ cc1: some warnings being treated as errors
But the warning is correct? If the loop is ever entered the access is out of bounds.
So it's at most a missed optimization that (for the qemu case - the reduced cannot be optimized) we do not eliminated the loop as never reached?
Created attachment 48436 [details] Original test-case $ gcc -c -O2 -Werror=array-bounds intelvf2.i -m32 intelvf2.i: In function ‘intelvf_mbox_poll’: intelvf2.i:2658:13: error: array subscript i is outside array bounds of ‘uint32_t[0]’ {aka ‘unsigned int[]’} [-Werror=array-bounds] 2658 | msg->dword[i] = ( { volatile uint32_t *_io_addr = ( ( volatile uint32_t * ) ( intptr_t ) (intel->regs + intel->mbox.mem + ( i * sizeof ( msg->dword[0] ) )) ); uint32_t _data = readl ( _io_addr ); do { if ( ( 0 & 8 ) ) { dbg_printf ( "[" "MEM" " %08lx] => %0" "8" "llx\n", io_to_bus ( _io_addr ), ( unsigned long long ) _data ); } } while ( 0 ); _data; } ) | ~~~~~~~~~~^~~ intelvf2.i:2615:11: note: while referencing ‘dword’ 2615 | uint32_t dword[0]; | ^~~~~ intelvf2.i:2666:20: note: defined here ‘msg’ 2666 | union intelvf_msg msg; | ^~~ intelvf2.i: In function ‘intelvf_mbox_wait’: intelvf2.i:2658:13: error: array subscript i is outside array bounds of ‘uint32_t[0]’ {aka ‘unsigned int[]’} [-Werror=array-bounds] 2658 | msg->dword[i] = ( { volatile uint32_t *_io_addr = ( ( volatile uint32_t * ) ( intptr_t ) (intel->regs + intel->mbox.mem + ( i * sizeof ( msg->dword[0] ) )) ); uint32_t _data = readl ( _io_addr ); do { if ( ( 0 & 8 ) ) { dbg_printf ( "[" "MEM" " %08lx] => %0" "8" "llx\n", io_to_bus ( _io_addr ), ( unsigned long long ) _data ); } } while ( 0 ); _data; } ) | ~~~~~~~~~~^~~ intelvf2.i:2615:11: note: while referencing ‘dword’ 2615 | uint32_t dword[0]; | ^~~~~ intelvf2.i:2666:20: note: defined here ‘msg’ 2666 | union intelvf_msg msg; | ^~~ cc1: some warnings being treated as errors
When the array is a member of a declared object of a struct type (e in the small test case in comment #0) it has no elements and attempting to access any of them is invalid. In the original translation unit the array is a member of an object of a union type: union intelvf_msg. The object, msg, is defined in function intelvf_mbox_set_mtu(), and the array is used to alias other members. Presumably GCC supports this sort of aliasing as long as it's done through the union type (as mentioned in -fstrict-aliasing), so the warning could be relaxed not to trigger for them. Here's a test case that more closely corresponds to the translation unit, reproduces the warning, and shows that GCC doesn't make assumptions about the two arrays being distinct: $ cat pr94940_c4.c && gcc -O2 -S -Wall -fdump-tree-optimized=/dev/stdout pr94940_c4.c struct S { int a[2]; }; union U { struct S s; int a[0]; }; static void f (union U *p, int i, int j) { int t = p->s.a[j]; p->a[i] = 0; if (t == p->s.a[j]) // not eleminated __builtin_abort (); } void sink (void*); void g (int i, int j) { union U u = { .s = { { 1, 2 } } }; f (&u, i, j); sink (&u); } pr94940_c4.c: In function ‘g’: pr94940_c4.c:11:7: warning: array subscript i is outside array bounds of ‘int[0]’ [-Warray-bounds] 11 | p->a[i] = 0; | ~~~~^~~ pr94940_c4.c:5:7: note: while referencing ‘a’ 5 | int a[0]; | ^ pr94940_c4.c:20:11: note: defined here ‘u’ 20 | union U u = { .s = { { 1, 2 } } }; | ^ ;; Function g (g, funcdef_no=1, decl_uid=1944, cgraph_uid=2, symbol_order=1) g (int i, int j) { int t; union U u; int _10; <bb 2> [local count: 1073741824]: MEM[(union U *)&u] = 8589934593; t_9 = u.s.a[j_5(D)]; u.a[i_4(D)] = 0; _10 = u.s.a[j_5(D)]; if (t_9 == _10) goto <bb 3>; [0.00%] else goto <bb 4>; [100.00%] <bb 3> [count: 0]: __builtin_abort (); <bb 4> [local count: 1073741824]: sink (&u); u ={v} {CLOBBER}; return; }
Thank you for the analysis, I'm gonna report that to qemu guys.
I think array_at_struct_end_p conservatively returns true for p->a[i] though. Indeed all calls to the function return the correct value. So is it somebody invented a "more clever" variant of said check? Or do we simply fail to call it? Ok, more clever it is: bool vrp_prop::check_array_ref (location_t location, tree ref, bool ignore_off_by_one) { ... if (!up_bound || TREE_CODE (up_bound) != INTEGER_CST || (warn_array_bounds < 2 && array_at_struct_end_p (ref))) { ... const bool compref = TREE_CODE (arg) == COMPONENT_REF; if (compref) { /* Try to determine the size of the trailing array from its initializer (if it has one). */ if (tree refsize = component_ref_size (arg, &interior_zero_len)) if (TREE_CODE (refsize) == INTEGER_CST) maxbound = refsize; and refsize == 0 here. Parsing an initializer for a union is tricky but this one clearly gets the wrong answer from that.
GCC 10.1 has been released.
Patch: https://gcc.gnu.org/pipermail/gcc-patches/2020-May/545443.html
The master branch has been updated by Martin Sebor <msebor@gcc.gnu.org>: https://gcc.gnu.org/g:3956244c58acceebf1ef2cf9a63e99f0f82abcb7 commit r11-469-g3956244c58acceebf1ef2cf9a63e99f0f82abcb7 Author: Martin Sebor <msebor@redhat.com> Date: Mon May 18 15:07:48 2020 -0600 PR middle-end/94940 - spurious -Warray-bounds for a zero length array member of union gcc/testsuite/ChangeLog: PR middle-end/94940 * gcc.dg/Warray-bounds-61.c: New test. gcc/ChangeLog: PR middle-end/94940 * tree-vrp.c (vrp_prop::check_mem_ref): Remove unreachable code. * tree.c (component_ref_size): Correct the handling or array members of unions. Drop a pointless test. Rename a local variable.
The releases/gcc-10 branch has been updated by Martin Sebor <msebor@gcc.gnu.org>: https://gcc.gnu.org/g:ff61c5bd1490a006c00bed9259dc672351eb217a commit r10-8154-gff61c5bd1490a006c00bed9259dc672351eb217a Author: Martin Sebor <msebor@redhat.com> Date: Mon May 18 16:04:56 2020 -0600 PR middle-end/94940 - spurious -Warray-bounds for a zero length array member of union gcc/testsuite/ChangeLog: PR middle-end/94940 * gcc.dg/Warray-bounds-61.c: New test. gcc/ChangeLog: PR middle-end/94940 * tree-vrp.c (vrp_prop::check_mem_ref): Remove unreachable code. * tree.c (component_ref_size): Correct the handling or array members of unions. Drop a pointless test. Rename a local variable.
Fixed for GCC 11 and 10.2.