https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128110

            Bug ID: 128110
           Summary: Bogus/misleading Warray-bounds warning due to
                    speculative devirtualization
           Product: gcc
           Version: 16.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: gccbugs at mail dot mesa42.com
  Target Milestone: ---

This is probably related to #56456 but I'm not sure if any of the bugs there is
this exact same case. Minimal reproducer:

----
void use(void *);

struct Base
{
    virtual ~Base();
    char data[128];
};

struct Derived : Base
{
    ~Derived() { use(p); }
    void *p;
};

struct Owner
{
    ~Owner() { delete p; }
    Base *p;
};

void f()
{
    Owner o{new Base};
}
----

Compiling with g++ 16.2.1 on Fedora 44:

----
$ g++ -Wall -O2 -c tmp.cc
In destructor ‘virtual Derived::~Derived()’,
    inlined from ‘virtual Derived::~Derived()’ at tmp.cc:11:22,
    inlined from ‘Owner::~Owner()’ at tmp.cc:17:19,
    inlined from ‘void f()’ at tmp.cc:24:1:
tmp.cc:11:17: warning: array subscript ‘Derived[0]’ is partly outside array
bounds of ‘unsigned char [136]’ [-Warray-bounds=]
   11 | ~Derived() { use(p); }
      |              ~~~^~~
tmp.cc: In function ‘void f()’:
tmp.cc:23:13: note: object of size 136 allocated by ‘operator new’
   23 | Owner o{new Base};
      |             ^~~~
In destructor ‘virtual Derived::~Derived()’,
    inlined from ‘virtual Derived::~Derived()’ at tmp.cc:11:22,
    inlined from ‘Owner::~Owner()’ at tmp.cc:17:19,
    inlined from ‘void f()’ at tmp.cc:24:1:
tmp.cc:11:22: warning: array subscript ‘Derived[0]’ is partly outside array
bounds of ‘unsigned char [136]’ [-Warray-bounds=]
   11 | ~Derived() { use(p); }
      |                      ^
tmp.cc: In function ‘void f()’:
tmp.cc:23:13: note: object of size 136 allocated by ‘operator new’
   23 | Owner o{new Base};
      |             ^~~~
----

The warning goes away if you pass -fno-devirtualize-speculatively.

I am not familiar with the GCC internals, so the following information may not
be accurate, it's just what Claude reported to me:

----
What's going on: the -fdump-tree-optimized output for f() shows:

_4 = operator new (136);
MEM[(struct Base *)_4]._vptr.Base = &_ZTV4Base + 16B;
if (__dt_del == __dt_del) // speculative devirt guard, not folded
{ ... _15 = MEM[(struct Derived *)_4].p; use (_15); ... operator delete (_4,
144); }
else
Base::~Base (_4);

* GCC speculatively inlines Derived's deleting destructor behind a guard that
compares the vtable slot.

* The vptr was stored just before as _ZTV4Base, so the guard is always false.

* GCC can't fold the guard because Base's vtable is defined in another TU
(~Base is out of line, which makes it the key function).

* -Warray-bounds then runs on the dead Derived branch and reports reading p at
offset 136 of a 136-byte object.

* Two things make the warning go away: defining ~Base inline, which emits the
vtable in this TU, or calling delete directly instead of through the Owner
wrapper, where GCC seems to devirtualize earlier because it knows the type from
the new.
----

Reply via email to