Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Casts on lvalues

Reply
Thread Tools

Casts on lvalues

 
 
James Kuyper
Guest
Posts: n/a
 
      12-07-2012
On 12/07/2012 03:16 PM, BartC wrote:
> "James Kuyper" <(E-Mail Removed)> wrote in message
> news:(E-Mail Removed)...

....
>> I'm not talking about all optimizations, just the ones that depend upon
>> C's anti-aliasing guarantees. Consider the following function:
>>
>> void func(int n, float array[static n], long *l)
>> {
>> for(int i=0; i<n; i++)
>> array[i] = *l;
>> *l = n;
>> }

>
>> An implementation is permitted, because of 6.5p7, to compile that
>> function as if it had been written:

>
>> void func(int n, float array[static n], long *l)
>> {
>> long temp = *l;
>> for(int i=0; i<n; i++)
>> array[i] = temp;
>> *l = n;
>> }
>>
>> The optimized version has exactly the same behavior as the original, so
>> long as 6.5p7 is not violated. If C were changed to allow a long integer
>> to be equivalenced with a element of a float array, the value of *l
>> might change during the loop.

>
> (Seems like it might just be copied to itself? In this example).


I think you're assuming that sizeof(long)==sizeof(float), which is
commonplace, but not required. However, even on systems where that is
true, the result is not a simple copy. The conversion to float will
produce a bit pattern that's pretty unlikely to be the same as the
original (unless the original represents 0).

> >If so, these two versions of the code
>> would no longer be equivalent, and there's an important question to be
>> asked about which one actually matches the user's intent. If the initial
>> value of *l was carefully chosen with the intent that it would, in fact,
>> be changed by this code inside that loop, the developer is going to be a
>> little upset if that didn't actually happen.

>
> I can't see that the problems are going to be that different from the ones
> you might get when using pointers. (And the solutions might be similar.)


In C, such code using pointer casts violates 6.5p7, and therefore has
undefined behavior, so the solution is simply not to write such code.

> Because even without equivalencing, 'l' might anyway point into the array,
> via a cast.


Not with defined behavior.

> If this was part of the language, at least the aliasing would be done in an
> overt manner, not hidden away in a pointer cast, in an operation done at
> runtime so you don't know at any time what is aliasing what.


As I said, the C language does provide a way to explicitly alias objects
of two different types, and the optimizations I've described above are
therefore prohibited when they occur within the scope of a union
declaration that connects the relevant types (except in those contexts
where the compiler can be certain that the relevant pointers do not
point at members of the same union object).

 
Reply With Quote
 
 
 
 
glen herrmannsfeldt
Guest
Posts: n/a
 
      12-07-2012
Keith Thompson <(E-Mail Removed)> wrote:

(snip, someone wrote)
>> I do not feel it safe to use union for type punning.


> Note that C99 doesn't have that footnote, but N1256 does. It was added
> by one of Technical Corrigenda 3 -- but N1256 (which incorporates TC3)
> still has that clause in Annex J.1. I suspect this is just an
> oversight.


(snip)

> But the implication is that the committee felt that the existing C99
> standard already implied what the footnote says. It's conceivable
> that a compiler could behave differently, (e.g., by optimizing away
> an assignment to a union member if that member is never access again)
> -- but such a compiler would break a good deal of existing code that
> depends on this behavior, whether it's guaranteed by the standard
> or not.


Some time ago, I was trying to figure out if it would be
possible for a C compiler to generate JVM code.

JVM has no operation that stores different types in the same memory.
I believe it isn't so hard to make a memcpy() that can do the
appropriate conversions and copies, though.

It seemed to me at the time that implementing union the same as
struct was one way that would work, and would also follow the
standard. (Though maybe less memory efficient.)

I asked here at the time, and many seemed to agree, though as you
say, it might break existing code.

-- glen
 
Reply With Quote
 
 
 
 
glen herrmannsfeldt
Guest
Posts: n/a
 
      12-07-2012
BartC <(E-Mail Removed)> wrote:
> "glen herrmannsfeldt" <(E-Mail Removed)> wrote in message
> news:k9t999$r5g$(E-Mail Removed)...
>> James Kuyper <(E-Mail Removed)> wrote:


