On 10/27/2017 04:54 PM, Richard Biener wrote: > On Fri, Oct 27, 2017 at 3:00 PM, Yubin Ruan <ablacktsh...@gmail.com> wrote: >> +Cc gcc-list. >> >> Does any gcc developer have any comments? > > See PR82224. The code is valid. > >> On Mon, Sep 25, 2017 at 01:41:55PM -0700, Myriachan wrote: >>> This question that "supercat" posted on Stack Overflow ran into an >>> interesting problem: >>> >>> https://stackoverflow.com/questions/46205744/is-this-use-of-unions-strictly-conforming/ >>> >>> A copy of the code involved is as follows: >>> >>> struct s1 {unsigned short x;}; >>> struct s2 {unsigned short x;}; >>> union s1s2 { struct s1 v1; struct s2 v2; }; >>> >>> static int read_s1x(struct s1 *p) { return p->x; } >>> static void write_s2x(struct s2 *p, int v) { p->x=v;} >>> >>> int test(union s1s2 *p1, union s1s2 *p2, union s1s2 *p3) >>> { >>> if (read_s1x(&p1->v1)) >>> { >>> unsigned short temp; >>> temp = p3->v1.x; >>> p3->v2.x = temp; >>> write_s2x(&p2->v2,1234); >>> temp = p3->v2.x; >>> p3->v1.x = temp; >>> } >>> return read_s1x(&p1->v1); >>> } >>> int test2(int x) >>> { >>> union s1s2 q[2]; >>> q->v1.x = 4321; >>> return test(q,q+x,q+x); >>> } >>> #include <stdio.h> >>> int main(void) >>> { >>> printf("%d\n",test2(0)); >>> } >>> >>> >>> Both GCC and Clang in -fstrict-aliasing mode with optimizations are acting >>> as if they ran into undefined behavior, and return 4321 instead of the >>> expected 1234. This happens in both C and C++ mode. Intel C++ and Visual >>> C++ return the expected 1234. All four compilers hardwire the result as a >>> constant parameter to printf rather than call test2 or modify memory at >>> runtime. >>> >>> From my reading of the C++ Standard, particularly [class.union]/5, >>> assignment expressions through a union member access changes the active >>> member of the union (if the union member has a trivial default constructor, >>> which it does here, being C code). Taking the address of p2->v2 and p1->v1 >>> ought to be legal because those are the active members of the union at the >>> time their pointers are taken. >>> >>> Is this a well-defined program, or is there subtle undefined behavior >>> happening here?
Thanks. As put in my stackoverflow answer[1], I believe this behavior is specified in the GCC-online-doc, in section `-fstrict-alisgin'[2]: Pay special attention to code like this: union a_union { int i; double d; }; int f() { union a_union t; t.d = 3.0; return t.i; } The practice of reading from a different union member than the one most recently written to (called *type-punning*) is common. Even with *-fstrict-aliasing*, type-punning is allowed, provided the memory is accessed through the union type. So, the code above works as expected. See Structures unions enumerations and bit-fields implementation. However, this code might not: int f() { union a_union t; int* ip; t.d = 3.0; ip = &t.i; return *ip; } Similarly, access by taking the address, casting the resulting pointer and dereferencing the result has undefined behavior, even if the cast uses a union type, e.g.: int f() { double d = 3.0; return ((union a_union *) &d)->i; } The -fstrict-aliasing option is enabled at levels -O2, -O3, -Os. I think the second example above is similar to the OP's question (although the C standard might not say so... so from my perspective if the second example above is true, the OP's code is invalid. Do anyone have any comment? Yubin [1]: https://stackoverflow.com/questions/46205744/is-this-use-of-unions-strictly-conforming/46968235?noredirect=1#comment80930683_46968235 [2]: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html