<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/74282>74282</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[Clang] Possible miscompilation of the destructor of a class with a virtual base class
</td>
</tr>
<tr>
<th>Labels</th>
<td>
clang
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
frederick-vs-ja
</td>
</tr>
</table>
<pre>
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.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJysV0tv6rgX_zRmc9QoOOW1YFGg9L_5j0a61fSxGZn4hPjWibm2A8Ms-OyjY4cS5tLbSoNEC3Fsn9_j2D4Wzql1jThlgxkbLHqi8aWx08KiRKvyt5utu_kueisj99PHEqEwWpudqtewsWZtRQXCe6w23oE3INF5a_YggPE7xu8qlEp4hJVwCLkWzoFrVmb1HXMPu9I4BL_fIJTCgYCtsr4RutM7zpKwdMHSu_Y_H397U5sNQfAlglMSAYsCc-_AFCAiiCb3xoJyUAmJsEOtbyQWqkYJWyWADWbzpwc-4BkbLBgfl95vHMvuGF8yvsw3unH0l6yVL5tVogzjy_nTA-NL5VyDjvElDU5KX2nGJwnjkzOQwzR-5ozP6NNCz1Sd60YisGyeOy-VYdl9d2BEDjNgo3YQAMCM8THjE2oE5yXhzO42VtW-CG_4LDbNgPHBhg3mNeOc8Tk4L7zK_8yF8xTR1M7D1ihJumb3jI99qRxh5xOWUdDFedA4Ysb4MITPFiBRo0eWddEB40OzQSu8sSxbfHXc4au0DlfjRT-OEM4EXzwDy-7eM7Cr_-L5M5iL59hGk1xJ_xA09qefv5IxRL2o_y9HHr7O63A9Yh8b8PKhAcTx5VOsL60HL9dcAyFuK-bLJza8fGjDJyMPX2d3uB69j514DU5QeswDqzMjXj-F-toa8XpVI15Pcr5-YsTrh0Z8MvLwdXaH69H72Qg6T7UgcPN4Zj6-nxJNrUwNv5lFe9CemfMIW6FPjN57HUnR-hL6RPFM4W7vCL7T8qFqHSSXNf_KJCT9T2A78H5WSNUeKqHq2PnYa3aJTTYnz-5BtjO-Rz33Ky7S4JNMgkqT5H1hnqK_Qzoe8F1UTyXW0DiqTOZa1GtgfOxERUWThYf5PJg_D1WLkNKic-ggZBdKEIVHC2yYHs9yNkxB1LLTdIhtFkHondg7kKoo0GLtKRIbzB6MXBntQav67VJds47vE2PXjC__Znz549a9mKfy8a-YmMkJOIWOmMEhVqG8w0p5qJTLhc4bLTzloikCIVMUDv3xKWAeppfrPlXT-86WRj0ZH2-Mc2ql9yAbDNGE1Xtw6D0put142wq4K1VeUm2n1RvqPeysqdewI_Xb4HHOYzwHtfEgoDLOA5W1W5Tty7PS8v_f_pifyB5JYpdfbqzF3Ov92cDHEi2CNOhqxkc-zAGi3vuSkKsIqy0FKe9qKawEVUuVCx8LWUEe8mPpSm0C_nv1zIl8Ux8r3xWWYquMTeB_ZodbtKRmKPxnYUAiafVS4iz7FwtjRJ3QJrbMIxlpReHp8TSaZ_0sJI3Yu1aj7J6CJEnCBgtKaYqqCvLpmWxSLuhz5k5kG7M6Tk60ElUrH3CFPFDedYv9XGgdpzo1urD22kiMj9wFmdAlJ3xdW2OeaWPeKA7dMNQWQThQHt6T1eKPRll0tKQVrQihoUIvpPCC0sij1pSbvkQLoiX2dQ9JncuZ25PTTE6yiejhtD9K-8NROhqPeuV00sdxmhUDMRIjnhf5eMzlSAyHoo-ZyKXsqSlPedbn6W067E9uR8lkNExT2R-g4P3h7Uqw2xQroXSi9bai3aIXbj3T0S0f854WK9QuXBg5z2m_oBNwsOjZKfW_WTVrx25TrZx3pxm88jrcMsMOQ5nwe5QQw45iqo0631E6zoZrXZRjp3x5UaheY_X0X9tdvLzlpmJ8SUDar5uNNSRh9zYXqP0TAAD__5umcds">