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

Reply via email to