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


Groups > comp.std.c > #6160 > unrolled thread

Assignment between union object members of incompatible types

Started byIan Abbott <ijabbott63@gmail.com>
First post2020-11-30 10:44 -0800
Last post2020-12-01 02:49 -0800
Articles 7 — 5 participants

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


Contents

  Assignment between union object members of incompatible types Ian Abbott <ijabbott63@gmail.com> - 2020-11-30 10:44 -0800
    Re: Assignment between union object members of incompatible types James Kuyper <jameskuyper@alumni.caltech.edu> - 2020-11-30 14:45 -0500
    Re: Assignment between union object members of incompatible types Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-11-30 12:20 -0800
      Re: Assignment between union object members of incompatible types Tim Rentsch <tr.17687@z991.linuxsc.com> - 2020-12-01 03:39 -0800
        Re: Assignment between union object members of incompatible types Francis Glassborow <francis.glassborow@btinternet.com> - 2020-12-05 14:27 +0000
          Re: Assignment between union object members of incompatible types Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-07-10 08:42 -0700
    Re: Assignment between union object members of incompatible types Tim Rentsch <tr.17687@z991.linuxsc.com> - 2020-12-01 02:49 -0800

#6160 — Assignment between union object members of incompatible types

FromIan Abbott <ijabbott63@gmail.com>
Date2020-11-30 10:44 -0800
SubjectAssignment between union object members of incompatible types
Message-ID<272ec269-16e7-4e06-b8a8-e5f6534e66a8n@googlegroups.com>
A question (not posted by myself) from https://stackoverflow.com/questions/65077630 :

Consider the following:

union { int i; char c; } x = {0};
x.c = x.i;

Does the assignment x.c = x.i result in undefined behavior?

C18 6.15.16.1/3 says:

| If the value being stored in an object is read from another object 
| that overlaps in any way the storage of the first object, then the 
| overlap shall be exact and the two objects shall have qualified or 
| unqualified versions of a compatible type; otherwise, the behavior is 
| undefined.

The objects x.c and x.i overlap, but have incompatible types, so on first glance it appears to be UB.

[toc] | [next] | [standalone]


#6161

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2020-11-30 14:45 -0500
Message-ID<rq3i51$b2q$1@dont-email.me>
In reply to#6160
On 11/30/20 1:44 PM, Ian Abbott wrote:
> A question (not posted by myself) from https://stackoverflow.com/questions/65077630 :
> 
> Consider the following:
> 
> union { int i; char c; } x = {0};
> x.c = x.i;
> 
> Does the assignment x.c = x.i result in undefined behavior?
> 
> C18 6.15.16.1/3 says:

That's actually 6.5.16.1/3.

> | If the value being stored in an object is read from another object 
> | that overlaps in any way the storage of the first object, then the 
> | overlap shall be exact and the two objects shall have qualified or 
> | unqualified versions of a compatible type; otherwise, the behavior is 
> | undefined.
> 
> The objects x.c and x.i overlap, but have incompatible types, so on first glance it appears to be UB.

That is correct.

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


#6162

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2020-11-30 12:20 -0800
Message-ID<87czzuoavh.fsf@nosuchdomain.example.com>
In reply to#6160
Ian Abbott <ijabbott63@gmail.com> writes:
> A question (not posted by myself) from https://stackoverflow.com/questions/65077630 :
>
> Consider the following:
>
> union { int i; char c; } x = {0};
> x.c = x.i;
>
> Does the assignment x.c = x.i result in undefined behavior?
>
> C18 6.15.16.1/3 says:
>
> | If the value being stored in an object is read from another object 
> | that overlaps in any way the storage of the first object, then the 
> | overlap shall be exact and the two objects shall have qualified or 
> | unqualified versions of a compatible type; otherwise, the behavior is 
> | undefined.
>
> The objects x.c and x.i overlap, but have incompatible types, so on
> first glance it appears to be UB.

On first glance, yes, but I think this passage needs to be updated to
reflect its intent.

The RHS of an assignment is not an lvalue.  If it starts out as an
lvalue, then lvalue conversion is applied.  Logically the value is
retrieved *and then* copied into the destination object.

A simple case like this is not likely to cause problems (in the absence
of agressive optimization), but we can construct more problematic cases
where the object being copied is arbitrarily large and the overlap might
not be detectable at compile time.

I've written an answer:
https://stackoverflow.com/a/65080498/827263

-- 
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */

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


#6164

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2020-12-01 03:39 -0800
Message-ID<865z5lpxfp.fsf@linuxsc.com>
In reply to#6162
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

