Issue 74282
Summary [Clang] Possible miscompilation of the destructor of a class with a virtual base class
Labels clang
Assignees
Reporter frederick-vs-ja
    The following program attempts to destroy a **mediate base class subobject whose type has a virtual base class**.

(Skipping the side effects of a destructor is made well-defined via [CWG2523](https://cplusplus.github.io/CWG/issues/2523.html).)

```C++
#include <cstdio>

struct B {
    B() { std::printf("B::B %p\n", static_cast<const void*>(this)); }
    B(const B&) = delete;
    B &operator=(const B&) = delete;
    ~B() { std::printf("B::~B %p\n", static_cast<const void*>(this)); }
};

struct DX : virtual B {
 DX() { std::printf("DX::DX %p\n", static_cast<const void*>(this)); }
 DX(const DX&) = delete;
    DX &operator=(const DX&) = delete;
 ~DX() { std::printf("DX::~DX %p\n", static_cast<const void*>(this)); }
};

struct DY : virtual B {
    DY() { std::printf("DY::DY %p\n", static_cast<const void*>(this)); }
    DY(const DY&) = delete;
    DY &operator=(const DY&) = delete;
    ~DY() { std::printf("DY::~DY %p\n", static_cast<const void*>(this)); }
};

struct DZ : DX, DY {
    DZ() { std::printf("DZ::DZ %p\n", static_cast<const void*>(this)); }
    DZ(const DZ&) = delete;
    DZ &operator=(const DZ&) = delete;
    ~DZ() { std::printf("DZ::~DZ %p\n", static_cast<const void*>(this)); }
};

template<class T>
union NoDestroy {
    T val;
 NoDestroy() : val() {}
    NoDestroy(const NoDestroy&) = delete;
 NoDestroy &operator=(const NoDestroy&) = delete;
    ~NoDestroy() {}
};

int main()
{
    NoDestroy<DZ> d{};
 static_cast<DY&>(d.val).~DY();
}
```

When using Clang (same for GCC), the addresses printed after `B::B` and `B::~B` are always different ([Godbolt link](https://godbolt.org/z/q4sYoWhTx)). Clang (and GCC) seems to emit miscalculation of the offset of the `B` base class subobject in `DY::~DY` (possibly due to early setting vptr), which is likely wrong when the `DY` object is not a most derived object.

MSVC seems to calculate the offset correctly.

There doesn't seem anything in the C++ standard indicating that "destructing a mediate base class subobject whose type has a virtual base class" is undefined behavior. However, [[class.dtor]/13](https://eel.is/c++draft/class.dtor#13) says
> [...] and, if `X` is the most derived class ([class.base.init]), its destructor calls the destructors for `X`'s virtual base classes. [...]

which looks defective as it possibly requires additional metadata to tell whether a class object whose type has a virtual base class is a most derived object.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to