When the following (reduced) code is compiled with Clang 8.0.0, with -O1 optimization level (or above), Valgrind detects a Conditional jump or move depends on uninitialised value(s) error in the resulting binary. No warnings are printed, even with the -Wall -pedantic arguments enabled. Adding -fsanitize=undefined just makes the issue go away, but doesn't report anything useful. And it is most likely not a false positive (from Valgrind), since this reduced version originated from a real codebase, where it caused a noticeable error.
When compiled with GCC 9.1.1, there is no UB.
#include <algorithm>
#include <iostream>
class X {
public:
int a, b;
X(int a) : a(a), b(0) {}
bool h() {
std::initializer_list<bool> bs({a == 0, a != 0 || b == 0});
return std::any_of(bs.begin(), bs.end(), [](bool t) { return t; });
}
};
void f(X g) {
std::cout << "";
}
int main(int argc, const char **) {
X i(argc);
f(i);
if (i.h())
std::cout << "";
}
Sure, the std::initializer_list<bool> bs({a == 0, a != 0 || b == 0}); line is a bit weird, and the outer parentheses are unnecessary. But according to cppinsights.io, this just adds a copy constructor call, so it should be fine? (See the same code in Compiler Explorer.)
Also, note that all of the seemingly useless details in the code are necessary to trigger the issue.
So, even if in most cases, the answer to this question is "no": Is this a bug in Clang? Or are we just missing something about the quirkiness of modern C++?
any_oftoall_of, removing any of thestd::cout <<lines, changing the boolean expressions passed intobs, or removing thef(i)call. It is really elusive. So we could leave it at that, and mark it as "solved", but if we can report an issue to Clang, might as well get to the end of it.{a == 0, a != 0 || b == 0}makes aninitializer_list, thenstd::initializer_list<bool> bs(...);makes a copy, but instead of copying the list, it copies a pointer to the source, which is destroyed at the end of the full expression leavingbswith garbage.