C++ Logo

std-discussion

Advanced search

Re: UB in P2641 'Checking if a union alternative is active'

From: Brian Bi <bbi5291_at_[hidden]>
Date: Mon, 19 Jun 2023 20:46:17 -0400
On Mon, Jun 19, 2023 at 8:04 PM Matthew House via Std-Discussion <
std-discussion_at_[hidden]> wrote:

> On Mon, Jun 19, 2023 at 5:33 PM language.lawyer--- via Std-Discussion
> <std-discussion_at_[hidden]> wrote:
> >
> > >>> How so? c1 is a punned reference to u, and c2 is a punned reference
> to
> > >>> u.i, so [basic.lval]/11 applies, does it not? Meanwhile, reading u.c
> > >>> for c3 would access an inactive variant.
> > >>
> > >> 'u.c' is pointer-interconvertible with 'u' (and 'u.i'), so '(char&)u'
> > >> refers to 'u.c'.
> > >
> > > Pointer-interconvertibility is always a relationship between two
> > > objects ([basic.compound]/4). If u.c exists, then the object u.c is
> > > pointer-interconvertible with the object u. But in this case, the only
> > > existing objects are u and u.i; the data member u.c does not refer to
> > > an object.
> >
> > What is u.c then, an «empty lvalue»?)))))))))))))
> > u.c denotes an out-of-lifetime object.
>
> By my reading of [basic.life]/7, it refers to allocated storage that
> the object could occupy, not any actual object. (I'm disregarding for
> now the issue regarding whether it only applies if the object is
> actually created at some point.) In particular, I interpret the
> "before the lifetime" part as applying to glvalues that would
> otherwise have referred to an object, had any object been created. If
> an object is later created in that storage, then only at that point
> will the glvalue start referring to the object.
>
> For [expr.static.cast]/14 to change a pointer value, it demands that
> "there is an object *b*" that is pointer-interconvertible, and I just
> don't see how "there is" could be read as applying before creation (or
> for that matter, after destruction). That would imply all sorts of
> silly things; e.g., at a given point in the program, how can an
> "out-of-lifetime" object have the same address as an object with which
> it is pointer-interconvertible ([basic.compound]/4), even while its
> properties haven't yet been determined since it hasn't yet been
> created ([intro.object]/1)?
>

I think you've noticed the fact that the standard looks as if it was
written at different times by different people, where at some points it
appears that objects can exist even when they are not within their
lifetimes, and at other points it appears that this is not the case. And
some of the rules only make sense if you take the first interpretation, and
some only make sense if you take the second. All these wording issues
should be addressed eventually, but, again, it's not so easy.

In recent years the first interpretation has generally been assumed.


>
> More generally, such an interpretation would completely break
> mechanisms enabled by [basic.lval]/11 using pointers that happen to
> refer to union members, since inactive union members would always have
> preference over reinterpretations allowed by the rule. For instance,
> suppose that u.c were declared as an unsigned char instead of a char.
> Then, std::memcpy(dest, &u.i, sizeof(int)) would be UB, since by
> reinterpreting its argument as an array of unsigned char, memcpy would
> produce a pointer to u.c, then read past its end. I don't think that's
> something that can be considered reasonable.
>

Well, `std::memcpy` can be defined by magic to do the right thing, but I
guess you're talking about a user-written analogue. Still, I don't
understand your argument. Under the current wording, you can't write such a
thing and have it have well-defined behavior according to the letter of the
law, *regardless of* what view you take on whether the `u.c` object exists
when it's not active.


>
> (Brian Bi refers to the issue of using [basic.lval]/11 to read the
> object representation being somewhat dubious with respect to the
> wording, but this issue also applies to other conversions that would
> be otherwise allowed, such as converting an int* to an unsigned int*
> when a containing union has an inactive unsigned int member.)
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>


-- 
*Brian Bi*

Received on 2023-06-20 00:46:31