Undefined behavior in C++ and C means that it's up to the implementor to decide what happens in a situation. Usually it ends up being whatever is most convenient or most performant on the target architecture.
One consequence is that portable code can't depend on any particular behavior because it can be different between implementations. Every implementation will do something, but each one may do something completely different.
Another consequence is that "undefined behavior" isn't undefined in the context of a specific implementation because you can look at what it does and see how it's defined in that implementation. In this case, libc++ is essentially part of the implementation, so it's fair game for it to depend on implementation details of clang.
> Undefined behavior in C++ and C means that it's up to the implementor to decide what happens in a situation
> Every implementation will do something, but each one may do something completely different.
If I'm reading this correctly, you're saying that we can depend on each compiler providing a consistent way of handling each kind of undefined behaviour.
That's not correct. That describes implementation-defined behaviour, which is different. [0]
Compilers do not have to decide what behaviour should result from a particular kind of undefined behaviour, and then commit to ensuring that behaviour occurs consistently. That's the point of undefined behaviour: the compiler is permitted to assume the absence of undefined behaviour, and to optimise accordingly.
If you have undefined behaviour in your C++ code, you are not guaranteed to see consistent program behaviour. Your program is ill-formed. All bets are off, throughout the entire lifetime of your program. [1] (In C++, undefined behaviour can 'travel back in time', meaning that if your program invokes undefined behaviour, the behaviour across the entire lifetime of your program is made undefined.)
A compiler may choose to commit to a certain behaviour for a certain type of undefined behaviour (such as guaranteeing wrap-around behaviour for signed overflow), but it is not required to.
A compiler is required to define a consistent value for sizeof(int), because that's implementation-defined. [0]
> Another consequence is that "undefined behavior" isn't undefined in the context of a specific implementation because you can look at what it does and see how it's defined in that implementation.
This isn't right.
Unless the compiler's documentation tells you that you can rely upon its handling of the relevant undefined behaviour, then then compiler is not required to provide consistent behaviour for any particular kind of undefined behaviour.
> In this case, libc++ is essentially part of the implementation
One consequence is that portable code can't depend on any particular behavior because it can be different between implementations. Every implementation will do something, but each one may do something completely different.
Another consequence is that "undefined behavior" isn't undefined in the context of a specific implementation because you can look at what it does and see how it's defined in that implementation. In this case, libc++ is essentially part of the implementation, so it's fair game for it to depend on implementation details of clang.