C++ Logo

std-discussion

Advanced search

Re: What is an access in [res.on.objects]?

From: language.lawyer_at <language.lawyer_at_[hidden]>
Date: Thu, 20 Oct 2022 21:38:17 +0500
>>> 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.

What do you mean by explicitly writing «storage»? You disagree with that the expression accesses `s`?

> But a read would not have undefined behavior.

[basic.lval]/11 apply uniformly to both kinds of access: reads and writes.

>> 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].

I know.

>> 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].

AFAIK, recursive invocations are handled by https://timsong-cpp.github.io/cppwp/n4868/reentrancy (plus some LWG issue saying that invoking method A on a library object during execution of method B on the same object is also (should also be) covered by this paragraph as a recursive invocation)

So you do not need [res.on.objects] to restrict a user destructor in calling a vector's methods during that vector's destruction which caused the invocation of the user's destructor.

Received on 2022-10-20 16:38:22