#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "rtl.h"
#include "expr.h"
#include "libfuncs.h"
#include "cp-tree.h"
#include "flags.h"
#include "output.h"
#include "except.h"
#include "toplev.h"
#include "tree-inline.h"
#include "tree-iterator.h"
static void push_eh_cleanup (tree);
static tree prepare_eh_type (tree);
static tree build_eh_type_type (tree);
static tree do_begin_catch (tree);
static int dtor_nothrow (tree);
static tree do_end_catch (tree);
static bool decl_is_java_type (tree decl, int err);
static void initialize_handler_parm (tree, tree);
static tree do_allocate_exception (tree);
static tree wrap_cleanups_r (tree *, int *, void *);
static int complete_ptr_ref_or_void_ptr_p (tree, tree);
static bool is_admissible_throw_operand (tree);
static int can_convert_eh (tree, tree);
static tree cp_protect_cleanup_actions (void);
void
init_exception_processing (void)
{
tree tmp;
push_namespace (std_identifier);
tmp = build_function_type (void_type_node, void_list_node);
terminate_node = build_cp_library_fn_ptr ("terminate", tmp);
TREE_THIS_VOLATILE (terminate_node) = 1;
TREE_NOTHROW (terminate_node) = 1;
pop_namespace ();
tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
tmp = build_function_type (void_type_node, tmp);
call_unexpected_node
= push_throw_library_fn (get_identifier ("__cxa_call_unexpected"), tmp);
eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS
? "__gxx_personality_sj0"
: "__gxx_personality_v0");
lang_eh_runtime_type = build_eh_type_type;
lang_protect_cleanup_actions = &cp_protect_cleanup_actions;
}
static tree
cp_protect_cleanup_actions (void)
{
return build_call (terminate_node, NULL_TREE);
}
static tree
prepare_eh_type (tree type)
{
if (type == NULL_TREE)
return type;
if (type == error_mark_node)
return error_mark_node;
type = non_reference (type);
type = TYPE_MAIN_VARIANT (type);
return type;
}
tree
eh_type_info (tree type)
{
tree exp;
if (type == NULL_TREE || type == error_mark_node)
return type;
if (decl_is_java_type (type, 0))
exp = build_java_class_ref (TREE_TYPE (type));
else
exp = get_tinfo_decl (type);
return exp;
}
static tree
build_eh_type_type (tree type)
{
tree exp = eh_type_info (type);
if (!exp)
return NULL;
mark_used (exp);
return convert (ptr_type_node, build_address (exp));
}
tree
build_exc_ptr (void)
{
return build0 (EXC_PTR_EXPR, ptr_type_node);
}
static tree
do_get_exception_ptr (void)
{
tree fn;
fn = get_identifier ("__cxa_get_exception_ptr");
if (!get_global_value_if_present (fn, &fn))
{
tree tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
fn = push_library_fn (fn, build_function_type (ptr_type_node, tmp));
}
return build_function_call (fn, tree_cons (NULL_TREE, build_exc_ptr (),
NULL_TREE));
}
tree objcp_build_eh_type_type (tree type)
{
return build_eh_type_type (type);
}
static tree
do_begin_catch (tree type)
{
tree fn;
if (c_dialect_objc () && objc2_valid_objc_catch_type (type))
fn = get_identifier ("objc_begin_catch");
else
fn = get_identifier ("__cxa_begin_catch");
if (!get_global_value_if_present (fn, &fn))
{
tree tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
fn = push_library_fn (fn, build_function_type (ptr_type_node, tmp));
}
return build_function_call (fn, tree_cons (NULL_TREE, build_exc_ptr (),
NULL_TREE));
}
static int
dtor_nothrow (tree type)
{
if (type == NULL_TREE)
return 0;
if (!CLASS_TYPE_P (type))
return 1;
if (CLASSTYPE_LAZY_DESTRUCTOR (type))
lazily_declare_fn (sfk_destructor, type);
return TREE_NOTHROW (CLASSTYPE_DESTRUCTORS (type));
}
static tree
do_end_catch (tree type)
{
tree fn, cleanup;
if (c_dialect_objc () && objc2_valid_objc_catch_type (type))
fn = get_identifier ("objc_end_catch");
else
fn = get_identifier ("__cxa_end_catch");
if (!get_global_value_if_present (fn, &fn))
{
fn = push_void_library_fn (fn, void_list_node);
TREE_NOTHROW (fn) = 0;
}
cleanup = build_function_call (fn, NULL_TREE);
TREE_NOTHROW (cleanup) = dtor_nothrow (type);
return cleanup;
}
static void
push_eh_cleanup (tree type)
{
finish_decl_cleanup (NULL_TREE, do_end_catch (type));
}
static bool
decl_is_java_type (tree decl, int err)
{
bool r = (TREE_CODE (decl) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE
&& TYPE_FOR_JAVA (TREE_TYPE (decl)));
if (err)
{
if (TREE_CODE (decl) == REFERENCE_TYPE
&& TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE
&& TYPE_FOR_JAVA (TREE_TYPE (decl)))
{
error ("type %qT is disallowed in Java %<throw%> or %<catch%>",
decl);
}
if (r)
{
tree jthrow_node
= IDENTIFIER_GLOBAL_VALUE (get_identifier ("jthrowable"));
if (jthrow_node == NULL_TREE)
fatal_error
("call to Java %<catch%> or %<throw%> with %<jthrowable%> undefined");
jthrow_node = TREE_TYPE (TREE_TYPE (jthrow_node));
if (! DERIVED_FROM_P (jthrow_node, TREE_TYPE (decl)))
{
error ("type %qT is not derived from %<java::lang::Throwable%>",
TREE_TYPE (decl));
}
}
}
return r;
}
void
choose_personality_routine (enum languages lang)
{
static enum {
chose_none,
chose_cpp,
chose_java,
gave_error
} state;
switch (state)
{
case gave_error:
return;
case chose_cpp:
if (lang != lang_cplusplus)
goto give_error;
return;
case chose_java:
if (lang != lang_java)
goto give_error;
return;
case chose_none:
;
}
switch (lang)
{
case lang_cplusplus:
state = chose_cpp;
break;
case lang_java:
state = chose_java;
eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS
? "__gcj_personality_sj0"
: "__gcj_personality_v0");
break;
default:
gcc_unreachable ();
}
return;
give_error:
error ("mixing C++ and Java catches in a single translation unit");
state = gave_error;
}
static void
initialize_handler_parm (tree decl, tree exp)
{
tree init;
tree init_type;
TREE_USED (decl) = 1;
init_type = TREE_TYPE (decl);
if (!POINTER_TYPE_P (init_type))
init_type = build_reference_type (init_type);
choose_personality_routine (decl_is_java_type (init_type, 0)
? lang_java : lang_cplusplus);
if (TREE_CODE (init_type) == REFERENCE_TYPE
&& TYPE_PTR_P (TREE_TYPE (init_type)))
exp = build_unary_op (ADDR_EXPR, exp, 1);
exp = ocp_convert (init_type, exp, CONV_IMPLICIT|CONV_FORCE_TEMP, 0);
init = convert_from_reference (exp);
if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl)))
{
init = ocp_convert (TREE_TYPE (decl), init,
CONV_IMPLICIT|CONV_FORCE_TEMP, 0);
init = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (init), init);
}
DECL_INITIAL (decl) = error_mark_node;
decl = pushdecl (decl);
start_decl_1 (decl);
cp_finish_decl (decl, init, NULL_TREE,
LOOKUP_ONLYCONVERTING|DIRECT_BIND);
}
tree
expand_start_catch_block (tree decl)
{
tree exp;
tree type;
if (! doing_eh (1))
return NULL_TREE;
if (decl && !complete_ptr_ref_or_void_ptr_p (TREE_TYPE (decl), NULL_TREE))
decl = NULL_TREE;
if (decl)
type = prepare_eh_type (TREE_TYPE (decl));
else
type = NULL_TREE;
if (decl && decl_is_java_type (type, 1))
{
exp = build_exc_ptr ();
exp = build1 (NOP_EXPR, build_pointer_type (type), exp);
exp = build2 (MINUS_EXPR, TREE_TYPE (exp), exp,
TYPE_SIZE_UNIT (TREE_TYPE (exp)));
exp = build_indirect_ref (exp, NULL);
initialize_handler_parm (decl, exp);
return type;
}
push_eh_cleanup (type);
if (decl == NULL)
finish_expr_stmt (do_begin_catch (NULL_TREE));
else if (flag_use_cxa_get_exception_ptr
&& TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl)))
{
exp = do_get_exception_ptr ();
initialize_handler_parm (decl, exp);
finish_expr_stmt (do_begin_catch (type));
}
else
{
tree init = do_begin_catch (type);
exp = create_temporary_var (ptr_type_node);
DECL_REGISTER (exp) = 1;
cp_finish_decl (exp, init, NULL_TREE, LOOKUP_ONLYCONVERTING);
finish_expr_stmt (build_modify_expr (exp, INIT_EXPR, init));
initialize_handler_parm (decl, exp);
}
return type;
}
void
expand_end_catch_block (void)
{
if (! doing_eh (1))
return;
if (in_function_try_handler
&& (DECL_CONSTRUCTOR_P (current_function_decl)
|| DECL_DESTRUCTOR_P (current_function_decl)))
finish_expr_stmt (build_throw (NULL_TREE));
}
tree
begin_eh_spec_block (void)
{
tree r = build_stmt (EH_SPEC_BLOCK, NULL_TREE, NULL_TREE);
add_stmt (r);
EH_SPEC_STMTS (r) = push_stmt_list ();
return r;
}
void
finish_eh_spec_block (tree raw_raises, tree eh_spec_block)
{
tree raises;
EH_SPEC_STMTS (eh_spec_block) = pop_stmt_list (EH_SPEC_STMTS (eh_spec_block));
for (raises = NULL_TREE;
raw_raises && TREE_VALUE (raw_raises);
raw_raises = TREE_CHAIN (raw_raises))
{
tree type = prepare_eh_type (TREE_VALUE (raw_raises));
tree tinfo = eh_type_info (type);
mark_used (tinfo);
raises = tree_cons (NULL_TREE, type, raises);
}
EH_SPEC_RAISES (eh_spec_block) = raises;
}
static tree
do_allocate_exception (tree type)
{
tree fn;
fn = get_identifier ("__cxa_allocate_exception");
if (!get_global_value_if_present (fn, &fn))
{
tree tmp = tree_cons (NULL_TREE, size_type_node, void_list_node);
fn = push_library_fn (fn, build_function_type (ptr_type_node, tmp));
}
return build_function_call (fn, tree_cons (NULL_TREE, size_in_bytes (type),
NULL_TREE));
}
static tree
do_free_exception (tree ptr)
{
tree fn;
fn = get_identifier ("__cxa_free_exception");
if (!get_global_value_if_present (fn, &fn))
{
fn = push_void_library_fn (fn, tree_cons (NULL_TREE, ptr_type_node,
void_list_node));
}
return build_function_call (fn, tree_cons (NULL_TREE, ptr, NULL_TREE));
}
static tree
wrap_cleanups_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
tree exp = *tp;
tree cleanup;
if (TYPE_P (exp))
{
*walk_subtrees = 0;
return NULL_TREE;
}
if (TREE_CODE (exp) != TARGET_EXPR)
return NULL_TREE;
cleanup = TARGET_EXPR_CLEANUP (exp);
if (cleanup)
{
cleanup = build1 (MUST_NOT_THROW_EXPR, void_type_node, cleanup);
TARGET_EXPR_CLEANUP (exp) = cleanup;
}
return NULL_TREE;
}
tree
build_throw (tree exp)
{
tree fn;
if (exp == error_mark_node)
return exp;
if (processing_template_decl)
{
current_function_returns_abnormally = 1;
return build_min (THROW_EXPR, void_type_node, exp);
}
if (exp == null_node)
warning ("throwing NULL, which has integral, not pointer type");
if (exp != NULL_TREE)
{
if (!is_admissible_throw_operand (exp))
return error_mark_node;
}
if (! doing_eh (1))
return error_mark_node;
if (exp && decl_is_java_type (TREE_TYPE (exp), 1))
{
tree fn = get_identifier ("_Jv_Throw");
if (!get_global_value_if_present (fn, &fn))
{
tree tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
tmp = build_function_type (ptr_type_node, tmp);
fn = push_throw_library_fn (fn, tmp);
}
else if (really_overloaded_fn (fn))
{
error ("%qD should never be overloaded", fn);
return error_mark_node;
}
fn = OVL_CURRENT (fn);
exp = build_function_call (fn, tree_cons (NULL_TREE, exp, NULL_TREE));
}
else if (c_dialect_objc ()
&& exp && objc2_valid_objc_catch_type (TREE_TYPE (exp)))
return objc2_build_throw_call (exp);
else if (exp)
{
tree throw_type;
tree cleanup;
tree object, ptr;
tree tmp;
tree temp_expr, allocate_expr;
bool elided;
if (!cleanup_type)
{
tmp = void_list_node;
tmp = tree_cons (NULL_TREE, ptr_type_node, tmp);
tmp = build_function_type (void_type_node, tmp);
cleanup_type = build_pointer_type (tmp);
}
fn = get_identifier ("__cxa_throw");
if (!get_global_value_if_present (fn, &fn))
{
tmp = void_list_node;
tmp = tree_cons (NULL_TREE, cleanup_type, tmp);
tmp = tree_cons (NULL_TREE, ptr_type_node, tmp);
tmp = tree_cons (NULL_TREE, ptr_type_node, tmp);
tmp = build_function_type (void_type_node, tmp);
fn = push_throw_library_fn (fn, tmp);
}
exp = decay_conversion (exp);
allocate_expr = do_allocate_exception (TREE_TYPE (exp));
allocate_expr = get_target_expr (allocate_expr);
ptr = TARGET_EXPR_SLOT (allocate_expr);
object = build1 (NOP_EXPR, build_pointer_type (TREE_TYPE (exp)), ptr);
object = build_indirect_ref (object, NULL);
elided = (TREE_CODE (exp) == TARGET_EXPR);
exp = build_init (object, exp, LOOKUP_ONLYCONVERTING);
if (exp == error_mark_node)
{
error (" in thrown expression");
return error_mark_node;
}
temp_expr = NULL_TREE;
stabilize_init (exp, &temp_expr);
exp = build1 (CLEANUP_POINT_EXPR, void_type_node, exp);
if (elided)
exp = build2 (TRY_CATCH_EXPR, void_type_node, exp,
do_free_exception (ptr));
else
exp = build1 (MUST_NOT_THROW_EXPR, void_type_node, exp);
exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), allocate_expr, exp);
if (temp_expr)
{
walk_tree_without_duplicates (&temp_expr, wrap_cleanups_r, 0);
exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), temp_expr, exp);
exp = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (exp), exp);
}
throw_type = build_eh_type_type (prepare_eh_type (TREE_TYPE (object)));
if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (object)))
{
cleanup = lookup_fnfields (TYPE_BINFO (TREE_TYPE (object)),
complete_dtor_identifier, 0);
cleanup = BASELINK_FUNCTIONS (cleanup);
mark_used (cleanup);
cxx_mark_addressable (cleanup);
cleanup = build1 (ADDR_EXPR, cleanup_type, cleanup);
}
else
cleanup = build_int_cst (cleanup_type, 0);
tmp = tree_cons (NULL_TREE, cleanup, NULL_TREE);
tmp = tree_cons (NULL_TREE, throw_type, tmp);
tmp = tree_cons (NULL_TREE, ptr, tmp);
tmp = build_function_call (fn, tmp);
exp = build2 (COMPOUND_EXPR, TREE_TYPE (tmp), exp, tmp);
}
else
{
tree fn = get_identifier ("__cxa_rethrow");
if (!get_global_value_if_present (fn, &fn))
{
fn = push_throw_library_fn
(fn, build_function_type (void_type_node, void_list_node));
}
exp = build_function_call (fn, NULL_TREE);
}
exp = build1 (THROW_EXPR, void_type_node, exp);
return exp;
}
static int
complete_ptr_ref_or_void_ptr_p (tree type, tree from)
{
int is_ptr;
type = complete_type_or_else (type, from);
if (!type)
return 0;
is_ptr = TREE_CODE (type) == POINTER_TYPE;
if (is_ptr || TREE_CODE (type) == REFERENCE_TYPE)
{
tree core = TREE_TYPE (type);
if (is_ptr && VOID_TYPE_P (core))
;
else if (!complete_type_or_else (core, from))
return 0;
}
return 1;
}
static bool
is_admissible_throw_operand (tree expr)
{
tree type = TREE_TYPE (expr);
if (!complete_ptr_ref_or_void_ptr_p (type, expr))
return false;
else if (CLASS_TYPE_P (type) && CLASSTYPE_PURE_VIRTUALS (type))
{
error ("expression %qE of abstract class type %qT cannot "
"be used in throw-expression", expr, type);
return false;
}
return true;
}
#include "cfns.h"
int
nothrow_libfn_p (tree fn)
{
tree id;
if (TREE_PUBLIC (fn)
&& DECL_EXTERNAL (fn)
&& DECL_NAMESPACE_SCOPE_P (fn)
&& DECL_EXTERN_C_P (fn))
;
else
return 0;
id = DECL_NAME (fn);
return !!libc_name_p (IDENTIFIER_POINTER (id), IDENTIFIER_LENGTH (id));
}
static int
can_convert_eh (tree to, tree from)
{
to = non_reference (to);
from = non_reference (from);
if (TREE_CODE (to) == POINTER_TYPE && TREE_CODE (from) == POINTER_TYPE)
{
to = TREE_TYPE (to);
from = TREE_TYPE (from);
if (! at_least_as_qualified_p (to, from))
return 0;
if (TREE_CODE (to) == VOID_TYPE)
return 1;
}
if (CLASS_TYPE_P (to) && CLASS_TYPE_P (from)
&& PUBLICLY_UNIQUELY_DERIVED_P (to, from))
return 1;
return 0;
}
static void
check_handlers_1 (tree master, tree_stmt_iterator i)
{
tree type = TREE_TYPE (master);
for (; !tsi_end_p (i); tsi_next (&i))
{
tree handler = tsi_stmt (i);
if (TREE_TYPE (handler) && can_convert_eh (type, TREE_TYPE (handler)))
{
warning ("%Hexception of type %qT will be caught",
EXPR_LOCUS (handler), TREE_TYPE (handler));
warning ("%H by earlier handler for %qT",
EXPR_LOCUS (master), type);
break;
}
}
}
void
check_handlers (tree handlers)
{
tree_stmt_iterator i;
if (TREE_CODE (handlers) != STATEMENT_LIST)
return;
i = tsi_start (handlers);
if (!tsi_end_p (i))
while (1)
{
tree handler = tsi_stmt (i);
tsi_next (&i);
if (tsi_end_p (i))
break;
if (TREE_TYPE (handler) == NULL_TREE)
pedwarn ("%H%<...%> handler must be the last handler for"
" its try block", EXPR_LOCUS (handler));
else
check_handlers_1 (handler, i);
}
}