I agree, this is a sensible interpretation of the standard.
However, that just goes to confirm what I said above, that the in-memory representation of a pointer (what you call it’s value) has to be separated from the actual, abstract value that is the pointer.
That’s why I would be careful with the term “value of a pointer here”.
The distinction I make here is equivalent to the distinction between the abstract, mathematical concept of the number “3”, and the bit sequence “00000011” that represents “3” as a member of the type unsigned char. Similarly, pointers will have an abstract value (and it’s unclear what this should best be, though the PhD thesis I quited above makes a proposal that I like), and a representation in terms of bits on the machine. The two must not be conflated.
So, it seems we agree that two pointers can have the same bit representation, but be abstractly different values.
This is indeed a very important question, and the standard is unclear - in fact, the standard and compiler writers don’t even agree on all points here: How far do these indeterminate values propagate, and what happens at the “end” of propagation - do we see undefined behavior, or “standard” non-determinism.
In your example, clang is using the fact that pointers with equal bit-representation can be different pointers, and on pointer-to-integer conversion preserving the bit representation. I would argue that the transformation relies on the fact that the indeterminate value propagates through the integer. Unfortunately, pointer-to-integer conversion is formally very poorly understood.
So, maybe one could say that while LLVM uses the liberty granted by the C standard (pointers become indeterminate when the object they point to is deallocated) to justify making a distinction between a pointer bit pattern, and a pointer value, such that the same bit pattern can be “different” pointers. However, other than that, it doesn’t do anything funky with the bit pattern; in particular, it does not allow the bit pattern to change in random ways, and pointer comparison would remain deterministic.
This would mean, for example, that even though LLVM assumes p and q in my example not to alias, it would not be allowed to optimize p == q or p != q away into a constant. it would have to always preserve the original check. Is that the case?
Of course, this means someone will have lots of fun, eventually, to figure out what the heck this all means formally…
LLVM is not a “pointers are just integers” language, so certainly unsafe Rust cannot be such a language, either. Maybe safe Rust can uphold that fiction. Since safe code cannot do anything with raw pointers except for comparing them (and casting them), right now I can’t find a way that safe code alone could witness strange pointer behavior - provided that LLVM only uses the “indeterminate values” license granted by C for optimizing pointer accesses, but not for pointer comparisons.