The first example should have changed the heading to "C++ code" as opposed to "C code" due to the use of `std::unreachable()`. This matches the godbolt link.
Another alternative is to use `__builtin_unreachable()` (not standard, but available in GCC and Clang).
This is sort of true, except that behavior can be undefined in the standard but defined in your implementation. One of the best examples is -fwrapv, which makes signed integer overflow not UB. And Fil-C has no undefined behavior that I'm aware of. But most implementations target the standard and "define" what the standard doesn't to be "whatever I feel like, in service of implementation simplicity and performance". What matters is what your implementation guarantees, which should be a superset of what the standard does.
The solution is to avoid UB. std::unreachable() is just an easy-to-invoke way of hitting UB, mentioned for the sake of the discussion. They did later take your advice “Let’s say we change std::unreachable() to int* ptr = nullptr; *ptr = 99; in the previous example.”. They still got improper codegen out of the compiler.
Though, it would be better if programmers read deliberate instances of UB as if they literally said "std::unreachable()" at that point in the code, as that would better convey how the optimizer passes will see things after dozens of frontend and middle-end passes have run before it.
> it would be better if programmers read deliberate instances of UB as if they literally said "std::unreachable()"
Except that isn't true.
There are basically two kinds of undefined behavior.
The first is undefinable behavior, the kind that easily goes disastrously wrong, like writing to random memory locations.
The second is incidentally undefined behavior, something where there just isn't enough implementation consensus for the standard to settle on it, like realloc(p, 0).
There's no standard terminology I'm aware of to distinguish the two. But the distinction exists.
And where programmers read deliberate instances of UB, it's usually the latter, e.g., writing directly to an address memory mapped to device I/O in that particular environment.
> There's no standard terminology I'm aware of to distinguish the two.
There is: implementation-defined behavior. Your example of writing to an MMIO address is such an example, as the standard says casting an int to a pointer is implementation-defined. (However, the C standard has plenty of cases of undefined behavior that probably should have been implementation-defined.)
im not an c++ expert, but it looks like the unreachable macro of rust.
In rust it allows optimizing the generated binary to not care about special paths.
If an integer can only be 1-4, then it doesn't make sense to dedicate an code path for 0 or 5+ that will never be called.
in an explicit for loop its easy to know for the compiler, but when this information crosses boundaries like function calls then it can make it easier and can reduce the binary size.
omoikane 40 minutes ago
Another alternative is to use `__builtin_unreachable()` (not standard, but available in GCC and Clang).
zarakshR 22 minutes ago
charleslmunger 56 minutes ago
pestatije 1 hour ago
johnea 1 hour ago
mpyne 1 hour ago
Though, it would be better if programmers read deliberate instances of UB as if they literally said "std::unreachable()" at that point in the code, as that would better convey how the optimizer passes will see things after dozens of frontend and middle-end passes have run before it.
torstenvl 32 minutes ago
Except that isn't true.
There are basically two kinds of undefined behavior.
The first is undefinable behavior, the kind that easily goes disastrously wrong, like writing to random memory locations.
The second is incidentally undefined behavior, something where there just isn't enough implementation consensus for the standard to settle on it, like realloc(p, 0).
There's no standard terminology I'm aware of to distinguish the two. But the distinction exists.
And where programmers read deliberate instances of UB, it's usually the latter, e.g., writing directly to an address memory mapped to device I/O in that particular environment.
NobodyNada 4 minutes ago
There is: implementation-defined behavior. Your example of writing to an MMIO address is such an example, as the standard says casting an int to a pointer is implementation-defined. (However, the C standard has plenty of cases of undefined behavior that probably should have been implementation-defined.)
stop50 1 hour ago
tyushk 51 minutes ago
[1] https://doc.rust-lang.org/std/macro.unreachable.html [2] https://doc.rust-lang.org/std/hint/fn.unreachable_unchecked....