The strict aliasing rule has the purpose of allowing better optimizations. How does this not create problems with unions, where two pointers to two different types can point to the same address?
Take this example code (godbolt):
#include <cstdint>
#include <iostream>
void test(uint32_t* ptr32, uint64_t* ptr64) {
uint64_t stored = *ptr64;
*ptr32 = 0;
*ptr64 = stored;
}
union MyUnion {
uint32_t el32;
uint64_t el64;
};
int main(int argc, char** argv) {
MyUnion u;
u.el64 = -1;
std::cout << u.el64 << std::endl;
test(&u.el32, &u.el64);
std::cout << u.el64 << std::endl;
}
which I'd naively expect to output 18446744073709551615 (2^64-1) two times. However, according to the godbolt link above, clang gives two different numbers because it optimizes out the part that loads and stores back the uint64_t pointer value.
I doubt it's a bug, so I believe that somewhere in this program we have undefined behavior and I would like to know where exactly.
I currently think its most likely that the problem is when writing to an inactive union member through a pointer, assuming it actives that union member. This would mean that
union MyUnion {
uint32_t el32;
uint64_t el64;
};
int main() {
MyUnion u;
*(&u.el64) = 0;
}
is also undefined behavior. Is this the case? Or is it something else?
When inlining the function body of test, clang gives the output I'd expect (godbolt). It also gives the expected output when casting back the union member pointers back to a union pointer before accessing it (godbolt)