(snip on EQUIVALENCE)

>>> Its been a couple of decades since the last time I wrote much Fortran
>>> code, and I don't think I ever used that feature, but I was aware of it.
>>> If it has the same capability you describe above, I didn't remember
>>> that fact, which isn't particularly surprising.


>> Fortran EQUIVALENCE has many of the same restrictions as union, though
>> it is often used to get bits between different types.


> This page shows you can still set up all sorts of relationships between
> unrelated data:


> http://docs.oracle.com/cd/E19957-01/...n9b/index.html


EQUIVALENCE goes back to the first Fortran compiler, usually called
Fortran I, in 1956. It required at least 4K (36 bit) words to run.
A 704 with the full 32K (36 bit) words was pretty much the super
computer of the time. EQUIVALENCE was needed to conserve memory
use, and much less for type punning.

But yes, in later years, with more memory available, EQUIVALENCE
was commonly used to move bits between different data types.

TRANSFER wasn't added until, I believe, Fortran 90.
(Fortran 90 added dynamic allocation, which doesn't work
with EQUIVALENCE.)

-- glen
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      12-07-2012
glen herrmannsfeldt <(E-Mail Removed)> writes:

> Keith Thompson <(E-Mail Removed)> wrote:
>
> (snip, someone wrote)
>>> I do not feel it safe to use union for type punning.

>
>> Note that C99 doesn't have that footnote, but N1256 does. It was added
>> by one of Technical Corrigenda 3 -- but N1256 (which incorporates TC3)
>> still has that clause in Annex J.1. I suspect this is just an
>> oversight.

>
> (snip)
>
>> But the implication is that the committee felt that the existing C99
>> standard already implied what the footnote says. It's conceivable
>> that a compiler could behave differently, (e.g., by optimizing away
>> an assignment to a union member if that member is never access again)
>> -- but such a compiler would break a good deal of existing code that
>> depends on this behavior, whether it's guaranteed by the standard
>> or not.

>
> Some time ago, I was trying to figure out if it would be
> possible for a C compiler to generate JVM code.
>
> JVM has no operation that stores different types in the same memory.
> I believe it isn't so hard to make a memcpy() that can do the
> appropriate conversions and copies, though.
>
> It seemed to me at the time that implementing union the same as
> struct was one way that would work, and would also follow the
> standard. (Though maybe less memory efficient.)
>
> I asked here at the time, and many seemed to agree, though as you
> say, it might break existing code.


I don't think that would work out. In particular, 6.5.8 p5 says "All
pointers to members of the same union object compare equal". Since you
are talking about a compiler here, that alone is not the end of the
matter because the compiler might be able to arrange for this to appear
to be true. For example, a pointer might be made to consist of two
parts, one that is used for comparison while the other part is an offset
used for union access, but I fear that this won't fly in the long run.
(For example, this particular ruse will go wrong implementing the
special provision for "common initial sequences" in 6.5.2.3 p5. Again,
a fix-up is possible but I am sceptical about far this can go on.)

It would be an interesting exercise to see if would work, but I'm not
hopeful.

--
Ben.
 
Reply With Quote
 
glen herrmannsfeldt
Guest
Posts: n/a
 
      12-08-2012
Ben Bacarisse <(E-Mail Removed)> wrote:

(snip, I wrote)
>> Some time ago, I was trying to figure out if it would be
>> possible for a C compiler to generate JVM code.


>> JVM has no operation that stores different types in the same memory.
>> I believe it isn't so hard to make a memcpy() that can do the
>> appropriate conversions and copies, though.


>> It seemed to me at the time that implementing union the same as
>> struct was one way that would work, and would also follow the
>> standard. (Though maybe less memory efficient.)


>> I asked here at the time, and many seemed to agree, though as you
>> say, it might break existing code.


> I don't think that would work out. In particular, 6.5.8 p5 says "All
> pointers to members of the same union object compare equal". Since you
> are talking about a compiler here, that alone is not the end of the
> matter because the compiler might be able to arrange for this to appear
> to be true. For example, a pointer might be made to consist of two
> parts, one that is used for comparison while the other part is an offset
> used for union access,


