6 ms·
> "If my program contains UB, and the compiler produced a binary that does X, is that a compiler bug?" It's not a compiler bug. True, if the language spec is t
by vintermann 4y ago
> "If my program contains UB, and the compiler produced a binary that does X, is that a compiler bug?" It's not a compiler bug.
True, if the language spec is the only thing in the universe you care about. But there are better and worse things compilers can do in the presence of UB, and the big one is warning you about it when it possibly can.
There's another big discussion on the front page about signed integer overflow in particular, and gcc screwing the developer over because it was UB when they thought it was just implementation defined:
https://news.ycombinator.com/item?id=33770277 https://news.ycombinator.com/item?id=33770277
- rich_sasha 4y agoExactly, some things are probably UB at compile time, these IMHO should by default be a compile time error. Integer overflow is harder ofc.
- morelisp 4y ago> gcc screwing the developer over because it was UB when they thought it was just implementation defined No, the developer thought "UB" produced an integer with an undefined (but still valid integral?) value. They misunderstood what UB is, not whether or not something was UB.
- iforgotpassword 4y agoThat's from the spec POV. On x86 for example, signed int overflow is well defined.
- morelisp 4y agoBoth "UB" and "IB" are terms defined in the language standards, and it's obvious that's what we're talking about here. x86 defines what specific instructions do, nothing so crass as "signed integer overflow" - that's part of why the language standard has to define it as such.
- torstenvl 4y agoYeah, except that they effectively aren't part of the language standard. Possible behavior is to ignore UB, behave in a documented manner, or issue a compiler error. Not remove other code or any other nonsense optimizing compilers do. I'm not gonna wage a holy war against optimizing compilers, but let's not pretend that this is a language standard issue.
- tsimionescu 4y ago> x86 defines what specific instructions do, nothing so crass as "signed integer overflow" I don't understand what you mean. The x86 standard defines what happens if an `add` instruction is performed and the result overflows, possibly affecting the sign bit (what happens is that the OF flag is set, and the target register is set to the two's complement representation of the sum modulo 2^bitlength).
- quietbritishjim 4y agoThat's irrelevant. A C compiler targetting x86 has no obligation at all to handle signed integer overflow in the same way the processor natively does. Because it is not an assembler - it is compiling against the theoretical C virtual machine. This is for the same sort of reason that unsigned overflow does have a well defined meaning in C, and will always do exactly that even for targets that do not natively do that (if necessary it would emulate that behaviour in software).
- rightbyte 4y ago> A C compiler targetting x86 has no obligation It kinda has an implied obligation to not screw with its users. And doing the same optimizations to fold bloated Cpp templates properly messes with my C code.
- adgjlsfhk1 4y agounfortunately for you, gcc and clang disagree with you.
- dooglius 4y agoCompiler developers may not think they have that obligation, but they should
- kalkin 4y agoShould loop-invariant code motion be an allowed optimization only when the compiler can statically prove that a for loop's increment won't overflow?
- dooglius 4y agoIf it would result in different behavior in the event of an overflow (which I think is generally not the case), then no. A typical for loop (using <) can easily been seen to not overflow, so this is a rare case anyway. Loops using signed comparison with <= where the compiler can't rule overflow out should result in a compiler or linter warning.
- josefx 4y agox86 also doesn't support 64 bit integers, so would those be entirely undefined behavior or simply compile time errors?
- layer8 4y agoIt’s well-defined on x86, but the compiler is allowed to insert INTO instructions (which trigger an interrupt on overflow), which is effectively UB (as the interrupt routine is outside the scope of the C language).
- Denvercoder9 4y ago> But there are better and worse things compilers can do in the presence of UB, and the big one is warning you about it when it possibly can. The problem is that this is really hard to do without producing either too much or too little warnings, as undefined behaviour is usually input-triggered; and the compiler usually doesn't know all constraints on the arguments that the programmer knows about. For example, the function f() in the article of the other discussion is perfectly well-defined for any argument x <= 0x402010, as in that case no overflow happens. The compiler doesn't know whether all callers ensure the argument is valid (e.g. this might be a library function, or the invariants might not be expressable in a way the compiler can understand). This is a lose-lose situation for the compiler: it can warn, but people will find the unnecessary warnings annoying and turn them off, while if it doesn't warn people will complain about perceivedly bad optimizations. Of course there's a point to be made that the real problem here is that the C standard makes signed integer overflow undefined behaviour, but that's not gcc's fault and can be easily worked around with -fno-strict-overflow or -Wstrict-overflow.
- xg15 4y ago> This is a lose-lose situation for the compiler: it can warn, but people will find the unnecessary warnings annoying and turn them off, while if it doesn't warn people will complain about perceivedly bad optimizations. This could still be greatly alleaviated with the use of asserts: A compiler could default to the most conservative set of assumptions for an input value - e.g. that a parameter of a global function assumes the full value range of its type - unless the programmer provides more assumptions through an assert statement. In debug builds, you could compile those asserts to actual runtime checks, so the program can be appropriately tested. Then, in production builds, the asserts would simply be skipped. The second improvement would be warnings about "nonensical" statements (from the compiler's POV) that will be optimised away. E.g., many of the most egregious examples of UB are from code where the programmer inserted a bounds check to protect against UB at another location - and then the compiler optimized away that same bounds check as a "nasal demon" action, caused by the UB it was supposed to protect against. In those cases, it would be useful to warn "hey, this bounds check can never fail because that other statement implies that either we're in bounds or there is UB. You might want to check for UB here."
- gpderetta 4y ago> there are better and worse things compilers can do in the presence of UB, and the big one is warning you about it when it possibly can. GCC should add -Wundefined-behaviour flag that will unconditionally print "warning: this program might or not have UB" as the first diagnostic.
- jxf 4y ago60% of the time, it's right every time!
- torstenvl 4y agoIt should also warn if there are infinite loops, so the dev can know whether it will ever halt.
- marcosdumay 4y ago"This program may or may not loop forever" Seems pretty viable.
- Kranar 4y agoInfinite loops are undefined behavior in C++.
- deleted 4y ago[deleted]
- Someone 4y agoNo, they aren’t. If they don’t make changes that are observable outside the loop, they can be assumed to terminate. ThTs a compile-time thing. https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1509.pdf https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1509.pdf (not guaranteed to be equal to the final wording of the standard): “An iteration statement that performs no input/output operations, does not access volatile objects, and performs no synchronization or atomic operations in its body, controlling expression, or (in the case of a for statement) its expression-3, may be assumed by the implementation to terminate.” Rationale for including that: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1528.htm https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1528.htm (basically, without it compilers can’t optimize loops that terminate as well as with it)