Date: Mon, 31 Jul 2023 19:52:40 -0400
On Mon, Jul 31, 2023 at 7:44 PM Thiago Macieira via Std-Discussion
<std-discussion_at_[hidden]> wrote:
>
> On Monday, 31 July 2023 13:35:15 PDT Brian Bi via Std-Discussion wrote:
> > You do need to call `std::launder` after the `reinterpret_cast` because the
> > object that is nested within the `storage_` array is not
> > pointer-interconvertible with it.
>
> Except that the standard has a loophole if the compiler doesn't see all
> possible object lifetime starts.
>
> http://eel.is/c++draft/basic.compound#4
> > Two objects a and b are pointer-interconvertible if:
> > - ...
> > - one is a union object and the other is a non-static data member of that
> > object
> > - ...
> > - there exists an object c such that a and c are pointer-interconvertible,
> > and c and b are pointer-interconvertible
>
> For any two types X1 and X2, you can define a union
>
> union U
> {
> X1 x1;
> X2 x2;
> };
>
> From 4.2, an object of type X1 and and one of U are pointer-interconvertible
> and ditto for X2 and U. From 4.4, because of that, X1 and X2 are pointer-
> interconvertible.
>
> This means this has to be allowed:
> X1 *x2 = get_pointer_x1();
> auto x2 = reinterpret_cast<X2 *>(x1);
> unless the compiler can prove that get_pointer_x1() did not create an object
> of type U there.
That's not how UB works. It's not defined on the basis of what the
compiler can "prove" happened. It's defined by what *actually
happens*. `get_pointer_x1` did something. If that something was to
create an object of type `U`, then this behavior may be defined.
Otherwise it is not.
This isn't about what people can expect to get away with from their
compilers. This is about what's actually going on in the object model.
What `get_pointer_x1` actually does is part of that.
<std-discussion_at_[hidden]> wrote:
>
> On Monday, 31 July 2023 13:35:15 PDT Brian Bi via Std-Discussion wrote:
> > You do need to call `std::launder` after the `reinterpret_cast` because the
> > object that is nested within the `storage_` array is not
> > pointer-interconvertible with it.
>
> Except that the standard has a loophole if the compiler doesn't see all
> possible object lifetime starts.
>
> http://eel.is/c++draft/basic.compound#4
> > Two objects a and b are pointer-interconvertible if:
> > - ...
> > - one is a union object and the other is a non-static data member of that
> > object
> > - ...
> > - there exists an object c such that a and c are pointer-interconvertible,
> > and c and b are pointer-interconvertible
>
> For any two types X1 and X2, you can define a union
>
> union U
> {
> X1 x1;
> X2 x2;
> };
>
> From 4.2, an object of type X1 and and one of U are pointer-interconvertible
> and ditto for X2 and U. From 4.4, because of that, X1 and X2 are pointer-
> interconvertible.
>
> This means this has to be allowed:
> X1 *x2 = get_pointer_x1();
> auto x2 = reinterpret_cast<X2 *>(x1);
> unless the compiler can prove that get_pointer_x1() did not create an object
> of type U there.
That's not how UB works. It's not defined on the basis of what the
compiler can "prove" happened. It's defined by what *actually
happens*. `get_pointer_x1` did something. If that something was to
create an object of type `U`, then this behavior may be defined.
Otherwise it is not.
This isn't about what people can expect to get away with from their
compilers. This is about what's actually going on in the object model.
What `get_pointer_x1` actually does is part of that.
Received on 2023-07-31 23:52:52