Well, most pointers have to have an Object reference to an array
and an offset into the array. Any scalar that can be pointed to
has to instead be an array of length 1.

> but I fear that this won't fly in the long run.
> (For example, this particular ruse will go wrong implementing the
> special provision for "common initial sequences" in 6.5.2.3 p5. Again,
> a fix-up is possible but I am sceptical about far this can go on.)


As well as I know, all that fun stuff only has to happen for
(unsigned char *), or (void *), so the compiler only has to do that
extra stuff when those occur.

But yes, getting that one right will be tricky.

> It would be an interesting exercise to see if would work, but I'm not
> hopeful.


It already gets tricky for struct. If you make a struct a Java class,
then you can have an object reference to the (instance of the) class,
which is different from a reference to a class member. So, extra
work to get that right.

-- glen
 
Reply With Quote
 
Phil Carmody
Guest
Posts: n/a
 
      12-09-2012
"BartC" <(E-Mail Removed)> writes:
> "Eric Sosman" <(E-Mail Removed)> wrote:

....
> >> I asked about doing the same on the left side of an assignment.
> >>
> >> It's this asymmetry that is the issue.

> >
> > Um, er, assignment itself is asymmetric ...

>
> Plenty of terms can appear interchangeably on both left and right sides.
>
> But while A can appear on either side, (T)A can't. And there doesn't
> appear to be a convincing reason why not.


Your posts are getting more and more detached from reality. What would

(T)A = expr;

even mean? What is getting modified? Certainly A can't be. One could argue
that some temporary non-addressible pseudo-object containing the cast value
of A is being modified, but as that pseudo-object fails to be referenceable
after that line, the assignment is absolutely useless.

I want neither meaninglessness nor uselessness as a language feature.

Phil
--
I'm not saying that google groups censors my posts, but there's a strong link
between me saying "google groups sucks" in articles, and them disappearing.

Oh - I guess I might be saying that google groups censors my posts.
 
Reply With Quote
 
James Kuyper
Guest
Posts: n/a
 
      12-09-2012
On 12/09/2012 10:54 AM, Phil Carmody wrote:
> "BartC" <(E-Mail Removed)> writes:
>> "Eric Sosman" <(E-Mail Removed)> wrote:

> ...
>>>> I asked about doing the same on the left side of an assignment.
>>>>
>>>> It's this asymmetry that is the issue.
>>>
>>> Um, er, assignment itself is asymmetric ...

>>
>> Plenty of terms can appear interchangeably on both left and right sides.
>>
>> But while A can appear on either side, (T)A can't. And there doesn't
>> appear to be a convincing reason why not.

>
> Your posts are getting more and more detached from reality. What would
>
> (T)A = expr;
>
> even mean? What is getting modified? Certainly A can't be. One could argue
> that some temporary non-addressible pseudo-object containing the cast value
> of A is being modified, but as that pseudo-object fails to be referenceable
> after that line, the assignment is absolutely useless.
>
> I want neither meaninglessness nor uselessness as a language feature.


He's made it clear what he wants it to mean. What he wants is equivalent
to the following C code, except for the fact that the following code
violates a constraint, and he wants this construct to have well-defined
behavior:

*(T*)&A = expr;

That this construct would have such a meaning is inconsistent with the
way casts work in the rest of the C language, which doesn't bother him;
in fact, I think he believes it's inconsistent for it to NOT work this
way. It would still be a horrible unsafe thing to do, even if it were
fully legal. It can be done somewhat more safely by declaring a union,
but he wants to be able to use it even in contexts where he can't change
the definition of A to be a union, and the syntax for doing it using a
union is more complicated than he wants it to be. Also, he thinks the
syntax to do this using a union is excessively complicated.
--
James Kuyper
 
Reply With Quote
 
BartC
Guest
Posts: n/a
 
      12-09-2012
"James Kuyper" <(E-Mail Removed)> wrote in message
news:ka2ibh$kau$(E-Mail Removed)...
> On 12/09/2012 10:54 AM, Phil Carmody wrote:


