4 ms·
To me, the very fact that one of the most used Programming languages ever, has undefined behavior - boggles the mind. IMO It is one of the worst parts of C (oth
by rishav_sharan 2y ago
To me, the very fact that one of the most used Programming languages ever, has undefined behavior - boggles the mind. IMO It is one of the worst parts of C (other than using pointers for Arrays, its string implementation and 0 based indices).
I really don't understand why the C standards body can't just define what should be the intended failure behavior in case of specific UB cases, and then the compiler developers to onboard this spec. There should be no impact to backwards compat because
a. this will be a new C version
b. No program ever should be defined around the UB behavior
But UB still exist in 2024 and will likely do till I am too old to whine on the internet about it.
- gpderetta 2y agoWhat would be, for example, the desirable failure mode of an use-after-free? Or a write past the end of an array? Or any of the many UBs that are extremely hard to detect but have unbounded effect on the program? Remember that the failure mode must be a) implementable, b) implementable at best minor loss of performance, c) not break the ABI. I agree that C and C++ have way too much UB, but no UB is a pipe dream.
- lelanthran 2y ago> What would be, for example, the desirable failure mode of an use-after-free? Implementation defined > Or a write past the end of an array? Ditto > Remember that the failure mode must be a) implementable, b) implementable at best minor loss of performance, c) not break the ABI. And "Implementation defined" matches that for the two examples you gave. What it does is force the compiler vendor to say "If you do $THIS, we will do $THAT." At least then users can make informed choices, sort of like "Clang guarantees that dereferencing a NULL pointer results in an effective address of zero, but GCC makes no guarantees, therefore we'll be going with Clang". I mean, seriously, there's a lot of footguns in C only because the standards writers said "Undefined behaviour" and never expected that compiler authors will take that to mean "remove code, or anything else, including nasal demons".
- int_19h 2y ago"Implementation-defined" means that there's something consistent that happens for a given implementation that can be documented for it. What consistent behavior would you expect to see from a typical implementation on use-after-free or writing past the end of an array? Can you give an example of how that would be documented?
- lelanthran 2y ago> What consistent behavior would you expect to see from a typical implementation on use-after-free or writing past the end of an array? Can you give an example of how that would be documented? Well, yes. "Writing to memory that is not available or allocated will always cause the instructions for the write to be emitted. The statement, expression or function call that performs the write will not be eliminated." The definition above is better than UB, which boils down to "Writing to memory that is not available or allocated may or may not cause all past and future lines of code to be eliminated from the emitting stage of compilation." See the difference? The implementation-defined definition forces the compiler documentation to be honest: "We'll remove lines of code from your input" is better than "You broke the standards rules, so we get to do anything."
- gpderetta 2y ago> Writing to memory that is not available or allocated will always cause the instructions for the write to be emitted. The statement, expression or function call that performs the write will not be eliminated. That would be a significant constraint on the optimiser.
- lelanthran 2y ago> That would be a significant constraint on the optimiser. Maybe. Maybe you work on GCC (or on Clang) and know the dirty details of the code inside ... I don't. What I do know is that the performance in past C compilers which emitted every memory access unconditionally was considered "blazing fast". If this is a blow to performance, how significant is it? Can you compile two benchmarks with and without the 'perform every memory access unconditionally' flag? Is there a flag for this? If no one ever did this, how can we tell there would be an impact on performance, nevermind significant impact? Because I recall seeing benchmarks for things like eliminating `if (x+100 < x)` showing the performance impact to be not even a rounding error. I also recall seeing a benchmark somewhere for with and without the flag that removes null-checks, and once again that performance impact was, for all practical purposes, zero. In the era of speculative pipelined execution, in respect of a test/check that would be speculated in the pipeline well before it is needed, how exactly will the code run faster if the test/check is removed from the sources? Once again, I admit that I am not in the weeds in GCC or LLVM or CLang development, but I fail to see how eliminating an instruction that never slows the processor can have a noticeable impact on performance.
- bloak 2y agoI agree it's surprising and can only suggest the following explanations: * Undefined behaviour lets compiler developers improve the performance of certain standard benchmarks (slightly but significantly) and they don't want to give that up. * Most of the people who are interested in programming language design have already deserted C/C++ so the C standards bodies are dominated by the remaining traditionalists.
- lelanthran 2y ago> To me, the very fact that one of the most used Programming languages ever, has undefined behavior - boggles the mind. IMO It is one of the worst parts of C (other than using pointers for Arrays, its string implementation and 0 based indices). Because the effect wasn't always this bad; past C compilers did not look at a null-dereference and say "This means that we can simply skip the dereference". They emitted the faulty code which would then crash reliably. Same with things like writing past the end of an array: you'd overwrite whatever was in the memory or stack. Now, if the compiler detects UB, it would silent discard it. It's the whole "compiler silently doing something unexpected, other than exhibiting the wrong behaviour that the programmer expected." that makes the issue worse now than it's been in the past.