https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70166
--- Comment #2 from Alexander Cherepanov <ch3root at openwall dot com> --- On 03/11/2016 12:16 PM, rguenth at gcc dot gnu.org wrote: > You are not accessing through the union type but through a pointer to double > and int because u.a and p->d decay to a pointer: > > *(int *) &u.a = 1; > *(double *) &p->d = 0.0; > if (*(int *) &u.a != 0) > { > abort (); This is true but I'm afraid it doesn't explain anything. First of all, some data points: - the example works fine with 1d array; - the example works fine with **u.a replaced by u.a[0][0], *u.a[0] or (*u.a)[0]; - the example fails in the same way with u.a and p->d wrapped in structs. In all these cases u.a and p->d decay to a pointer too but gcc does the right thing anyway. Furthermore, saying that the problem is in array-to-pointer decay means that arrays inside unions (including recursively) cannot be accessed directly at all (they would work only wrapped in structs and copied in and out inside these structs). I guess this is not intended and arrays inside unions are supposed to work somehow. Ok, let's change if to "if (**u.a != (*u.a)[0])" and do -fdump-tree-all. In 003t.original we have this: *(int *) &u.a = 1; *(double *) &p->d = 0.0; if (*(int *) &u.a != (*(int[1] *) &u.a)[0]) { abort (); and in 004t.gimple we have this: D.1636 = &u.a; D.1639 = MEM[(int *)D.1636]; D.1640 = u.a[0][0]; if (D.1639 != D.1640) goto <D.1641>; else goto <D.1642>; <D.1641>: abort (); It seems the general direction is to convert * to [] but gcc fails to convert **. Different treatment of **x and x[0][0] is not limited to unions in any way. In other cases I guess it will just prevent some optimizations but with unions it has a user-visible effect.