New things in clang land (5.0.0)
productive-cpp.com
productive-cpp.com
How do you know what other people are using? I personally use the clang static analyzer all the time. Also it seems like you think these options are mutually exclusive they are not.
Everybody uses -Wall, some -Wextra and even -pedantic, a few asan or ubsan, nobody uses clang specific analyze.
That's a pretty bold claim. I use it for all my C projects, FreeBSD use's it[1], Chromium uses it[2]. It seems your basing this whole claim on personal anecdotes which by definition is unreliable.
[1]: https://chromium.googlesource.com/chromium/src/+/lkcr/docs/c...
[2]: https://github.com/freebsd/freebsd-ci/tree/master/scan-build
See Herb Sutter's talk at CppCon 2015. At a given moment he asks the audience if they use any kind of static analysis tools.
About 1% of the audience said they were using one.
Which is why everything that requires external tooling, instead of being part of the language is easily ignored.
It will be, if you use clang-tidy (or clang's static analyzer):
$ clang-tidy main.cpp -- -std=c++14
1 warning generated.
main.cpp:10:5: warning: Forming reference to null pointer [clang-analyzer-core.NonNullParamChecker]
f_unused_parameter(*i);
^
main.cpp:19:7: note: Passing null pointer value via 1st parameter 'i'
f(nullptr);
^
main.cpp:19:5: note: Calling 'f'
f(nullptr);
^
main.cpp:10:5: note: Forming reference to null pointer
f_unused_parameter(*i);
^That is, undefined behavior propagates not only forwards (if you dereference a pointer, from them on you can be sure that it's not null), but also backwards (if you dereference a null pointer, that's a contradiction since null pointers can't be dereferenced, and the contradiction can only be resolved if the statement is unreachable).
Does that happen when the compiler assumes you'll never take branches that lead to UB, and then finds a path through your branches that more or less does nothing? Or can it happen even if you're not branching at all?
Also, say my program is
int main() {
printf("hello world\n");
do_some_UB();
}
Is the compiler really allowed to produce a program that doesn't print?Yes and yes. How far it will propagate these backwards depends on the compiler.
> Is the compiler really allowed to produce a program that doesn't print?
Yes. A program which contains UB is illegal, there are no guarantees about any of its behaviour, including behaviour which precedes any possible actual invocation of UB, this is pointed rather clearly by the standard:
> However, if any such execution contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).
It's nice that UBSan will detect the problem at runtime, but that requires one to actually use UBSan in development and have all relevant code paths triggered in a UBSan-instrumented build. Which is unrealistic at least when 3rd party code (libraries etc.) comes into play that is not entirely under the application's control. So I would really favor optimizations that do not make a program less safe, or are only applied when can be proven that they do not cause any harm.
1. that's what -fsanitize=undefined is for
2. because the code you see here plainly written could also be the result of multiple rounds of previous optimisations (mostly inlining) which ends up leaving a bunch of redundant or nonsensical null checks
Thank you very much. Maybe the defenders of idiotic compiler behaviour can go now and hide under a rock.
As for the slide at https://youtu.be/yG1OZ69H_-o?t=508 Correct or incorrect programs are defined in terms of the language. With an underspecified language like C you get what you deserve: -O2 being a different language than -O0
I've seen enough of this video where incorrect programs are being equaled to undefined language behavior. I'll stop looking further.
A compiler that doesn't allow any undefined behavior in C or C++ code will reject most of the existing C or C++ code bases out there.
Idealistically speaking, it would be a good thing. But a C compiler that wouldn't compile the Linux kernel or Chromium or Firefox wouldn't be that great in practice.
Clang and LLVM are doing good work in detecting, reporting and rejecting undefined behavior but there's a lot of work to be done before rejecting such code is a realistic option.
A well-known modern example is Rust's borrow checker, there are cases which it will reject despite the code being correct (there are no actual issues in it) but the borrow checker can't be sure.
At a fundamental, "UB" simply means the compiler assumes it doesn't happen and carries on from there, yielding predictable behaviour to an UB is nonsensical. And incidentally while C is rife with UB most languages do have them to a certain extent e.g. sorting assumes your comparator is correct/stable, feeding a nonsensical comparator to a sorting routine is usually UB (neither the language nor the function define what'll happen), likewise hashmaps and hash/eq being coherent. Of course the consequences are also more significant in C given the language also is not memory-safe (and even then… "safe" Rust is memory safe but if you create an UB in unsafe code and leak it to safe Rust all bets are off) (and yes Rust does have UBs though not that many compared to C: https://doc.rust-lang.org/beta/reference/behavior-considered...)
Said undefined behaviour is also always only undefined within the scope of the C++ language, the machine defines the behaviour perfectly fine. I can, for instance, install a segmentation fault handler and catch places where I dereference a null pointer. In fact, the null page could even be mapped to some hardware device, making null pointers valid. Right now I don't have a way to communicate that fact to the compiler. Worse, we don't have tools that can tell us when code changes semantics from what a trivial -O0 compilation would produce.
So your problem is with C and C++, not Clang?
> I think if I had something like this accidentally in my code then I would want it to tell me that something's wrong instead of just giving bizarre results.
Then you misunderstand the problem.
Compilers don't seek out UB and then generate zany instruction sequences to spite the programmer. Instead, they assume there is no UB in the program, and optimise accordingly. If the code is bad and contains UB, all bets are off.
When I say 'all bets are off', I really mean it. Bizarre things happen in practice, legal by virtue of UB. I've read tales of bad C code printing that x is 0, and then evaluating that (0 == x) is false. (This was due to the compiler taking advantage of an 'is zero?' flag in the underlying CPU, which didn't perfectly align with the bits that stored the value of x.)
A good blog post on UB: https://blog.regehr.org/archives/213 To paraphrase:
> One might ask: Wouldn’t it be great if the compiler provided a warning or something when it detected [the UB]? Sure! But that is not the compiler’s priority.
This approach is part of what makes C, C. Very few other modern languages share this approach (the ones that do tend to be C based), and instead set out to have no UB. Java and C#, for instance, have no concept of UB.
It's impossible, even in theoretical terms, to build a perfect UB-detection program. This follows directly from Rice's theorem. The best we can do is better-than-nothing static-analysis tools, and dynamic-analysis tools. Neither can be perfect and exhaustive.
The good news is that these tools can much better than nothing, even if we know they're always going to be imperfect.