What do we do about the fact that undefined behavior actually makes it harder or impossible to write efficient code in some cases, like checking for integer overflow?
Even if your goal is performance over anything, there are a whole lot of undefined behaviors that have absolutely zero performance benefit.
This is a good example of why we should be moving away from C. :) Signed overflow being undefined is basically necessary due to a self-inflicted wound from 1978: the fact that "int" is the easiest thing to type for the loop index when iterating over arrays. Nobody is going to go through and fix all the C code in existence to use unsigned, so we're stuck with that UB forever. The realistic solution is to start migrating away from C (and I have no illusions about how long that will take, but we'll never get there if we don't start now).
> Even if your goal is performance over anything, there are a whole lot of undefined behaviors that have absolutely zero performance benefit.
Sure. But compilers don't exploit those in practice (because compiler authors are not language lawyers for fun), so they're basically harmless in practice. They're essentially just spec bugs for the committee to fix in the next revision.
C should be writable by humans.
This entire discussion is predicated on the idea that undef values having a live range is something that actually prevents bugs, which I find extremely dubious. This is not like the "null dereference is UB" thing in the Linux kernel (which I also found to be overstated in importance, but it has caused at least one problem). I can't think of a single bug that this has caused, other than a compiler bug in Rust that was only exposed by people intentionally trying to write code that would break--that is, the bug had no practical effect. It strikes me as "friendly C lawyering" for little benefit.
Then smack the spec writers for never removing undefined behavior. You don't have to smack both, but the two groups are working together to make C more dangerous every day.
>They are doing this because their customers demand it.
If you show a chart of how often the customer's code will stop working because of these optimizations, perhaps they will stop demanding it. But even if it's implemented, there should be a --reduce-undefined-optimization flag.
> I can't think of a single bug that this has caused
If it was defined, then the Debian RNG bug likely would not have happened.
If an undefined behavior is rare, that's a good reason to flatly remove it from the spec. If we could have 10 undefined behaviors instead of 200, the world would be better off. There are undefined behaviors you can hit during preprocessing, for crying out loud.
(Also even in the most extreme version, you wouldn't need to count it as live until the first access, and if that's the last access then there's no live range at all.)
No. The Debian bug was not the result of a compiler optimization based on undefined behavior. It was due to OpenSSL doing something dodgy [1]. The compilers, being conservative, all declined to do any optimizations based on the undefined behavior. It was Valgrind that called OpenSSL out for the uninitialized use of memory. This is not a bug in Valgrind either! If the C standard defined live ranges for uninitialized values, then Valgrind would still be right to flag uses of uninitialized values, because they usually indicate bugs, and Valgrind is a bug finding tool. The Debian bug occurred because OpenSSL was doing something silly, Valgrind was doing its job properly pointing out that silly thing that OpenSSL was doing, and the fix was dangerously incorrect.
If it had been valid by the C standard, it is not unlikely that they would have kept it. Then there would have been no disaster. They would not have gotten confused about what was valid and what wasn't.
Extra entropy to mix in isn't that silly.
Edit: In other words, making "Is this code valid?" more complex to answer can have bad effects even in the absence of aggressive compilers.
Once again, if the C standard were to change to allow uninitialized variables to have live ranges, Valgrind would not just "git rm -rf memcheck; git commit". That is because Valgrind is a tool that is designed to check for uninitialized memory. It did its job properly. It is not designed to check for undefined behavior according to the C standard.
You seem to be determined to lay the blame for every problem at some combination of C compiler authors and C spec writers, regardless of whether the problem actually is theirs.
I don't blame all problems on C, but I think it should be easier to write valid C. Dealing with uninitialized variables was just an example of something that could be changed. It wasn't supposed to be the biggest most horrible problem with C.
Compilers may not turn functions that hit most kinds of UB into { return; }, but they can. As a general category, that's a bad thing.
It's highly unlikely that the maintainer of OpenSSL at Debian is a spec lawyer. The maintainer ran Valgrind because, as a general principle, reading undefined memory is bad. That is independent of whether the C standard says that it's OK. If I were the maintainer of the package in question, I would not ignore reads of undefined memory just because the C committee has declared that it has some kind of behavior. That would be silly.
Valgrind contains many tools that check for things that are not violations of the C standard: for example, memcheck checks for "fishy" arguments to malloc that are not C standard violations but are nevertheless usually bugs.
> Compilers may not turn functions that hit most kinds of UB into { return; }, but they can. As a general category, that's a bad thing.
Depends on the undefined behavior. For undefined behavior coming from the C preprocessor, sure, that shouldn't be undefined, and the committee should change the spec. For other kinds of UB, if compiler authors hadn't figured out those tricks, we'd have slower programs today. Undefined behavior is much of what has allowed performance of our programs to keep improving despite the language not changing over decades.
My position, by the way, is hardly uncommon among longtime C compiler developers.