Bug 94940 - [10 Regression] spurious -Warray-bounds for a zero length array member of union since r10-4300-g49fb45c81f4ac068
Summary: [10 Regression] spurious -Warray-bounds for a zero length array member of uni...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 10.0
: P3 normal
Target Milestone: 10.2
Assignee: Martin Sebor
URL:
Keywords: diagnostic, patch
Depends on:
Blocks: Warray-bounds
  Show dependency treegraph
 
Reported: 2020-05-04 07:42 UTC by Martin Liška
Modified: 2020-05-18 22:06 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work: 9.3.0
Known to fail: 10.0
Last reconfirmed: 2020-05-04 00:00:00


Attachments
Original test-case (12.23 KB, text/plain)
2020-05-04 08:17 UTC, Martin Liška
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Liška 2020-05-04 07:42:32 UTC
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
Comment 1 Richard Biener 2020-05-04 07:55:05 UTC
But the warning is correct?  If the loop is ever entered the access is out of bounds.
Comment 2 Richard Biener 2020-05-04 07:56:05 UTC
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?
Comment 3 Martin Liška 2020-05-04 08:17:45 UTC
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
Comment 4 Martin Sebor 2020-05-04 15:53:37 UTC
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;

}
Comment 5 Martin Liška 2020-05-04 18:19:27 UTC
Thank you for the analysis, I'm gonna report that to qemu guys.
Comment 6 Richard Biener 2020-05-05 11:33:32 UTC
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.
Comment 7 Jakub Jelinek 2020-05-07 11:56:19 UTC
GCC 10.1 has been released.
Comment 9 GCC Commits 2020-05-18 21:10:02 UTC
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.
Comment 10 GCC Commits 2020-05-18 22:05:32 UTC
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.
Comment 11 Martin Sebor 2020-05-18 22:06:27 UTC
Fixed for GCC 11 and 10.2.