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

Andrew Pinski <pinskia at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
         Resolution|---                         |INVALID
             Status|UNCONFIRMED                 |RESOLVED

--- Comment #3 from Andrew Pinski <pinskia at gcc dot gnu.org> ---
There is a copy of Tester involved before F's constructor fully happens so we
call ~FBase (on the unwind) which then calls the bad (vtable) deconstructor.
Why it happens at -O0 only is just by accident. At -O1 we do some
de-virtualization which allows the (right) deconstructor to be called. Note
GCC's VTable::~VTable sets the vtable to be VTable's vtable which exposes the
bug. This only happens at -O0 because at -O1 and above it is considered a dead
store.

This is definitely undefined code without the following change because you
might get ~VTable being called twice.



Note if you change VTable to:
```
class VTable
{
  public:
        virtual void func(int x) = 0; // #1 this line affects behavior
        virtual ~VTable() {__builtin_printf("~vtable\n");}
};
```

You will see there is 2 calls to ~VTable which is incorrect.



Changing FBase/F to this:
```

struct FBase
{
        static constexpr int S = sizeof(void*) + sizeof(VTable);
        bool constructed = false;
        std::byte _data[S];

        ~FBase() { if (!constructed) return;
static_cast<VTable*>(static_cast<void*>(&_data[0]))->~VTable(); } // #2 this
line affects behavior
};

struct F : FBase
{
        template <typename T>
        F(T&& t) {
                static_assert(sizeof(Handler<std::decay_t<T>>) <= S);
                ::new (&_data[0]) Handler<std::decay_t<T>>(std::forward<T>(t));
                constructed = true;
        }

        ~F() = default;
};
```

Fixes the issue and only one call to ~VTable happens.

Reply via email to