Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #22865 > unrolled thread

Function casting - UB?

Started bynroberts <roberts.noah@gmail.com>
First post2012-06-27 08:50 -0700
Last post2012-07-08 11:06 -0700
Articles 20 on this page of 22 — 12 participants

Back to article view | Back to comp.lang.c


Contents

  Function casting - UB? nroberts <roberts.noah@gmail.com> - 2012-06-27 08:50 -0700
    Re: Function casting - UB? Eric Sosman <esosman@ieee-dot-org.invalid> - 2012-06-27 12:49 -0400
      Re: Function casting - UB? Heikki Kallasjoki <fis+usenet@zem.fi> - 2012-06-27 17:54 +0000
        Re: Function casting - UB? "io_x" <a@b.c.invalid> - 2012-06-28 18:58 +0200
    Re: Function casting - UB? Tim Rentsch <txr@alumni.caltech.edu> - 2012-06-27 10:37 -0700
      Re: Function casting - UB? Johannes Bauer <dfnsonfsduifb@gmx.de> - 2012-06-28 09:23 +0200
        Re: Function casting - UB? Johannes Bauer <dfnsonfsduifb@gmx.de> - 2012-06-28 09:27 +0200
          Re: Function casting - UB? Jens Gustedt <jens.gustedt@loria.fr> - 2012-06-28 12:03 +0200
            Re: Function casting - UB? Tim Rentsch <txr@alumni.caltech.edu> - 2012-06-29 11:10 -0700
        Re: Function casting - UB? Tim Rentsch <txr@alumni.caltech.edu> - 2012-06-29 11:03 -0700
    Re: Function casting - UB? Les Cargill <lcargill99@comcast.com> - 2012-06-27 12:58 -0500
    Re: Function casting - UB? Les Cargill <lcargill99@comcast.com> - 2012-06-27 12:59 -0500
      Re: Function casting - UB? James Kuyper <jameskuyper@verizon.net> - 2012-06-27 14:45 -0400
        Re: Function casting - UB? "christian.bau" <christian.bau@cbau.wanadoo.co.uk> - 2012-06-27 14:55 -0700
          Re: Function casting - UB? Tim Rentsch <txr@alumni.caltech.edu> - 2012-06-27 17:44 -0700
            Re: Function casting - UB? Joshua Maurice <joshuamaurice@gmail.com> - 2012-07-07 16:46 -0700
              Re: Function casting - UB? Barry Schwarz <schwarzb@dqel.com> - 2012-07-08 09:19 -0700
                Re: Function casting - UB? Tim Rentsch <txr@alumni.caltech.edu> - 2012-07-08 11:13 -0700
                  Re: Function casting - UB? Barry Schwarz <schwarzb@dqel.com> - 2012-07-08 15:24 -0700
                    Re: Function casting - UB? Joshua Maurice <joshuamaurice@gmail.com> - 2012-07-08 18:12 -0700
                    Re: Function casting - UB? Tim Rentsch <txr@alumni.caltech.edu> - 2012-07-10 10:40 -0700
              Re: Function casting - UB? Tim Rentsch <txr@alumni.caltech.edu> - 2012-07-08 11:06 -0700

Page 1 of 2  [1] 2  Next page →


#22865 — Function casting - UB?

Fromnroberts <roberts.noah@gmail.com>
Date2012-06-27 08:50 -0700
SubjectFunction casting - UB?
Message-ID<0c176b1e-39f3-4e65-8a7b-eca16b8955c5@d24g2000yqh.googlegroups.com>
In C++ this would be totally undefined.  How about in C?  It works on
my machine with my compiler...

If it's UB, is it one of those rules that pretty much has to work
anyway?  What I mean is things like the fact that it's UB to assign to
one part of a union and read from another but it generally works and
is a pretty important construct.  Is this like that?

#include <stdio.h>

typedef void (*fun)(int, int);

void f(int i) {
	int q = 33;
	printf("%d %d\n", i, q);
}

int main(void) {
	fun funptr = (fun)f;
	funptr(3,2);

	return 0;
}

output => 3 33

[toc] | [next] | [standalone]


#22870

FromEric Sosman <esosman@ieee-dot-org.invalid>
Date2012-06-27 12:49 -0400
Message-ID<jsfdik$vtr$1@dont-email.me>
In reply to#22865
On 6/27/2012 11:50 AM, nroberts wrote:
> In C++ this would be totally undefined.  How about in C?  It works on
> my machine with my compiler...
>
> If it's UB, is it one of those rules that pretty much has to work
> anyway?  What I mean is things like the fact that it's UB to assign to
> one part of a union and read from another but it generally works and
> is a pretty important construct.  Is this like that?
>
> #include <stdio.h>
>
> typedef void (*fun)(int, int);
>
> void f(int i) {
> 	int q = 33;
> 	printf("%d %d\n", i, q);
> }
>
> int main(void) {
> 	fun funptr = (fun)f;
> 	funptr(3,2);
>
> 	return 0;
> }
>
> output => 3 33

     Undefined behavior, because the type of the pointer expression
