Skip to content

Commit 10c1d73

Browse files
NinaRannsiains
andcommitted
c++, contracts: noexcept function wrapping with contract checks.
For consistency, we need to have a single MUST_NOT_THROW_EXPR as the outer-most tree of a noexcept function, rather than any instance(s) of such expressions wrapping parts of it. Co-authored-by: Iain Sandoe <[email protected]>
1 parent 8043971 commit 10c1d73

File tree

4 files changed

+142
-8
lines changed

4 files changed

+142
-8
lines changed

gcc/cp/contracts.cc

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2665,9 +2665,26 @@ maybe_apply_function_contracts (tree fndecl)
26652665
//debug_tree (DECL_CONTRACTS (fndecl));
26662666
}
26672667

2668-
/* This copies the approach used for function try blocks. */
2669-
tree fnbody = pop_stmt_list (DECL_SAVED_TREE (fndecl));
2670-
DECL_SAVED_TREE (fndecl) = push_stmt_list ();
2668+
/* If the function is noexcept, the user's written body will be wrapped in a
2669+
MUST_NOT_THROW expression. In that case we want to extract the body from
2670+
that and build the replacement (including the pre and post-conditions as
2671+
needed) in its place. */
2672+
tree fnbody;
2673+
if (TYPE_NOEXCEPT_P (TREE_TYPE (fndecl)))
2674+
{
2675+
tree m_n_t_expr = expr_first (DECL_SAVED_TREE (fndecl));
2676+
gcc_checking_assert (TREE_CODE (m_n_t_expr) == MUST_NOT_THROW_EXPR);
2677+
fnbody = TREE_OPERAND (m_n_t_expr, 0);
2678+
TREE_OPERAND (m_n_t_expr, 0) = push_stmt_list ();
2679+
}
2680+
else
2681+
{
2682+
fnbody = DECL_SAVED_TREE (fndecl);
2683+
DECL_SAVED_TREE (fndecl) = push_stmt_list ();
2684+
}
2685+
2686+
/* Now add the pre and post conditions to the existing function body.
2687+
This copies the approach used for function try blocks. */
26712688
tree compound_stmt = begin_compound_stmt (0);
26722689
current_binding_level->artificial = 1;
26732690

@@ -3013,12 +3030,16 @@ build_contract_wrapper_function (tree fndecl, bool check_post)
30133030

30143031
tree wrapper_type = build_function_type ( wrapper_return_type,
30153032
wrapper_args);
3016-
3033+
/* Make the wrapper function noexcept if the checked function is
3034+
noexcept. */
3035+
if (flag_exceptions && type_noexcept_p (TREE_TYPE (fndecl)))
3036+
wrapper_type = build_exception_variant (wrapper_type, noexcept_true_spec);
30173037

30183038
/* this will create a member function if fndecl is a member function,
30193039
so we will need to adjust the type later */
30203040
tree wrapdecl
3021-
= build_lang_decl_loc (loc, FUNCTION_DECL, get_identifier (name), wrapper_type);
3041+
= build_lang_decl_loc (loc, FUNCTION_DECL, get_identifier (name),
3042+
wrapper_type);
30223043

30233044

30243045
DECL_CONTEXT (wrapdecl) = NULL_TREE;
@@ -3177,13 +3198,12 @@ maybe_contract_wrap_call (tree fndecl, tree call)
31773198

31783199
bool define_contract_wrapper_func(const tree& fndecl, const tree& wrapdecl, void*)
31793200
{
3180-
3181-
start_preparsed_function (wrapdecl, /*DECL_ATTRIBUTES (wrapdecl)*/ NULL_TREE, SF_DEFAULT | SF_PRE_PARSED);
3201+
start_preparsed_function (wrapdecl, /*DECL_ATTRIBUTES (wrapdecl)*/ NULL_TREE,
3202+
SF_DEFAULT | SF_PRE_PARSED);
31823203

31833204
tree body = begin_function_body ();
31843205
tree compound_stmt = begin_compound_stmt (BCS_FN_BODY);
31853206

3186-
31873207
vec<tree, va_gc> * args = build_arg_list (wrapdecl);
31883208

31893209
tree fn = fndecl;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// { dg-do run }
2+
// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr -fcontract-evaluation-semantic=observe -fcontracts-nonattr-client-contracts=all " }
3+
4+
#include <experimental/contract>
5+
#include <iostream>
6+
#include <exception>
7+
#include <cstdlib>
8+
9+
struct MyException{};
10+
11+
// Test that there is an active exception when we reach the terminate handler.
12+
void my_term()
13+
{
14+
try { throw; }
15+
catch(MyException) { std::exit(0); }
16+
}
17+
18+
19+
void handle_contract_violation(const std::experimental::contract_violation& violation)
20+
{
21+
throw MyException{};
22+
}
23+
24+
void f(int x) noexcept pre(x >= 0) {}
25+
26+
int main()
27+
{
28+
29+
std::set_terminate (my_term);
30+
try
31+
{
32+
f(-42);
33+
} catch (...) {
34+
}
35+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// { dg-do run }
2+
// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr -fcontract-evaluation-semantic=observe " }
3+
4+
#include <experimental/contract>
5+
#include <exception>
6+
#include <cstdlib>
7+
8+
struct MyException{};
9+
10+
// Test that there is an active exception when we reach the terminate handler.
11+
void my_term()
12+
{
13+
try { throw; }
14+
catch(MyException) { std::exit(0); }
15+
}
16+
17+
18+
void handle_contract_violation(const std::experimental::contract_violation& violation)
19+
{
20+
throw MyException{};
21+
}
22+
23+
struct X
24+
{
25+
void f(int x) noexcept pre(x>1) {
26+
int i = 1;
27+
}
28+
};
29+
30+
int main()
31+
{
32+
std::set_terminate (my_term);
33+
try
34+
{
35+
X x;
36+
x.f(-42);
37+
} catch (...) {
38+
}
39+
40+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// { dg-do run }
2+
// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr -fcontract-evaluation-semantic=observe" }
3+
4+
#include <experimental/contract>
5+
#include <exception>
6+
#include <cstdlib>
7+
8+
struct MyException{};
9+
10+
// Test that there is an active exception when we reach the terminate handler.
11+
void my_term()
12+
{
13+
try { throw; }
14+
catch(MyException) { std::exit(0); }
15+
}
16+
17+
18+
void handle_contract_violation(const std::experimental::contract_violation& violation)
19+
{
20+
throw MyException{};
21+
}
22+
23+
struct X
24+
{
25+
virtual void f(const int x) noexcept pre (x>2) post(x>1){
26+
int i = 1;
27+
}
28+
};
29+
30+
int main()
31+
{
32+
std::set_terminate (my_term);
33+
try
34+
{
35+
X x;
36+
x.f(-42);
37+
} catch (...) {
38+
}
39+
}

0 commit comments

Comments
 (0)