Home > front end >  clang constexpr compile error in lambda, gcc and msvc ok. clang bug?
clang constexpr compile error in lambda, gcc and msvc ok. clang bug?

Time:09-12

I ran across a compile error in clang with this code. I don't see any problem with it and it works in msvc and gcc. Am I missing something or is it a clang bug?

#include <iostream>
#include <string>
#include <type_traits>

constexpr bool do_tests()
{
    // prints error message at runtime if test is false
    auto verify = [](bool test, std::string message = "error") {
        if (!std::is_constant_evaluated() && !test)
            std::cout << message << '\n';
        return test;
    };
    return verify(1 == 1, "test");
};


int main()
{
    constexpr bool b = do_tests();
    std::cout << b << '\n';
}

compiler explorer

Inexplicable error message from clang:

basic_string.h:356:10: note: assignment to member '_M_local_buf' of union with no active member is not allowed in a constant expression

CodePudding user response:

There is no problem with the code and Clang also compiles it correctly if you use libc (-stdlib=libc ). Compiler explorer by default uses libstdc for Clang (-stdlib=libstdc ). There simply seems to be a compatibility issue between the two. My guess is that libstdc is implementing the constexpr string in a way that is technically not allowed in constant expressions, but GCC tends to be less strict in that regard than Clang is, so that Clang fails with the implementation while GCC doesn't have a problem with it.


To be more precise in libstdc the implementation of std::string contains an anonymous union with one member being a _CharT array named _M_local_buf. Then libstdc is doing the following if the evaluation happens in a constant expression evaluation (from https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/bits/basic_string.h#L355)

for (_CharT& __c : _M_local_buf)
    __c = _CharT();

in order to set the union member _M_local_buf to active and zero it.

The problem is that prior to this loop no member of the union has been set to active and setting a member (subobject) through a reference, rather than directly by name or through member access expressions, is technically not specified in the standard to set a member to active. As a consequence that technically has undefined behavior, which is why Clang (correctly) doesn't want to accept it as constant expression. If the member was set as active beforehand, e.g. with an extra line _M_local_buf[0] = _CharT(); it would be fine.

Now of course it seems a bit silly that setting _M_local_buf[0] directly is fine, but setting it through a reference __c isn't, which is probably why GCC still accepts that as a constant expression and I guess there is probably a CWG issue about this.


There is a libstdc bug report relating to a very similar issue here, but that is already supposed to be fixed for a while.

It seems that the commit https://github.com/gcc-mirror/gcc/commit/98a0d72a610a87e8e383d366e50253ddcc9a51dd has (re-)introduced the particular issue you are seeing here. Before the commit the member was set active correctly. Might be worth it to open an issue about this, although as mentioned above Clang is being very pedantic here and as the linked bug report also says, the standard should probably be changed to allow this.

  • Related