I think I'm really asking: is aliasing "transitive"? If the compiler knows that A might alias B, and B might alias C, then surely it should remember that A might therefore alias C. Perhaps this "obvious" transitive logic isn't required however?
An example, for clarity. The most interesting example, to me, of a strict-aliasing issue:
// g++ -fstrict-aliasing -std=c++11 -O2
#include <iostream>
union
{
int i;
short s;
} u;
int * i = &u.i;
int main()
{
u.i = 1; // line 1
*i += 1; // line 2
short & s = u.s;
s += 100; // line 3
std::cout
<< " *i\t" << *i << std::endl // prints 2
<< "u.i\t" << u.i << std::endl // prints 101
;
return 0;
}
g++ 5.3.0, on x86_64 (but not clang 3.5.0) gives the above output, where *i
and u.i
give different numbers. But they should give exactly the same number, because i
is defined at int * i = &u.i;
and i
doesn't change.
I have a theory: When 'predicting' the value of u.i
, the compiler asks which lines might affect the contents of u.i
. That includes line 1 obviously. And line 2 because int*
can alias an int
member of a union. And line 3 also, because anything that can affect one union member (u.s
) can affect another member of the same union. But when predicting *i
it doesn't realise that line 3 can affect the int
lvalue at *i
.
Does this theory seem reasonable?
I find this example funny because I don't have any casting in it. I managed to break strict-aliasing with doing any casting.