Std::variant exception specification

I was looking through bug reports for some issue unrelated to the LLVM project. I happened to stumble upon this bug report which mentions libc++'s std::variant as having the “wrong noexcept-specifier”. Is the wrong-ness a bug?

The difference is that GCC defines the std::variant default constructor as defaulted, but libc++ defines it as:

constexpr variant()
noexcept(is_nothrow_default_constructible_v<variant_alternative<0, Types…>)

That means the constructor isn’t deleted for libc++, it just has the wrong noexcept-specifier.

cc @ldionne @EricWF

I’m not exactly sure what the context is, but to me the exception specification looks correct according to [variant.variant].

This isn’t a runtime problem, but a language problem.

The issue is that Clang will get the wrong answer for is_nothrow_default_constructible_v in the example case, because it’s asking the question of a nested class, from within the outer class’s body. At that point, the inner class hasn’t yet had its data member initializers evaluated, so we return false – but it should’ve been true.

This is a can of worms. At the end of the GCC bugs you can see the proposal to make __is_constructible error out, instead of returning the wrong answer. But that broke real-world codebases, and was reverted. The next, pending, proposal is to also tentatively attempt to parse the initializers at the end of the nested class, so the error will trigger less. (Actually making that change would require modifying the C++ standard, though.)