> Ian Abbott <ijabbott63@gmail.com> writes:
>
>> A question (not posted by myself) from
>> https://stackoverflow.com/questions/65077630 :
>>
>> Consider the following:
>>
>> union { int i; char c; } x = {0};
>> x.c = x.i;
>>
>> Does the assignment x.c = x.i result in undefined behavior?
>>
>> C18 6.15.16.1/3 says:
>>
>> | If the value being stored in an object is read from another
>> | object that overlaps in any way the storage of the first object,
>> | then the overlap shall be exact and the two objects shall have
>> | qualified or unqualified versions of a compatible type;
>> | otherwise, the behavior is undefined.
>>
>> The objects x.c and x.i overlap, but have incompatible types, so on
>> first glance it appears to be UB.
>
> On first glance, yes, but I think this passage needs to be updated
> to reflect its intent.
>
> The RHS of an assignment is not an lvalue.  If it starts out as an
> lvalue, then lvalue conversion is applied.  Logically the value is
> retrieved *and then* copied into the destination object.
>
> A simple case like this is not likely to cause problems (in the
> absence of agressive optimization), but we can construct more
> problematic cases where the object being copied is arbitrarily large
> and the overlap might not be detectable at compile time.
>
> I've written an answer:
> https://stackoverflow.com/a/65080498/827263

Let me start with where we agree.  I agree that 6.5.16.1 p3 deserves
some clarification.  After that however my reading and yours reach
different conclusions.

First I think the passage as written clearly conveys the meaning
intended in this case, that this assignment is undefined behavior.
The value being stored was read out of x.i, and is being stored
into x.c.  The types of those two objects don't mesh, and so the
assignment is undefined behavior.  This assignment is about the
clearest possible case that would violate 6.5.16.1 p3, and the
passage as written conveys that, IMO with no room for argument.

Second I think whether the RHS is an lvalue has no bearing on the
question.  The cited paragraph does not mention anything about
lvalues;  it speaks only of "the value being stored".  Consider a
slight variation:

    x.c = (printf( "hello world\n" ), x.i);

The RHS of this assignment is not an lvalue.  But the /value/
being stored was read from x.i.  As I read the Standard this
assignment too is undefined behavior, and IMO the Standard does
convey that intention.

Here is another variation:

    x.c = 0 ? printf( "hello world\n" ) : x.i;

Once again the value being stored was read from x.i, and again
as I read the Standard this assignment too is undefined behavior,
and IMO the Standard does convey that intention.

Now let's look at an example you give in your stackoverflow answer.
Quoting a whole paragraph:

    The passage says that the value "is read from another
    object".  That's ambiguous.  Must name of the object be the
    entire RHS expression, or can it be just a subexpression?
    If the latter, then x.c = x.i + 1;  would have undefined
    behavior, which in my opinion would be absurd.

You left out an important part:  "the value /being stored/".
Here the value being stored is x.i + 1.  That /value/ was not
read from x.i;  it was formed by an addition operation after
reading x.i.  Ostensibly this assignment would not be undefined
behavior.  I say "ostensibly" because actually I think this case
is not clear, and may have been intended to be undefined behavior
along with the others.  And I don't see anything absurd about
having it be undefined behavior, or even surprising if it were
discovered that this case had been meant to be undefined behavior
all along.

Disclaimer:  I haven't yet done any research into Defect Reports
or committee meeting notes to see if this question has been
addressed there.  I suspect it has, but I just haven't looked
yet.

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


#6166

