> How did the compiler writers reach the point where they were able to read "undefined behavior" as "does not happen" rather than "specification left this undefined so that the compiler can define it"
If they want the compiler to define it, the specification can say so, and on several things it says exactly that. This is what the phrase "implementation defined" is for.
The example of Undefined Behaviour which is most commonly given in this sort of argument - and you chose the same - is integer overflow. Why can't it just do what I meant? And you're right, the language could have chosen to do what you meant here and it did not. There are lots of options and you don't like the option C chose. I'll talk about that in a moment.
But more generally, Undefined Behaviour isn't at all like that. What happens if I cast my local telephone number to an integer pointer and then dereference the pointer ? Today that's Undefined Behaviour in C, but if we think that means "the compiler documentation should say" then what should it say?
"It does whatever the underlying hardware would do" is a circular definition, this is a computer program, what the hardware does is whatever we told it to do. So maybe you say well, it emits this particular machine code. Congratulations now your "compiler documentation" reads like the source code for the compiler and your users still don't know the answer because guess what, the CPU vendor can't do any better with that either.
Now, back to those integer overflows. We can do a whole bunch of things here, and they all have different consequences.
WUFFS says this is forbidden, you get a compiler error. If your code can overflow, that's a bad WUFFS function it does not compile.
Several languages including Python just don't have overflow. Your integer types just get bigger, this may be annoyingly slow in some cases, but like actual integers from grade school you won't just accidentally make one that's too big by mistake and something weird happens.
We could wrap the integers as you seem to prefer. This is what Rust's Wrapping<> types always do and several languages provide alternate arithmetic operators to request wrapping
We could saturate the integers, which means they stop at the edge of the overflow. This is what Rust's Saturating<> types do, and again I believe some languages have saturating operators (at least addition and multiplication anyway)
We could make arithmetic operations all "checked" so that they can fail if there would be overflow. The check could be in the form of a soft error, or it could cause something more dramatic like Rust's panic.
Or like C we can just say we refuse to define this, don't do it.