c++/contracts: ICE in C++ Contracts with '-fno-exceptions' [PR 110159]

We currently only initialise terminate_fn if exceptions are enabled.
However, contract handling requires terminate_fn when building the
contract because a contract failure may result in std::terminate call
regardless of whether the exceptions are enabled. Refactored
init_exception_processing to extract the initialisation of
terminate_fn. New function init_terminate_fn added that initialises
terminate_fn if it hasn't already been initialised. Call to terminate_fn
added in cxx_init_decl_processing if contracts are enabled.

	PR c++/110159

gcc/cp/ChangeLog:

	* cp-tree.h (init_terminate_fn): Declaration of a new function.
	* decl.cc (cxx_init_decl_processing): If contracts are enabled,
	call init_terminate_fn.
	* except.cc (init_exception_processing): Function refactored to
	call init_terminate_fn.
	(init_terminate_fn): Added new function that initializes
	terminate_fn if	it hasn't already been initialised.

gcc/testsuite/ChangeLog:

	* g++.dg/contracts/pr110159.C: New test.

Signed-off-by: Nina Ranns <dinka.ranns@gmail.com>
This commit is contained in:
Nina Ranns 2024-07-11 17:47:34 +01:00 committed by Jason Merrill
parent a3d1469c7c
commit 40a990c8b5
4 changed files with 54 additions and 8 deletions

View File

@ -7194,6 +7194,7 @@ extern void qualified_name_lookup_error (tree, tree, tree,
location_t);
/* in except.cc */
extern void init_terminate_fn (void);
extern void init_exception_processing (void);
extern tree expand_start_catch_block (tree);
extern void expand_end_catch_block (void);

View File

@ -5172,6 +5172,9 @@ cxx_init_decl_processing (void)
if (flag_exceptions)
init_exception_processing ();
if (flag_contracts)
init_terminate_fn ();
if (modules_p ())
init_modules (parse_in);

View File

@ -42,6 +42,28 @@ static tree wrap_cleanups_r (tree *, int *, void *);
static bool is_admissible_throw_operand_or_catch_parameter (tree, bool,
tsubst_flags_t);
/* Initializes the node to std::terminate, which is used in exception
handling and contract handling. */
void
init_terminate_fn (void)
{
if (terminate_fn)
return;
tree tmp;
push_nested_namespace (std_node);
tmp = build_function_type_list (void_type_node, NULL_TREE);
terminate_fn = build_cp_library_fn_ptr ("terminate", tmp,
ECF_NOTHROW | ECF_NORETURN
| ECF_COLD);
gcc_checking_assert (TREE_THIS_VOLATILE (terminate_fn)
&& TREE_NOTHROW (terminate_fn));
pop_nested_namespace (std_node);
}
/* Sets up all the global eh stuff that needs to be initialized at the
start of compilation. */
@ -51,14 +73,7 @@ init_exception_processing (void)
tree tmp;
/* void std::terminate (); */
push_nested_namespace (std_node);
tmp = build_function_type_list (void_type_node, NULL_TREE);
terminate_fn = build_cp_library_fn_ptr ("terminate", tmp,
ECF_NOTHROW | ECF_NORETURN
| ECF_COLD);
gcc_checking_assert (TREE_THIS_VOLATILE (terminate_fn)
&& TREE_NOTHROW (terminate_fn));
pop_nested_namespace (std_node);
init_terminate_fn ();
/* void __cxa_call_unexpected(void *); */
tmp = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);

View File

@ -0,0 +1,27 @@
// check that contracts can be handled even when exceptions are disabled
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fno-exceptions " }
// { dg-output "contract violation in function f at .* a<5" }
#include <exception>
#include <cstdlib>
int terminate_called = 0;
void my_term()
{
std::exit(0);
}
void f(int a)
[[ pre : a<5 ]]
{
}
int
main ()
{
std::set_terminate (my_term);
f(3);
f(10);
}