C++ Logo

SG12

Advanced search

Subject: Re: [ub] [isocpp-core] bit_cast and indeterminate values
From: JF Bastien (cxx_at_[hidden])
Date: 2019-06-21 19:49:43


On Fri, Jun 21, 2019 at 5:47 PM Gabriel Dos Reis via Core <
core_at_[hidden]> wrote:

>
>
> On Jun 20, 2019, at 10:45 PM, JF Bastien <cxx_at_[hidden]> wrote:
>
> Thanks Richard, your description matches the original design intent, and
> what I thought we'd worded through LEWG / LWG / Core reviews.
>
>
> On Thu, Jun 20, 2019 at 6:50 PM Richard Smith <richardsmith_at_[hidden]>
> wrote:
>
>> On Thu, Jun 20, 2019 at 2:22 PM Richard Smith <richardsmith_at_[hidden]>
>> wrote:
>>
>>> As currently specified, bit_cast from an indeterminate value produces an
>>> unspecified value rather than an indeterminate value. That means this can't
>>> be implemented by a simple load on some implementations, and instead will
>>> require some kind of removing-the-taint-of-an-uninitialized-value operation
>>> to be performed. (A similar concern applies to reading from padding bits.)
>>>
>>> Is that the intent?
>>>
>>
>> I chatted with JF about this. The intent is as follows:
>>
>> * bits of the input that don't have defined values result in the
>> corresponding bit of the output being "bad"
>> * if any part of a scalar object is "bad", that object has an
>> indeterminate value
>>
>
> Something to note above: Richard pointed out that the Standard currently
> doesn't track bits. We could technically track them and have finer
> granularity, but that seems much bigger than bit_cast and is worth doing as
> a separate effort, if at all.
>
>
> I suspect that will invalidate lot of constexpr evaluators. Is it worth
> it?
>

I’m explicitly saying that such a change is out of scope for this
discussion, and I take no stance on whether we should do it or not.

What Richard proposes still matches the design intent, even if it's not as
> constrained as it could be (basically, we could track bits... but really
> you're playing with fire if you need that).
>
>
> Some examples:
>>
>>
>> struct A { char c; /* char padding : 8; */ short s; };
>> struct B { char x[4]; };
>>
>> B one() {
>> A a = {1, 2};
>> return std::bit_cast<B>(a);
>> }
>>
>> In one(), the second byte of the object representation of a is bad. That
>> means that the second byte of the produced B object is bad, so x[1] in the
>> produced B object is an indeterminate value. The above function, if
>> declared constexpr, would be usable in constant expressions so long as you
>> don't look at one().x[1].
>>
>>
>> A two() {
>> B b;
>> b.x[0] = 'a';
>> b.x[2] = 1;
>> b.x[3] = 2;
>> return std::bit_cast<A>(b);
>> }
>>
>> In two(), the second byte of the object representation of b is bad. But a
>> bit_cast to A doesn't care because it never looks at that byte. The above
>> function returns an A with a fully-defined value. If declared constexpr, it
>> would produce a normal, fully-initialized value.
>>
>>
>> int three() {
>> int n;
>> return std::bit_cast<int>(n);
>> }
>>
>> In three(), the entirety of n is bad. A bit_cast from it produces an int
>> whose value is indeterminate. And because we have an expression of
>> non-byte-like type that produced an indeterminate value, the behavior is
>> undefined.
>>
>>
>> B four() {
>> int n;
>> return std::bit_cast<B>(n);
>> }
>>
>> In four(), just like three(), the entirety of n is bad, so the scalar
>> subobjects of B are bad too. But because they're of byte-like type, that's
>> OK: we can copy them about and produce them from prvalue expressions.
>>
>>
>> I think the above is captured by the following wording change:
>>
>> Change in [bit.cast]p1:
>>
>> """
>> Returns: An object of type To. Each bit of the value representation of
>> the result is equal to the
>> corresponding bit in the object representation of from. Padding bits of
>> the To object are unspecified.
>> If there is no value of type To corresponding to the value representation
>> produced, the behavior is
>> undefined. If there are multiple such values, which value is produced is
>> unspecified.
>> <ins>A bit in the value representation of the result is indeterminate if
>> does not correspond to a bit in the value
>> representation of from or corresponds to a bit of an object that is not
>> within its lifetime or has an indeterminate value ([basic.indet]).
>> For each bit in the value representation of the result that is
>> indeterminate,
>> the smallest object containing that bit has an indeterminate value;
>>
>
> The above sentence makes me wonder, what effect does it have on:
>
> struct A { char x[4]; };
> union B { int i; short s; };
>
> A a;
> a.x[1] = a.x[2] = a.x[3] = 0;
> B b = bit_cast<B>(a);
>
> ?
>
>
>
>> the behavior is undefined unless that object is of unsigned ordinary
>> character type or std::byte type.
>>
>
> What's the intent / effect of the above?
>
>
>
>> The result does not otherwise contain any indeterminate values.</ins>
>> """
>>
> _______________________________________________
> ub mailing list
> ub_at_[hidden]
>
> https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.open-std.org%2Fmailman%2Flistinfo%2Fub&amp;data=02%7C01%7Cgdr%40microsoft.com%7Cb0c3874ebf0a4d53f6be08d6f60bab69%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636966927331276486&amp;sdata=BE2z9xvEHQzykENxaSjSd6veCjKKscobJl1ON%2B01moo%3D&amp;reserved=0
>
> _______________________________________________
> Core mailing list
> Core_at_[hidden]
> Subscription: http://lists.isocpp.org/mailman/listinfo.cgi/core
> Link to this post: http://lists.isocpp.org/core/2019/06/6736.php
>



SG12 list run by herb.sutter at gmail.com