-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Please see the following:
#include <variant>
struct X {
constexpr ~X() {}
};
struct Y {
X x;
};
struct Z1 {
std::variant<Y, int> z;
};
struct Z2 {
std::variant<int, Y> z;
};
constexpr auto z11 = Z1{0}.z.index();
constexpr auto z12 = Z1{Y{}}.z.index();
constexpr auto z21 = Z2{0}.z.index();
constexpr auto z22 = Z2{Y{}}.z.index();cl compiles the above just fine under /std:c++20 and /std:c++latest, and I believe this should work fine under C++20 and above. Recent versions of gcc and clang both also work fine with their default standard library: https://godbolt.org/z/YhfW8M9b6.
But clang-cl (with MS STL) rejects this code, saying that z11 and z12 cannot be initialized by constant expressions:
C:\test>clang-cl /EHsc /std:c++20 .\repro.cpp
.\repro.cpp(19,16): error: constexpr variable 'z11' must be initialized by a constant expression
19 | constexpr auto z11 = Z1{0}.z.index();
| ^ ~~~~~~~~~~~~~~~
.\repro.cpp(19,22): note: subexpression not valid in a constant expression
19 | constexpr auto z11 = Z1{0}.z.index();
| ^
.\repro.cpp(19,22): note: in call to 'Z1{0}.z.~_Variant_storage_()'
.\repro.cpp(19,22): note: in call to 'Z1{0}.z.~_Variant_base()'
.\repro.cpp(19,22): note: in call to 'Z1{0}.z.~_Variant_destroy_layer_()'
.\repro.cpp(19,22): note: in call to 'Z1{0}.z.~_Non_trivial_copy()'
.\repro.cpp(19,22): note: in call to 'Z1{0}.z.~_Non_trivial_move()'
.\repro.cpp(19,22): note: in call to 'Z1{0}.z.~_Non_trivial_copy_assign()'
.\repro.cpp(19,22): note: in call to 'Z1{0}.z.~_Non_trivial_move_assign()'
.\repro.cpp(19,22): note: in call to 'Z1{0}.z.~variant()'
.\repro.cpp(19,22): note: in call to 'Z1{0}.~Z1()'
.\repro.cpp(20,16): error: constexpr variable 'z12' must be initialized by a constant expression
20 | constexpr auto z12 = Z1{Y{}}.z.index();
| ^ ~~~~~~~~~~~~~~~~~
.\repro.cpp(20,22): note: subexpression not valid in a constant expression
20 | constexpr auto z12 = Z1{Y{}}.z.index();
| ^
.\repro.cpp(20,22): note: in call to 'Z1{Y{}}.z.~_Variant_storage_()'
.\repro.cpp(20,22): note: in call to 'Z1{Y{}}.z.~_Variant_base()'
.\repro.cpp(20,22): note: in call to 'Z1{Y{}}.z.~_Variant_destroy_layer_()'
.\repro.cpp(20,22): note: in call to 'Z1{Y{}}.z.~_Non_trivial_copy()'
.\repro.cpp(20,22): note: in call to 'Z1{Y{}}.z.~_Non_trivial_move()'
.\repro.cpp(20,22): note: in call to 'Z1{Y{}}.z.~_Non_trivial_copy_assign()'
.\repro.cpp(20,22): note: in call to 'Z1{Y{}}.z.~_Non_trivial_move_assign()'
.\repro.cpp(20,22): note: in call to 'Z1{Y{}}.z.~variant()'
.\repro.cpp(20,22): note: in call to 'Z1{Y{}}.~Z1()'
2 errors generated.
STL version
Microsoft Visual Studio Community 2022
Version 17.11.0
But I believe the issue persists in the most recent version too.
Additional context
In my real code, X is std::vector. std::variant works just fine if std::vector is directly in the list of alternatives, but it fails if one of the alternatives contains std::vector as a subobject.
If I comment out z11 and z12, then now it compiles fine. Or if I explicitly default the destructor of Y, then it now accepts the code.
Another funny thing is that this issue appears only if std::variant contains both a trivially destructible type and a non-trivially destructible type. Like, if I replace int by X then now it works fine.
I believe this is a clang bug not MS STL's fault, but will it make sense if MS STL can provide a workaround? Even if you don't want to fix this from your side, I thought it would be helpful for you to be aware of the issue.
Not sure if this is related to one of the issues you are tracking: llvm/llvm-project#59854.
Note: I did not make a bug report to LLVM because I'm not sure what exactly is causing this. As I said clang works with both libstdc++ and libc++ just fine (only with the most recent version though; it seems older versions still use placement new so it doesn't work but for a totally different reason), but I couldn't spot what exactly is different between libc++/libstdc++ and MS STL.