FromFrancis Glassborow <francis.glassborow@btinternet.com>
Date2020-12-05 14:27 +0000
Message-ID<rqg5d5$k2$1@dont-email.me>
In reply to#6164
On 01/12/2020 11:39, Tim Rentsch wrote:
> Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
> 
>> Ian Abbott <ijabbott63@gmail.com> writes:
>>
>>> A question (not posted by myself) from
>>> https://stackoverflow.com/questions/65077630 :
>>>
>>> Consider the following:
>>>
>>> union { int i; char c; } x = {0};
>>> x.c = x.i;
>>>
>>> Does the assignment x.c = x.i result in undefined behavior?
>>>
>>> C18 6.15.16.1/3 says:
>>>
>>> | If the value being stored in an object is read from another
>>> | object that overlaps in any way the storage of the first object,
>>> | then the overlap shall be exact and the two objects shall have
>>> | qualified or unqualified versions of a compatible type;
>>> | otherwise, the behavior is undefined.
>>>
>>> The objects x.c and x.i overlap, but have incompatible types, so on
>>> first glance it appears to be UB.
>>
>> On first glance, yes, but I think this passage needs to be updated
>> to reflect its intent.
>>
>> The RHS of an assignment is not an lvalue.  If it starts out as an
>> lvalue, then lvalue conversion is applied.  Logically the value is
>> retrieved *and then* copied into the destination object.
>>
>> A simple case like this is not likely to cause problems (in the
>> absence of agressive optimization), but we can construct more
>> problematic cases where the object being copied is arbitrarily large
>> and the overlap might not be detectable at compile time.
>>
>> I've written an answer:
>> https://stackoverflow.com/a/65080498/827263
> 
> Let me start with where we agree.  I agree that 6.5.16.1 p3 deserves
> some clarification.  After that however my reading and yours reach
> different conclusions.
> 
> First I think the passage as written clearly conveys the meaning
> intended in this case, that this assignment is undefined behavior.
> The value being stored was read out of x.i, and is being stored
> into x.c.  The types of those two objects don't mesh, and so the
> assignment is undefined behavior.  This assignment is about the
> clearest possible case that would violate 6.5.16.1 p3, and the
> passage as written conveys that, IMO with no room for argument.
> 
> Second I think whether the RHS is an lvalue has no bearing on the
> question.  The cited paragraph does not mention anything about
> lvalues;  it speaks only of "the value being stored".  Consider a
> slight variation:
> 
>      x.c = (printf( "hello world\n" ), x.i);
> 
> The RHS of this assignment is not an lvalue.  But the /value/
> being stored was read from x.i.  As I read the Standard this
> assignment too is undefined behavior, and IMO the Standard does
> convey that intention.
> 
> Here is another variation:
> 
>      x.c = 0 ? printf( "hello world\n" ) : x.i;
> 
> Once again the value being stored was read from x.i, and again
> as I read the Standard this assignment too is undefined behavior,
> and IMO the Standard does convey that intention.
> 
> Now let's look at an example you give in your stackoverflow answer.
> Quoting a whole paragraph:
> 
>      The passage says that the value "is read from another
>      object".  That's ambiguous.  Must name of the object be the
>      entire RHS expression, or can it be just a subexpression?
>      If the latter, then x.c = x.i + 1;  would have undefined
>      behavior, which in my opinion would be absurd.

if
x.c = x.i + 1;

has defined behaviour then

x.c = x.i + 0;

also has defined behaviour.

So IMO, consistency requires that we tighten the requirement so that

x.c = any expression that uses x.i;

has undefined behaviour.

However I still have reservations because:

{int const i = x.i; x.c = i;}

is surely OK. However any optimising compiler will concatenate that to

x.c = x.i;

Note the braces limit the scope of i.

Francis

> 
> You left out an important part:  "the value /being stored/".
> Here the value being stored is x.i + 1.  That /value/ was not
> read from x.i;  it was formed by an addition operation after
> reading x.i.  Ostensibly this assignment would not be undefined
> behavior.  I say "ostensibly" because actually I think this case
> is not clear, and may have been intended to be undefined behavior
> along with the others.  And I don't see anything absurd about
> having it be undefined behavior, or even surprising if it were
> discovered that this case had been meant to be undefined behavior
> all along.
> 
> Disclaimer:  I haven't yet done any research into Defect Reports
> or committee meeting notes to see if this question has been
> addressed there.  I suspect it has, but I just haven't looked
> yet.
> 

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


#6262

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2021-07-10 08:42 -0700
Message-ID<864kd29nvy.fsf@linuxsc.com>
In reply to#6166
Francis Glassborow <francis.glassborow@btinternet.com> writes:

> On 01/12/2020 11:39, Tim Rentsch wrote:
>
>> Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
>>
>>> Ian Abbott <ijabbott63@gmail.com> writes:
>>>
>>>> A question (not posted by myself) from
>>>> https://stackoverflow.com/questions/65077630 :
>>>>
>>>> Consider the following:
>>>>
>>>> union { int i; char c; } x = {0};
>>>> x.c = x.i;
>>>>
>>>> Does the assignment x.c = x.i result in undefined behavior?
>>>>
>>>> C18 6.15.16.1/3 says:
>>>>
>>>> | If the value being stored in an object is read from another
>>>> | object that overlaps in any way the storage of the first object,
>>>> | then the overlap shall be exact and the two objects shall have
>>>> | qualified or unqualified versions of a compatible type;
>>>> | otherwise, the behavior is undefined.
>>>>
>>>> The objects x.c and x.i overlap, but have incompatible types, so on
>>>> first glance it appears to be UB.
>>>
>>> On first glance, yes, but I think this passage needs to be updated
>>> to reflect its intent.
>>>
>>> The RHS of an assignment is not an lvalue.  If it starts out as an
>>> lvalue, then lvalue conversion is applied.  Logically the value is
>>> retrieved *and then* copied into the destination object.
>>>
>>> A simple case like this is not likely to cause problems (in the
>>> absence of agressive optimization), but we can construct more
>>> problematic cases where the object being copied is arbitrarily large
>>> and the overlap might not be detectable at compile time.
>>>
>>> I've written an answer:
>>> https://stackoverflow.com/a/65080498/827263
>>
>> Let me start with where we agree.  I agree that 6.5.16.1 p3 deserves
>> some clarification.  After that however my reading and yours reach
>> different conclusions.
>>
>> First I think the passage as written clearly conveys the meaning
>> intended in this case, that this assignment is undefined behavior.
>> The value being stored was read out of x.i, and is being stored
>> into x.c.  The types of those two objects don't mesh, and so the
>> assignment is undefined behavior.  This assignment is about the
>> clearest possible case that would violate 6.5.16.1 p3, and the
>> passage as written conveys that, IMO with no room for argument.
>>
>> Second I think whether the RHS is an lvalue has no bearing on the
>> question.  The cited paragraph does not mention anything about
>> lvalues;  it speaks only of "the value being stored".  Consider a
>> slight variation:
>>
>>      x.c = (printf( "hello world\n" ), x.i);
>>
>> The RHS of this assignment is not an lvalue.  But the /value/
>> being stored was read from x.i.  As I read the Standard this
>> assignment too is undefined behavior, and IMO the Standard does
>> convey that intention.
>>
>> Here is another variation:
>>
>>      x.c = 0 ? printf( "hello world\n" ) : x.i;
>>
>> Once again the value being stored was read from x.i, and again
>> as I read the Standard this assignment too is undefined behavior,
>> and IMO the Standard does convey that intention.

