https://lists.isocpp.org/std-proposals/2020/05/1322.php
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p28...
Low level platform-specific code that needs to hot spin until an interrupt happens can use assembly for that part which it will need to do for the interrupt handler anyway.
Can you spot the infinite loop in this function?
char* strcpy(char* restrict d, const char* restrict s) {
stpcpy(d, s);
return d;
}
I'll help. A call to `stpcpy` that ignores the return value can be swapped with a call to the (more likely to be optimized) `strcpy`. Since that's infinite recursion, and there is no forward progress, it's undefined behavior and anything goes.This isn't just theory, it actually broke things in practice for me.
So `str` and `mem` are reserved. But then so are `to` and `is` (by <ctype.h>). Just forget about having a function named `is_valid_user`.
And so are `mtx_`, `cnd_`, `thrd_`, `atomic_`, `memory_`...
Which is why... everything in C is UB.
In worse scenarios, your programme will silently continue with garbage, or format your hard disk or give attackers the key to the kingdom.
Another commenter suggested using LLMs, but I disagree. Having clangd emit warning squiggles for unchecked operations (like signed addition) would be a good start.
Dead code elimination is essential for performance, especially when using templates (this is basically what enables the fabled "zero cost abstraction" because complex template code may generate a lot of 'inactive' code which needs to be removed by the optimizer).
The actual issue is that the compiler is free to eliminate code paths after UB, but that's also not trivial to fix (and some optimizations are actually enabled by manually injecting UB (like `__builtin_unreachable()` which can make a measurable difference in the right places).
Not, that the compiler can also emit code paths before UB, as UB is a property of the whole program, not just of a single statement.
before.
enum op_t{ add, mul };
int exec(op_t op, int a, int b) {
if(op == add) { return a+b; }
if(op == mul) { return a\*b; }
}
c = exec(add, a,b);
Should be the compiler be prevented from inlining exec and constant-propagating op and removing the mul branch? What about if a and b are constants and the addition itself is optimized away?