`funptr' used in the call does not match the type of the called
function `f'.  (The fact that the `(fun)' cast was necessary
should alert you to the mismatch.)

     It's likely to "work" on a good many systems.  For example,
on a system where the first four or so "sufficiently small"
arguments are passed in a few designated registers, `main' will
fill two registers and `f' will use only one of them, and there's
a decent chance that the one `f' uses will be the one `main' put
the 3 into.  But it would be a bad idea to generalize from the
apparent success of this simple case to the notion "It works!"
Throw in a floating-point argument, or a union argument, or even
more than MagicNumber plain int arguments, and things may well go
blooey.  Make the function struct- or union-valued, and things are
more likely than not to go blooey.

     And then, there's wide variety in function linkage mechanisms.
Sometimes the caller takes care of both setting up and disposing
of the argument list, but sometimes the caller sets it up and the
callee disposes -- and if the callee does cleanup for a one-argument
list while the caller expected it to clean up two, it's blooey again.

     Does it "pretty much" have to work?  That sort of depends on
your definition of "pretty much," but my take would be "No."

     Besides: Can you come up with a good reason to want to do such
a perverted thing in the first place?  In what way does this merit
being called "a pretty important construct?"

-- 
Eric Sosman
esosman@ieee-dot-org.invalid

[toc] | [prev] | [next] | [standalone]


#22877

FromHeikki Kallasjoki <fis+usenet@zem.fi>
Date2012-06-27 17:54 +0000
Message-ID<slrnjumi6h.dba.fis@iris.zem.fi>
In reply to#22870
On 2012-06-27, Eric Sosman <esosman@ieee-dot-org.invalid> wrote:
> On 6/27/2012 11:50 AM, nroberts wrote:
>> In C++ this would be totally undefined.  How about in C?  It works on
>> my machine with my compiler...
...
>> typedef void (*fun)(int, int);
>>
>> void f(int i) {
>> 	int q = 33;
>> 	printf("%d %d\n", i, q);
>> }
>>
>> int main(void) {
>> 	fun funptr = (fun)f;
>> 	funptr(3,2);
>>
>> 	return 0;
>> }
...
> the 3 into.  But it would be a bad idea to generalize from the
> apparent success of this simple case to the notion "It works!"

I fully agree that empirical experimentation is not terribly useful
(and/or perhaps interesting), but FWIW, a counter-example:

$ cat tmp.c
#include <windows.h>

int WINAPI f(int a) { return a; }

int g(void) {
    int WINAPI (*p)(int, int) = (int WINAPI (*)(int, int))f;
    return p(0, 1);
}

int main(void) {
    return g();
}

$ i586-mingw32msvc-gcc -o tmp.exe tmp.c -O2 -fomit-frame-pointer
$ wine ./tmp.exe
wine: Unhandled page fault on read access to 0x00000001 at address 0x1
(thread 0022), starting debugger...

(For the "not really comp.lang.c material" details, see below.)

>      And then, there's wide variety in function linkage mechanisms.
> Sometimes the caller takes care of both setting up and disposing
> of the argument list, but sometimes the caller sets it up and the
> callee disposes -- and if the callee does cleanup for a one-argument
> list while the caller expected it to clean up two, it's blooey again.

The above case (involving the "WINAPI"/"stdcall" calling convention) is
an example of the latter, and with the frame pointer omitted, 'g' ends
up using the second provided parameter as its return address.

-- 
Heikki Kallasjoki

[toc] | [prev] | [next] | [standalone]


#22935