(see note given below)

>> Now let's look at an example you give in your stackoverflow answer.
>> Quoting a whole paragraph:
>>
>>      The passage says that the value "is read from another
>>      object".  That's ambiguous.  Must name of the object be the
>>      entire RHS expression, or can it be just a subexpression?
>>      If the latter, then x.c = x.i + 1;  would have undefined
>>      behavior, which in my opinion would be absurd.

My apologies for taking so long to respond to your comments.

> if
> x.c = x.i + 1;
>
> has defined behaviour then
>
> x.c = x.i + 0;
>
> also has defined behaviour.

Yes.  It does, and it should.

> So IMO, consistency requires that we tighten the requirement so that
>
> x.c = any expression that uses x.i;
>
> has undefined behaviour.

Surely that is not the intent.  It is only when the value being
stored is _read directly_ from an overlapping object, and is
not instead _formed as the result of a computation using_ an
overlapping object, that undefined behavior occurs.

> However I still have reservations because:
>
> {int const i = x.i; x.c = i;}
>
> is surely OK.  However any optimising compiler will concatenate that to
>
> x.c = x.i;
>
> Note the braces limit the scope of i.

What an optimizing compiler might do has no effect on the program's
semantics, which is defined in terms of an abstract machine where
no optimizations occur.  Each compiler has a responsibility to
ensure its optimizer faithfully preserves the program semantics
that the Standard requires, which in this case has defined behavior
because the value being stored is read out of 'i' and not out of
'x.i'.


(Note for the "see below" remark:  I'm willing to be convinced that
the example

      x.c = 0 ? printf( "hello world\n" ) : x.i;

has defined behavior, because the value being stored is the result
of a ?: operator, and thus not just a direct read out of x.i.  This
argument can be seen more clearly if we consider

      x.c = 0 ? 0ULL : x.i;

because the value of the RHS clearly is not the same as what is
read out of x.i, which is an int.  Similarly

      x.c = (unsigned long long) x.i;

has defined behavior, because the value being stored is the result
of a conversion operation, not the value read from x.i.)

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


#6163

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2020-12-01 02:49 -0800
Message-ID<86a6uxpzrt.fsf@linuxsc.com>
In reply to#6160
Ian Abbott <ijabbott63@gmail.com> writes:

> A question (not posted by myself) from https://stackoverflow.com/questions/65077630 :
>
> Consider the following:
>
> union { int i; char c; } x = {0};
> x.c = x.i;
>
> Does the assignment x.c = x.i result in undefined behavior?
>
> C18 6.15.16.1/3 says:

As elsewhere noted, the reference is 6.5.16.1 paragraph 3.

> | If the value being stored in an object is read from another object
> | that overlaps in any way the storage of the first object, then the
> | overlap shall be exact and the two objects shall have qualified or
> | unqualified versions of a compatible type;  otherwise, the behavior is
> | undefined.
>
> The objects x.c and x.i overlap, but have incompatible types, so on
> first glance it appears to be UB.

The value to be stored is read from object x.i.  This value is
being stored in object x.c.

The object x.i overlaps with object x.c.

The overlap is not exact (probably).  The two types involved are
not qualified or unqualified versions of a compatible type.

The assignment has undefined behavior.  No doubt about it.

[toc] | [prev] | [standalone]


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


csiph-web