Francis Moreau said:
Ben Bacarisse <
[email protected]> writes:
Well a recent discussion I have on gcc-help mailing list about GCC's
strict aliasing stuff confused me about this point. Please have a look
to
Message-ID: <
[email protected]>
if you're interested.
Basically GCC's man page gives an example of code which might not work
as expected if '-fstrict-aliasing' switch is passed. Here's the piece of
code taken from my recent discussion:
> Looking again at the second example:
>
> int i;
> double d;
> };
>
> int f() {
> union a_union t;
> int* ip;
> t.d = 3.0;
> ip = &t.i;
> return *ip;
> }
>
We agreed that this invokes undefined behaviour, but I think the reasons
why are different.
My reason was because it's reinterpreting a double as an int which gives
undefined behaviour.
That may or may not be undefined. If the implementation's int type has
no trap representations then all bit patterns are valid int values. In
fact the gcc manual has an example a couple of lines before this one of
re-interpreting the double as an int (by not using pointers) and it
declares that the fragment is not undefined. The gcc doc are well
placed to say this since the authors will know if there can be any trap
representations. I know this not the key issue with this code fragment
but it's important: if the aliasing rules permit the above code, then it
won't be undefined (as far as gcc is concerned) due to the
re-interpretation.
The other reason is I _think_ because of aliasing issue (again taken
from the discussion):
int, if you can say that object exists at all: it does not have a
stored value. The stored value of t is a double with value 3.0 .
You can take its address and access it via that as "double" (or
"char"), or you can access it as the union it is. You can not
access it as "int".
which actually doesn't answer to the question.
I think the problem relates to what constitutes "the object" for the
purposes of 6.5 paragraphs 6-7.
Clearly the person you are talking to thinks that t.i might not be
considered "an object" at least not one that can be accessed via an
lvalue expression of type int at this point in the code. They consider
"the object" being accessed to be t.d since that is what is stored in
the union.
This does not conflict with the previous example in the manual which is:
| 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 will
| work as expected.
Here, if the "the object" being accessed is t.d, then access via t.i is
valid since it is an access to "the object" (t.d) via a union that
includes, as one of its members, the effective type (double) of that
object. In the pointer case, the lvalue expression used (*ip) is not an
aggregate or union that includes double as one of its members, nor is a
lvalue expression with a type compatible with that of the "the object"
being accessed (double).
This is my best analysis of what is being claimed from what you present
here (I don't know how to access gmane via anything by the crudest
web-based interface and I have therefore not bothered to read the other
thread -- sorry).
The flip-side of the argument (which I don't hold) is that "the object"
being accessed in both examples is t.i and, since that has a declared
type (and hence effective type) of int, accessing it via t.i and *ip are
both fine.
Until I hear other arguments, I'm going with the gcc documentation
partly because they are smart people who put these examples there
after careful consideration, but mainly because the intent of the
standard seems to be that a union "holds" only one object at a time.
Another argument is this: the aliasing rules would hardly permit any
conclusions at all unless my interpretation of what is being said is
correct. A function taking
void g(int *ip, double *dp);
could not conclude that *ip and *dp don't alias storage because we could
pass
g(&t.i, &t.u);