3

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++?

8
  • If you remove the parentheses, does the valgrind error go away? Commented Jul 26, 2019 at 21:03
  • Yes, it does. Other changes that make it go away: switching any_of to all_of, removing any of the std::cout << lines, changing the boolean expressions passed into bs, or removing the f(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. Commented Jul 26, 2019 at 21:07
  • 1
    It looks like a bug or false positive to me. I wonder if what is going on is {a == 0, a != 0 || b == 0} makes an initializer_list, then std::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 leaving bs with garbage. Commented Jul 26, 2019 at 21:11
  • That is entirely possible. Do you have any idea how we could find it out? I'm not familiar with the internals of Clang... Commented Jul 26, 2019 at 21:12
  • Checking the assembly could lend some insight. Unfortunately I'm not great with assembly so I can't really help there. Commented Jul 26, 2019 at 21:12

1 Answer 1

1

I think we ran into an issue that is already reported thrice in the LLVM bug tracker:

And is most likely related to (or might even be the same as) this SO question:

The current standing of the LLVM people on this, is: "... we ought to issue a warning on this." Well, yes, I agree.

Oh, C++... :)

Sign up to request clarification or add additional context in comments.

1 Comment

It would help if you explained the issue, rather than referencing external sites.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.