From 2dc8074ed9dc73f366cd7e4491218fdbeda9906b Mon Sep 17 00:00:00 2001 From: Alisdair Meredith Date: Tue, 1 Oct 2024 09:20:10 -0400 Subject: [PATCH 1/7] [except.uncaught] Tidy the specification for uncaught exceptions Several concurrent fixes. First include the normative wording that 'uncaught_exceptions' returns the number of uncaught exceptions *on the current thread*. This wording is present in the core language. Then move the core wording for when an exception is uncaught directly into the text that talks about caught and uncaught exceptions. In the process, turn the reference to into a note, so that there is only one normative specification. Finally, remove [except.uncaught] as it is now empty, and add the missing descriptive sentence to the intro paragraph of [except.special.general]. This happens to produce quite a pleasing page-break, but that is just luck, not design. --- source/exceptions.tex | 44 +++++++++++++++++++++++-------------------- source/support.tex | 2 +- source/xrefdelta.tex | 3 +++ 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/source/exceptions.tex b/source/exceptions.tex index a462fe0bff..4cc22a60fd 100644 --- a/source/exceptions.tex +++ b/source/exceptions.tex @@ -317,6 +317,15 @@ the selected constructor is odr-used\iref{basic.def.odr} and the destructor of \tcode{T} is potentially invoked\iref{class.dtor}. +\pnum +An exception is considered uncaught +after completing the initialization of the exception object +until completing the activation of a handler for the exception\iref{except.handle}. +\begin{note} +As a consequence, an exception is considered uncaught +during any stack unwinding resulting from it being thrown. +\end{note} + \pnum \indextext{exception handling!rethrow}% \indextext{rethrow|see{exception handling, rethrow}}% @@ -327,11 +336,21 @@ it is rethrown. \end{note} +\pnum +\indexlibraryglobal{uncaught_exceptions}% +If an exception is rethrown\iref{expr.throw,propagation}, +it is considered uncaught from the point of rethrow +until the rethrown exception is caught. +\begin{note} +The function \tcode{std::uncaught_exceptions}\iref{uncaught.exceptions} +returns the number of uncaught exceptions in the current thread. +\end{note} + \pnum \indextext{exception handling!terminate called@\tcode{terminate} called}% \indextext{\idxcode{terminate}!called}% If the exception handling mechanism -handling an uncaught exception\iref{except.uncaught} +handling an uncaught exception directly invokes a function that exits via an exception, the function \tcode{std::terminate} is invoked\iref{except.terminate}. \begin{example} @@ -1003,8 +1022,10 @@ The function \tcode{std::terminate}\iref{except.terminate} is used by the exception handling mechanism for coping with errors related to the exception handling -mechanism itself. The function -\tcode{std::current_exception()}\iref{propagation} and the class +mechanism itself. +The function \tcode{std::uncaught_exceptions}\iref{uncaught.exceptions} +reports how many exceptions are uncaught in the current thread. +The function \tcode{std::current_exception}\iref{propagation} and the class \tcode{std::nested_exception}\iref{except.nested} can be used by a program to capture the currently handled exception. @@ -1128,21 +1149,4 @@ prematurely based on a determination that the unwind process will eventually cause an invocation of the function \tcode{std::terminate}. - -\rSec2[except.uncaught]{The \tcode{std::uncaught_exceptions} function}% -\indexlibraryglobal{uncaught_exceptions} - -\pnum -An exception is considered uncaught -after completing the initialization of the exception object\iref{except.throw} -until completing the activation of a handler for the exception\iref{except.handle}. -\begin{note} -As a consequence, an exception is considered uncaught -during any stack unwinding resulting from it being thrown. -\end{note} -If an exception is rethrown\iref{expr.throw,propagation}, -it is considered uncaught from the point of rethrow -until the rethrown exception is caught. -The function \tcode{std::uncaught_exceptions}\iref{uncaught.exceptions} -returns the number of uncaught exceptions in the current thread.% \indextext{exception handling|)} diff --git a/source/support.tex b/source/support.tex index 28c15ac913..c8e0d789ae 100644 --- a/source/support.tex +++ b/source/support.tex @@ -4013,7 +4013,7 @@ \begin{itemdescr} \pnum \returns -The number of uncaught exceptions\iref{except.uncaught}. +The number of uncaught exceptions\iref{except.throw} in the current thread. \pnum \remarks diff --git a/source/xrefdelta.tex b/source/xrefdelta.tex index 04302a06d0..a726c44f41 100644 --- a/source/xrefdelta.tex +++ b/source/xrefdelta.tex @@ -95,6 +95,9 @@ \movedxref{stoptoken.cons}{stopsource} \movedxref{stoptoken.nonmembers}{stopsource} +% https://github.com/cplusplus/draft/pull/7276 +\movedxref{except.uncaught}{except.throw} + %%% Deprecated features. %%% Example: % From 0ba172ea911fdd742a9930f55c7ce609a81e5e66 Mon Sep 17 00:00:00 2001 From: Alisdair Meredith Date: Wed, 2 Oct 2024 14:59:41 -0400 Subject: [PATCH 2/7] [except.terminate] Better describe the function While 'std:terminate' was originally conceived as the way to report failures in the exception handling machinery, it has evolved to become a more general tool for reporting unrecoverable failures in the C++ runtime. This rewording attempts to address that evolving design, and in doing so addresses the outstanding %FIXME% that the current text is not adequately descriptive in the first place. --- source/exceptions.tex | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/source/exceptions.tex b/source/exceptions.tex index 4cc22a60fd..107c5f7d06 100644 --- a/source/exceptions.tex +++ b/source/exceptions.tex @@ -1033,9 +1033,10 @@ \pnum \indextext{\idxcode{terminate}}% -% FIXME: What does it mean to abandon exception handling? -In some situations, exception handling is abandoned -for less subtle error handling techniques. +Some errors in a program cannot be recovered from, such as when an exception +is not handled or a \tcode{std::thread} object is destroyed while its thread +function is still running. In such cases, +the function \tcode{std::terminate}\iref{exception.terminate} is invoked. \begin{note} These situations are: \indextext{\idxcode{terminate}!called}% @@ -1129,8 +1130,6 @@ \pnum \indextext{\idxcode{terminate}}% -In such cases, -the function \tcode{std::terminate} is invoked\iref{exception.terminate}. In the situation where no matching handler is found, it is \impldef{stack unwinding before invocation of \tcode{std::terminate}} whether or not the stack is unwound From 8c718eae4e8be4d07cc71a0d036ae064befd2bf5 Mon Sep 17 00:00:00 2001 From: Alisdair Meredith Date: Wed, 16 Oct 2024 10:12:30 -0400 Subject: [PATCH 3/7] [except.spec] Exception specifications are function declarators --- source/declarations.tex | 266 ++++++++++++++++++++++++++++++++++++++++ source/exceptions.tex | 266 ---------------------------------------- 2 files changed, 266 insertions(+), 266 deletions(-) diff --git a/source/declarations.tex b/source/declarations.tex index 446aebd3aa..6a960ea03b 100644 --- a/source/declarations.tex +++ b/source/declarations.tex @@ -4109,6 +4109,272 @@ \end{footnote} \indextext{declarator!function|)} +\rSec3[except.spec]{Exception specifications}% +\indextext{exception specification|(} + +\pnum +The predicate indicating whether a function cannot exit via an exception +is called the \defn{exception specification} of the function. +If the predicate is false, +the function has a +\indextext{exception specification!potentially-throwing}% +\defnx{potentially-throwing exception specification}% +{potentially-throwing!exception specification}, +otherwise it has a +\indextext{exception specification!non-throwing}% +\defn{non-throwing exception specification}. +The exception specification is either defined implicitly, +or defined explicitly +by using a \grammarterm{noexcept-specifier} +as a suffix of a function declarator\iref{dcl.fct}. + +\begin{bnf} +\nontermdef{noexcept-specifier}\br + \keyword{noexcept} \terminal{(} constant-expression \terminal{)}\br + \keyword{noexcept}\br +\end{bnf} + +\pnum +\indextext{exception specification!noexcept!constant expression and}% +In a \grammarterm{noexcept-specifier}, the \grammarterm{constant-expression}, +if supplied, shall be a contextually converted constant expression +of type \keyword{bool}\iref{expr.const}; +that constant expression is the exception specification of +the function type in which the \grammarterm{noexcept-specifier} appears. +A \tcode{(} token that follows \keyword{noexcept} is part of the +\grammarterm{noexcept-specifier} and does not commence an +initializer\iref{dcl.init}. +The \grammarterm{noexcept-specifier} \keyword{noexcept} +without a \grammarterm{constant-expression} +is +equivalent to the \grammarterm{noexcept-specifier} +\tcode{\keyword{noexcept}(\keyword{true})}. +\begin{example} +\begin{codeblock} +void f() noexcept(sizeof(char[2])); // error: narrowing conversion of value 2 to type \keyword{bool} +void g() noexcept(sizeof(char)); // OK, conversion of value 1 to type \keyword{bool} is non-narrowing +\end{codeblock} +\end{example} + +\pnum +If a declaration of a function +does not have a \grammarterm{noexcept-specifier}, +the declaration has a potentially throwing exception specification +unless it is a destructor or a deallocation function +or is defaulted on its first declaration, +in which cases the exception specification +is as specified below +and no other declaration for that function +shall have a \grammarterm{noexcept-specifier}. +In an explicit instantiation\iref{temp.explicit} +a \grammarterm{noexcept-specifier} may be specified, +but is not required. +If a \grammarterm{noexcept-specifier} is specified +in an explicit instantiation, +the exception specification shall be the same as +the exception specification of all other declarations of that function. +A diagnostic is required only if the +exception specifications are not the same +within a single translation unit. + +\pnum +\indextext{exception specification!virtual function and}% +If a virtual function has a +non-throwing exception specification, +all declarations, including the definition, of any function +that overrides that virtual function in any derived class +shall have a non-throwing +exception specification, +unless the overriding function is defined as deleted. +\begin{example} +\begin{codeblock} +struct B { + virtual void f() noexcept; + virtual void g(); + virtual void h() noexcept = delete; +}; + +struct D: B { + void f(); // error + void g() noexcept; // OK + void h() = delete; // OK +}; +\end{codeblock} + +The declaration of +\tcode{D::f} +is ill-formed because it +has a potentially-throwing exception specification, +whereas +\tcode{B::f} +has a non-throwing exception specification. +\end{example} + +\pnum +An expression $E$ is +\defnx{potentially-throwing}{potentially-throwing!expression} if +\begin{itemize} +\item +$E$ is a function call\iref{expr.call} +whose \grammarterm{postfix-expression} +has a function type, +or a pointer-to-function type, +with a potentially-throwing exception specification, +or +\item +$E$ implicitly invokes a function +(such as an overloaded operator, +an allocation function in a \grammarterm{new-expression}, +a constructor for a function argument, +or a destructor if $E$ is a full-expression\iref{intro.execution}) +that has a potentially-throwing exception specification, +or +\item +$E$ is a \grammarterm{throw-expression}\iref{expr.throw}, +or +\item +$E$ is a \keyword{dynamic_cast} expression that casts to a reference type and +requires a runtime check\iref{expr.dynamic.cast}, +or +\item +$E$ is a \keyword{typeid} expression applied to a +(possibly parenthesized) built-in unary \tcode{*} operator +applied to a pointer to a +polymorphic class type\iref{expr.typeid}, +or +\item +any of the immediate subexpressions\iref{intro.execution} +of $E$ is potentially-throwing. +\end{itemize} + +\pnum +An implicitly-declared constructor for a class \tcode{X}, +or a constructor without a \grammarterm{noexcept-specifier} +that is defaulted on its first declaration, +has a potentially-throwing exception specification +if and only if +any of the following constructs is potentially-throwing: +\begin{itemize} +\item +the invocation of a constructor selected by overload resolution +in the implicit definition of the constructor +for class \tcode{X} +to initialize a potentially constructed subobject, or +\item +a subexpression of such an initialization, +such as a default argument expression, or, +\item +for a default constructor, a default member initializer. +\end{itemize} +\begin{note} +Even though destructors for fully-constructed subobjects +are invoked when an exception is thrown +during the execution of a constructor\iref{except.ctor}, +their exception specifications do not contribute +to the exception specification of the constructor, +because an exception thrown from such a destructor +would call the function \tcode{std::terminate} +rather than escape the constructor\iref{except.throw,except.terminate}. +\end{note} + +\pnum +The exception specification for an implicitly-declared destructor, +or a destructor without a \grammarterm{noexcept-specifier}, +is potentially-throwing if and only if +any of the destructors +for any of its potentially constructed subobjects +has a potentially-throwing exception specification or +the destructor is virtual and the destructor of any virtual base class +has a potentially-throwing exception specification. + +\pnum +The exception specification for an implicitly-declared assignment operator, +or an assignment-operator without a \grammarterm{noexcept-specifier} +that is defaulted on its first declaration, +is potentially-throwing if and only if +the invocation of any assignment operator +in the implicit definition is potentially-throwing. + +\pnum +A deallocation function\iref{basic.stc.dynamic.deallocation} +with no explicit \grammarterm{noexcept-specifier} +has a non-throwing exception specification. + +\pnum +The exception specification for a comparison operator function\iref{over.binary} +without a \grammarterm{noexcept-specifier} +that is defaulted on its first declaration +is potentially-throwing if and only if +any expression +in the implicit definition is potentially-throwing. + +\pnum +\begin{example} +\begin{codeblock} +struct A { + A(int = (A(5), 0)) noexcept; + A(const A&) noexcept; + A(A&&) noexcept; + ~A(); +}; +struct B { + B() noexcept; + B(const B&) = default; // implicit exception specification is \tcode{\keyword{noexcept}(\keyword{true})} + B(B&&, int = (throw 42, 0)) noexcept; + ~B() noexcept(false); +}; +int n = 7; +struct D : public A, public B { + int * p = new int[n]; + // \tcode{D::D()} potentially-throwing, as the \keyword{new} operator may throw \tcode{bad_alloc} or \tcode{bad_array_new_length} + // \tcode{D::D(const D\&)} non-throwing + // \tcode{D::D(D\&\&)} potentially-throwing, as the default argument for \tcode{B}'s constructor may throw + // \tcode{D::\~D()} potentially-throwing +}; +\end{codeblock} +Furthermore, if +\tcode{A::\~{}A()} +were virtual, +the program would be ill-formed since a function that overrides a virtual +function from a base class +shall not have a potentially-throwing exception specification +if the base class function has a non-throwing exception specification. +\end{example} + +\pnum +An exception specification is considered to be \defnx{needed}{needed!exception specification} when: +\begin{itemize} +\item in an expression, the function is selected by +overload resolution\iref{over.match,over.over}; + +\item the function is odr-used\iref{term.odr.use} or, if it appears in an +unevaluated operand, would be odr-used if the expression were +potentially-evaluated; + +\item the exception specification is compared to that of another +declaration (e.g., an explicit specialization or an overriding virtual +function); + +\item the function is defined; or + +\item the exception specification is needed for a defaulted +function that calls the function. +\begin{note} +A defaulted declaration does not require the +exception specification of a base member function to be evaluated +until the implicit exception specification of the derived +function is needed, but an explicit \grammarterm{noexcept-specifier} needs +the implicit exception specification to compare against. +\end{note} +\end{itemize} +The exception specification of a defaulted +function is evaluated as described above only when needed; similarly, the +\grammarterm{noexcept-specifier} of a specialization of a function +template or member function of a class template is instantiated only when +needed. +% +\indextext{exception specification|)} + \rSec3[dcl.fct.default]{Default arguments}% \indextext{declaration!default argument|(} diff --git a/source/exceptions.tex b/source/exceptions.tex index 107c5f7d06..464b2786c8 100644 --- a/source/exceptions.tex +++ b/source/exceptions.tex @@ -748,272 +748,6 @@ \indextext{exception handling!handler!match|)}% \indextext{exception handling!handler|)} -\rSec1[except.spec]{Exception specifications}% -\indextext{exception specification|(} - -\pnum -The predicate indicating whether a function cannot exit via an exception -is called the \defn{exception specification} of the function. -If the predicate is false, -the function has a -\indextext{exception specification!potentially-throwing}% -\defnx{potentially-throwing exception specification}% -{potentially-throwing!exception specification}, -otherwise it has a -\indextext{exception specification!non-throwing}% -\defn{non-throwing exception specification}. -The exception specification is either defined implicitly, -or defined explicitly -by using a \grammarterm{noexcept-specifier} -as a suffix of a function declarator\iref{dcl.fct}. - -\begin{bnf} -\nontermdef{noexcept-specifier}\br - \keyword{noexcept} \terminal{(} constant-expression \terminal{)}\br - \keyword{noexcept}\br -\end{bnf} - -\pnum -\indextext{exception specification!noexcept!constant expression and}% -In a \grammarterm{noexcept-specifier}, the \grammarterm{constant-expression}, -if supplied, shall be a contextually converted constant expression -of type \keyword{bool}\iref{expr.const}; -that constant expression is the exception specification of -the function type in which the \grammarterm{noexcept-specifier} appears. -A \tcode{(} token that follows \keyword{noexcept} is part of the -\grammarterm{noexcept-specifier} and does not commence an -initializer\iref{dcl.init}. -The \grammarterm{noexcept-specifier} \keyword{noexcept} -without a \grammarterm{constant-expression} -is -equivalent to the \grammarterm{noexcept-specifier} -\tcode{\keyword{noexcept}(\keyword{true})}. -\begin{example} -\begin{codeblock} -void f() noexcept(sizeof(char[2])); // error: narrowing conversion of value 2 to type \keyword{bool} -void g() noexcept(sizeof(char)); // OK, conversion of value 1 to type \keyword{bool} is non-narrowing -\end{codeblock} -\end{example} - -\pnum -If a declaration of a function -does not have a \grammarterm{noexcept-specifier}, -the declaration has a potentially throwing exception specification -unless it is a destructor or a deallocation function -or is defaulted on its first declaration, -in which cases the exception specification -is as specified below -and no other declaration for that function -shall have a \grammarterm{noexcept-specifier}. -In an explicit instantiation\iref{temp.explicit} -a \grammarterm{noexcept-specifier} may be specified, -but is not required. -If a \grammarterm{noexcept-specifier} is specified -in an explicit instantiation, -the exception specification shall be the same as -the exception specification of all other declarations of that function. -A diagnostic is required only if the -exception specifications are not the same -within a single translation unit. - -\pnum -\indextext{exception specification!virtual function and}% -If a virtual function has a -non-throwing exception specification, -all declarations, including the definition, of any function -that overrides that virtual function in any derived class -shall have a non-throwing -exception specification, -unless the overriding function is defined as deleted. -\begin{example} -\begin{codeblock} -struct B { - virtual void f() noexcept; - virtual void g(); - virtual void h() noexcept = delete; -}; - -struct D: B { - void f(); // error - void g() noexcept; // OK - void h() = delete; // OK -}; -\end{codeblock} - -The declaration of -\tcode{D::f} -is ill-formed because it -has a potentially-throwing exception specification, -whereas -\tcode{B::f} -has a non-throwing exception specification. -\end{example} - -\pnum -An expression $E$ is -\defnx{potentially-throwing}{potentially-throwing!expression} if -\begin{itemize} -\item -$E$ is a function call\iref{expr.call} -whose \grammarterm{postfix-expression} -has a function type, -or a pointer-to-function type, -with a potentially-throwing exception specification, -or -\item -$E$ implicitly invokes a function -(such as an overloaded operator, -an allocation function in a \grammarterm{new-expression}, -a constructor for a function argument, -or a destructor if $E$ is a full-expression\iref{intro.execution}) -that has a potentially-throwing exception specification, -or -\item -$E$ is a \grammarterm{throw-expression}\iref{expr.throw}, -or -\item -$E$ is a \keyword{dynamic_cast} expression that casts to a reference type and -requires a runtime check\iref{expr.dynamic.cast}, -or -\item -$E$ is a \keyword{typeid} expression applied to a -(possibly parenthesized) built-in unary \tcode{*} operator -applied to a pointer to a -polymorphic class type\iref{expr.typeid}, -or -\item -any of the immediate subexpressions\iref{intro.execution} -of $E$ is potentially-throwing. -\end{itemize} - -\pnum -An implicitly-declared constructor for a class \tcode{X}, -or a constructor without a \grammarterm{noexcept-specifier} -that is defaulted on its first declaration, -has a potentially-throwing exception specification -if and only if -any of the following constructs is potentially-throwing: -\begin{itemize} -\item -the invocation of a constructor selected by overload resolution -in the implicit definition of the constructor -for class \tcode{X} -to initialize a potentially constructed subobject, or -\item -a subexpression of such an initialization, -such as a default argument expression, or, -\item -for a default constructor, a default member initializer. -\end{itemize} -\begin{note} -Even though destructors for fully-constructed subobjects -are invoked when an exception is thrown -during the execution of a constructor\iref{except.ctor}, -their exception specifications do not contribute -to the exception specification of the constructor, -because an exception thrown from such a destructor -would call the function \tcode{std::terminate} -rather than escape the constructor\iref{except.throw,except.terminate}. -\end{note} - -\pnum -The exception specification for an implicitly-declared destructor, -or a destructor without a \grammarterm{noexcept-specifier}, -is potentially-throwing if and only if -any of the destructors -for any of its potentially constructed subobjects -has a potentially-throwing exception specification or -the destructor is virtual and the destructor of any virtual base class -has a potentially-throwing exception specification. - -\pnum -The exception specification for an implicitly-declared assignment operator, -or an assignment-operator without a \grammarterm{noexcept-specifier} -that is defaulted on its first declaration, -is potentially-throwing if and only if -the invocation of any assignment operator -in the implicit definition is potentially-throwing. - -\pnum -A deallocation function\iref{basic.stc.dynamic.deallocation} -with no explicit \grammarterm{noexcept-specifier} -has a non-throwing exception specification. - -\pnum -The exception specification for a comparison operator function\iref{over.binary} -without a \grammarterm{noexcept-specifier} -that is defaulted on its first declaration -is potentially-throwing if and only if -any expression -in the implicit definition is potentially-throwing. - -\pnum -\begin{example} -\begin{codeblock} -struct A { - A(int = (A(5), 0)) noexcept; - A(const A&) noexcept; - A(A&&) noexcept; - ~A(); -}; -struct B { - B() noexcept; - B(const B&) = default; // implicit exception specification is \tcode{\keyword{noexcept}(\keyword{true})} - B(B&&, int = (throw 42, 0)) noexcept; - ~B() noexcept(false); -}; -int n = 7; -struct D : public A, public B { - int * p = new int[n]; - // \tcode{D::D()} potentially-throwing, as the \keyword{new} operator may throw \tcode{bad_alloc} or \tcode{bad_array_new_length} - // \tcode{D::D(const D\&)} non-throwing - // \tcode{D::D(D\&\&)} potentially-throwing, as the default argument for \tcode{B}'s constructor may throw - // \tcode{D::\~D()} potentially-throwing -}; -\end{codeblock} -Furthermore, if -\tcode{A::\~{}A()} -were virtual, -the program would be ill-formed since a function that overrides a virtual -function from a base class -shall not have a potentially-throwing exception specification -if the base class function has a non-throwing exception specification. -\end{example} - -\pnum -An exception specification is considered to be \defnx{needed}{needed!exception specification} when: -\begin{itemize} -\item in an expression, the function is selected by -overload resolution\iref{over.match,over.over}; - -\item the function is odr-used\iref{term.odr.use} or, if it appears in an -unevaluated operand, would be odr-used if the expression were -potentially-evaluated; - -\item the exception specification is compared to that of another -declaration (e.g., an explicit specialization or an overriding virtual -function); - -\item the function is defined; or - -\item the exception specification is needed for a defaulted -function that calls the function. -\begin{note} -A defaulted declaration does not require the -exception specification of a base member function to be evaluated -until the implicit exception specification of the derived -function is needed, but an explicit \grammarterm{noexcept-specifier} needs -the implicit exception specification to compare against. -\end{note} -\end{itemize} -The exception specification of a defaulted -function is evaluated as described above only when needed; similarly, the -\grammarterm{noexcept-specifier} of a specialization of a function -template or member function of a class template is instantiated only when -needed. -% -\indextext{exception specification|)} - \rSec1[except.special]{Special functions} \rSec2[except.special.general]{General} From 494fd23808d7d48438395d6704980bd7a0e18fd6 Mon Sep 17 00:00:00 2001 From: Alisdair Meredith Date: Thu, 17 Oct 2024 19:44:12 -0400 Subject: [PATCH 4/7] [except.terminate] Move the 'terminate' function to [basic.start] Now that the terminate function is more general than just failures of the exception machinery, it belongs with Start and termination. --- source/basic.tex | 121 +++++++++++++++++++++++++++++++++++++ source/exceptions.tex | 135 ------------------------------------------ source/xrefdelta.tex | 5 ++ 3 files changed, 126 insertions(+), 135 deletions(-) diff --git a/source/basic.tex b/source/basic.tex index 9524265f81..b33482a13b 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -7194,5 +7194,126 @@ \libheaderref{cstdlib} terminates the program without executing any destructors and without calling the functions passed to \tcode{std::atexit()} or \tcode{std::at_quick_exit()}.% + +\rSec3[except.terminate]{The \tcode{std::terminate} function} + +\pnum +\indextext{\idxcode{terminate}}% +Some errors in a program cannot be recovered from, such as when an exception +is not handled or a \tcode{std::thread} object is destroyed while its thread +function is still running. In such cases, +the function \tcode{std::terminate}\iref{exception.terminate} is invoked. +\begin{note} +These situations are: +\indextext{\idxcode{terminate}!called}% +\begin{itemize} +\item% +when the exception handling mechanism, after completing +the initialization of the exception object +but before +activation of a handler for the exception\iref{except.throw}, +calls a function that exits +via an exception, or + +\item% +when the exception handling mechanism cannot find a handler for a thrown exception\iref{except.handle}, or + +\item when the search for a handler\iref{except.handle} encounters the +outermost block of a function +with a non-throwing exception specification\iref{except.spec}, or + +\item% +when the destruction of an object during stack unwinding\iref{except.ctor} +terminates by throwing an exception, or + +\item% +when initialization of a non-block +variable with static or thread storage duration\iref{basic.start.dynamic} +exits via an exception, or + +\item% +when destruction of an object with static or thread storage duration exits +via an exception\iref{basic.start.term}, or + +\item% +when execution of a function registered with +\tcode{std::atexit} or \tcode{std::at_quick_exit} +exits via an exception\iref{support.start.term}, or + +\item% +when a +\grammarterm{throw-expression}\iref{expr.throw} +with no operand attempts to rethrow an exception and no exception is being +handled\iref{except.throw}, or + +\item% +when the function \tcode{std::nested_exception::rethrow_nested} is called for an object +that has captured no exception\iref{except.nested}, or + +\item% +when execution of the initial function of a thread exits via +an exception\iref{thread.thread.constr}, or + +\item% +for a parallel algorithm whose \tcode{ExecutionPolicy} specifies such +behavior\iref{execpol.seq,execpol.par,execpol.parunseq}, +when execution of an element access function\iref{algorithms.parallel.defns} +of the parallel algorithm exits via an exception\iref{algorithms.parallel.exceptions}, or + +\item% +when the destructor or the move assignment operator is invoked on an object +of type \tcode{std::thread} that refers to +a joinable thread\iref{thread.thread.destr,thread.thread.assign}, or + +\item% +when a call to a \tcode{wait()}, \tcode{wait_until()}, or \tcode{wait_for()} +function on a condition variable\iref{thread.condition.condvar,thread.condition.condvarany} +fails to meet a postcondition, or + +\item% +when a callback invocation exits via an exception +when requesting stop on +a \tcode{std::stop_source} or +a \tcode{std::in\-place_stop_source}\iref{stopsource.mem,stopsource.inplace.mem}, +or in the constructor of +\tcode{std::stop_callback} or +\tcode{std::inplace_stop_callback}\iref{stopcallback.cons,stopcallback.inplace.cons} +when a callback invocation exits via an exception, or + +\item% +when a \tcode{run_loop} object is destroyed +that is still in the \tcode{running} state\iref{exec.run.loop}, or + +\item% +when \tcode{unhandled_stopped} is called on +a \tcode{with_awaitable_senders} object\iref{exec.with.awaitable.senders} +whose continuation is not a handle to a coroutine +whose promise type has an \tcode{unhandled_stopped} member function. + +\end{itemize} + +\end{note} + +\pnum +\indextext{\idxcode{terminate}}% +In the situation where no matching handler is found, it is +\impldef{stack unwinding before invocation of \tcode{std::terminate}} +whether or not the stack is unwound +before \tcode{std::terminate} is invoked. +In the situation where the search for a handler\iref{except.handle} encounters the +outermost block of a function +with a non-throwing exception specification\iref{except.spec}, it is +\impldef{whether stack is unwound before invoking the function \tcode{std::terminate} +when a \tcode{noexcept} specification is violated} +whether the stack is unwound, unwound partially, or not unwound at all +before the function \tcode{std::terminate} is invoked. +In all other situations, the stack shall not be unwound before +the function \tcode{std::terminate} +is invoked. +An implementation is not permitted to finish stack unwinding +prematurely based on a determination that the unwind process +will eventually cause an invocation of the function +\tcode{std::terminate}. + \indextext{program!termination|)} \indextext{program execution|)} diff --git a/source/exceptions.tex b/source/exceptions.tex index 464b2786c8..2ff14373a5 100644 --- a/source/exceptions.tex +++ b/source/exceptions.tex @@ -747,139 +747,4 @@ exception object and will have effect should that object be rethrown.% \indextext{exception handling!handler!match|)}% \indextext{exception handling!handler|)} - -\rSec1[except.special]{Special functions} - -\rSec2[except.special.general]{General} - -\pnum -The function \tcode{std::terminate}\iref{except.terminate} -is used by the exception -handling mechanism for coping with errors related to the exception handling -mechanism itself. -The function \tcode{std::uncaught_exceptions}\iref{uncaught.exceptions} -reports how many exceptions are uncaught in the current thread. -The function \tcode{std::current_exception}\iref{propagation} and the class -\tcode{std::nested_exception}\iref{except.nested} can be used by a program to -capture the currently handled exception. - -\rSec2[except.terminate]{The \tcode{std::terminate} function} - -\pnum -\indextext{\idxcode{terminate}}% -Some errors in a program cannot be recovered from, such as when an exception -is not handled or a \tcode{std::thread} object is destroyed while its thread -function is still running. In such cases, -the function \tcode{std::terminate}\iref{exception.terminate} is invoked. -\begin{note} -These situations are: -\indextext{\idxcode{terminate}!called}% -\begin{itemize} -\item% -when the exception handling mechanism, after completing -the initialization of the exception object -but before -activation of a handler for the exception\iref{except.throw}, -calls a function that exits -via an exception, or - -\item% -when the exception handling mechanism cannot find a handler for a thrown exception\iref{except.handle}, or - -\item when the search for a handler\iref{except.handle} encounters the -outermost block of a function -with a non-throwing exception specification\iref{except.spec}, or - -\item% -when the destruction of an object during stack unwinding\iref{except.ctor} -terminates by throwing an exception, or - -\item% -when initialization of a non-block -variable with static or thread storage duration\iref{basic.start.dynamic} -exits via an exception, or - -\item% -when destruction of an object with static or thread storage duration exits -via an exception\iref{basic.start.term}, or - -\item% -when execution of a function registered with -\tcode{std::atexit} or \tcode{std::at_quick_exit} -exits via an exception\iref{support.start.term}, or - -\item% -when a -\grammarterm{throw-expression}\iref{expr.throw} -with no operand attempts to rethrow an exception and no exception is being -handled\iref{except.throw}, or - -\item% -when the function \tcode{std::nested_exception::rethrow_nested} is called for an object -that has captured no exception\iref{except.nested}, or - -\item% -when execution of the initial function of a thread exits via -an exception\iref{thread.thread.constr}, or - -\item% -for a parallel algorithm whose \tcode{ExecutionPolicy} specifies such -behavior\iref{execpol.seq,execpol.par,execpol.parunseq}, -when execution of an element access function\iref{algorithms.parallel.defns} -of the parallel algorithm exits via an exception\iref{algorithms.parallel.exceptions}, or - -\item% -when the destructor or the move assignment operator is invoked on an object -of type \tcode{std::thread} that refers to -a joinable thread\iref{thread.thread.destr,thread.thread.assign}, or - -\item% -when a call to a \tcode{wait()}, \tcode{wait_until()}, or \tcode{wait_for()} -function on a condition variable\iref{thread.condition.condvar,thread.condition.condvarany} -fails to meet a postcondition, or - -\item% -when a callback invocation exits via an exception -when requesting stop on -a \tcode{std::stop_source} or -a \tcode{std::in\-place_stop_source}\iref{stopsource.mem,stopsource.inplace.mem}, -or in the constructor of -\tcode{std::stop_callback} or -\tcode{std::inplace_stop_callback}\iref{stopcallback.cons,stopcallback.inplace.cons} -when a callback invocation exits via an exception, or - -\item% -when a \tcode{run_loop} object is destroyed -that is still in the \tcode{running} state\iref{exec.run.loop}, or - -\item% -when \tcode{unhandled_stopped} is called on -a \tcode{with_awaitable_senders} object\iref{exec.with.awaitable.senders} -whose continuation is not a handle to a coroutine -whose promise type has an \tcode{unhandled_stopped} member function. - -\end{itemize} - -\end{note} - -\pnum -\indextext{\idxcode{terminate}}% -In the situation where no matching handler is found, it is -\impldef{stack unwinding before invocation of \tcode{std::terminate}} -whether or not the stack is unwound -before \tcode{std::terminate} is invoked. -In the situation where the search for a handler\iref{except.handle} encounters the -outermost block of a function -with a non-throwing exception specification\iref{except.spec}, it is -\impldef{whether stack is unwound before invoking the function \tcode{std::terminate} -when a \tcode{noexcept} specification is violated} -whether the stack is unwound, unwound partially, or not unwound at all -before the function \tcode{std::terminate} is invoked. -In all other situations, the stack shall not be unwound before -the function \tcode{std::terminate} -is invoked. -An implementation is not permitted to finish stack unwinding -prematurely based on a determination that the unwind process -will eventually cause an invocation of the function -\tcode{std::terminate}. \indextext{exception handling|)} diff --git a/source/xrefdelta.tex b/source/xrefdelta.tex index a726c44f41..38e3895f08 100644 --- a/source/xrefdelta.tex +++ b/source/xrefdelta.tex @@ -73,6 +73,10 @@ \removedxref{type.index.members} \removedxref{type.index.hash} +% Dissolving [except] +\removedxref{except.special} +\removedxref{except.special.general} + %%% Renamed sections. %%% Examples: % @@ -98,6 +102,7 @@ % https://github.com/cplusplus/draft/pull/7276 \movedxref{except.uncaught}{except.throw} + %%% Deprecated features. %%% Example: % From 42d33f1314364162ece7a896c85de4b8a000c949 Mon Sep 17 00:00:00 2001 From: Alisdair Meredith Date: Thu, 17 Oct 2024 21:29:13 -0400 Subject: [PATCH 5/7] [except.throw][except.ctor] Move to [basic.exec] --- source/basic.tex | 306 +++++++++++++++++++++++++++++++++++++++++ source/exceptions.tex | 308 ------------------------------------------ 2 files changed, 306 insertions(+), 308 deletions(-) diff --git a/source/basic.tex b/source/basic.tex index b33482a13b..ca9e4df559 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -6760,6 +6760,312 @@ \indextext{atomic!operation|)}% \indextext{threads!multiple|)} +\rSec2[except.throw]{Throwing an exception}% +\indextext{exception handling!throwing}% +\indextext{throwing|see{exception handling, throwing}} + +\pnum +Throwing an exception transfers control to a handler. +\begin{note} +An exception can be thrown from one of the following contexts: +\grammarterm{throw-expression}{s}\iref{expr.throw}, +allocation functions\iref{basic.stc.dynamic.allocation}, +\keyword{dynamic_cast}\iref{expr.dynamic.cast}, +\keyword{typeid}\iref{expr.typeid}, +\grammarterm{new-expression}{s}\iref{expr.new}, and standard library +functions\iref{structure.specifications}. +\end{note} +An object is passed and the type of that object determines which handlers +can catch it. +\begin{example} +\begin{codeblock} +throw "Help!"; +\end{codeblock} +can be caught by a +\grammarterm{handler} +of +\keyword{const} +\tcode{\keyword{char}*} +type: +\begin{codeblock} +try { + // ... +} catch(const char* p) { + // handle character string exceptions here +} +\end{codeblock} +and +\begin{codeblock} +class Overflow { +public: + Overflow(char,double,double); +}; + +void f(double x) { + throw Overflow('+',x,3.45e107); +} +\end{codeblock} +can be caught by a handler for exceptions of type +\tcode{Overflow}: +\begin{codeblock} +try { + f(1.2); +} catch(Overflow& oo) { + // handle exceptions of type \tcode{Overflow} here +} +\end{codeblock} +\end{example} + +\pnum +\indextext{exception handling!throwing}% +\indextext{exception handling!handler}% +\indextext{exception handling!nearest handler}% +When an exception is thrown, control is transferred to the nearest handler with +a matching type\iref{except.handle}; ``nearest'' means the handler +for which the +\grammarterm{compound-statement} or +\grammarterm{ctor-initializer} +following the +\keyword{try} +keyword was most recently entered by the thread of control and not yet exited. + +\pnum +Throwing an exception +initializes an object with dynamic storage duration, +called the +\defnx{exception object}{exception handling!exception object}. +If the type of the exception object would be +an incomplete type\iref{basic.types.general}, +an abstract class type\iref{class.abstract}, +or a pointer to an incomplete type other than +\cv{}~\keyword{void}\iref{basic.compound}, +the program is ill-formed. + +\pnum +\indextext{exception handling!memory}% +\indextext{exception handling!rethrowing}% +\indextext{exception handling!exception object}% +The memory for the exception object is +allocated in an unspecified way, except as noted in~\ref{basic.stc.dynamic.allocation}. +If a handler exits by rethrowing, control is passed to another handler for +the same exception object. +The points of potential destruction for the exception object are: +\begin{itemize} +\item +when an active handler for the exception exits by +any means other than +rethrowing, +immediately after the destruction of the object (if any) +declared in the \grammarterm{exception-declaration} in the handler; + +\item +when an object of type \tcode{std::exception_ptr}\iref{propagation} +that refers to the exception object is destroyed, +before the destructor of \tcode{std::exception_ptr} returns. +\end{itemize} + +Among all points of potential destruction for the exception object, +there is an unspecified last one +where the exception object is destroyed. +All other points happen before that last one\iref{intro.races}. +\begin{note} +No other thread synchronization is implied in exception handling. +\end{note} +The implementation may then +deallocate the memory for the exception object; any such deallocation +is done in an unspecified way. +\begin{note} +A thrown exception does not +propagate to other threads unless caught, stored, and rethrown using +appropriate library functions; see~\ref{propagation} and~\ref{futures}. +\end{note} + +\pnum +\indextext{exception handling!exception object!constructor}% +\indextext{exception handling!exception object!destructor}% +Let \tcode{T} denote the type of the exception object. +Copy-initialization of an object of type \tcode{T} from +an lvalue of type \tcode{const T} in a context unrelated to \tcode{T} +shall be well-formed. +If \tcode{T} is a class type, +the selected constructor is odr-used\iref{basic.def.odr} and +the destructor of \tcode{T} is potentially invoked\iref{class.dtor}. + +\pnum +An exception is considered uncaught +after completing the initialization of the exception object +until completing the activation of a handler for the exception\iref{except.handle}. +\begin{note} +As a consequence, an exception is considered uncaught +during any stack unwinding resulting from it being thrown. +\end{note} + +\pnum +\indextext{exception handling!rethrow}% +\indextext{rethrow|see{exception handling, rethrow}}% +An exception is considered caught when a handler for that exception +becomes active\iref{except.handle}. +\begin{note} +An exception can have active handlers and still be considered uncaught if +it is rethrown. +\end{note} + +\pnum +\indexlibraryglobal{uncaught_exceptions}% +If an exception is rethrown\iref{expr.throw,propagation}, +it is considered uncaught from the point of rethrow +until the rethrown exception is caught. +\begin{note} +The function \tcode{std::uncaught_exceptions}\iref{uncaught.exceptions} +returns the number of uncaught exceptions in the current thread. +\end{note} + +\pnum +\indextext{exception handling!terminate called@\tcode{terminate} called}% +\indextext{\idxcode{terminate}!called}% +If the exception handling mechanism +handling an uncaught exception +directly invokes a function that exits via an +exception, the function \tcode{std::terminate} is invoked\iref{except.terminate}. +\begin{example} +\begin{codeblock} +struct C { + C() { } + C(const C&) { + if (std::uncaught_exceptions()) { + throw 0; // throw during copy to handler's \grammarterm{exception-declaration} object\iref{except.handle} + } + } +}; + +int main() { + try { + throw C(); // calls \tcode{std::terminate} if construction of the handler's + // \grammarterm{exception-declaration} object is not elided\iref{class.copy.elision} + } catch(C) { } +} +\end{codeblock} +\end{example} +\begin{note} +If a destructor directly invoked by stack unwinding exits via an exception, +\tcode{std::terminate} is invoked. +\end{note} + +\rSec2[except.ctor]{Stack unwinding}% +\indextext{exception handling!constructors and destructors}% +\indextext{constructor!exception handling|see{exception handling, constructors and destructors}}% +\indextext{destructor!exception handling|see{exception handling, constructors and destructors}} + +\pnum +\indextext{unwinding!stack}% +As control passes from the point where an exception is thrown +to a handler, +objects are destroyed by a process, +specified in this subclause, called \defn{stack unwinding}. + +\pnum +Each object with automatic storage duration is destroyed if it has been +constructed, but not yet destroyed, +since the try block was entered. +If an exception is thrown during the destruction of temporaries or +local variables for a \keyword{return} statement\iref{stmt.return}, +the destructor for the returned object (if any) is also invoked. +The objects are destroyed in the reverse order of the completion +of their construction. +\begin{example} +\begin{codeblock} +struct A { }; + +struct Y { ~Y() noexcept(false) { throw 0; } }; + +A f() { + try { + A a; + Y y; + A b; + return {}; // \#1 + } catch (...) { + } + return {}; // \#2 +} +\end{codeblock} +At \#1, the returned object of type \tcode{A} is constructed. +Then, the local variable \tcode{b} is destroyed\iref{stmt.jump}. +Next, the local variable \tcode{y} is destroyed, +causing stack unwinding, +resulting in the destruction of the returned object, +followed by the destruction of the local variable \tcode{a}. +Finally, the returned object is constructed again at \#2. +\end{example} + +\pnum +If the initialization of an object +other than by delegating constructor +is terminated by an exception, +the destructor is invoked for +each of the object's subobjects +that were known to be initialized by the object's initialization and +whose initialization has completed\iref{dcl.init}. +\begin{note} +If such an object has a reference member +that extends the lifetime of a temporary object, +this ends the lifetime of the reference member, +so the lifetime of the temporary object is effectively not extended. +\end{note} +\indextext{subobject!initialized, known to be}% +A subobject is \defn{known to be initialized} +if it is not an anonymous union member and +its initialization is specified +\begin{itemize} +\item in \ref{class.base.init} for initialization by constructor, +\item in \ref{class.copy.ctor} for initialization by defaulted copy/move constructor, +\item in \ref{class.inhctor.init} for initialization by inherited constructor, +\item in \ref{dcl.init.aggr} for aggregate initialization, +\item in \ref{expr.prim.lambda.capture} for the initialization of +the closure object when evaluating a \grammarterm{lambda-expression}, +\item in \ref{dcl.init.general} for +default-initialization, value-initialization, or direct-initialization +of an array. +\end{itemize} +\begin{note} +This includes virtual base class subobjects +if the initialization +is for a complete object, and +can include variant members +that were nominated explicitly by +a \grammarterm{mem-initializer} or \grammarterm{designated-initializer-clause} or +that have a default member initializer. +\end{note} +If the destructor of an object is terminated by an exception, +each destructor invocation +that would be performed after executing the body of the destructor\iref{class.dtor} and +that has not yet begun execution +is performed. +\begin{note} +This includes virtual base class subobjects if +the destructor was invoked for a complete object. +\end{note} +The subobjects are destroyed in the reverse order of the completion of +their construction. Such destruction is sequenced before entering a +handler of the \grammarterm{function-try-block} of the constructor or destructor, +if any. + +\pnum +If the \grammarterm{compound-statement} +of the \grammarterm{function-body} +of a delegating constructor +for an object exits via +an exception, the object's destructor is invoked. +Such destruction is sequenced before entering a handler of the +\grammarterm{function-try-block} of a delegating constructor for that object, if any. + +\pnum +\begin{note} +If the object was allocated by a \grammarterm{new-expression}\iref{expr.new}, +the matching deallocation function\iref{basic.stc.dynamic.deallocation}, +if any, is called to free the storage occupied by the object. +\end{note} + \rSec2[basic.start]{Start and termination} \rSec3[basic.start.main]{\tcode{main} function} diff --git a/source/exceptions.tex b/source/exceptions.tex index 2ff14373a5..c6a6d3bb71 100644 --- a/source/exceptions.tex +++ b/source/exceptions.tex @@ -186,314 +186,6 @@ In this Clause, ``before'' and ``after'' refer to the ``sequenced before'' relation\iref{intro.execution}. -\rSec1[except.throw]{Throwing an exception}% -\indextext{exception handling!throwing}% -\indextext{throwing|see{exception handling, throwing}} - -\pnum -Throwing an exception transfers control to a handler. -\begin{note} -An exception can be thrown from one of the following contexts: -\grammarterm{throw-expression}{s}\iref{expr.throw}, -allocation functions\iref{basic.stc.dynamic.allocation}, -\keyword{dynamic_cast}\iref{expr.dynamic.cast}, -\keyword{typeid}\iref{expr.typeid}, -\grammarterm{new-expression}{s}\iref{expr.new}, and standard library -functions\iref{structure.specifications}. -\end{note} -An object is passed and the type of that object determines which handlers -can catch it. -\begin{example} -\begin{codeblock} -throw "Help!"; -\end{codeblock} -can be caught by a -\grammarterm{handler} -of -\keyword{const} -\tcode{\keyword{char}*} -type: -\begin{codeblock} -try { - // ... -} catch(const char* p) { - // handle character string exceptions here -} -\end{codeblock} -and -\begin{codeblock} -class Overflow { -public: - Overflow(char,double,double); -}; - -void f(double x) { - throw Overflow('+',x,3.45e107); -} -\end{codeblock} -can be caught by a handler for exceptions of type -\tcode{Overflow}: -\begin{codeblock} -try { - f(1.2); -} catch(Overflow& oo) { - // handle exceptions of type \tcode{Overflow} here -} -\end{codeblock} -\end{example} - -\pnum -\indextext{exception handling!throwing}% -\indextext{exception handling!handler}% -\indextext{exception handling!nearest handler}% -When an exception is thrown, control is transferred to the nearest handler with -a matching type\iref{except.handle}; ``nearest'' means the handler -for which the -\grammarterm{compound-statement} or -\grammarterm{ctor-initializer} -following the -\keyword{try} -keyword was most recently entered by the thread of control and not yet exited. - -\pnum -Throwing an exception -initializes an object with dynamic storage duration, -called the -\defnx{exception object}{exception handling!exception object}. -If the type of the exception object would be -an incomplete type\iref{basic.types.general}, -an abstract class type\iref{class.abstract}, -or a pointer to an incomplete type other than -\cv{}~\keyword{void}\iref{basic.compound}, -the program is ill-formed. - -\pnum -\indextext{exception handling!memory}% -\indextext{exception handling!rethrowing}% -\indextext{exception handling!exception object}% -The memory for the exception object is -allocated in an unspecified way, except as noted in~\ref{basic.stc.dynamic.allocation}. -If a handler exits by rethrowing, control is passed to another handler for -the same exception object. -The points of potential destruction for the exception object are: -\begin{itemize} -\item -when an active handler for the exception exits by -any means other than -rethrowing, -immediately after the destruction of the object (if any) -declared in the \grammarterm{exception-declaration} in the handler; - -\item -when an object of type \tcode{std::exception_ptr}\iref{propagation} -that refers to the exception object is destroyed, -before the destructor of \tcode{std::exception_ptr} returns. -\end{itemize} - -Among all points of potential destruction for the exception object, -there is an unspecified last one -where the exception object is destroyed. -All other points happen before that last one\iref{intro.races}. -\begin{note} -No other thread synchronization is implied in exception handling. -\end{note} -The implementation may then -deallocate the memory for the exception object; any such deallocation -is done in an unspecified way. -\begin{note} -A thrown exception does not -propagate to other threads unless caught, stored, and rethrown using -appropriate library functions; see~\ref{propagation} and~\ref{futures}. -\end{note} - -\pnum -\indextext{exception handling!exception object!constructor}% -\indextext{exception handling!exception object!destructor}% -Let \tcode{T} denote the type of the exception object. -Copy-initialization of an object of type \tcode{T} from -an lvalue of type \tcode{const T} in a context unrelated to \tcode{T} -shall be well-formed. -If \tcode{T} is a class type, -the selected constructor is odr-used\iref{basic.def.odr} and -the destructor of \tcode{T} is potentially invoked\iref{class.dtor}. - -\pnum -An exception is considered uncaught -after completing the initialization of the exception object -until completing the activation of a handler for the exception\iref{except.handle}. -\begin{note} -As a consequence, an exception is considered uncaught -during any stack unwinding resulting from it being thrown. -\end{note} - -\pnum -\indextext{exception handling!rethrow}% -\indextext{rethrow|see{exception handling, rethrow}}% -An exception is considered caught when a handler for that exception -becomes active\iref{except.handle}. -\begin{note} -An exception can have active handlers and still be considered uncaught if -it is rethrown. -\end{note} - -\pnum -\indexlibraryglobal{uncaught_exceptions}% -If an exception is rethrown\iref{expr.throw,propagation}, -it is considered uncaught from the point of rethrow -until the rethrown exception is caught. -\begin{note} -The function \tcode{std::uncaught_exceptions}\iref{uncaught.exceptions} -returns the number of uncaught exceptions in the current thread. -\end{note} - -\pnum -\indextext{exception handling!terminate called@\tcode{terminate} called}% -\indextext{\idxcode{terminate}!called}% -If the exception handling mechanism -handling an uncaught exception -directly invokes a function that exits via an -exception, the function \tcode{std::terminate} is invoked\iref{except.terminate}. -\begin{example} -\begin{codeblock} -struct C { - C() { } - C(const C&) { - if (std::uncaught_exceptions()) { - throw 0; // throw during copy to handler's \grammarterm{exception-declaration} object\iref{except.handle} - } - } -}; - -int main() { - try { - throw C(); // calls \tcode{std::terminate} if construction of the handler's - // \grammarterm{exception-declaration} object is not elided\iref{class.copy.elision} - } catch(C) { } -} -\end{codeblock} -\end{example} -\begin{note} -If a destructor directly invoked by stack unwinding exits via an exception, -\tcode{std::terminate} is invoked. -\end{note} - - -\rSec1[except.ctor]{Stack unwinding}% -\indextext{exception handling!constructors and destructors}% -\indextext{constructor!exception handling|see{exception handling, constructors and destructors}}% -\indextext{destructor!exception handling|see{exception handling, constructors and destructors}} - -\pnum -\indextext{unwinding!stack}% -As control passes from the point where an exception is thrown -to a handler, -objects are destroyed by a process, -specified in this subclause, called \defn{stack unwinding}. - -\pnum -Each object with automatic storage duration is destroyed if it has been -constructed, but not yet destroyed, -since the try block was entered. -If an exception is thrown during the destruction of temporaries or -local variables for a \keyword{return} statement\iref{stmt.return}, -the destructor for the returned object (if any) is also invoked. -The objects are destroyed in the reverse order of the completion -of their construction. -\begin{example} -\begin{codeblock} -struct A { }; - -struct Y { ~Y() noexcept(false) { throw 0; } }; - -A f() { - try { - A a; - Y y; - A b; - return {}; // \#1 - } catch (...) { - } - return {}; // \#2 -} -\end{codeblock} -At \#1, the returned object of type \tcode{A} is constructed. -Then, the local variable \tcode{b} is destroyed\iref{stmt.jump}. -Next, the local variable \tcode{y} is destroyed, -causing stack unwinding, -resulting in the destruction of the returned object, -followed by the destruction of the local variable \tcode{a}. -Finally, the returned object is constructed again at \#2. -\end{example} - -\pnum -If the initialization of an object -other than by delegating constructor -is terminated by an exception, -the destructor is invoked for -each of the object's subobjects -that were known to be initialized by the object's initialization and -whose initialization has completed\iref{dcl.init}. -\begin{note} -If such an object has a reference member -that extends the lifetime of a temporary object, -this ends the lifetime of the reference member, -so the lifetime of the temporary object is effectively not extended. -\end{note} -\indextext{subobject!initialized, known to be}% -A subobject is \defn{known to be initialized} -if it is not an anonymous union member and -its initialization is specified -\begin{itemize} -\item in \ref{class.base.init} for initialization by constructor, -\item in \ref{class.copy.ctor} for initialization by defaulted copy/move constructor, -\item in \ref{class.inhctor.init} for initialization by inherited constructor, -\item in \ref{dcl.init.aggr} for aggregate initialization, -\item in \ref{expr.prim.lambda.capture} for the initialization of -the closure object when evaluating a \grammarterm{lambda-expression}, -\item in \ref{dcl.init.general} for -default-initialization, value-initialization, or direct-initialization -of an array. -\end{itemize} -\begin{note} -This includes virtual base class subobjects -if the initialization -is for a complete object, and -can include variant members -that were nominated explicitly by -a \grammarterm{mem-initializer} or \grammarterm{designated-initializer-clause} or -that have a default member initializer. -\end{note} -If the destructor of an object is terminated by an exception, -each destructor invocation -that would be performed after executing the body of the destructor\iref{class.dtor} and -that has not yet begun execution -is performed. -\begin{note} -This includes virtual base class subobjects if -the destructor was invoked for a complete object. -\end{note} -The subobjects are destroyed in the reverse order of the completion of -their construction. Such destruction is sequenced before entering a -handler of the \grammarterm{function-try-block} of the constructor or destructor, -if any. - -\pnum -If the \grammarterm{compound-statement} -of the \grammarterm{function-body} -of a delegating constructor -for an object exits via -an exception, the object's destructor is invoked. -Such destruction is sequenced before entering a handler of the -\grammarterm{function-try-block} of a delegating constructor for that object, if any. - -\pnum -\begin{note} -If the object was allocated by a \grammarterm{new-expression}\iref{expr.new}, -the matching deallocation function\iref{basic.stc.dynamic.deallocation}, -if any, is called to free the storage occupied by the object. -\end{note} - - \rSec1[except.handle]{Handling an exception} \indextext{exception handling!handler|(}% From f142ecc072cf5f7e6e728ab6f4e8fb78e09d6d8b Mon Sep 17 00:00:00 2001 From: Alisdair Meredith Date: Fri, 18 Oct 2024 08:49:04 -0400 Subject: [PATCH 6/7] [except] Complete dissolving this clause into the rest of the standard --- source/support.tex | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/support.tex b/source/support.tex index c8e0d789ae..ef7d0aace1 100644 --- a/source/support.tex +++ b/source/support.tex @@ -3760,6 +3760,15 @@ \pnum The header \libheaderdef{exception} defines several types and functions related to the handling of exceptions in a \Cpp{} program. +The function \tcode{std::terminate}\iref{except.terminate} +is used by the exception +handling mechanism for coping with errors related to the exception handling +mechanism itself. +The function \tcode{std::uncaught_exceptions}\iref{uncaught.exceptions} +reports how many exceptions are uncaught in the current thread. +The function \tcode{std::current_exception}\iref{propagation} and the class +\tcode{std::nested_exception}\iref{except.nested} can be used by a program to +capture the currently handled exception. \rSec2[exception.syn]{Header \tcode{} synopsis} From 2af6f24378674090a1ff84abf4a376f38b4f43ec Mon Sep 17 00:00:00 2001 From: Alisdair Meredith Date: Fri, 18 Oct 2024 08:51:14 -0400 Subject: [PATCH 7/7] [except.pre] Clean-up pass to ensure consistency --- source/basic.tex | 271 +++++++++++++++++++++++- source/compatibility.tex | 84 ++++---- source/exceptions.tex | 442 --------------------------------------- source/expressions.tex | 2 +- source/lib-intro.tex | 2 +- source/statements.tex | 175 ++++++++++++++++ source/std.tex | 1 - source/xrefdelta.tex | 4 + 8 files changed, 487 insertions(+), 494 deletions(-) delete mode 100644 source/exceptions.tex diff --git a/source/basic.tex b/source/basic.tex index ca9e4df559..9274dcd517 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -6760,7 +6760,14 @@ \indextext{atomic!operation|)}% \indextext{threads!multiple|)} -\rSec2[except.throw]{Throwing an exception}% +\rSec2[except.flow]{Exceptional control flow} + +\rSec3[except.general]{General} +\pnum +In this Clause, ``before'' and ``after'' refer to the +``sequenced before'' relation\iref{intro.execution}. + +\rSec3[except.throw]{Throwing an exception}% \indextext{exception handling!throwing}% \indextext{throwing|see{exception handling, throwing}} @@ -6951,7 +6958,7 @@ \tcode{std::terminate} is invoked. \end{note} -\rSec2[except.ctor]{Stack unwinding}% +\rSec3[except.ctor]{Stack unwinding}% \indextext{exception handling!constructors and destructors}% \indextext{constructor!exception handling|see{exception handling, constructors and destructors}}% \indextext{destructor!exception handling|see{exception handling, constructors and destructors}} @@ -7066,6 +7073,260 @@ if any, is called to free the storage occupied by the object. \end{note} +\rSec3[except.handle]{Handling an exception} +\indextext{exception handling!handler|(}% + +\pnum +The +\grammarterm{exception-declaration} +in a +\grammarterm{handler} +describes the type(s) of exceptions that can cause +that +\grammarterm{handler} +to be entered. +\indextext{exception handling!handler!incomplete type in}% +\indextext{exception handling!handler!rvalue reference in}% +\indextext{exception handling!handler!array in}% +\indextext{exception handling!handler!pointer to function in}% +The +\grammarterm{exception-declaration} +shall not denote an incomplete type, an abstract class type, or an rvalue reference type. +The +\grammarterm{exception-declaration} +shall not denote a pointer or reference to an +incomplete type, other than ``pointer to \cv{}~\keyword{void}''. + +\pnum +A handler of type +\indextext{array!handler of type}% +``array of \tcode{T}'' or +\indextext{function!handler of type}% +function type \tcode{T} +is adjusted to be of type +``pointer to \tcode{T}''. + +\pnum +\indextext{exception handling!handler!match|(}% +A +\grammarterm{handler} +is a match for +an exception object +of type +\tcode{E} +if +\begin{itemize} +\item% +The \grammarterm{handler} is of type \cv{}~\tcode{T} or +\cv{}~\tcode{T\&} and +\tcode{E} and \tcode{T} +are the same type (ignoring the top-level \grammarterm{cv-qualifier}{s}), or +\item% +the \grammarterm{handler} is of type \cv{}~\tcode{T} or +\cv{}~\tcode{T\&} and +\tcode{T} is an unambiguous public base class of \tcode{E}, or +\item% +the \grammarterm{handler} is of type \cv{}~\tcode{T} or \tcode{const T\&} +where \tcode{T} is a pointer or pointer-to-member type and +\tcode{E} is a pointer or pointer-to-member type +that can be converted to \tcode{T} by one or more of +\begin{itemize} + +\item% +a standard pointer conversion\iref{conv.ptr} not involving conversions +to pointers to private or protected or ambiguous classes +\item% +a function pointer conversion\iref{conv.fctptr} +\item% +a qualification conversion\iref{conv.qual}, or + +\end{itemize} + +\item +the \grammarterm{handler} is of type \cv{}~\tcode{T} or \tcode{const T\&} where \tcode{T} is a pointer or pointer-to-member type and \tcode{E} is \tcode{std::nullptr_t}. + +\end{itemize} + +\begin{note} +A +\grammarterm{throw-expression} +whose operand is an integer literal with value zero does not match a handler of +pointer or pointer-to-member type. +A handler of reference to array or function type +is never a match for any exception object\iref{expr.throw}. +\end{note} + +\begin{example} +\begin{codeblock} +class Matherr { @\commentellip@ virtual void vf(); }; +class Overflow: public Matherr { @\commentellip@ }; +class Underflow: public Matherr { @\commentellip@ }; +class Zerodivide: public Matherr { @\commentellip@ }; + +void f() { + try { + g(); + } catch (Overflow oo) { + // ... + } catch (Matherr mm) { + // ... + } +} +\end{codeblock} +Here, the +\tcode{Overflow} +handler will catch exceptions of type +\tcode{Overflow} +and the +\tcode{Matherr} +handler will catch exceptions of type +\tcode{Matherr} +and of all types publicly derived from +\tcode{Matherr} +including exceptions of type +\tcode{Underflow} +and +\tcode{Zerodivide}. +\end{example} + +\pnum +The handlers for a try block are tried in order of appearance. +\begin{note} +This makes it possible to write handlers that can never be +executed, for example by placing a handler for a final derived class after +a handler for a corresponding unambiguous public base class. +\end{note} + +\pnum +A +\tcode{...} +in a handler's +\grammarterm{exception-declaration} +specifies a match for any exception. +If present, a +\tcode{...} +handler shall be the last handler for its try block. + +\pnum +If no match is found among the handlers for a try block, +the search for a matching +handler continues in a dynamically surrounding try block +of the same thread. + +\pnum +\indextext{exception handling!terminate called@\tcode{terminate} called}% +\indextext{\idxcode{terminate}!called}% +If the search for a handler +encounters the outermost block of a function with a +non-throwing exception specification, +the function \tcode{std::terminate}\iref{except.terminate} is invoked. +\begin{note} +An implementation is not permitted to reject an expression merely because, when +executed, it throws or might +throw an exception from a function with a non-throwing exception specification. +\end{note} +\begin{example} +\begin{codeblock} +extern void f(); // potentially-throwing + +void g() noexcept { + f(); // valid, even if \tcode{f} throws + throw 42; // valid, effectively a call to \tcode{std::terminate} +} +\end{codeblock} +The call to +\tcode{f} +is well-formed despite the possibility for it to throw an exception. +\end{example} + +\pnum +If no matching handler is found, +the function \tcode{std::terminate} is invoked; +whether or not the stack is unwound before this invocation of +\tcode{std::terminate} is +\impldef{whether stack is unwound before invoking the function \tcode{std::terminate} +when no matching handler is found}\iref{except.terminate} + +\pnum +A handler is considered \defnx{active}{exception handling!handler!active} when +initialization is complete for the parameter (if any) of the catch clause. +\begin{note} +The stack will have been unwound at that point. +\end{note} +Also, an implicit handler is considered active when +the function \tcode{std::terminate} +is entered due to a throw. A handler is no longer considered active when the +catch clause exits. + +\pnum +\indextext{currently handled exception|see{exception handling, currently handled exception}}% +The exception with the most recently activated handler that is +still active is called the +\defnx{currently handled exception}{exception handling!currently handled exception}. + +\pnum +Referring to any non-static member or base class of an object +in the handler for a +\grammarterm{function-try-block} +of a constructor or destructor for that object results in undefined behavior. + +\pnum +Exceptions thrown in destructors of objects with static storage duration or in +constructors of objects associated with non-block variables with static storage duration are not caught by a +\grammarterm{function-try-block} +on +the \tcode{main} function\iref{basic.start.main}. +Exceptions thrown in destructors of objects with thread storage duration or in constructors of objects associated with non-block variables with thread storage duration are not caught by a +\grammarterm{function-try-block} +on the initial function of the thread. + +\pnum +If a \keyword{return} statement\iref{stmt.return} appears in a handler of the +\grammarterm{function-try-block} +of a +constructor, the program is ill-formed. + +\pnum +The currently handled exception +is rethrown if control reaches the end of a handler of the +\grammarterm{function-try-block} +of a constructor or destructor. +Otherwise, flowing off the end of +the \grammarterm{compound-statement} +of a \grammarterm{handler} +of a \grammarterm{function-try-block} +is equivalent to flowing off the end of +the \grammarterm{compound-statement} +of that function (see \ref{stmt.return}). + +\pnum +The variable declared by the \grammarterm{exception-declaration}, of type +\cv{}~\tcode{T} or \cv{}~\tcode{T\&}, is initialized from the exception object, +of type \tcode{E}, as follows: +\begin{itemize} +\item +if \tcode{T} is a base class of \tcode{E}, +the variable is copy-initialized\iref{dcl.init} +from an lvalue of type \tcode{T} designating the corresponding base class subobject +of the exception object; +\item otherwise, the variable is copy-initialized\iref{dcl.init} +from an lvalue of type \tcode{E} designating the exception object. +\end{itemize} + +The lifetime of the variable ends +when the handler exits, after the +destruction of any objects with automatic storage duration initialized +within the handler. + +\pnum +When the handler declares an object, +any changes to that object will not affect the exception object. +When the handler declares a reference to an object, +any changes to the referenced object are changes to the +exception object and will have effect should that object be rethrown.% +\indextext{exception handling!handler!match|)}% +\indextext{exception handling!handler|)} + \rSec2[basic.start]{Start and termination} \rSec3[basic.start.main]{\tcode{main} function} @@ -7603,9 +7864,9 @@ \pnum \indextext{\idxcode{terminate}}% In the situation where no matching handler is found, it is -\impldef{stack unwinding before invocation of \tcode{std::terminate}} -whether or not the stack is unwound -before \tcode{std::terminate} is invoked. +\impldef{whether stack is unwound before invoking the function \tcode{std::terminate} +when no matching handler is found} +whether or not the stack is unwound before \tcode{std::terminate} is invoked. In the situation where the search for a handler\iref{except.handle} encounters the outermost block of a function with a non-throwing exception specification\iref{except.spec}, it is diff --git a/source/compatibility.tex b/source/compatibility.tex index 93baac8554..9bc289e8c1 100644 --- a/source/compatibility.tex +++ b/source/compatibility.tex @@ -951,6 +951,25 @@ \end{codeblock} \end{example} +\diffref{except.spec} +\change +Remove \tcode{throw()} exception specification. +\rationale +Removal of obsolete feature that has been replaced by \keyword{noexcept}. +\effect +A valid \CppXVII{} function declaration, member function declaration, function +pointer declaration, or function reference declaration that uses \tcode{throw()} +for its exception specification will be rejected as ill-formed in this +revision of \Cpp{}. It should simply be replaced with \keyword{noexcept} for no +change of meaning since \CppXVII{}. +\begin{note} +There is no way to write a function declaration +that is non-throwing in this revision of \Cpp{} +and is also non-throwing in \CppIII{} +except by using the preprocessor to generate +a different token sequence in each case. +\end{note} + \rSec2[diff.cpp17.class]{\ref{class}: classes} \diffref{class.ctor,class.conv.fct} @@ -1134,27 +1153,6 @@ \end{codeblock} \end{example} -\rSec2[diff.cpp17.except]{\ref{except}: exception handling} - -\diffref{except.spec} -\change -Remove \tcode{throw()} exception specification. -\rationale -Removal of obsolete feature that has been replaced by \keyword{noexcept}. -\effect -A valid \CppXVII{} function declaration, member function declaration, function -pointer declaration, or function reference declaration that uses \tcode{throw()} -for its exception specification will be rejected as ill-formed in this -revision of \Cpp{}. It should simply be replaced with \keyword{noexcept} for no -change of meaning since \CppXVII{}. -\begin{note} -There is no way to write a function declaration -that is non-throwing in this revision of \Cpp{} -and is also non-throwing in \CppIII{} -except by using the preprocessor to generate -a different token sequence in each case. -\end{note} - \rSec2[diff.cpp17.library]{\ref{library}: library introduction} \diffref{headers} @@ -1560,6 +1558,27 @@ \end{codeblock} \end{example} +\diffref{except.spec} +\change +Remove dynamic exception specifications. +\rationale +Dynamic exception specifications were a deprecated feature +that was complex and brittle in use. +They interacted badly with the type system, +which became a more significant issue in this revision of \Cpp{} +where (non-dynamic) exception specifications are part of the function type. +\effect +A valid \CppXIV{} function declaration, +member function declaration, +function pointer declaration, +or function reference declaration, +if it has a potentially throwing dynamic exception specification, +is rejected as ill-formed in this revision of \Cpp{}. +Violating a non-throwing dynamic exception specification +calls \tcode{terminate} rather than \tcode{unexpected}, +and it is unspecified whether stack unwinding is performed +prior to such a call. + \rSec2[diff.cpp14.class]{\ref{class}: classes} \diffref{class.inhctor.init} @@ -1613,29 +1632,6 @@ \end{codeblock} \end{example} -\rSec2[diff.cpp14.except]{\ref{except}: exception handling} - -\diffref{except.spec} -\change -Remove dynamic exception specifications. -\rationale -Dynamic exception specifications were a deprecated feature -that was complex and brittle in use. -They interacted badly with the type system, -which became a more significant issue in this revision of \Cpp{} -where (non-dynamic) exception specifications are part of the function type. -\effect -A valid \CppXIV{} function declaration, -member function declaration, -function pointer declaration, -or function reference declaration, -if it has a potentially throwing dynamic exception specification, -is rejected as ill-formed in this revision of \Cpp{}. -Violating a non-throwing dynamic exception specification -calls \tcode{terminate} rather than \tcode{unexpected}, -and it is unspecified whether stack unwinding is performed -prior to such a call. - \rSec2[diff.cpp14.library]{\ref{library}: library introduction} \diffref{headers} diff --git a/source/exceptions.tex b/source/exceptions.tex deleted file mode 100644 index c6a6d3bb71..0000000000 --- a/source/exceptions.tex +++ /dev/null @@ -1,442 +0,0 @@ -%!TEX root = std.tex -\rSec0[except]{Exception handling}% -\indextext{exception handling|(} - -\gramSec[gram.except]{Exception handling} - -\indextext{exception object|see{exception handling, exception object}}% -\indextext{object!exception|see{exception handling, exception object}} - -\rSec1[except.pre]{Preamble} - -\pnum -Exception handling provides a way of transferring control and information -from a point in the execution of a thread to an exception handler -associated with a point previously passed by the execution. -A handler will be invoked only by throwing an exception -in code executed in the handler's try block -or in functions called from the handler's try block. - -\indextext{\idxcode{try}}% -% -\begin{bnf} -\nontermdef{try-block}\br - \keyword{try} compound-statement handler-seq -\end{bnf} - -\indextext{\idxcode{try}}% -% -\begin{bnf} -\nontermdef{function-try-block}\br - \keyword{try} \opt{ctor-initializer} compound-statement handler-seq -\end{bnf} - -\begin{bnf} -\nontermdef{handler-seq}\br - handler \opt{handler-seq} -\end{bnf} - -\indextext{\idxcode{catch}}% -% -\begin{bnf} -\nontermdef{handler}\br - \keyword{catch} \terminal{(} exception-declaration \terminal{)} compound-statement -\end{bnf} - -\begin{bnf} -\nontermdef{exception-declaration}\br - \opt{attribute-specifier-seq} type-specifier-seq declarator\br - \opt{attribute-specifier-seq} type-specifier-seq \opt{abstract-declarator}\br - \terminal{...} -\end{bnf} - -The optional \grammarterm{attribute-specifier-seq} in an \grammarterm{exception-declaration} -appertains to the parameter of the catch clause\iref{except.handle}. - -\pnum -\indextext{exception handling!try block}% -\indextext{exception handling!handler}% -\indextext{try block|see{exception handling, try block}}% -\indextext{handler|see{exception handling, handler}}% -A \grammarterm{try-block} is a \grammarterm{statement}\iref{stmt.pre}. -\begin{note} -Within this Clause -``try block'' is taken to mean both \grammarterm{try-block} and -\grammarterm{function-try-block}. -\end{note} - -\pnum -\indextext{exception handling!\idxcode{goto}}% -\indextext{exception handling!\idxcode{switch}}% -\indextext{\idxcode{goto}!and try block}% -\indextext{\idxcode{switch}!and try block}% -\indextext{\idxcode{goto}!and handler}% -\indextext{\idxcode{switch}!and handler}% -The \grammarterm{compound-statement} of a try block or of a handler is a -control-flow-limited statement\iref{stmt.label}. -\begin{example} -\begin{codeblock} -void f() { - goto l1; // error - goto l2; // error - try { - goto l1; // OK - goto l2; // error - l1: ; - } catch (...) { - l2: ; - goto l1; // error - goto l2; // OK - } -} - -\end{codeblock} -\end{example} -\indextext{\idxcode{goto}!and try block}% -\indextext{\idxcode{switch}!and try block}% -\indextext{\idxcode{return}!and try block}% -\indextext{\idxcode{continue}!and try block}% -\indextext{\idxcode{goto}!and handler}% -\indextext{\idxcode{switch}!and handler}% -\indextext{\idxcode{return}!and handler}% -\indextext{\idxcode{continue}!and handler}% -A -\keyword{goto}, -\keyword{break}, -\keyword{return}, -or -\keyword{continue} -statement can be used to transfer control out of -a try block or handler. -When this happens, each variable declared in the try block -will be destroyed in the context that -directly contains its declaration. -\begin{example} -\begin{codeblock} -lab: try { - T1 t1; - try { - T2 t2; - if (@\grammarterm{condition}@) - goto lab; - } catch(...) { @\tcode{/* handler 2 */}@ } - } catch(...) { @\tcode{/* handler 1 */}@ } -\end{codeblock} - -Here, executing -\tcode{goto lab;} -will destroy first -\tcode{t2}, -then -\tcode{t1}, -assuming the -\grammarterm{condition} -does not declare a variable. -Any exception thrown while destroying -\tcode{t2} -will result in executing -\tcode{handler 2}; -any exception thrown while destroying -\tcode{t1} -will result in executing -\tcode{handler 1}. -\end{example} - -\pnum -\indextext{function try block|see{exception handling, function try block}}% -\indextext{exception handling!function try block}% -A -\grammarterm{function-try-block} -associates a -\grammarterm{handler-seq} -with the -\grammarterm{ctor-initializer}, -if present, and the -\grammarterm{compound-statement}. -An exception -thrown during the execution of the -\grammarterm{compound-statement} -or, for constructors and destructors, during the initialization or -destruction, respectively, of the class's subobjects, -transfers control to a handler in a -\grammarterm{function-try-block} -in the same way as an exception thrown during the execution of a -\grammarterm{try-block} -transfers control to other handlers. -\begin{example} -\begin{codeblock} -int f(int); -class C { - int i; - double d; -public: - C(int, double); -}; - -C::C(int ii, double id) -try : i(f(ii)), d(id) { - // constructor statements -} catch (...) { - // handles exceptions thrown from the ctor-initializer and from the constructor statements -} -\end{codeblock} -\end{example} - -\pnum -In this Clause, ``before'' and ``after'' refer to the -``sequenced before'' relation\iref{intro.execution}. - -\rSec1[except.handle]{Handling an exception} -\indextext{exception handling!handler|(}% - -\pnum -The -\grammarterm{exception-declaration} -in a -\grammarterm{handler} -describes the type(s) of exceptions that can cause -that -\grammarterm{handler} -to be entered. -\indextext{exception handling!handler!incomplete type in}% -\indextext{exception handling!handler!rvalue reference in}% -\indextext{exception handling!handler!array in}% -\indextext{exception handling!handler!pointer to function in}% -The -\grammarterm{exception-declaration} -shall not denote an incomplete type, an abstract class type, or an rvalue reference type. -The -\grammarterm{exception-declaration} -shall not denote a pointer or reference to an -incomplete type, other than ``pointer to \cv{}~\keyword{void}''. - -\pnum -A handler of type -\indextext{array!handler of type}% -``array of \tcode{T}'' or -\indextext{function!handler of type}% -function type \tcode{T} -is adjusted to be of type -``pointer to \tcode{T}''. - -\pnum -\indextext{exception handling!handler!match|(}% -A -\grammarterm{handler} -is a match for -an exception object -of type -\tcode{E} -if -\begin{itemize} -\item% -The \grammarterm{handler} is of type \cv{}~\tcode{T} or -\cv{}~\tcode{T\&} and -\tcode{E} and \tcode{T} -are the same type (ignoring the top-level \grammarterm{cv-qualifier}{s}), or -\item% -the \grammarterm{handler} is of type \cv{}~\tcode{T} or -\cv{}~\tcode{T\&} and -\tcode{T} is an unambiguous public base class of \tcode{E}, or -\item% -the \grammarterm{handler} is of type \cv{}~\tcode{T} or \tcode{const T\&} -where \tcode{T} is a pointer or pointer-to-member type and -\tcode{E} is a pointer or pointer-to-member type -that can be converted to \tcode{T} by one or more of -\begin{itemize} - -\item% -a standard pointer conversion\iref{conv.ptr} not involving conversions -to pointers to private or protected or ambiguous classes -\item% -a function pointer conversion\iref{conv.fctptr} -\item% -a qualification conversion\iref{conv.qual}, or - -\end{itemize} - -\item -the \grammarterm{handler} is of type \cv{}~\tcode{T} or \tcode{const T\&} where \tcode{T} is a pointer or pointer-to-member type and \tcode{E} is \tcode{std::nullptr_t}. - -\end{itemize} - -\begin{note} -A -\grammarterm{throw-expression} -whose operand is an integer literal with value zero does not match a handler of -pointer or pointer-to-member type. -A handler of reference to array or function type -is never a match for any exception object\iref{expr.throw}. -\end{note} - -\begin{example} -\begin{codeblock} -class Matherr { @\commentellip@ virtual void vf(); }; -class Overflow: public Matherr { @\commentellip@ }; -class Underflow: public Matherr { @\commentellip@ }; -class Zerodivide: public Matherr { @\commentellip@ }; - -void f() { - try { - g(); - } catch (Overflow oo) { - // ... - } catch (Matherr mm) { - // ... - } -} -\end{codeblock} -Here, the -\tcode{Overflow} -handler will catch exceptions of type -\tcode{Overflow} -and the -\tcode{Matherr} -handler will catch exceptions of type -\tcode{Matherr} -and of all types publicly derived from -\tcode{Matherr} -including exceptions of type -\tcode{Underflow} -and -\tcode{Zerodivide}. -\end{example} - -\pnum -The handlers for a try block are tried in order of appearance. -\begin{note} -This makes it possible to write handlers that can never be -executed, for example by placing a handler for a final derived class after -a handler for a corresponding unambiguous public base class. -\end{note} - -\pnum -A -\tcode{...} -in a handler's -\grammarterm{exception-declaration} -specifies a match for any exception. -If present, a -\tcode{...} -handler shall be the last handler for its try block. - -\pnum -If no match is found among the handlers for a try block, -the search for a matching -handler continues in a dynamically surrounding try block -of the same thread. - -\pnum -\indextext{exception handling!terminate called@\tcode{terminate} called}% -\indextext{\idxcode{terminate}!called}% -If the search for a handler -encounters the outermost block of a function with a -non-throwing exception specification, -the function \tcode{std::terminate}\iref{except.terminate} is invoked. -\begin{note} -An implementation is not permitted to reject an expression merely because, when -executed, it throws or might -throw an exception from a function with a non-throwing exception specification. -\end{note} -\begin{example} -\begin{codeblock} -extern void f(); // potentially-throwing - -void g() noexcept { - f(); // valid, even if \tcode{f} throws - throw 42; // valid, effectively a call to \tcode{std::terminate} -} -\end{codeblock} -The call to -\tcode{f} -is well-formed despite the possibility for it to throw an exception. -\end{example} - -\pnum -If no matching handler is found, -the function \tcode{std::terminate} is invoked; -whether or not the stack is unwound before this invocation of -\tcode{std::terminate} -is \impldef{stack unwinding before invocation of -\tcode{std::terminate}}\iref{except.terminate}. - -\pnum -A handler is considered \defnx{active}{exception handling!handler!active} when -initialization is complete for the parameter (if any) of the catch clause. -\begin{note} -The stack will have been unwound at that point. -\end{note} -Also, an implicit handler is considered active when -the function \tcode{std::terminate} -is entered due to a throw. A handler is no longer considered active when the -catch clause exits. - -\pnum -\indextext{currently handled exception|see{exception handling, currently handled exception}}% -The exception with the most recently activated handler that is -still active is called the -\defnx{currently handled exception}{exception handling!currently handled exception}. - -\pnum -Referring to any non-static member or base class of an object -in the handler for a -\grammarterm{function-try-block} -of a constructor or destructor for that object results in undefined behavior. - -\pnum -Exceptions thrown in destructors of objects with static storage duration or in -constructors of objects associated with non-block variables with static storage duration are not caught by a -\grammarterm{function-try-block} -on -the \tcode{main} function\iref{basic.start.main}. -Exceptions thrown in destructors of objects with thread storage duration or in constructors of objects associated with non-block variables with thread storage duration are not caught by a -\grammarterm{function-try-block} -on the initial function of the thread. - -\pnum -If a \keyword{return} statement\iref{stmt.return} appears in a handler of the -\grammarterm{function-try-block} -of a -constructor, the program is ill-formed. - -\pnum -The currently handled exception -is rethrown if control reaches the end of a handler of the -\grammarterm{function-try-block} -of a constructor or destructor. -Otherwise, flowing off the end of -the \grammarterm{compound-statement} -of a \grammarterm{handler} -of a \grammarterm{function-try-block} -is equivalent to flowing off the end of -the \grammarterm{compound-statement} -of that function (see \ref{stmt.return}). - -\pnum -The variable declared by the \grammarterm{exception-declaration}, of type -\cv{}~\tcode{T} or \cv{}~\tcode{T\&}, is initialized from the exception object, -of type \tcode{E}, as follows: -\begin{itemize} -\item -if \tcode{T} is a base class of \tcode{E}, -the variable is copy-initialized\iref{dcl.init} -from an lvalue of type \tcode{T} designating the corresponding base class subobject -of the exception object; -\item otherwise, the variable is copy-initialized\iref{dcl.init} -from an lvalue of type \tcode{E} designating the exception object. -\end{itemize} - -The lifetime of the variable ends -when the handler exits, after the -destruction of any objects with automatic storage duration initialized -within the handler. - -\pnum -When the handler declares an object, -any changes to that object will not affect the exception object. -When the handler declares a reference to an object, -any changes to the referenced object are changes to the -exception object and will have effect should that object be rethrown.% -\indextext{exception handling!handler!match|)}% -\indextext{exception handling!handler|)} -\indextext{exception handling|)} diff --git a/source/expressions.tex b/source/expressions.tex index 54e40c91a9..6d74239ecb 100644 --- a/source/expressions.tex +++ b/source/expressions.tex @@ -5643,7 +5643,7 @@ \indextext{\idxcode{bad_alloc}}% \indexlibraryglobal{bad_alloc}% \tcode{std::bad_alloc} -exception\iref{basic.stc.dynamic.allocation,except,bad.alloc}; +exception\iref{basic.stc.dynamic.allocation,except.throw,bad.alloc}; it returns a non-null pointer otherwise. If the allocation function has a non-throwing exception specification, it returns null to indicate failure to allocate storage diff --git a/source/lib-intro.tex b/source/lib-intro.tex index d608fa2067..55581f1d7f 100644 --- a/source/lib-intro.tex +++ b/source/lib-intro.tex @@ -53,7 +53,7 @@ The language support library\iref{support} provides components that are required by certain parts of the \Cpp{} language, such as memory allocation\iref{expr.new,expr.delete} and -exception processing\iref{except}. +exception processing\iref{support.exception.general}. \pnum The concepts library\iref{concepts} describes library components that \Cpp{} diff --git a/source/statements.tex b/source/statements.tex index 9940ee28de..4fd6aee9d3 100644 --- a/source/statements.tex +++ b/source/statements.tex @@ -1033,6 +1033,181 @@ \indextext{label}% label\iref{stmt.label} located in the current function. +\rSec1[except.pre]{\keyword{try} blocks} + +\pnum +Exception handling provides a way of transferring control and information +from a point in the execution of a thread to an exception handler +associated with a point previously passed by the execution. +A handler will be invoked only by throwing an exception +in code executed in the handler's try block +or in functions called from the handler's try block. + +\indextext{\idxcode{try}}% +% +\begin{bnf} +\nontermdef{try-block}\br + \keyword{try} compound-statement handler-seq +\end{bnf} + +\indextext{\idxcode{try}}% +% +\begin{bnf} +\nontermdef{function-try-block}\br + \keyword{try} \opt{ctor-initializer} compound-statement handler-seq +\end{bnf} + +\begin{bnf} +\nontermdef{handler-seq}\br + handler \opt{handler-seq} +\end{bnf} + +\indextext{\idxcode{catch}}% +% +\begin{bnf} +\nontermdef{handler}\br + \keyword{catch} \terminal{(} exception-declaration \terminal{)} compound-statement +\end{bnf} + +\begin{bnf} +\nontermdef{exception-declaration}\br + \opt{attribute-specifier-seq} type-specifier-seq declarator\br + \opt{attribute-specifier-seq} type-specifier-seq \opt{abstract-declarator}\br + \terminal{...} +\end{bnf} + +The optional \grammarterm{attribute-specifier-seq} in an \grammarterm{exception-declaration} +appertains to the parameter of the catch clause\iref{except.handle}. + +\pnum +\indextext{exception handling!try block}% +\indextext{exception handling!handler}% +\indextext{try block|see{exception handling, try block}}% +\indextext{handler|see{exception handling, handler}}% +A \grammarterm{try-block} is a \grammarterm{statement}\iref{stmt.pre}. +\begin{note} +Within this Clause +``try block'' is taken to mean both \grammarterm{try-block} and +\grammarterm{function-try-block}. +\end{note} + +\pnum +\indextext{exception handling!\idxcode{goto}}% +\indextext{exception handling!\idxcode{switch}}% +\indextext{\idxcode{goto}!and try block}% +\indextext{\idxcode{switch}!and try block}% +\indextext{\idxcode{goto}!and handler}% +\indextext{\idxcode{switch}!and handler}% +The \grammarterm{compound-statement} of a try block or of a handler is a +control-flow-limited statement\iref{stmt.label}. +\begin{example} +\begin{codeblock} +void f() { + goto l1; // error + goto l2; // error + try { + goto l1; // OK + goto l2; // error + l1: ; + } catch (...) { + l2: ; + goto l1; // error + goto l2; // OK + } +} + +\end{codeblock} +\end{example} +\indextext{\idxcode{goto}!and try block}% +\indextext{\idxcode{switch}!and try block}% +\indextext{\idxcode{return}!and try block}% +\indextext{\idxcode{continue}!and try block}% +\indextext{\idxcode{goto}!and handler}% +\indextext{\idxcode{switch}!and handler}% +\indextext{\idxcode{return}!and handler}% +\indextext{\idxcode{continue}!and handler}% +A +\keyword{goto}, +\keyword{break}, +\keyword{return}, +or +\keyword{continue} +statement can be used to transfer control out of +a try block or handler. +When this happens, each variable declared in the try block +will be destroyed in the context that +directly contains its declaration. +\begin{example} +\begin{codeblock} +lab: try { + T1 t1; + try { + T2 t2; + if (@\grammarterm{condition}@) + goto lab; + } catch(...) { @\tcode{/* handler 2 */}@ } + } catch(...) { @\tcode{/* handler 1 */}@ } +\end{codeblock} + +Here, executing +\tcode{goto lab;} +will destroy first +\tcode{t2}, +then +\tcode{t1}, +assuming the +\grammarterm{condition} +does not declare a variable. +Any exception thrown while destroying +\tcode{t2} +will result in executing +\tcode{handler 2}; +any exception thrown while destroying +\tcode{t1} +will result in executing +\tcode{handler 1}. +\end{example} + +\pnum +\indextext{function try block|see{exception handling, function try block}}% +\indextext{exception handling!function try block}% +A +\grammarterm{function-try-block} +associates a +\grammarterm{handler-seq} +with the +\grammarterm{ctor-initializer}, +if present, and the +\grammarterm{compound-statement}. +An exception +thrown during the execution of the +\grammarterm{compound-statement} +or, for constructors and destructors, during the initialization or +destruction, respectively, of the class's subobjects, +transfers control to a handler in a +\grammarterm{function-try-block} +in the same way as an exception thrown during the execution of a +\grammarterm{try-block} +transfers control to other handlers. +\begin{example} +\begin{codeblock} +int f(int); +class C { + int i; + double d; +public: + C(int, double); +}; + +C::C(int ii, double id) +try : i(f(ii)), d(id) { + // constructor statements +} catch (...) { + // handles exceptions thrown from the ctor-initializer and from the constructor statements +} +\end{codeblock} +\end{example} + \rSec1[stmt.dcl]{Declaration statement}% \indextext{statement!declaration} diff --git a/source/std.tex b/source/std.tex index fea8f0a37c..3a5132f9f8 100644 --- a/source/std.tex +++ b/source/std.tex @@ -120,7 +120,6 @@ \include{classes} \include{overloading} \include{templates} -\include{exceptions} \include{preprocessor} \include{lib-intro} \include{support} diff --git a/source/xrefdelta.tex b/source/xrefdelta.tex index 38e3895f08..688ec44f56 100644 --- a/source/xrefdelta.tex +++ b/source/xrefdelta.tex @@ -74,8 +74,12 @@ \removedxref{type.index.hash} % Dissolving [except] +\removedxref{diff.cpp14.except} +\removedxref{diff.cpp17.except} +\removedxref{except} \removedxref{except.special} \removedxref{except.special.general} +\removedxref{gram.except} %%% Renamed sections. %%% Examples: