Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<variant>: clang-cl considers std::variant not constexpr for certain situations when non-trivially destructible types are involved #4901

Closed
jk-jeon opened this issue Aug 21, 2024 · 1 comment · Fixed by #4903
Labels
bug Something isn't working fixed Something works now, yay!

Comments

@jk-jeon
Copy link

jk-jeon commented Aug 21, 2024

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.

jk-jeon added a commit to jk-jeon/idiv that referenced this issue Aug 21, 2024
Replace with std::variant. Turns out, the reason why it's not constexpr is a clang bug (microsoft/STL#4901)
@frederick-vs-ja
Copy link
Contributor

Not sure if this is related to one of the issues you are tracking: llvm/llvm-project#59854.

I think this is right, and one workaround posted in that issue works for variant.

@CaseyCarter CaseyCarter added the bug Something isn't working label Aug 21, 2024
@StephanTLavavej StephanTLavavej added the fixed Something works now, yay! label Aug 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixed Something works now, yay!
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants