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.
 
     
     
    