Issue |
129803
|
Summary |
[libc++] `std::variant` introduces padding if a variant member contains a variant
|
Labels |
libc++
|
Assignees |
|
Reporter |
zygoloid
|
[Testcase](https://godbolt.org/z/r7WEoEzTh):
```c++
#include <variant>
struct A {
int x;
};
struct B {
int y;
int z;
};
static_assert(sizeof(B) == 8);
static_assert(sizeof(std::variant<A, B>) == 12);
struct C {
std::variant<int> v;
};
static_assert(sizeof(C) == 8);
static_assert(sizeof(std::variant<A, C>) == 16);
```
`variant<A, C>` ought to be only 12 bytes, but is actually 16 bytes. The reason for this is that `std::variant` derives from `__sfinae_ctor_base<...>` and `__sfinae_assign_base<...>`, and those base classes are the *same* for `std::variant<A, C>` and for `std::variant<int>`.
This prevents the variant's first field (the `__union`) from being put at offset 0 within the variant, because that would mean we have two different `__sfinae_ctor_base<...>` subobjects at the same offset within the same object, and the C++ language rules don't permit that struct layout.
The solution is to change `variant` so that it doesn't derive from a class that is, or can be, independent of the `variant`'s template arguments. Perhaps either change the `__sfinae_...` types to use CRTP (even though they don't care what the derived class is), or remove them and rely on getting the special members' properties from the `__impl` type instead.
Of course, fixing this will break `std::variant`'s ABI, so it'd need to be done only in the unstable ABI. :(
`std::optional` appears to use the same implementation strategy, so I would imagine it has the same deficiency (assuming it puts the `T` first, not the `bool`), but I've not checked. And it looks like `std::tuple` may also suffer from the same issue.
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs