Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
| From | Shao Miller <sha0.miller@gmail.com> |
|---|---|
| Newsgroups | comp.std.c |
| Subject | Re: Tracking and Reporting Effective Type |
| Date | 2013-01-15 05:39 -0500 |
| Organization | A noiseless patient Spider |
| Message-ID | <kd3bk8$l2o$1@dont-email.me> (permalink) |
| References | <kcovdt$1ef$1@dont-email.me> <alhgt2Fme55U1@mid.individual.net> <kd0732$q7v$1@dont-email.me> <alk7j2Fb7lqU1@mid.individual.net> |
On 1/15/2013 00:28, Wojtek Lerch wrote:
> On 14/01/2013 1:03 AM, Shao Miller wrote:
>> On 1/13/2013 23:48, Wojtek Lerch wrote:
>>>
>>> union u {
>>> struct sl { int16_t s; int32_t l; } sl;
>>> struct ls { int32_t l; int16_t s; } ls;
>>> uint32_t u;
>>> float f;
>>> };
>>>
>>> int main(void) {
>>> void *p;
>>> if ( sizeof(union u) == 6 // no padding
>>> && ( p = malloc(6) ) != NULL ) {
>>> union u u;
>>> memset( u, 0, 6 );
>>> memcpy( p, (void*)&u.sl, 6 );
>>> ...
>>> }
>>> }
>>>
>>> At the "...", does the 6-byte object that p points to have an effective
>>> type? Is it union u, struct sl, or perhaps struct ls? Would the answer
>>> change if the second argument of the memcpy were (void*)&u instead of
>>> (void*)&u.sl? Or (void*)&u.f?
>>
>> Assuming you meant 'memset( &u, 0, 6 );',
>
> Indeed I did, thank you.
>
>> I think I saw something like this in Defect Report #219.
>
> That's a good find, thanks. But there seems to be an inconsistency
> between the "committee Discussion" (which I believe is non-normative)
> and the "Committee Response" (which is normative) about the treatment of
> character types.
>
Which part/parts is/are inconsistent?
> The DR clarifies one important thing though: it's not only objects named
> in declarations that have a "declared type", but also their subobjects,
> such as members or elements.
Yes, I also thought that was a note-worthy point of clarification.
> But that's the case, then it's still not
> clear to me what happens when a compound object
(I assume you mean "aggregate")
> and its element or
> member, or two members of a union, are exactly the same object. It
> would make sense to me that the object might have several declared types
> at the same time, but the standard's discussion of effective types
> doesn't create the impression that that was considered a possibility,
> does it?
>
I don't think I agree. When there's a declared type, I think the
object's effective type can only be that declared type. I certainly
hope that this is the case, else tracking and reporting could be more
complicated!
If you are talking about how 'int ia[1];' declares an object with
(sizeof (int) * CHAR_BIT) bits and its declared type seems to be both
'int[1]' and 'int', I don't think that's the case. I think the
effective type here must match the declared type, 'int[1]'. The pointer
expression 'ia' (after conversion from array type) no longer refers to
the same object, but refers to its first element. The first element has
some properties in common with with its containing object, but not all
of them, including its effective type. (I would say.)
By using the declared type as the effective type, I don't think there
can be multiple effective types for the object. I think that we are
able to discuss sub-objects separately from a containing object, even if
there is exact overlap.
If we copied from '&ia' ('int(*)[1]') to allocated storage, I'd say the
effective type for the modified storage becomes 'int[1]'. If we have an
'int *' and point to this storage with it, we can argue about whether we
are pointing to the array or to its first element. The obvious answer
is that we are pointing to its first element, because our pointer's
referenced type matches the element type.
>> I think that the effective type is indeed 'struct sl' due to the
>> declared type of the only lvalue in sight in the 'memcpy': 'u.sl'. I do
>> think the answer would change.
>
> But that lvalue is never used to access the object -- its only role is
> to produce a pointer to the first byte of the object. The pointers
> (char*)&u and (char*)&u.sl point to the same byte, right?
>
I agree that the lvalue 'u.sl' is not used to access the object, but it
still designates that sub-object. The value of '&u.sl' thus addresses
that sub-object and 'memcpy' is thus working with that sub-object, in my
view.
I agree that those two 'char *' pointers address the same byte, but only
as a consequence of the two objects occupying the same location... So I
don't quite agree that its only role is to produce a pointer to the
first byte; I think the pointer has more properties than which byte it
addresses.
> Imagine that u is a union of two different structures x and y, and it
> happens that u.x.r and u.y.s have the same offsets and sizes. Would you
> claim that
>
> memcpy( p, (char*)&u + offsetof(struct x, r), sizeof(u.x.r) );
>
> and
>
> memcpy( p, (char*)&u + offsetof(struct y, s), sizeof(u.y.s) );
>
> copy different effective types into p, even if they pass exactly the
> same values to memcpy?
>
This case depends on the offset and number of bytes copied, to me.
'(char *) &u' addresses the first byte of 'u'. If the offset is 0 and
the number of bytes equals 'sizeof u', then I'd say the effective type
of the modified storage is the union type.
For any other case of offset or number of bytes, I'd ask "which object
is being copied?" The answer might depend on the last-stored value, for
union involvement. Regardless of what the answer depends on, if there
are multiple answers, then all we know for certain is that a
'char[num_of_bytes]' is being copied. I think that answer could be a
default, in the case where there is no single better answer.
> Remember that an object is just a region of storage; when we talk about
> its type, that's just a short way of talking about types of lvalues, or
> declared types, or effective types.
>
"is just": I think this is consistent with the initial definition of
'object' under section 3, but I think the actual definition accumulates
some more from other parts of the Standard. The discussion of pointer
arithmetic, for example, distinguishes between an "array object" and its
"array elements", but these different objects can exactly overlap. But
in general, I agree with this.
> Back to my original example, what if I used &u.sl.s in the memcpy?
That pointer addresses 'u.sl.s'. The number of bytes to copy is more
than that, however. I think this yields undefined behaviour, just as:
*( ((char *) ((void *) &u.sl.s)) + sizeof u.sl.s )
would. (Bounds-checking.)
> Do
> you think the effective type of my allocated object would be int16_t? Or
> an array of three int16_ts? Something else? Or do you think such a
> memcpy() would be invalid?
>
The last one. :)
>>> Does the four-byte sub-object at the beginning of "u" have an effective
>>> type?
>>
>> I'd say each of the 4 bytes would be 'unsigned char', due to the
>> 'memset' copying as an array of 'unsigned char', but the effective type
>> of the whole would be 'union u', due to the lvalue '&u' in the 'memset',
>> and the fact that all bytes were populated.
>
> You explained four one-byte objects and one six-byte object. But what
> about my four-byte object that can (I hope) be accessed using such
> lvalues as u.ls.l, u.u, and u.f?
>
In order to determine the effective type, I'd ask "which object(s) do
the first four bytes refer to?" For a union, I think the answer would
first depend on the last-stored value. The last-stored value of the
union was not set via any of the members, so that does not resolve the
answer. After that, there are multiple answers, but none of those
scores as highly as the type of the value that was stored, making the
answer "4 bytes of an 'unsigned char[6]'".
However, the 'memset' wasn't working with only the first 4 bytes. The
'memset' did more than that. It involved '&u' and involved all of the
bytes, so it produced a union value. I believe that union value could
be used with another union of the same type with 'union u u2 = u;' or
'union u u2; u2 = u;', for example.
'u.ls.l', 'u.u' and 'u.f' designate members which do not have a
last-stored value, and no union member was used for the last-stored
value, so I'd say that evaluating any of them yields undefined
behaviour. (!!)
Because there is no padding for meta-data in your example, you could
circumvent the undefined behaviour with any kind of code that makes it
impossible to prove any claim about such meta-data (such as whether or
not a member was used to store a value), yielding defined behaviour. In
practice, I'd guess that for most implementations, the call to 'memset'
itself would be enough that the implementation drops any special
knowledge about 'u' and its stored value. In that case, type-punning
would happen without a 6.5p7 violation, but could still be a trap if
there's a scalar trap representation.
>>> Is it int32_t, uint32_t, or perhaps (if sizeof(float)==4) float?
>>
>> If I understand a point from DR #219, the bytes "can be treated" as
>> these others,
>
> Is the "can be treated" supposed to mean that an object can have more
> than one effective type at the same time? I wouldn't mind that, but I
> would prefer some normative text to say it a little more clearly.
>
Strongly agreed; a little more precision could help, there.
>> but could be a trap representation for a 'float'.
>
> My concern (for now) is not about the contents of the object but about
> its effective type. In particular, the "sticky" kind of effective type
> that can conflict with read accesses using an lvalue with the wrong
> type, and gets propagated by memcpying to other typeless objects. In
> other words, whether one of the standard's statements about the
> effective type "for subsequent accesses that do not modify the value"
> apply to it or not.
>
We share the same concern. Some of the considerations of the Standard
involve subjects which invoke undefined behaviour, but some of those
subjects appear to be hard to prove, under the right circumstances.
"Effective type" strikes me as one such subject. My interest is in an
implementation which attempts to trap _all_ code which yields undefined
behaviour and report it.
>>> Or somehow all of the above?
>>
>> I don't think so, for tracking and reporting purposes.
>>
>>> What about the four-byte object starting
>>> at the byte that p points to?
>>
>> I'd say no. The 4 bytes so happen to be correctly aligned and contain a
>> correct representation for some of the other members, though.
>
> Sure, but do they have an effective type? The definition of the
> effective type does not refer to alignment or contents -- it seems to be
> based only on the types of lvalues used for accesses, and effective
> types of objects that contents are copied from.
>
It sure does seem that way.
> Hey, here's another one:
>
> #define N sizeof(float)
> float x = 1.0;
> char y[ N ];
> float *p = memcpy( malloc(N), memcpy( y, &x, N ), N );
> // Does *p have a "sticky" effective type now? Is it char or float?
> float z = *p; // Is this valid?
>
I would indeed track and report the effective type of the modified
storage pointed to by 'p' as having effective type 'float'. An array of
character type is always the case, so it's an uninteresting case (to
me), if there's more that can be proven about the provenance of the
"stored value".
>> But if "provenance" was tracked, I'd track it to 'u.sl'; there'd be an
>> 'int16_t' and an 'int32_t'. So I might trap '((union u *) p)->ls.l',
>> for example, but not '((union u *) p)->sl.s' or '((struct sl *) p)->l'.
>
> What about the examples using offsetof and sizeof? Are we tracking the
> provenance of integers now?
>
I definitely wouldn't. :) I did mention how I might use an offset as
part of consideration, above.
>>> And the two-byte object starting at the
>>> same byte?
>>
>> I'd say yes, as that'd have the effective type of 'u.sl.s'; 'int16_t'.
>> What do you think?
>
> I don't know. I still find the standard's explanation of effective
> types hopelessly unclear.
>
"The intent of this list is to specify those circumstances in which
an object may or may not be aliased."
(Not offered as an explanatory response to your last line, but just as
associated, additional information, which you're likely to have already
read.)
--
- Shao Miller
--
"Thank you for the kind words; those are the kind of words I like to hear.
Cheerily," -- Richard Harter
Back to comp.std.c | Previous | Next — Previous in thread | Next in thread | Find similar | Unroll thread
Tracking and Reporting Effective Type Shao Miller <sha0.miller@gmail.com> - 2013-01-11 07:09 -0500
Re: Tracking and Reporting Effective Type Wojtek Lerch <wojtek_l@yahoo.ca> - 2013-01-13 23:48 -0500
Re: Tracking and Reporting Effective Type Shao Miller <sha0.miller@gmail.com> - 2013-01-14 01:03 -0500
Re: Tracking and Reporting Effective Type Wojtek Lerch <wojtek_l@yahoo.ca> - 2013-01-15 00:28 -0500
Re: Tracking and Reporting Effective Type Shao Miller <sha0.miller@gmail.com> - 2013-01-15 05:39 -0500
Re: Tracking and Reporting Effective Type Wojtek Lerch <wojtek_l@yahoo.ca> - 2013-01-17 00:32 -0500
Re: Tracking and Reporting Effective Type Wojtek Lerch <wojtek_l@yahoo.ca> - 2013-01-17 01:23 -0500
Re: Tracking and Reporting Effective Type Shao Miller <sha0.miller@gmail.com> - 2013-01-17 22:47 -0500
Re: Tracking and Reporting Effective Type Wojtek Lerch <wojtek_l@yahoo.ca> - 2013-01-18 16:14 -0500
Re: Tracking and Reporting Effective Type Wojtek Lerch <wojtek_l@yahoo.ca> - 2013-01-18 16:21 -0500
csiph-web