4 ms·
> No, the C/C++ standards specify what is UB. And the compiler writers have a stranglehold on the standards bodies. They hold more than 50% of the voting power
by gavinhoward 2y ago
> No, the C/C++ standards specify what is UB.
And the compiler writers have a stranglehold on the standards bodies. They hold more than 50% of the voting power last time I checked.
So yeah, compiler writers decide what's UB.
- dzaima 2y agoThe vast majority of UB usually considered problematic has been in the standards for decades, long before compilers took as much advantage of it as they do now (and the reasons for including said UB back then were actual hardware differences, not appeasing compiler developers). Are there even that many UB additions? The only thing I can remember is realloc with size zero going from implementation-defined to undefined in C23.
- gavinhoward 2y agoYes, but that does not change the fact that compilers writers have control of the standard, have had that control since probably C99, and have introduced new UB along with pushing the 00UB worldview.
- dzaima 2y agoWhat introduced UB are you thinking of? I'll admit I don't know how much has changed, but the usually-complained-about things (signed overflow, null pointer dereferencing, strict aliasing) are clearly listed as UB in some C89 draft I found. C23's introduced stdc_trailing_zeros & co don't even UB on 0, even though baseline x86-64's equivalent instructions are literally specified to leave their destination undefined on such! 00UB is something one can argue about, but I can't think of a meaningful way to define UB that doesn't impose significant restrictions on even basic compilers, without precisely defining how UB-result values are allowed to propagate. e.g. one might expect that 'someFloat == (float)(int8_t)someFloat' give false on an input of 1000, but guaranteeing that takes intentional effort - namely, on hardware whose int↔float conversions only operate on ≥32-bit integers (i.e. everything - x86, ARM, RISC-V), there'd need to be an explicit 8-to-32-bit sign-extend, and the most basic compiler just emitting the two f32→i32 & i32→f32 instructions would fail (but is imo pretty clearly within "ignoring the situation completely with unpredictable results" that the C89 draft contains). Sure it doesn't summon cthulhu, but it'll quite likely break things very badly anyway. (whether it'd be useful to not have UB here in the first place is a separate question) Even for 'x+100 < x' one can imagine a similar case where the native addition & comparison instructions operate on inputs wider than int; using such for assuming-no-signed-wrap addition always works, but would mean that the comparison wouldn't detect overflow. Though here x86-64, aarch64, and RISC-V all do provide instructions for 32-bit arith, matching their int. This would be a bigger thing if it were possible to have sub-int-sized arith.
- pertymcpert 2y agoWhich UB upsets you? Can you be specific so we can revert it?
- gavinhoward 2y agoAll of it. But especially anything added after C89 that was not already there implicitly. Edit: okay, not all of it. I was hyperbolic. Race conditions and data races should be UB. But anything that can be implementation-defined should be.
- dzaima 2y agoSo your issue is not at all any specific thing or action anyone took, but just in general having UB in places not strictly necessary. And "Especially anything [different from The Golden Days]", besides being extremely cliche, is a completely arbitrary cutoff point. A given compiler is free to define specific behavior for UB (and indeed you can add compiler flags to do that for many things); the standard explicitly acknowledges that with "Possible undefined behavior ranges from […], to behaving during translation or program execution in a documented manner characteristic of the environment".
- gavinhoward 2y agoSigh...yes, I don't want any UB where it's not necessary. But if you must have a concrete example, how about realloc? In C89 [1] (page 155), realloc with a 0 size and a non-NULL pointer was defined as free: > If size is zero and ptr is not a null pointer, the object it points to is freed. In C99 [2] (page 314), that sentence was removed, making it undefined behavior when it wasn't before. This is a pure example of behavior becoming undefined when it was not before. In C11 [3] (page 349), that sentence remains gone. In C17 [4] (page 254), we get an interesting addition: > If size is zero and memory for the new object is not allocated, it is implementation-defined whether the old object is deallocated. If the old object is not deallocated, its value shall be unchanged. So the behavior switches from undefined to implementation-defined. In C23 [5] (page 357), the wording completely changes to: > ...or if the size is zero, the behavior is undefined. So WG14 made it UB again after making implementation-defined. SQLite targets C89, but people compile it with modern compilers all the time, and those modern compilers generally default to at least C99, where the behavior is UB. I don't know if SQLite uses realloc that way, but if it does, are you going to call it buggy just because the authors stick to C89 and their users use later standards? [1]: https://web.archive.org/web/20200909074736if_/https://www.pdf-archive.com/2014/10/02/ansi-iso-9899-1990-1/ansi-iso-9899-1990-1.pdf https://web.archive.org/web/20200909074736if_/https://www.pd... [2]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf [3]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf [4]: https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf https://web.archive.org/web/20181230041359if_/http://www.ope... [5]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3047.pdf https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3047.pdf