From"io_x" <a@b.c.invalid>
Date2012-06-28 18:58 +0200
Message-ID<4fec8bc3$0$1385$4fafbaef@reader2.news.tin.it>
In reply to#22877
"Heikki Kallasjoki" <fis+usenet@zem.fi> ha scritto nel messaggio
news:slrnjumi6h.dba.fis@iris.zem.fi...
> On 2012-06-27, Eric Sosman <esosman@ieee-dot-org.invalid> wrote:
>> On 6/27/2012 11:50 AM, nroberts wrote:
>>> In C++ this would be totally undefined.  How about in C?  It works on
>>> my machine with my compiler...
> ...
>>> typedef void (*fun)(int, int);
>>>
>>> void f(int i) {
>>> int q = 33;
>>> printf("%d %d\n", i, q);
>>> }
>>>
>>> int main(void) {
>>> fun funptr = (fun)f;
>>> funptr(3,2);
>>>
>>> return 0;
>>> }
> ...
>> the 3 into.  But it would be a bad idea to generalize from the
>> apparent success of this simple case to the notion "It works!"
>
> I fully agree that empirical experimentation is not terribly useful
> (and/or perhaps interesting), but FWIW, a counter-example:
>
> $ cat tmp.c
> #include <windows.h>
>
> int WINAPI f(int a) { return a; }
>
> int g(void) {
>    int WINAPI (*p)(int, int) = (int WINAPI (*)(int, int))f;

so here you cast as a function of 2 args one function of 1 arg ...

>    return p(0, 1);
> }
>
> int main(void) {
>    return g();
> }
>
> $ i586-mingw32msvc-gcc -o tmp.exe tmp.c -O2 -fomit-frame-pointer
> $ wine ./tmp.exe
> wine: Unhandled page fault on read access to 0x00000001 at address 0x1
> (thread 0022), starting debugger...
>
> (For the "not really comp.lang.c material" details, see below.)
>
>>      And then, there's wide variety in function linkage mechanisms.
>> Sometimes the caller takes care of both setting up and disposing
>> of the argument list, but sometimes the caller sets it up and the
>> callee disposes -- and if the callee does cleanup for a one-argument
>> list while the caller expected it to clean up two, it's blooey again.
>
> The above case (involving the "WINAPI"/"stdcall" calling convention) is
> an example of the latter, and with the frame pointer omitted, 'g' ends
> up using the second provided parameter as its return address.
>
> -- 
> Heikki Kallasjoki

i not understand what you whant to say; but i like function pointers
and using pointers [and cast to functions] and call as function...






[toc] | [prev] | [next] | [standalone]


#22876

FromTim Rentsch <txr@alumni.caltech.edu>
Date2012-06-27 10:37 -0700
Message-ID<kfnzk7o4pxf.fsf@x-alumni2.alumni.caltech.edu>
In reply to#22865
nroberts <roberts.noah@gmail.com> writes:

> In C++ this would be totally undefined.  How about in C?  It works on
> my machine with my compiler...

The example you show is clearly and explicitly undefined behavior.

> If it's UB, is it one of those rules that pretty much has to work
> anyway?  What I mean is things like the fact that it's UB to assign to
> one part of a union and read from another but it generally works and
> is a pretty important construct.  Is this like that?

No, it's completely different, because it is both allowed to fail
and is likely to fail on some platforms.  The other example you give,
assigning to one member of a union and reading from another, is
actually defined behavior, _not_ undefined behavior.

> #include <stdio.h>
>
> typedef void (*fun)(int, int);
>
> void f(int i) {
> 	int q = 33;
> 	printf("%d %d\n", i, q);
> }
>
> int main(void) {
> 	fun funptr = (fun)f;
> 	funptr(3,2);
>
> 	return 0;
> }
>
> output => 3 33

The call to f() through 'funptr' is undefined behavior because
the type of the function pointer used to call is not compatible
with the type of the function actually being called.  And that
isn't just a theoretical problem.

[toc] | [prev] | [next] | [standalone]


#22916

FromJohannes Bauer <dfnsonfsduifb@gmx.de>
Date2012-06-28 09:23 +0200
Message-ID<jsh0qa$49g$1@news.albasani.net>
In reply to#22876
On 27.06.2012 19:37, Tim Rentsch wrote:
> The other example you give,
> assigning to one member of a union and reading from another, is
> actually defined behavior, _not_ undefined behavior.

I also was under the impression that writing to member x and reading
member y of a union is UB. Wikipedia says "This is not, however, a safe
use of unions in general.", which is pretty vague (i.e. it's not clear
which cases are safe and which are not).

Could you elaborate on why you think this is well-defined?

Best regards,
Johannes

-- 
>> Wo hattest Du das Beben nochmal GENAU vorhergesagt?
> Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
 - Karl Kaos über Rüdiger Thomas in dsa <hidbv3$om2$1@speranza.aioe.org>

[toc] | [prev] | [next] | [standalone]


#22917

FromJohannes Bauer <dfnsonfsduifb@gmx.de>
Date2012-06-28 09:27 +0200
Message-ID<jsh10i$49g$2@news.albasani.net>
In reply to#22916
On 28.06.2012 09:23, Johannes Bauer wrote:
> On 27.06.2012 19:37, Tim Rentsch wrote:
>> The other example you give,
>> assigning to one member of a union and reading from another, is
>> actually defined behavior, _not_ undefined behavior.
> 
> I also was under the impression that writing to member x and reading
> member y of a union is UB. Wikipedia says "This is not, however, a safe
> use of unions in general.", which is pretty vague (i.e. it's not clear
> which cases are safe and which are not).
> 
> Could you elaborate on why you think this is well-defined?

