Some of the recent work.
This commit is contained in:
+83
-64
@@ -47,6 +47,10 @@ cannotTolerateExceptions() noexcept( true )
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
element= someVector.at( 42 );
|
element= someVector.at( 42 );
|
||||||
|
if( someVector.capacity() == someVector.size() )
|
||||||
|
{
|
||||||
|
someVector.reserve( someVector.size() * 2 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch( ... )
|
catch( ... )
|
||||||
{
|
{
|
||||||
@@ -58,6 +62,7 @@ cannotTolerateExceptions() noexcept( true )
|
|||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
someVector.push_back( std::move( element ) );
|
someVector.push_back( std::move( element ) );
|
||||||
tickingTimeBomb();
|
tickingTimeBomb();
|
||||||
|
|
||||||
@@ -65,7 +70,7 @@ cannotTolerateExceptions() noexcept( true )
|
|||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
While some of the code and functions called in `cannotTolerateExceptions` is capable of
|
While some of the code and functions called in `cannotTolerateExceptions` are capable of
|
||||||
throwing, the caller attempts to resolve exceptions and handle them without program
|
throwing, the caller attempts to resolve exceptions and handle them without program
|
||||||
termination. However, the `tickingTimeBomb` function is a hidden hazard. While
|
termination. However, the `tickingTimeBomb` function is a hidden hazard. While
|
||||||
it declares itself to not throw exceptions (which is true), the exceptions thrown
|
it declares itself to not throw exceptions (which is true), the exceptions thrown
|
||||||
@@ -98,40 +103,40 @@ We propose the introduction of `static noexcept` as both a function declaration
|
|||||||
"decorator" and an operator. `static noexcept( true )` or `static noexcept`
|
"decorator" and an operator. `static noexcept( true )` or `static noexcept`
|
||||||
tagged functions have similar semantics to `noexcept` tagged or colored functions:
|
tagged functions have similar semantics to `noexcept` tagged or colored functions:
|
||||||
|
|
||||||
- An evaluation of `noexcept( someStaticNoexceptFunction )` will evaluate as `true`.
|
1. An evaluation of `noexcept( someStaticNoexceptFunction )` will evaluate as `true`.
|
||||||
- An evaluation of `static noexcept( someStaticNoexceptFunction )` will evaluate as `true`.
|
2. An evaluation of `static noexcept( someStaticNoexceptFunction )` will evaluate as `true`.
|
||||||
- An evaluation of `static noexcept( someClassicNoexceptFunction )` will evaluate as `false`.
|
3. An evaluation of `static noexcept( someClassicNoexceptFunction )` will evaluate as `false`.
|
||||||
- A call to a `static noexcept( true )` function will not throw.
|
4. A call to a `static noexcept( true )` function will not throw.
|
||||||
- If an exception unwind (somehow) attempts to "emerge" from a
|
5. If an exception unwind (somehow) attempts to "emerge" from a
|
||||||
`static noexcept( true )` function, then the program will terminate.
|
`static noexcept( true )` function, then the program will terminate.
|
||||||
|
|
||||||
However, we propose to add the following behaviors and restrictions to such functions:
|
However, we propose to add the following behaviors and restrictions to such functions:
|
||||||
|
|
||||||
- If a `noexcept( false )` function is called outside of the body of a `try` block,
|
1. If a `noexcept( false )` function is called outside of the body of a `try` block,
|
||||||
then the program is ill formed, and a compile-time diagnostic is required.
|
then the program is ill formed, and a compile-time diagnostic is required.
|
||||||
- If a `noexcept( false )` function is called inside the body of a `try` block, then
|
2. If a `noexcept( false )` function is called inside the body of a `try` block, then
|
||||||
a `catch( ... )` block must exist.
|
a `catch( ... )` block must exist.
|
||||||
- A `catch` block's body is not considered part of a `try` block.
|
3. A `catch` block's body is not considered part of its `try` block.
|
||||||
- When checking if a statement is "in" a `try` block, one must walk up the nested
|
4. When checking if a statement is "in" a `try` block, one must walk up the nested
|
||||||
block structure until a try block is reached. A `catch` block attached to a
|
block structure until a try block is reached. A `catch` block attached to a
|
||||||
`try` block which is inside of a try block at broader scope is considered to be
|
`try` block which is inside of a try block at broader scope is considered to be
|
||||||
"inside" the broader try block, for the purpose of these rules.
|
"inside" the broader try block, for the purpose of these rules.
|
||||||
- A simple model of this is that invoking `noexcept( false )` functions is ill formed
|
5. A simple model of this is that invoking `noexcept( false )` functions is ill formed
|
||||||
unless a `try` block scope can be found by walking outward from the calling scope.
|
unless a `try` block scope can be found by walking outward from the calling scope.
|
||||||
- It is unclear at this time if language UB on expressions should be considered
|
6. It is unclear at this time if language UB on expressions should be considered
|
||||||
throwing or not. There are tradeoffs.
|
throwing or not. There are tradeoffs.
|
||||||
- If UB is considered `noexcept( true )`, then there is a potential that UB
|
1. If UB is considered `noexcept( true )`, then there is a potential that UB
|
||||||
can cause program termination by "leaking" an exception out from checked
|
can cause program termination by "leaking" an exception out from checked
|
||||||
try blocks.
|
try blocks.
|
||||||
- If UB is considered `noexcept( false )`, then it makes writing
|
2. If UB is considered `noexcept( false )`, then it makes writing
|
||||||
`static noexcept( true )` extremely difficult. Many situations will
|
`static noexcept( true )` extremely difficult. Many situations will
|
||||||
require noisy and defensive
|
require noisy and defensive
|
||||||
`try { /* code */ } catch( ... ) { /* silently ignore exception and do nothing */ }`
|
`try { /* code */ } catch( ... ) { /* silently ignore exception and do nothing */ }`
|
||||||
patterns.
|
patterns.
|
||||||
|
|
||||||
The authors of this paper favor the interpretation that UB and language primitive behavior
|
The authors of this paper favor the interpretation that despite UB, most language primitive
|
||||||
should be considered `noexcept( true )` for the purpose of this checking. There already
|
behavior should be considered `noexcept( true )` for the purpose of this checking. There
|
||||||
exists the possibility of "deeper down" time bombs in this construct and language UB
|
already exists the possibility of "deeper down" time bombs in this construct, and thus language UB
|
||||||
leading to exceptions is a somewhat niche case.
|
leading to exceptions is a somewhat niche case.
|
||||||
|
|
||||||
#### Rewriting the original painful example using this feature
|
#### Rewriting the original painful example using this feature
|
||||||
@@ -157,6 +162,10 @@ illFormedCannotTolerateExceptions() static noexcept( true )
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
element= someVector.at( 42 );
|
element= someVector.at( 42 );
|
||||||
|
if( someVector.capacity() == someVector.size() )
|
||||||
|
{
|
||||||
|
someVector.reserve( someVector.size() * 2 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch( ... )
|
catch( ... )
|
||||||
{
|
{
|
||||||
@@ -182,7 +191,9 @@ cannotTolerateExceptions() static noexcept( true )
|
|||||||
auto element= someVector.at( 42 );
|
auto element= someVector.at( 42 );
|
||||||
|
|
||||||
// This line has to be moved into the try block to satisfy the `static noexcept`
|
// This line has to be moved into the try block to satisfy the `static noexcept`
|
||||||
// requirements
|
// requirements.
|
||||||
|
//
|
||||||
|
// As such the "somewhat defensive against exceptions" reserve can be eliminated.
|
||||||
someVector.push_back( std::move( element ) );
|
someVector.push_back( std::move( element ) );
|
||||||
}
|
}
|
||||||
catch( ... )
|
catch( ... )
|
||||||
@@ -203,12 +214,12 @@ cannotTolerateExceptions() static noexcept( true )
|
|||||||
|
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
### Second new feature: A stronger (strawman) statically checked function - `false static noexcept( true )`
|
### Attempt at a second new feature: A stronger (strawman) statically checked function - `false static noexcept( true )`
|
||||||
|
|
||||||
Because the troublesome `tickingTimeBomb` function, which was marked `noexcept` still
|
Because the troublesome `tickingTimeBomb` function, which was marked `noexcept` still
|
||||||
could be called, it's clear that `static noexcept` is insufficiently "sharp" as to catch
|
could be called, it's clear that `static noexcept` is insufficiently "sharp" as to catch
|
||||||
all time bombs. Yet is still has utility as a tool to help carefully construct functions
|
all time bombs. Yet is still has utility as a tool to help carefully construct functions
|
||||||
which are more resilient to unexpected termination. That form provides a comfortable middle
|
which are more resilient to unexpected termination. That form (`static noexcept`) provides a comfortable middle
|
||||||
ground for many cases. This will become apparent when we introduce our stronger checking mechanisms,
|
ground for many cases. This will become apparent when we introduce our stronger checking mechanisms,
|
||||||
`false static noexcept` and `double static noexcept`. It should be noted that
|
`false static noexcept` and `double static noexcept`. It should be noted that
|
||||||
`false static noexcept` is a "straw man". It appears to be the correct solution, but it
|
`false static noexcept` is a "straw man". It appears to be the correct solution, but it
|
||||||
@@ -217,34 +228,34 @@ solution helps expose and explore the problem space.
|
|||||||
|
|
||||||
For the `false static noexcept` operator, we propose the following:
|
For the `false static noexcept` operator, we propose the following:
|
||||||
|
|
||||||
- An evaluation of `noexcept( someFalseStaticNoexceptFunction )` will evaluate as `true`.
|
1. An evaluation of `noexcept( someFalseStaticNoexceptFunction )` will evaluate as `true`.
|
||||||
- An evaluation of `static noexcept( someFalseStaticNoexceptFunction )` will evaluate as `true`.
|
2. An evaluation of `static noexcept( someFalseStaticNoexceptFunction )` will evaluate as `true`.
|
||||||
- An evaluation of `false static noexcept( someClassicNoexceptFunction )` will evaluate as `false`.
|
3. An evaluation of `false static noexcept( someClassicNoexceptFunction )` will evaluate as `false`.
|
||||||
- An evaluation of `false static noexcept( someStaticNoexceptFunction )` will evaluate as `false`.
|
4. An evaluation of `false static noexcept( someStaticNoexceptFunction )` will evaluate as `false`.
|
||||||
- An evaluation of `false static noexcept( someFalseStaticNoexceptFunction )` will evaluate as `true`.
|
5. An evaluation of `false static noexcept( someFalseStaticNoexceptFunction )` will evaluate as `true`.
|
||||||
- A call to a `false static noexcept( true )` function will not throw.
|
6. A call to a `false static noexcept( true )` function will not throw.
|
||||||
- If an exception unwind (somehow) attempts to "emerge" from a
|
7. If an exception unwind (somehow) attempts to "emerge" from a
|
||||||
`false static noexcept( true )` function, then the program will terminate.
|
`false static noexcept( true )` function, then the program will terminate.
|
||||||
|
|
||||||
However, we propose to add the following behaviors and restrictions to such functions:
|
However, we propose to add the following behaviors and restrictions to such functions:
|
||||||
|
|
||||||
- If a `false static noexcept( false )` function is called outside of the body of a `try` block,
|
1. If a `false static noexcept( false )` function is called outside of the body of a `try` block,
|
||||||
then the program is ill formed, and a compile-time diagnostic is required.
|
then the program is ill formed, and a compile-time diagnostic is required.
|
||||||
- If a `false static noexcept( false )` function is called inside the body of a `try` block, then
|
2. If a `false static noexcept( false )` function is called inside the body of a `try` block, then
|
||||||
a `catch( ... )` block must exist.
|
a `catch( ... )` block must exist.
|
||||||
- A `catch` block's body is not considered part of a `try` block.
|
3. A `catch` block's body is not considered part of its `try` block.
|
||||||
- When checking if a statement is "in" a `try` block, one must walk up the nested
|
4. When checking if a statement is "in" a `try` block, one must walk up the nested
|
||||||
block structure until a try block is reached. A `catch` block attached to a
|
block structure until a try block is reached. A `catch` block attached to a
|
||||||
`try` block which is inside of a try block at broader scope is considered to be
|
`try` block which is inside of a try block at broader scope is considered to be
|
||||||
"inside" the broader try block, for the purpose of these rules.
|
"inside" the broader try block, for the purpose of these rules.
|
||||||
- A simple model of this is that invoking `false static noexcept( false )` functions is ill formed
|
5. A simple model of this is that invoking `false static noexcept( false )` functions is ill formed
|
||||||
unless a `try` block scope can be found by walking outward from the calling scope.
|
unless a `try` block scope can be found by walking outward from the calling scope.
|
||||||
- It is unclear at this time if language UB on expressions should be considered
|
6. It is unclear at this time if language UB on expressions should be considered
|
||||||
throwing or not. There are tradeoffs.
|
throwing or not. There are tradeoffs.
|
||||||
- If UB is considered `noexcept( true )`, then there is a potential that UB
|
1. If UB is considered `noexcept( true )`, then there is a potential that UB
|
||||||
can cause program termination by "leaking" an exception out from checked
|
can cause program termination by "leaking" an exception out from checked
|
||||||
try blocks.
|
try blocks.
|
||||||
- If UB is considered `noexcept( false )`, then it makes writing
|
2. If UB is considered `noexcept( false )`, then it makes writing
|
||||||
`static noexcept( true )` extremely difficult. Many situations will
|
`static noexcept( true )` extremely difficult. Many situations will
|
||||||
require noisy and defensive
|
require noisy and defensive
|
||||||
`try { /* code */ } catch( ... ) { /* silently ignore exception and do nothing */ }`
|
`try { /* code */ } catch( ... ) { /* silently ignore exception and do nothing */ }`
|
||||||
@@ -267,46 +278,23 @@ try
|
|||||||
{
|
{
|
||||||
if( not ( rand() % 32 ) )
|
if( not ( rand() % 32 ) )
|
||||||
{
|
{
|
||||||
silentlyThrowing();
|
silentlyThrowing(); // This gets handled in the catch below.
|
||||||
}
|
}
|
||||||
|
|
||||||
if( not ( rand() % 32 ) )
|
if( not ( rand() % 32 ) )
|
||||||
{
|
{
|
||||||
|
// The rules for `false static noexcept` permit this function to be called,
|
||||||
|
// despite it being a program termination hazard.
|
||||||
wrappedSilentlyThrowing();
|
wrappedSilentlyThrowing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch( ... )
|
catch( ... )
|
||||||
{
|
{
|
||||||
|
// Handle unexpected exceptions.
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Result { Success, Failure };
|
enum Result { Success, Failure };
|
||||||
|
|
||||||
Result
|
|
||||||
illFormedCannotTolerateExceptions() false static noexcept( true )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto element= someVector.at( 42 );
|
|
||||||
|
|
||||||
someVector.push_back( std::move( element ) );
|
|
||||||
}
|
|
||||||
catch( ... )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::cerr << "Something went wrong" << std::endl;
|
|
||||||
}
|
|
||||||
catch( ... ) { /* Failure to log is troublesome but not fatal */ }
|
|
||||||
return Failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This line is ill formed and has to be moved into the try block to satisfy the `false static noexcept`
|
|
||||||
// requirements
|
|
||||||
tickingTimeBomb();
|
|
||||||
|
|
||||||
return Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result
|
Result
|
||||||
cannotTolerateExceptions() false static noexcept( true )
|
cannotTolerateExceptions() false static noexcept( true )
|
||||||
{
|
{
|
||||||
@@ -315,8 +303,6 @@ cannotTolerateExceptions() false static noexcept( true )
|
|||||||
auto element= someVector.at( 42 );
|
auto element= someVector.at( 42 );
|
||||||
|
|
||||||
someVector.push_back( std::move( element ) );
|
someVector.push_back( std::move( element ) );
|
||||||
|
|
||||||
tickingTimeBomb();
|
|
||||||
}
|
}
|
||||||
catch( ... )
|
catch( ... )
|
||||||
{
|
{
|
||||||
@@ -328,11 +314,44 @@ cannotTolerateExceptions() false static noexcept( true )
|
|||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Despite the `false static noexcept` rules claiming that this is safe,
|
||||||
|
// it is still a ticking timebomb in its own right, and thus for this function, too.
|
||||||
|
//
|
||||||
|
// The key take-away here is that despite `false static noexcept`'s recursive enforcement
|
||||||
|
// rule, we still have termination leaks.
|
||||||
|
tickingTimeBomb();
|
||||||
|
|
||||||
return Success;
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
### A second new feature: A static no-termination guarantee -- `do not break if using this`
|
||||||
|
|
||||||
|
All of the major troubles we find with recursive exception safety guarantees bump into a limitation. It is
|
||||||
|
currently not possible in C++ to know whether a function has a termination hazard. We thus propose
|
||||||
|
a `do not break if using this` operator and function decorator.
|
||||||
|
|
||||||
|
#### A note on name choice
|
||||||
|
|
||||||
|
While `noterminate` is probably the most obvious name here, there are some concerns with it. Particularly that
|
||||||
|
`noterminate` implies that it would never terminate or call `std::terminate`.
|
||||||
|
|
||||||
|
#### The `do not break if using this` specification
|
||||||
|
|
||||||
|
We propose the introduction of `static noexcept` as both a function declaration
|
||||||
|
"decorator" and an operator. `static noexcept( true )` or `static noexcept`
|
||||||
|
tagged functions have similar semantics to `noexcept` tagged or colored functions:
|
||||||
|
|
||||||
|
1. An evaluation of `noexcept( someStaticNoexceptFunction )` will evaluate as `true`.
|
||||||
|
2. An evaluation of `static noexcept( someStaticNoexceptFunction )` will evaluate as `true`.
|
||||||
|
3. An evaluation of `static noexcept( someClassicNoexceptFunction )` will evaluate as `false`.
|
||||||
|
4. A call to a `static noexcept( true )` function will not throw.
|
||||||
|
5. If an exception unwind (somehow) attempts to "emerge" from a
|
||||||
|
`static noexcept( true )` function, then the program will terminate.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user