Consider the following example. #include <stdio.h> struct s1 { int a; }; struct s2 { int a, b; }; int main (void) { union { struct s1 m1[1]; struct s2 m2[1]; } u; (u.m2)->b = 17; printf ("%d\n", ((struct s2 *) (struct s1 *) u.m2)->b); printf ("%d\n", ((struct s2 *) u.m1)->b); return 0; } zira:~> gcc-10 tst.c -o tst -O2 -Wstrict-aliasing tst.c: In function ‘main’: tst.c:22:20: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] 22 | printf ("%d\n", ((struct s2 *) u.m1)->b); | ~^~~~~~~~~~~~~~~~~~~ But there is no type-punning here. All accesses are done via struct s2. Everything else is pointer conversions, which are not related to the aliasing rules.
The warning only looks at the single expression it quotes which isn't really enough to discover you are doing right. It tries to be helpful - if you know better then disable the warning.
Why not having a level with no false positives? This would avoid to disable the warning globally. IMHO, using it when a union is involved is likely to generate false positives.
Hmm... The code might actually be invalid, but I'm not sure. In all the accesses, the effective type is the union type. But the C standard does not say what happens when one takes the member of a union type. * If the effective type remains the same (a union), then using any pointer cast to another type would be invalid, but this could be unintuitive and break a lot of code (and GCC should have emitted a warning for both printf lines). * I suspect that the effective type is locally[*] changed to the type implied by the type of the union member (this would be consistent with the GCC warning). However, it seems that there is nothing about that in the C standard, probably because the above example is really a corner case. [*] not sure how this is supposed to mean precisely.
(In reply to Vincent Lefèvre from comment #3) > * If the effective type remains the same (a union), then using any pointer > cast to another type would be invalid, but this could be unintuitive and > break a lot of code (and GCC should have emitted a warning for both printf > lines). Well, not the pointer cast itself, but an access to the memory after a conversion of the pointer type (even in the case where the pointer type matches the type of the member, as in ((struct s2 *) u.m2)->b). Note: the code example is derived from experimental GNU MPFR code (ubf2 branch) as an attempt to avoid aliasing issues in the current code without breaking the API or introducing drawbacks.