Ah, I just read James' response further down. Interesting. Really
thought this was undefined. Is this a recent change?

Best regards,
Johannes

-- 
>> Wo hattest Du das Beben nochmal GENAU vorhergesagt?
> Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
 - Karl Kaos über Rüdiger Thomas in dsa <hidbv3$om2$1@speranza.aioe.org>

[toc] | [prev] | [next] | [standalone]


#22920

FromJens Gustedt <jens.gustedt@loria.fr>
Date2012-06-28 12:03 +0200
Message-ID<4FEC2BDA.3090801@loria.fr>
In reply to#22917
Am 28.06.2012 09:27, schrieb Johannes Bauer:
> On 28.06.2012 09:23, Johannes Bauer wrote:
>> On 27.06.2012 19:37, Tim Rentsch wrote:
>>> The other example you give,
>>> assigning to one member of a union and reading from another, is
>>> actually defined behavior, _not_ undefined behavior.
>>
>> I also was under the impression that writing to member x and reading
>> member y of a union is UB. Wikipedia says "This is not, however, a safe
>> use of unions in general.", which is pretty vague (i.e. it's not clear
>> which cases are safe and which are not).
>>
>> Could you elaborate on why you think this is well-defined?
> 
> Ah, I just read James' response further down. Interesting. Really
> thought this was undefined. Is this a recent change?

I think this was not considered as a change in contents but given as
more precision on the intent. n1256.pdf has modification marks in this
region so I suppose that these came with TC3. They state

 > When a value is stored in a member of an object of union type, the
 > bytes of the object representation that do not correspond to that
 > member but do correspond to other members take unspecified values.

which in terms of the standard means that it is only UB if these
unspecific values are "forbidden" values for that type, in particular
trap representations.

This means that for most of modern architectures manipulating integer
values (except of _Bool) through unions is completely ok. Floating
point values, _Bool, and pointer types must be treated with more care.

Jens

[toc] | [prev] | [next] | [standalone]


#22991

FromTim Rentsch <txr@alumni.caltech.edu>
Date2012-06-29 11:10 -0700
Message-ID<kfn8vf62dmo.fsf@x-alumni2.alumni.caltech.edu>
In reply to#22920
Jens Gustedt <jens.gustedt@loria.fr> writes:

> Am 28.06.2012 09:27, schrieb Johannes Bauer:
>> On 28.06.2012 09:23, Johannes Bauer wrote:
>>> On 27.06.2012 19:37, Tim Rentsch wrote:
>>>> The other example you give,
>>>> assigning to one member of a union and reading from another, is
>>>> actually defined behavior, _not_ undefined behavior.
>>>
>>> I also was under the impression that writing to member x and reading
>>> member y of a union is UB. Wikipedia says "This is not, however, a safe
>>> use of unions in general.", which is pretty vague (i.e. it's not clear
>>> which cases are safe and which are not).
>>>
>>> Could you elaborate on why you think this is well-defined?
>> 
>> Ah, I just read James' response further down. Interesting. Really
>> thought this was undefined. Is this a recent change?
>
> I think this was not considered as a change in contents but given as
> more precision on the intent. n1256.pdf has modification marks in this
> region so I suppose that these came with TC3. [snip]

Yes, if you read the Defect Report that prompted the change I
think you'll find that the intention was that the behavior
required was supposed to be the same all along (ie, since C90 and
presumably also before that), but changes in wording in other
places raised a concern that this (unchanged) requirement was not
evident enough without the footnote.

[toc] | [prev] | [next] | [standalone]


#22990

FromTim Rentsch <txr@alumni.caltech.edu>
Date2012-06-29 11:03 -0700
Message-ID<kfnd34i2dya.fsf@x-alumni2.alumni.caltech.edu>
In reply to#22916
Johannes Bauer <dfnsonfsduifb@gmx.de> writes:

> On 27.06.2012 19:37, Tim Rentsch wrote:
>> The other example you give,
>> assigning to one member of a union and reading from another, is
>> actually defined behavior, _not_ undefined behavior.
>
> I also was under the impression that writing to member x and reading
> member y of a union is UB. Wikipedia says "This is not, however, a safe
> use of unions in general.", which is pretty vague (i.e. it's not clear
> which cases are safe and which are not).
>
> Could you elaborate on why you think this is well-defined?

Besides the particular footnote (which you already mentioned in
your own followup), there is just the normative text pertaining to
types and storage access.  If you read through the two main
sections on types (6.2.5 and 6.2.6), and also the description of
what happens on lvalue-to-value conversion, I think it's pretty
easy to see that the definition is there (although I freely admit
it isn't expressed as directly as one might like).  Basically, the
same passages that explain how ordinary (ie, non-union-member)
access works also explain how access to union members work;  the
only thing that's missing is knowing that the respective memories
overlap, which is stated in 6.2.5.  There is another detail having
to do with effective type rules, but that doesn't contribute to
defining the semantics;  it just needs to be checked to make sure
the effective type rules don't _un_define the semantics (and they
don't, but if you're interested look at 6.5 p6&7).

