C++ Logo

sg12

Advanced search

Re: [SG12] Are we OK with objects which are subobjects of 2 (or more) objects at the same time?

From: Richard Smith <richardsmith_at_[hidden]>
Date: Mon, 11 Oct 2021 13:06:47 -0700
+SG12 for UB / object model question.

On Sun, Oct 10, 2021 at 4:27 AM Andrey Erokhin <language.lawyer_at_[hidden]>
wrote:

> [intro.object]/2 says:
> > If an object is created in storage associated with a member subobject or
> array element /e/ (which may or may not be within its lifetime), the
> created object is a subobject of /e/'s containing object if:
> > — the lifetime of /e/'s containing object has begun and not ended, and
> > — the storage for the new object exactly overlays the storage location
> associated with /e/, and
> > — the new object is of the same type as /e/ (ignoring cv-qualification).
>
> Now, lets look at
>
> struct S
> {
> std::byte m;
>

Let's imagine there's some more stuff here so sizeof(S) is larger:

char padding[123];
int align_to_int;


> };
>
> std::byte buf[sizeof(S)];
>
> auto p = new (buf) S {}; //1
> new (buf) std::byte{}; //2
>

If we continue this example:

int *p = new (buf + sizeof(int)) int;

... then we know at this point the lifetime of S has ended because its
storage was reused. So if the object created at //2 was a subobject of the
S object created at //1, then that object is in a bad state -- it's a
subobject of an object whose lifetime has ended. That sounds problematic --
at least in principle -- for things that look like variant<S, std::byte>.

So I think either we want to say that the newly-created object at //2 is a
subobject of two different objects or we want to say that we use angelic
nondeterminism here (//2 either creates a subobject of S, replacing s.m, or
creates a subobject of buf, replacing buf[0], depending on how the program
continues). Given that we already have angelic nondeterminsm in the object
model and don't otherwise have cases where the subobject structure is not a
tree, I think the angelic nondeterminism model would likely be the better
option. That and, I don't think it's desirable to allow a newly-created
object to be used as two different subobjects -- the program should not be
engaging in such object identity punning.

A few words about //1: well, an object of std::byte type which is a
> subobject corresponding to S::m NSDM is "created" there, but I don't think
> [intro.object]/2 cares about it. So lets ignore line //1 here.
>
> In //2, however, the newly-created complete object of std::byte type
> becomes both array element of buf and member subobject of *p, according to
> [intro.object]/2. And I don't think this is what we want.
>
> What if we rewrite the rule as follows:
> > If an object is created in storage associated with a member subobject or
> array element /e/ (which may or may not be within its lifetime), the
> created object is a subobject of /e/'s containing object <ins>/c/</ins> if:
> > — the lifetime of <del>/e/'s containing object</del><ins>/c/</ins> has
> begun and not ended, and
> > — the storage for the new object exactly overlays the storage location
> associated with /e/, and
> > — the new object is of the same type as /e/ (ignoring
> cv-qualification)<del>.</del><ins>, and</ins>
> > <ins>— there is no member subobject or array element /e1/ and its
> containing object /c1/ satisfying these constraints nested within /c/.</ins>
>
> Then, the newly-created complete object of std::byte type will only become
> a member subobject of *p.
>

 (Note from Andrey for SG12's benefit: *In the previous message, the word
"complete" should be completely ignored.)

Received on 2021-10-11 15:07:01