On Mon, Oct 11, 2021 at 2:47 PM Daveed Vandevoorde <daveed@edg.com> wrote:
On Oct 11, 2021, at 4:06 PM, Richard Smith via Core <core@lists.isocpp.org> wrote:

+SG12 for UB / object model question.

On Sun, Oct 10, 2021 at 4:27 AM Andrey Erokhin <language.lawyer@gmail.com> 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.

+1, but can we call it (“angelic nondeterminism”) something else ;-) ?  Maybe “retroacted nondeterminism”?

Sure, we can call it something else, and probably should. We need to distinguish between the "we pick the option that makes your program work" nondeterminism (here) and "we pick the option that breaks your program" nondeterminism (UB, formally), though, so having two parallel but opposing terms seems nice. Maybe optimistic nondeterminism and pessimistic nondeterminism?
 
I haven’t thought about the consequences for constexpr evaluation yet, but it’s not giving me the warm-and-fuzzies...

We probably should not permit this in constant evaluation, like we said we wouldn't for other kinds of optimistic nondeterminism.
 
Daveed



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.)
_______________________________________________
Core mailing list
Core@lists.isocpp.org
Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/core
Link to this post: http://lists.isocpp.org/core/2021/10/11627.php