[toc] | [prev] | [next] | [standalone]


#22878

FromLes Cargill <lcargill99@comcast.com>
Date2012-06-27 12:58 -0500
Message-ID<jsfhe5$m4n$1@dont-email.me>
In reply to#22865
nroberts wrote:
> In C++ this would be totally undefined.  How about in C?  It works on
> my machine with my compiler...
>
> If it's UB, is it one of those rules that pretty much has to work
> anyway?  What I mean is things like the fact that it's UB to assign to
> one part of a union and read from another but it generally works and
> is a pretty important construct.  Is this like that?
>

No. The example given is just wrong, period.

> #include <stdio.h>
>
> typedef void (*fun)(int, int);
>
> void f(int i) {
> 	int q = 33;
> 	printf("%d %d\n", i, q);
> }
>
> int main(void) {
> 	fun funptr = (fun)f;
> 	funptr(3,2);
>
> 	return 0;
> }
>
> output => 3 33
>


--
Les Cargill

[toc] | [prev] | [next] | [standalone]


#22879

FromLes Cargill <lcargill99@comcast.com>
Date2012-06-27 12:59 -0500
Message-ID<jsfhfs$m4n$2@dont-email.me>
In reply to#22865
nroberts wrote:
> In C++ this would be totally undefined.  How about in C?  It works on
> my machine with my compiler...
>
> If it's UB, is it one of those rules that pretty much has to work
> anyway?  What I mean is things like the fact that it's UB to assign to
> one part of a union and read from another

I am not sure that that is undefined behavior. That's sort of what
unions are *for*.

> but it generally works and
> is a pretty important construct.  Is this like that?
>
> #include <stdio.h>
>
> typedef void (*fun)(int, int);
>
> void f(int i) {
> 	int q = 33;
> 	printf("%d %d\n", i, q);
> }
>
> int main(void) {
> 	fun funptr = (fun)f;
> 	funptr(3,2);
>
> 	return 0;
> }
>
> output => 3 33
>


--
Les Cargill

[toc] | [prev] | [next] | [standalone]


#22882

FromJames Kuyper <jameskuyper@verizon.net>
Date2012-06-27 14:45 -0400
Message-ID<4FEB54D7.2030106@verizon.net>
In reply to#22879
On 06/27/2012 01:59 PM, Les Cargill wrote:
> nroberts wrote:
...
>> anyway?  What I mean is things like the fact that it's UB to assign to
>> one part of a union and read from another
> 
> I am not sure that that is undefined behavior. That's sort of what
> unions are *for*.

A footnote in the current version of the standard says that the result
of reading from a different member of a union than the one last written
is that the bit pattern stored in that memory is reinterpreted according
to the type of the member being read; it would therefore have defined
behavior, so long as that bit pattern is a valid one for that type.
Some have claimed that this conclusion can be derived from the normative
text of the standard, but I find the argument supporting that claim
weak. There's certainly no normative text that says so directly.
However, that is how unions were always intended to work, whether or not
the normative text of the standard has ever actually said so.

[toc] | [prev] | [next] | [standalone]


#22902

From"christian.bau" <christian.bau@cbau.wanadoo.co.uk>
Date2012-06-27 14:55 -0700
Message-ID<dd47ef15-63a6-4d9b-a2f4-9673ac644260@fr28g2000vbb.googlegroups.com>
In reply to#22882
On Jun 27, 7:45 pm, James Kuyper <jameskuy...@verizon.net> wrote:

> A footnote in the current version of the standard says that the result
> of reading from a different member of a union than the one last written
> is that the bit pattern stored in that memory is reinterpreted according
> to the type of the member being read; it would therefore have defined
> behavior, so long as that bit pattern is a valid one for that type.
> Some have claimed that this conclusion can be derived from the normative
> text of the standard, but I find the argument supporting that claim
> weak. There's certainly no normative text that says so directly.
> However, that is how unions were always intended to work, whether or not
> the normative text of the standard has ever actually said so.

I had to check that, and you are right (footnote 95 in the N1570
draft). I think there is a problem. Say long and float have the same
size, I have a union containing a long and a float, I write to the
long and read the float, then I am supposed to get a float with
exactly those bits that I stored. That's perfectly fine.

But what if the compiler doesn't know that both are elements of the
same union? If I just have a long*, and a float*, which _might_ point
to members of the same union, but the compiler doesn't know. Does the
rule apply then as well? That would completely destroy what is said in
other places.

