cplusplus / CWG

Core Working Group
23 stars 7 forks source link

[class.dtor] Explicit destruction of an object usable in constant expressions followed by non-cleaning termination #481

Open frederick-vs-ja opened 6 months ago

frederick-vs-ja commented 6 months ago

Full name of submitter (unless configured in github; will be published with the issue): Jiang An

Reference (section label): [expr.const], [class.dtor]

Link to reflector thread (if any):

Issue description:

The execution of the following program doesn't seem to have undefined behavior according to the current standard wording, because terminating via _Exit doesn't execute destructor for b ([support.start.term] p3) and hence b won't be destroyed twice.

#include <cstdlib>

struct B { int n = 0; constexpr ~B() { n = -1; } };
constexpr B b{};

int main()
{
    b.~B();
    std::_Exit(0);
}

However, segmentation fault can be raised on some implementations (especially when optimizatoin is disabled, Godbolt link), which shouldn't happen if there's no UB.

The current wording seemingly forbids implementations to place b into read-only storage, even if it is usable in constant expressions and has no mutable subobject.

Suggested resolution:

Option A: NAD, which means that the existing strategy is non-conforming.

Option B: Disallowing explicit destructor invocation for objects usable constant expressions.

Change [expr.call] p4 on the top of the current possible resolution of CWG2839:

[...] If the postfix-expression P names a destructor or a pseudo-destructor, the postfix-expression is a possibly-parenthesized class member access. If P names a destructor, the behavior is undefined if the object expression denotes a base class subobject and that base class is or has a virtual base class or a virtual function (11.4.7 [class.dtor]) or an object usable in constant expressions (7.7 [expr.const]). [...]

Option C: Allowing explicit destructor invocation for objects usable constant expressions with modification disallowed.

Change [class.dtor] p16 as indicated:

[...] the program has undefined behavior. In an explicit destructor call for an object usable in constant expressions (7.7 [expr.const]), if any non-mutable subobject of that object (possibly recursive) is modified, the program has undefined behavior.

jensmaurer commented 5 months ago

This is undefined behavior because the explicit destructor call on b modifies a const object, per [dcl.type.cv] p4.

If your counterargument is that b is outside its lifetime, I think we should rephrase "outside its lifetime" to also including "during its period of destruction".

frederick-vs-ja commented 5 months ago

If your counterargument is that b is outside its lifetime, I think we should rephrase "outside its lifetime" to also including "during its period of destruction".

Hmm... it seems intended that const and volatile shouldn't be in effect during destruction per [class.dtor] p5:

[...] A destructor can be invoked for a const, volatile or const volatile object. const and volatile semantics ([dcl.type.cv]) are not applied on an object under destruction. They stop being in effect when the destructor for the most derived object ([intro.object]) starts.

Perhaps there should be some additional rules for objects that are variables.