#define assert(c) while (!(c)) __builtin_trap()
#define assert(c) while (!(c)) __builtin_unreachable()
#define assert(c) while (!(c)) *(volatile int *)0 = 0
> Each […] has the most important property: Immediately halt the program directly on the defect.
No, only 2 of these 3 have this property. __builtin_unreachable() tells the compiler that this code path can be assumed to never execute, with the intent to allow the compiler to optimize however it wants based on this assumption. Whether the program halts, goes into an infinite loop, executes "rm -rf /", or starts a nuclear war is up to the compiler.
(What will happen in reality is that the compiler deduces that the loop condition will never be true and therefore it can elide the entire loop.)
Sadly, other posts by this author have similar issues.
Probably it should be stated explicitly, but this is implied if you add `-fsanitize=undefined`, where you will get a diagnostic if the unreachable code is reached. With `-fsanitize-trap=undefined`, you get a trap instruction there, the compiler won't delete anything. In a "release build" without sanitizers, this also serves as an optimization hint. It's a sensible idea, this snarky comment is unwarranted.
Also, code after __builtin_trap() is treated as dead.
It's nice that it works if you adjust your compilation environment like that, but the thing with the "release build" is actually the wrong way around.
- in a debug build (assertions enabled) with UBSAN you get a diagnostic or trap, so far so good.
- in a debug build without UBSAN, you get… undefined behavior, possibly some optimizations. Optimizations would generally be set to a low level, but that doesn't preclude you from getting UB.
- in a release build without assertions, … the assertion would be disabled … so you don't get any optimizations; instead you get proper "DB" for the case that should never happen.
It's essentially the wrong way around. If anything this is an argument for converting assertions into this definition in a release build, which might be reasonable in some code bases. (In my main code base, we have an "assume()" macro instead; it is by policy very sparingly used to avoid risks of running into UB.)
> It's a sensible idea, this snarky comment is unwarranted.
It's not intended to be snarky, just as noted I've run into similar issues with posts by this author. I haven't counted it up but it feels like a 90% rate of being just dangerously half-right about something. (Considering how subjective perception works, 90% subjective is probably 40% actual, but still.)
(I probably should've deleted the P.S.; I reworded my post and it was a leftover.)
Why would I ever have a debug build without sanitizers? Nothing stops you from removing the asserts in this case, anyway. I don't see what the problem is with using __builtin_unreachable in this context.
I think you didn't understand - what I was implying is to leave the asserts in for a release build, because they (theoretically) never happen, because they are free optimization hints.
So effectively, instead of a macro, your asserts become controlled by the presence of sanitizers.
I think it's nice because it lets you keep the same definition for both debug and release.
> I think you didn't understand - what I was implying is to leave the asserts in for a release build, because they (theoretically) never happen, because they are free optimization hints.
Remember we're not discussing our personal approaches, we're discussing what the linked post describes. It does mention UBSAN down one level of link but does not point out the pitfalls, which is the thing I'm taking issue with. I agree your approach is reasonable. It is, however, not what people reading the linked article would arrive at.
> I think it's nice because it lets you keep the same definition for both debug and release.
I would never consider turning blanket-all assert()s into assumptions for a release build. Every single assert would become a source of potential UB. This is why we have separate assert() and assume() in our codebase.
No, only 2 of these 3 have this property. __builtin_unreachable() tells the compiler that this code path can be assumed to never execute, with the intent to allow the compiler to optimize however it wants based on this assumption. Whether the program halts, goes into an infinite loop, executes "rm -rf /", or starts a nuclear war is up to the compiler.
(What will happen in reality is that the compiler deduces that the loop condition will never be true and therefore it can elide the entire loop.)
Sadly, other posts by this author have similar issues.
P.S.: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Jumpi...