On Mon, 11 Oct 2021 at 21:08, Richard Smith via SG12 <sg12@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. 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.

I don't know all the C++ context here, but I expect that angelic nondeterminism would be a nightmare for anyone trying to make the object model precise, in tools or in maths.  If at all possible, I'd suggest finding some way to identify the lifetime end points (either implicitly or, analogous to placement-new, with an explicit kill), so that one can maintain an unambiguous tree-shaped subobject structure.

Peter

 

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.)
_______________________________________________
SG12 mailing list
SG12@lists.isocpp.org
Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/sg12
Searchable archives: http://lists.isocpp.org/sg12/2021/10/index.php