>> Your posts are getting more and more detached from reality. What would
>>
>> (T)A = expr;


>> I want neither meaninglessness nor uselessness as a language feature.


(Neither do I.)

> He's made it clear what he wants it to mean.


> *(T*)&A = expr;


> It can be done somewhat more safely by declaring a union,
> but he wants to be able to use it even in contexts where he can't change
> the definition of A to be a union, and the syntax for doing it using a
> union is more complicated than he wants it to be. Also, he thinks the
> syntax to do this using a union is excessively complicated.


It's not that complicated, but it's intrusive.

It would also affect uses of 'A' throughout the project. (And perhaps a
different module might want to apply a different union!) Sometimes you might
just want to use such a cast locally. Or it might be experimental, and you
want to try out something without turning the whole project upside-down.

There are plenty of uses for the feature. And if a union-like behaviour is
the way to do it, perhaps the compiler can take care of it, in trivial
cases. Or the rules for unions can be relaxed. So instead of having to
change;

S* p;

to:

union {
S* s;
T* t;
} p;

and changing every p to p.s or p.t, simpler to allow an anonymous union, for
example:

union {
S* p;
};

Now p can be used as before, but the compiler knows there might be aliasing
issues. This still requires the cast. Or:

union {
S* p;
T* pt;
};

Now p is only changed in a few places to pt, instead of using a cast. (As a
bonus, it would be nice to augment an existing union too.)

--
Bartc



 
Reply With Quote
 
James Kuyper
Guest
Posts: n/a
 
      12-09-2012
On 12/09/2012 01:50 PM, BartC wrote:
....
> There are plenty of uses for the feature. And if a union-like behaviour is
> the way to do it, perhaps the compiler can take care of it, in trivial
> cases. Or the rules for unions can be relaxed. So instead of having to
> change;
>
> S* p;
>
> to:
>
> union {
> S* s;
> T* t;
> } p;
>
> and changing every p to p.s or p.t, simpler to allow an anonymous union, for
> example:
>
> union {
> S* p;
> };


That wouldn't do anything useful. It doesn't tell the compiler which
other types p is aliased with, so it can't know how much space to
reserve for the union, nor what alignment requirements the union will have.

> Now p can be used as before, but the compiler knows there might be aliasing
> issues. This still requires the cast. Or:
>
> union {
> S* p;
> T* pt;
> };


That, on the other hand, would work, and is trivially compatible with
the current C language. Anonymous unions are already allowed as members
in structures (as of C2011), and stand-alone anonymous unions have, I
believe, long been a common extension to C.
--
James Kuyper
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      12-10-2012
James Kuyper <(E-Mail Removed)> writes:
> On 12/09/2012 01:50 PM, BartC wrote:

[...]
>> union {
>> S* p;
>> T* pt;
>> };

>
> That, on the other hand, would work, and is trivially compatible with
> the current C language. Anonymous unions are already allowed as members
> in structures (as of C2011), and stand-alone anonymous unions have, I
> believe, long been a common extension to C.


I think you may be mistaken on that last point.

Are you saying that (with such an extension), this:

union {
unsigned u;
float f;
};
f = 1.0;
printf("0x%x\n", u);

would be allowed? I haven't seen that; gcc warns "unnamed
struct/union that defines no instances". In other words, a
standalone anonymous union is already valid but useless.

--
Keith Thompson (The_Other_Keith) http://www.velocityreviews.com/forums/(E-Mail Removed) <http://www.ghoti.net/~kst>
Will write code for food.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
conversion of array to pointer not limited to lvalues nicolas.sitbon@gmail.com C Programming 18 08-06-2008 04:41 PM
casts and lvalues jacob navia C Programming 68 06-27-2007 03:32 PM
Lvalues and Rvalues ramasubramanian.rahul@gmail.com C Programming 3 10-14-2006 09:55 PM
Avoiding "use of cast expressions in lvalues is deprecated" steve.j.donovan@gmail.com C Programming 23 09-21-2006 05:45 PM
lvalues -> incomplete types Mantorok Redgormor C Programming 7 02-07-2004 03:45 PM



Advertisments