Before that the observable side-effect, device_command=42, has already happened. With time travel allowed, could also have been omitted, which would have been a real problem.
I misread the article to think that is was about the latter problem. You are right, my mistake. I didn't consider that anyone could see the former as a problem at all: It's just the compiler doing exactly what it's told to do.
It's not a command to compiler to stop generating code.
It's a permission slip that allows the compiler to assume that code that follows is dead code, eligible to be optimized out, including eliminating potentially dead stores and loads.
Which is to say, it's precisely what any other UB is, which is why deliberate use of UB is very bad idea.
double f(void* p) {
return * (double*) p;
}
No one is surprised that defensive code isn't issued to guard against p being NULL, unaligned, or pointing to a uint16_t. You told the compiler that p is actually pointing to a double, and it generates code accordingly.It's the same thing here: You told the compiler very explicitly that anything following this point is unreachable, and it generated code accordingly.
I'm not defending writing code with UB. I would have preferred the compiler issuing a warning saying "you say this is unreachable, I don't believe that it is", when it's this obvious. Maybe issue a trap instruction. But there's nothing wrong it, per se. When you choose to override the compiler's judgment and insist that this point in code is unreachable, then the compiler takes your word for it.
Of course I would also prefer a warning, or at least a guarding trap instruction. And maybe compilers should try harder to be friendly in this way.
But they can't be perfect, because the program specified something syntactically valid, but semantically invalid. The module/compiler pass that could theoretically detect it may simply not be issued the necessary information. E.g. a solver may be given the input that a value is in a given range, and from that simplify an operation. If the input is wrong, so is the output.
Reminds me of:
On two occasions I have been asked, 'Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?' I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question.
In the example from the article, though, there could be a pass that simply eliminated dead code. That pass is not a parser of the raw code, so how is it to know that actually, the "dead code" isn't?And a standard that would require dead code elimination to replace not with empty, but with a trap instruction, could go very wrong. You don't want `if (false) { … }` to become `if (false) { trap(); }`. You want it just removed, if a previous pass deemed it dead. Or do you want `int main() { for(;;) {cli_loop();} }` to force adding a trap in the unreachable code after the loop? That's a lot of generated trap instructions.
So in general this means solving the halting problem. Yes, specifically in the post's example, "surely a sufficiently smart compiler could detect something this obvious". Maybe so, but how often is the bug actually this obvious?
Now if this actually made it into the C++26 standard, I've not paid to get the official one, but it's in the N5050 draft: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/n50...
Yes, into the shame cube I go. It doesn't change anything, and it applies equally to C and C++, but you are correct. Thanks.
Another alternative is to use `__builtin_unreachable()` (not standard, but available in GCC and Clang).