I'd prefer if this was said in the standard explicitely, but with the
restriction that the value must be written, then read, using the . or -
> operators.

[toc] | [prev] | [next] | [standalone]


#22911

FromTim Rentsch <txr@alumni.caltech.edu>
Date2012-06-27 17:44 -0700
Message-ID<kfn395g465i.fsf@x-alumni2.alumni.caltech.edu>
In reply to#22902
"christian.bau" <christian.bau@cbau.wanadoo.co.uk> writes:

> On Jun 27, 7:45 pm, James Kuyper <jameskuy...@verizon.net> wrote:
>
>> A footnote in the current version of the standard says that the result
>> of reading from a different member of a union than the one last written
>> is that the bit pattern stored in that memory is reinterpreted according
>> to the type of the member being read; it would therefore have defined
>> behavior, so long as that bit pattern is a valid one for that type.
>> Some have claimed that this conclusion can be derived from the normative
>> text of the standard, but I find the argument supporting that claim
>> weak. There's certainly no normative text that says so directly.
>> However, that is how unions were always intended to work, whether or not
>> the normative text of the standard has ever actually said so.
>
> I had to check that, and you are right (footnote 95 in the N1570
> draft). I think there is a problem. Say long and float have the same
> size, I have a union containing a long and a float, I write to the
> long and read the float, then I am supposed to get a float with
> exactly those bits that I stored. That's perfectly fine.
>
> But what if the compiler doesn't know that both are elements of the
> same union? If I just have a long*, and a float*, which _might_ point
> to members of the same union, but the compiler doesn't know. Does the
> rule apply then as well? That would completely destroy what is said in
> other places.

This case is different, because it is addressed by different
portions of the effective type rules.  In particular, using
the '.' or '->' form of access, the lvalue being accessed
has a declared type, and so those accesses never violate effective
type rules.  When access is done using pointers, the rule for
determining effective type is different, so the two accesses
may very well run afoul of the effective type requirements.

> I'd prefer if this was said in the standard explicitely, but with the
> restriction that the value must be written, then read, using the . or -
>> operators.

