On Thu, 20 Oct 2022 at 10:14, language.lawyer--- via Std-Discussion <std-discussion@lists.isocpp.org> wrote:
On 20/10/2022 07:11, Eric Schmidt via Std-Discussion wrote:
> This issue came up on a StackOverflow question:
>
> https://stackoverflow.com/questions/74080177/double-free-in-the-c-standard-library-using-only-stdfunction-and-stdshared/74080676
>
> This led to a discussion here:
>
> https://chat.stackoverflow.com/rooms/248862/discuss-lwg2224
>
> Now, https://eel.is/c++draft/res.on.objects states
>
> "If an object of a standard library type is accessed, and the beginning of the object's lifetime does not happen before the access, or the access does not happen before the end of the object's lifetime, the behavior is undefined unless otherwise specified."
>
> According to a non-normative note in https://eel.is/c++draft/defns.access,
>
> "Only glvalues of scalar type can be used to access objects. ... Attempts to read or modify an object of class type typically invoke a constructor or assignment operator; such invocations do not themselves constitute accesses, although they may involve accesses of scalar subobjects."
>
> The question is, can objects of class type be accessed?

Can, in some sense. But it would be UB. E.g.

struct S
{
        char buf[sizeof(int)];
} s;

reinterpret_cast<int&>(s) = 0; // kinda accesses `s`, but this violates strict aliasing, so the behavior is actually undefined

It accesses `s`'s storage, sure. But a read would not have undefined behavior.

> Is it the intent that an access to a scalar subobject also constitutes an access to the object itself?

No

> If not, then it is impossible for objects of most library types to be accessed.

There are other things which are undefined if an object (or rather a pointer/glvalue referring to it) is used before the lifetime has started
https://timsong-cpp.github.io/cppwp/n4868/basic.life#6.sentence-5
https://timsong-cpp.github.io/cppwp/n4868/basic.life#7.sentence-4

That doesn't apply to objects of class type within construction/destruction; it only applies before their constructor starts or after the destructor ends. During construction/destruction, we are referred to [class.cdtor].

> If so, then objects of many library types are unavoidably accessed by their own destructor, after the end of their lifetime. Either way, [res.on.objects] doesn't work.

Treat [res.on.objects] as a non-normative reminder about above-mentioned parts from [basic.life]
(But in general, there are lots of nonsensical wording in the library part, if read it from strict core part POV.)

The library desires to be more restrictive than the language here. It is possible to write user code that is robust against recursive invocation (by ensuring that invariants hold before calling any operation not directly under the control of the user code, and by ensuring that all and any recursive modifications called from therein are handled in some defined way). But we don't want to constrain the library to that extent, so we add UB under [constraints].

> "11.9.5 [class.cdtor]/p4 explicitly permits member function calls during destruction, so the behavior of this code is well-defined as far as the core language is concerned, despite the fact that it accesses a library object after the end of the object's lifetime. If we want this code to have undefined behavior, we need to specify that at the library level."

In 2414, Tim appears to be following the "scalar subobject" interpretation (the reference to data races makes this fairly clear). For 2224, surely the same interpretation is in play ("access" was defined in CWG 1531 the previous year, and the power of that definition is clearly on respondents' minds). Perhaps one point that can save us from concluding that library classes aren't allowed to access their scalar subobjects from their cdtors would be to note that [res.on.objects] is a subclause of [constraints], so the restriction should only hold for access caused by user code. But that's a bit of a reach.

Another concern would be: is invocation of a virtual member function (which may not access a scalar) an access? It seems that it should be, otherwise we might contrive to invoke virtual member functions on standard library classes during construction/destruction, but that isn't access to a glvalue scalar.

> For reference, here is some previous discussion from 2014:
>
> https://groups.google.com/a/isocpp.org/g/std-discussion/c/BGsVyc0Ittw/m/atiDfIqnc-YJ
>
> And the LWG issue that led to the current wording:
>
> https://cplusplus.github.io/LWG/issue2224
--
Std-Discussion mailing list
Std-Discussion@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion