Remix.run Logo
▲ drdexebtjl 4 hours ago

This isn’t a good mental model. The compiler isn’t limited to just affecting the code after the undefined behavior. It is allowed to assume undefined behavior never happens for the entire program.

For example, this code with an improper guard:

    if (!p) puts("error");
    printf("%d", *p);
Since the program dereferences p in line 2, and dereferencing null is UB, the compiler is allowed to assume p is never null, so it’s allowed to delete line 1, even though it would have executed before the point where UB would happen.

Even worse, the compiler isn't just allowed to not do things you told it to do, it's also allowed to do anything too.

▲dgrunwald an hour ago | parent | next [-]

The compiler is only allowed to do that when it can prove `puts` will return (i.e. it must prove `puts` does not call `exit`). In a world with SIGPIPE that shouldn't happen. (unless maybe the compiler can prove that there's enough space available in the stdlib IO buffers so that puts won't actually output anything. but then there's no obversable difference in behavior)

▲Veserv 3 hours ago | parent | prev [-]

The question was: “The concept of undefined behaviour specific to C/C++ has always seemed batshit insane to me”.

I was explaining why undefined behavior as a concept is a very sensible idea. Whether the expansive interpretation of the optimizations you are allowed to do when encountering the “impossible” are reasonable is a different question.

▲p1necone an hour ago | parent | next [-]

Hence the 'specific to C/C++' part of that sentence. 'Undefined behaviour' is a probably unavoidable part of programming language design. The way that C/C++ interpret/handle it is batshit insane.

▲drdexebtjl 3 hours ago | parent | prev [-]

The part that is batshit insane is precisely that it makes the entire program impossible to reason about.

If UB meant what you described, it would be sensible, but it doesn't, and it's not.