You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Throwing an exception copy-initializes ([dcl.init], [class.copy.ctor]) a temporary object, called the exception object.
The sentence does not specify the source of the copy-initialization. A possible improvement maybe that
Throwing an exception copy-initializes ([dcl.init], [class.copy.ctor]) a temporary object, called the exception object, from the operand of the throw-expression.
[except.throw] p2
When an exception is thrown, control is transferred to the nearest handler with a matching type ([except.handle]); “nearest” means the handler for which the compound-statement or ctor-initializer following the try keyword was most recently entered by the thread of control and not yet exited.
Is the following an improvement to the definition of "nearest handler"?
“nearest” means the handler of a try-block or function-try-block whose compound-statement or ctor-initializer was most recently entered by the thread of control and not yet exited.
The improvement seems to be simpler to read.
[except.throw] p5
When the thrown object is a class object, the constructor selected for the copy-initialization as well as the constructor selected for a copy-initialization considering the thrown object as an lvalue shall be non-deleted and accessible, even if the copy/move operation is elided ([class.copy.elision]). The destructor is potentially invoked ([class.dtor]).
"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"?
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 object exception 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 t be the operand of the throw-expression, VT be the type of the exception object. For invented variables e and ec, the variable definitions VT e = t; and VT 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 e and ec denote 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.
e and ec are 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_exception call may only copy the exception object when it's nested within some matched handler, the std::current_exception function itself has no access to private constructors.
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_exception and make_exception_ptr are also contexts where needs to check the corresponding constructor. It could augment your proposal to
Let E be the operand of the throw-expression, T be the type of the exception object. If the copy/move operation elision is not considered, for invented variables, v and vc:
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 B of T and the copy/move constructor of B is 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 B of T and the copy/move constructor of B is 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_exception is still problematic. Currently, no mainstream implementation performs the necessary check, and it's even unclear whether the source of copy-initialization needed for std::current_exception is 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_exception and 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.
Activity
frederick-vs-ja commentedon Jan 26, 2022
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.
xmh0511 commentedon Jan 26, 2022
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
should be rephrased into two parts
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.
frederick-vs-ja commentedon Jan 26, 2022
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:
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.
xmh0511 commentedon Jan 27, 2022
what do
eandecdenote respectively, in your wording?"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.
frederick-vs-ja commentedon Jan 27, 2022
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.
std::current_exception. The second constructor is checked.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.xmh0511 commentedon Jan 27, 2022
In general, the two constructors that need to be checked refer to them:
It seems that
rethrow_exceptionandmake_exception_ptrare also contexts where needs to check the corresponding constructor. It could augment your proposal toHowever, 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.xmh0511 commentedon Jan 27, 2022
the confusion use of exceptions and exception objects.
[propagation] p1 states
The same meaning is also appeared in [propagation] p10, [except.throw] p4.2. However, [propagation] p8 instead says that
The currently handled exception is an exception rather than an exception object. We should clarify the relationship between an exception and the exception object.
frederick-vs-ja commentedon May 30, 2023
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.jensmaurer commentedon Jun 2, 2023
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.
frederick-vs-ja commentedon Apr 17, 2024
Should be already fixed by #6906 (c37fc84).
[-]Some unclear wording in [except][/-][+]Some unclear wording in [except] CWG2775[/+]