5 ms·
You are asking for the impossible. It is not generally possible for the compiler to detect whether undefined behaviour can occur. Take a function that returns
by zAy0LfpBZLC8mAC 10y ago
You are asking for the impossible. It is not generally possible for the compiler to detect whether undefined behaviour can occur.
Take a function that returns the square of a signed integer: As long as it's called with arguments that are small enough, the behaviour is perfectly defined. But the compiler cannot necessarily know which values might get passed to it at runtime.
Now, it could in principle add runtime checks that do something defined, like abort the program, when the preconditions for defined behaviour are not met--but that often makes the generated code slow.
That is why the standard says that the compiler can do anything it wants in the case of undefined behaviour: It's just a different way of saying that it's the responsibility of the programmer to make sure that the preconditions for defined behaviour are never violated, so the compiler can generate code based on the assumption that that is the case, instead of littering the code with tons of overhead for the case that the programmer did something wrong.
Compilers don't detect undefined behaviour and then use those detected instances of UB in order to mess up your software, they simply operate on the assumption that there is no UB in your code--which means that the code they generate is correct if there is indeed no UB, but there are no guarantees as to what happens if you don't hold up your implicit promise to not invoke UB.
- kbenson 10y agoAny time a compiler has elided some code it's because it has detected that it can because it's undefined, so in the context of this argument saying that the compiler cannot determine undefined behavior is not useful, as we are only talking about the times it already has. The obvious interpretation of the request to have the compiler error is to have the compiler error in any case it would elide statements. That may be impractical in many or most cases, but it's a different problem than what you are talking about. Presumably there would be some cases where the compiler could warn or abort (if a flag was used) if branches were elided based on undefined behavior and those same branches also included an abort of some type shortly thereafter. Or maybe some better heuristic that I'm not thinking of. Hard is not the same as impossible, and imperfect is often better than nothing.
- zAy0LfpBZLC8mAC 10y ago> Any time a compiler has elided some code it's because it has detected that it can because it's undefined, so in the context of this argument saying that the compiler cannot determine undefined behavior is not useful, as we are only talking about the times it already has. No, you have it all backwards, and that's not what's happening. The compiler doesn't elide code because it is undefined. The compiler elides code because it would never be executed unless it happens to be called with arguments that would produce undefined behaviour anyway. Take this, for example: void foo(int *x){ *x=5; if(x==NULL)abort(); } The operation that is potentially UB here is the *x=5--if x happens to be NULL. But the compiler doesn't remove that assignment, it removes the check following it. And it does so because execution could never reach that check in a defined manner unless it is false: Either x is NULL, then the behaviour of the assignment is undefined (and commonly would cause a segfault, for example), or it is not NULL, then the check is superfluous. And there is nothing necessarily wrong with that code: It could be (and such code is common) that it is actually never called with a NULL pointer, in which case the behaviour is perfectly defined. If a compiler actually determines that your code will always exhibit undefined behaviour, then chances are it will indeed warn you, but that just isn't what usually happens, and it's not the cause of such bugs. The compiler doesn't say "this is undefined, therefore, let's screw it up", it's exactly the opposite: It says "if we assume that this code is never called with arguments that would cause undefined behaviour, what is the most efficient machine code that we could map it to?" > The obvious interpretation of the request to have the compiler error is to have the compiler error in any case it would elide statements. That would be plain idiotic. It is perfectly normal to have tons of dead code, erroring out in that case would just make it impossible to compile anything. You have to consider that the compiler isn't interested in the elegant structure of your code, so it mashes it all up to figure out the most efficient machine code for the whole thing. So, you might have code that calls lots of functions on an array, say, where each function does a bounds check. Now, the compiler potentially will inline it all into one big spaghetti function. And then it will figure out that many of the bounds checks are actually redundant (again, under the assumption that the code doesn't invoke undefined behaviour), so it will remove any checks and corresponding error handling paths that it can prove to be implied by preceding checks, or it might try and combine multiple checks into one. That does not mean that there is anything wrong with your code, or that you should manually remove all the redundant checks (which might not be redundant for other call sites, after all). The code is perfectly fine, and the compiler tries its best to remove anything that you don't actually need. > Hard is not the same as impossible, and imperfect is often better than nothing. True, but also besides the point. Compilers do issue warnings for lots of stuff, and more are added regularly, but much of what you are suggesting is actually not in any way even a coherent idea, and as such, is indeed not just hard, but impossible.