Repository navigation
Some unclear wording in [except] CWG2775 #5238
Description
Activity
I think the thrown object means the operand of the throw-expression. As the exception object is the target of initialization, it's meaningless whether it's considered as an lvalue.
I think the thrown object means the operand of the throw-expression. As the exception object is the target of initialization, it's meaningless whether it's considered as an lvalue.
The operand of the throw-expression is an expression that does not necessarily denote an object, it can be a prvalue of a certain type, the result of which is not an object. The sentence
the constructor selected for the copy-initialization as well as the constructor selected for a copy-initialization considering the thrown object as an lvalue
should be rephrased into two parts
- the constructor selected for the copy-initialization of the exception object
- the constructor selected for a copy-initialization of the variable declared by the exception-declaration considering the
thrown objectexception object as an lvalue
The latter is consistent with [except.handle] p14. The lvalue purposes to otherwise specify the category of the initializer expression when copy-initializing the target where "a temporary object" would have been denoted by an xvalue in common cases.
The first occurrence of "thrown object" in [except.throw] has been present since C++98, and the second occurrence is introduced by CWG1863 (adopted at Oct. 2015). Both occurrences are older than the adoption of P0135R1 (at Jun. 2016) which makes prvalues of classes and arrays not objects.
What I meant is the first occurrence of "thrown object" might correctly mean the operand until C++17/P0135R1, but is incorrect now. It seems that the second occurrence of "thrown object" is wrong at first, because it is the exception object, whose type is never cv-qualified, that is needed to be copied, while the type of the operand of a throw-expression may be cv-qualified.
Since C++20/P0848R3, a copy/move constructor may be constrained, so there may be no such constructor "selected for the copy-initialization", in which case the program is presumably ill-formed. But it seems that the current wording does not cover such cases.
I want to use the following wording:
Let
tbe the operand of the throw-expression,VTbe the type of the exception object. For invented variableseandec, the variable definitionsVT e = t;andVT ec = e;shall be well-formed in the context of the throw-expression , even if the copy/move operation that would be used to create the exception object is elided.It seems that the current usage of "accessible" is also problematic, as the initialization and the copy of the exception object may happen in contexts with different access.
what do
eandecdenote respectively, in your wording?It seems that the current usage of "accessible" is also problematic, as the initialization and the copy of the exception object may happen in contexts with different access.
"accessible" is checked in the context of the first matched handler, except that the exception is rethrown, "accessible" will continue to be checked for the context of the first matched handler with which the rethrown exception matches, and so on.
eandecare not things. They are not created, and are only needed to check the validity of copy-initialization."Accessible" in [except.throw] p5 may refer to different contexts, and possibly two different constructors.
- The context where the throw-expression appears. The first constructor is checked.
- The context of the first matched handler. The second constructor is checked.
- The context inside
std::current_exception. The second constructor is checked.- However, even though a
std::current_exceptioncall may only copy the exception object when it's nested within some matched handler, thestd::current_exceptionfunction itself has no access to private constructors.
- However, even though a
In general, the two constructors that need to be checked refer to them:
- the constructor selected for constructing the exception object from the operand, which is the first one you refer to.
- the constructor selected for copying the exception object, which is the second one you refer to.
It seems that
rethrow_exceptionandmake_exception_ptrare also contexts where needs to check the corresponding constructor. It could augment your proposal toLet
Ebe the operand of the throw-expression,Tbe the type of the exception object. If the copy/move operation elision is not considered, for invented variables,vandvc:- the variable definition
T v = E;shall be well-formed in any context where constructs an exception object, T vc = v;shall be well-formed in any context where copies an exception object,
However, this proposal would ignore a case that the target object of the initialization has a base class type
BofTand the copy/move constructor ofBis inaccessible at that point.the confusion use of exceptions and exception objects.
[propagation] p1 states
The type exception_ptr can be used to refer to an exception object.
The same meaning is also appeared in [propagation] p10, [except.throw] p4.2. However, [propagation] p8 instead says that
An exception_ptr object that refers to the currently handled exception or a copy of the currently handled exception
The currently handled exception is an exception rather than an exception object. We should clarify the relationship between an exception and the exception object.
However, this proposal would ignore a case that the target object of the initialization has a base class type
BofTand the copy/move constructor ofBis inaccessible at that point.You're right. There can be three different constructors involved.
Given CWG2711 has made some clarification, the first two cases are addressed, and there's arguably no accessibility issue - it might be implied that accessibility check is made in the context of the throw-expression or the handler (which is also clang's current behavior).
The case for
std::current_exceptionis still problematic. Currently, no mainstream implementation performs the necessary check, and it's even unclear whether the source of copy-initialization needed forstd::current_exceptionis a cv-unqualified lvalue.We need to "capture" the (or a) copy constructor at the point of the "throw" (which may or may not be a throw-expression), because
current_exceptionand similar functions operate on type-erased exceptions, thus we can't perform any overload resolution or accessibility checks at that point.It seems to make sense to use a "const lvalue" for the source of the copy.
Reacted by A. Jiang- addedcwgIssue must be reviewed by CWG.Issue must be reviewed by CWG.not-editorialIssue is not deemed editorial; the editorial issue is kept open for tracking.Issue is not deemed editorial; the editorial issue is kept open for tracking.
on Jun 2, 2023 - changed the title
[-]Some unclear wording in [except][/-][+]Some unclear wording in [except] CWG2775[/+]on Apr 17, 2024
[except.throw] p3
The sentence does not specify the source of the copy-initialization. A possible improvement maybe that
[except.throw] p2
Is the following an improvement to the definition of "nearest handler"?
The improvement seems to be simpler to read.
[except.throw] p5
"thrown object" seems not to be a formal wording. Because we have defined exception object. Isn't that better to be used instead of "thrown object"?