Unfortunately the Standard often expresses itself rather obliquely,
and this case certainly falls into that category.  However, it should
be easy to see that the two different cases you bring up are covered
under different areas of the effective type rules.  See 6.5 p6.
Note especially the first sentence, which applies in the case of
member access (ie, through '.' or '->', but which does not apply
in the case of pointer access.

[toc] | [prev] | [next] | [standalone]


#23193

FromJoshua Maurice <joshuamaurice@gmail.com>
Date2012-07-07 16:46 -0700
Message-ID<d90ed80f-f97e-4d01-8dee-09cf8fa94165@f8g2000pbf.googlegroups.com>
In reply to#22911
On Jun 27, 5:44 pm, Tim Rentsch <t...@alumni.caltech.edu> wrote:
> "christian.bau" <christian....@cbau.wanadoo.co.uk> writes:
> > On Jun 27, 7:45 pm, James Kuyper <jameskuy...@verizon.net> wrote:
>
> >> A footnote in the current version of the standard says that the result
> >> of reading from a different member of a union than the one last written
> >> is that the bit pattern stored in that memory is reinterpreted according
> >> to the type of the member being read; it would therefore have defined
> >> behavior, so long as that bit pattern is a valid one for that type.
> >> Some have claimed that this conclusion can be derived from the normative
> >> text of the standard, but I find the argument supporting that claim
> >> weak. There's certainly no normative text that says so directly.
> >> However, that is how unions were always intended to work, whether or not
> >> the normative text of the standard has ever actually said so.
>
> > I had to check that, and you are right (footnote 95 in the N1570
> > draft). I think there is a problem. Say long and float have the same
> > size, I have a union containing a long and a float, I write to the
> > long and read the float, then I am supposed to get a float with
> > exactly those bits that I stored. That's perfectly fine.
>
> > But what if the compiler doesn't know that both are elements of the
> > same union? If I just have a long*, and a float*, which _might_ point
> > to members of the same union, but the compiler doesn't know. Does the
> > rule apply then as well? That would completely destroy what is said in
> > other places.
>
> This case is different, because it is addressed by different
> portions of the effective type rules.  In particular, using
> the '.' or '->' form of access, the lvalue being accessed
> has a declared type, and so those accesses never violate effective
> type rules.  When access is done using pointers, the rule for
> determining effective type is different, so the two accesses
> may very well run afoul of the effective type requirements.
>
> > I'd prefer if this was said in the standard explicitely, but with the
> > restriction that the value must be written, then read, using the . or -
> >> operators.
>
> Unfortunately the Standard often expresses itself rather obliquely,
> and this case certainly falls into that category.  However, it should
> be easy to see that the two different cases you bring up are covered
> under different areas of the effective type rules.  See 6.5 p6.
> Note especially the first sentence, which applies in the case of
> member access (ie, through '.' or '->', but which does not apply
> in the case of pointer access.

Sorry. Some silly questions if I may, please? Consider the following
programs:

    int main(void)
    {
      union { int x; float y; } u;
      u.y = 2;
      u.x = 1;
      return u.x;
    }
/* ---- */
    int main(void)
    {
      union { int x; float y; } u;
      float * y = &u.y;
      *y = 2;
      int * x = &u.x;
      *x = 1;
      return u.x;
    }
/* ---- */
    int main(void)
    {
      union { int x; float y; } u;
      float * y = &u.y;
      int * x = &u.x;
      *y = 2;
      *x = 1;
      return u.x;
    }
/* ---- */
    void foo(int * x, float * y)
    {
      *y = 2;
      *x = 1;
    }
    int main(void)
    {
      union { int x; float y; } u;
      float * y = &u.y;
      int * x = &u.x;
      foo(x, y);
      return u.x;
    }
/* ---- */
The last program above, except with foo in a different translation
unit.

Where exactly do you think we cross from defined behavior to undefined
behavior? I would argue that the first example is clearly not UB, and
the last example with foo() in a different translation unit is
probably UB. Specifically, the intent of the effective type rules is
to allow the compiler to do additional aliasing analysis and reorder
reads and writes that are sufficiently differently typed. With foo()
in a different translation unit, we want the compiler to be able to
reorder the writes to x and y in foo() from type aliasing analysis,
but if we do that then we'll change the semantics of the last program
and have it return garbage.

I don't have a strong opinion on this one. It seems that the intent of
the type access rules and the existence of unions is an inherent
contradiction - with several plausible ways out, of course.

[toc] | [prev] | [next] | [standalone]


#23202

FromBarry Schwarz <schwarzb@dqel.com>
Date2012-07-08 09:19 -0700
Message-ID<lcbjv751nk5eg1pama9vbkpof1b45ars45@4ax.com>
In reply to#23193
On Sat, 7 Jul 2012 16:46:42 -0700 (PDT), Joshua Maurice
<joshuamaurice@gmail.com> wrote:

>On Jun 27, 5:44 pm, Tim Rentsch <t...@alumni.caltech.edu> wrote:
>> "christian.bau" <christian....@cbau.wanadoo.co.uk> writes:
>> > On Jun 27, 7:45 pm, James Kuyper <jameskuy...@verizon.net> wrote:
>>
>> >> A footnote in the current version of the standard says that the result
>> >> of reading from a different member of a union than the one last written
>> >> is that the bit pattern stored in that memory is reinterpreted according
>> >> to the type of the member being read; it would therefore have defined
>> >> behavior, so long as that bit pattern is a valid one for that type.

<snip>

>Sorry. Some silly questions if I may, please? Consider the following
>programs:
>
>    int main(void)
>    {
>      union { int x; float y; } u;
>      u.y = 2;
>      u.x = 1;
>      return u.x;
>    }

<snip three similar examples>

>Where exactly do you think we cross from defined behavior to undefined
>behavior? I would argue that the first example is clearly not UB, and

None of your examples perform the sequence of operations under
discussion.  In every case, you store a value in one member of the
union, store a value in a different member of the union, and then
access the member which was last stored.  Accessing the last stored
member never yields undefined behavior.

-- 
Remove del for email

[toc] | [prev] | [next] | [standalone]


#23205

FromTim Rentsch <txr@alumni.caltech.edu>
Date2012-07-08 11:13 -0700
Message-ID<kfnmx3a6rz2.fsf@x-alumni2.alumni.caltech.edu>
In reply to#23202
Barry Schwarz <schwarzb@dqel.com> writes:

> On Sat, 7 Jul 2012 16:46:42 -0700 (PDT), Joshua Maurice
> <joshuamaurice@gmail.com> wrote:
>
>>On Jun 27, 5:44 pm, Tim Rentsch <t...@alumni.caltech.edu> wrote:
>>> "christian.bau" <christian....@cbau.wanadoo.co.uk> writes:
>>> > On Jun 27, 7:45 pm, James Kuyper <jameskuy...@verizon.net> wrote:
>>>
>>> >> A footnote in the current version of the standard says that the result
>>> >> of reading from a different member of a union than the one last written
>>> >> is that the bit pattern stored in that memory is reinterpreted according
>>> >> to the type of the member being read; it would therefore have defined
>>> >> behavior, so long as that bit pattern is a valid one for that type.
>
> <snip>
>
>>Sorry. Some silly questions if I may, please? Consider the following
>>programs:
>>
>>    int main(void)
>>    {
>>      union { int x; float y; } u;
>>      u.y = 2;
>>      u.x = 1;
>>      return u.x;
>>    }
>
> <snip three similar examples>
>
>>Where exactly do you think we cross from defined behavior to undefined
>>behavior? I would argue that the first example is clearly not UB, and
>
> None of your examples perform the sequence of operations under
> discussion.  In every case, you store a value in one member of the
> union, store a value in a different member of the union, and then
> access the member which was last stored.  Accessing the last stored
> member never yields undefined behavior.

Only the first example (ie, the only one not snipped) stores into
members.  The other examples store into objects that happen to
coincide with memory areas corresponding to members of u, but
that's not the same as storing into members.  If nothing else,
which parts of the effective type rules govern the accesses
are different in the two cases.

[toc] | [prev] | [next] | [standalone]


#23209

FromBarry Schwarz <schwarzb@dqel.com>
Date2012-07-08 15:24 -0700
Message-ID<732kv7ps7h8b8g33fo9vn8j54mf60nesda@4ax.com>
In reply to#23205
On Sun, 08 Jul 2012 11:13:53 -0700, Tim Rentsch
<txr@alumni.caltech.edu> wrote:

>Barry Schwarz <schwarzb@dqel.com> writes:
snip

>> None of your examples perform the sequence of operations under
>> discussion.  In every case, you store a value in one member of the
>> union, store a value in a different member of the union, and then
>> access the member which was last stored.  Accessing the last stored
>> member never yields undefined behavior.
>
>Only the first example (ie, the only one not snipped) stores into
>members.  The other examples store into objects that happen to
>coincide with memory areas corresponding to members of u, but
>that's not the same as storing into members.  If nothing else,
>which parts of the effective type rules govern the accesses
>are different in the two cases.

Do I understand correctly that storing into a member and storing into
the memory occupied by that member are somehow different?

-- 
Remove del for email

[toc] | [prev] | [next] | [standalone]


#23210

FromJoshua Maurice <joshuamaurice@gmail.com>
Date2012-07-08 18:12 -0700
Message-ID<4e13d30a-e5a5-49a3-a7ae-f4796acbc1a8@l6g2000pbi.googlegroups.com>
In reply to#23209
On Jul 8, 3:24 pm, Barry Schwarz <schwa...@dqel.com> wrote:
> On Sun, 08 Jul 2012 11:13:53 -0700, Tim Rentsch
> >Only the first example (ie, the only one not snipped) stores into
> >members.  The other examples store into objects that happen to
> >coincide with memory areas corresponding to members of u, but
> >that's not the same as storing into members.  If nothing else,
> >which parts of the effective type rules govern the accesses
> >are different in the two cases.
>
> Do I understand correctly that storing into a member and storing into
> the memory occupied by that member are somehow different?

I would hope not! (But maybe.) I agree that the current rules are
unclear.

I think/hope that:
    struct foo { int x; };
    int main(void)
    {
      struct foo f;
      f.x = 1;
    }
is definitionally equivalent to:
    struct foo { int x; };
    int main(void)
    {
      struct foo f;
      int * y;
      y = &f.x;
      *y = 1;
    }
Any decision that makes "f.x = 1;" somehow different than "y = &f.x;
*y = 1;" is my least preferred alternative.

I'd much rather have rules that require the compiler to limit its type
aliasing optimizations when unions are in scope. Basically, a rule in
the standard somewhere which says something like the following. Please
note that I just whipped this up, and I have no clue if it's actually
"correct". It could very probably/definitely be fixed, improved, etc.
I'm just trying to get the ball rolling. There's closely related
alternative formulations that would also be appealing to me.

Quickie Definition: The "lifetime" of a pointer value is the
contiguous interval of time of the program execution, starting when
the pointer value is "created", and ending when the last "copy" or
"derivation" of the pointer value ceases to exist in an object.
Example:
  #include <stdlib.h>
  int main(void)
  {
    {
      int a[2];
      int * x;
      int * y;
      {
        x = a; /* this statement "creates" a pointer value */
      }
      /*the pointer object "x" exists, and it contains the pointer
value, so it's still "alive" */
      y = x + 1;
      x = 0;
      /* the pointer value is still "alive" because a "derivation" of
it exists in the pointer object "y" */
    }
    /* the pointer value is now "dead", and the pointer value lifetime
has ended */
  }

New Rule: For two accesses to two sufficiently differently typed
members of a union, if:
- the accesses are a write and a read, or two writes, to the union
member objects or sub-objects thereof, and
- the pointer value lifetimes of the pointer values used to do the
accesses overlap, and
- both accesses are done in scopes where the union definition is not
visible, then
- the program has undefined behavior.

This approach formulated disallows all aliasing optimization with the
types in a union when the union definition is in scope. Perhaps there
are "nicer" ways to do this without such a substantial penalty.

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.c


csiph-web