7 ms·
If the compiler can take advantage of undefined behavior to optimize a program, what is keeping the compiler from also warning you that it is doing so?
by sbov 9y ago
If the compiler can take advantage of undefined behavior to optimize a program, what is keeping the compiler from also warning you that it is doing so?
- jjnoakes 9y agoBecause, after inlining and macro expansion and other passes, you'd get warnings about everything under the sun and they would be useless.
- kazinator 9y ago"I'm statically assuming that a pointer that can never be anything other than null actually refers to NeverCalled" is definitely worth a diagnostic.
- jjnoakes 9y agoThe compiler doesn't know the pointer isn't set, only the linker may know that. So you'd have to put a whole lot more smarts into multiple tools to get that warning. I'm not convinced it is useful.
- kazinator 9y agoThe point of the warning is precisely that the compiler is making a dangerous assumption without proof. More smarts is needed only to eliminate false positives occurrences in the warning.
- jjnoakes 9y agoIt depends on your definition of dangerous. And eliminating false positives in warnings is hugely important. Too many important warnings (more important than this one) are ignored today because people get desensitized as a result of so many false positives. Let's not add more for trivial issues like this please.
- kazinator 9y agoWarnings are ignored because people don't use -Werror and then fine tune which warnings warnings they want and don't want to see.
- jjnoakes 9y agoWarnings are ignored because it's too hard or not possible to configure the compiler to only issue the warnings a particular developer finds useful and actionable. Adding more warnings, especially for silly corner cases like the example we're discussing, is not the solution.
- kazinator 9y ago> Warnings are ignored because it's too hard or not possible to configure the compiler to only issue the warnings a particular developer finds useful and actionable. That's why you don't do that based on the whims of an individual developer. Simply have a "no warnings" policy. Then the project decides what is enabled and what isn't. A commit must not introduce warnings. If a code change triggers a warning, it must be accompanied with a disabling of that diagnostic, which must then pass review so that it is peer approved. If the warning cannot be disabled, the change must be reworked. This "silly corner case" is not silly at all; it reveals a dangerous translation strategy in the compiler. The appropriate treatment isn't the issuance of a warning; rather, this translation strategy should be turned off unless explicitly requested by an exotic code generation option. (And then it can be applied without any warning.)
- jjnoakes 9y agoIf your warnings policy isn't useful for developers it isn't a good policy. And the translation strategy understand discussion here isn't dangerous.
- cesarb 9y ago> the compiler is making a dangerous assumption without proof. The whole issue is that, from the compiler point of view, it has a proof! It can prove from the language rules that the pointer can only have NULL and EraseAll as its value; since a call through the NULL pointer is invalid, at that line the only value left is EraseAll; QED. It might not be the proof you wanted, since you disagree with the premises, but it's still a valid proof.
- kazinator 9y agoIt isn't a valid proof, because it's perfectly possible that the variable has a null value and that the call is invalid. Detection of that null value is already there and essentially free of charge. The implementation is going out of its way to prevent an instance of undefined behavior from being detected, without providing a useful, documented extension in its place, and in a situation when the detection costs nothing.
- jjnoakes 9y agoIf the variable has a null value and the call is invalid then the compiler isn't required to compile it to anything specific; this includes the idea that the compiler isn't required to compile it to a jump-to-address-zero.
- kazinator 9y ago> the compiler isn't required to compile it to anything specific That might be true if the ISO C standard were the only source of requirements going into the making of that compiler; it isn't. There are other issues. Obviously, the compiler is in fact compiling it to something very specific. It's not simply an accident due to the situation being ignored that the indirect call gets replaced by a direct jump. The translation is deliberate. From ISO C: Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). This is definitely not the result of "ignoring the situation completely"; ignoring the situation completely means translating the code earnestly and subsequently the translation doing the indirect jump attempt through a null pointer. That's what it means not to do anything specific. It's not terminating the translation, so it's not the third kind of behavior. So it must be "behaving during translation or programing execution in a documented manner characteristic of the environment". I don't see how this is a characteristic of the environment; nobody can claim with a straight face that this is environmentally dictated! Also, the undefined behavior never actually occurs. A pointer variable may be null; null is a valid value. Undefined behavior only ensues when the attempt is made to dereference. That's not actually happening in the program; the translation is devoid of any attempt to dereference null. The idea that the null pointer dereference caused the translation and the subsequent call to that function requires a perverted view of causality in which later events cause earlier events.
- orodley 9y agoBecause the useful types of optimisation around undefined behaviour aren't: "we can prove that your program definitely contains undefined behaviour, so we'll compile it into 'rm -rf /', mwahahaha" but rather: "if property P about your program is false, then it would definitely contain undefined behaviour, therefore P must be true" followed by using P to do useful optimisations. This applies even when your original program contains zero undefined behaviour. Warning here isn't useful.