from ISO/IEC 9899:2011 "Programming Languages -- C"
3.4.3
1 undefined behavior behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements
2 NOTE 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).
It doesn't look like a leap to go from this definition to that of ignoring the situation completely with unpredictable results.
It's not a huge leap, but it still doesn't mean that UB by definition means that the compiler is allowed to assume UB doesn't happen. Allowing the compiler to assume this is one reasonable result of this definition (I give an example of how this reasoning works somewhere else), but it is not equivalent to the definition of UB, in principle.
The definition logically implies that compilers are allowed to assume UB doesn't happen. The definition is:
> undefined behavior: behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements.
With these optimisations, either:
1. The program contains no UB, the transformed code acts as expected, and the compilation is valid.
2. There's UB; the standard imposes no requirements on what behaviour that translates to; and so the transformed code is valid, regardless of what it does.
...and yet the article completely ignores the "with unpredictable results" part and instead spends a lot of time discussing all the other valid consequences (which are also only mentioned as examples, at least in the common understanding of "from ... to ...").
Downthread commenters go into more detail regarding the "ignoring e.g. the possibility of signed overflow may mean to assume that it never happens" reading, so I won't elaborate on it here.
Leaving overflow to the processor is an example of ignoring it with unpredictable results. Deleting overflow checks because you assume, incorrectly, that overflow is impossible is not an example of ignoring with unpredictable effects, it does produce unpredictable effects, though.
How is the compiler supposed to know that a particular operation is intended as an overflow check though? It isn't a human and it doesn't actually comprehend the code it operates on. It just blindly applies rules.
I want the compiler to eliminate redundant operations. That's a large part of the point of doing optimizations in my view! Best effort attempts to avoid eliminating obvious sanity checks are desired of course, but I doubt it's feasible to reliably identify those short of AGI. (And at that point, why are you still writing code?)
The original article's interpretation seemed untenable.
While the difference between "Permissible" and "Possible" could be quite significant, in this case, it was qualifying:
> [Permissible/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).
The previously-"Permissible" behaviors were so broad that they basically allowed anything, including translating the source-code in any documented manner.. which basically means that, as long as a compiler says how it'll treat undefined-behavior, it can do it that way, because it's free to completely reinterpret the source-code in any (documented) manner.
To me ignore the situation completely with unpredictable results would mean: the compiler generates an assembly that could not be correct, and then the behaviour of the program is determined by what the processor does.
Doing something like removing checks is not ignoring the situation: is acting in some particular way when undefined behaviour is detected.
And it has neither unpredictable results: it specifies what happens, since these checks are added systematically.
I don't see anywhere in the standard that the compilers are free to change at their choice the semantic of the program if undefined behaviour is detected. Rather undefined means to me that the compiler generates code where the result cannot be known because it will depend on external factors (basically the hardware implementation).
This is untenable, because different compilers will generate different sequences of instructions which will misbehave in different ways. For example, one compiler may choose to reorder the actual memory access until much later, to a branch of the code that doesn't execute in some cases, so "the hardware implementation" could vary from "nothing happens" to "consistent SIGSEGV".
Compilers don't "detect undefined behaviour": they assume no undefined behaviour is present. It is not possible to change the semantics of the program that contains undefined behaviour because undefined behaviour means there are no valid semantics to begin with.
This is exactly the same situation as dividing by zero in mathematics. If your proof relies on division by zero, you can always prove anything true (the classic 1 == 0 proof for example).