/* Handle exceptional things in C++.
Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
Contributed by Michael Tiemann <tiemann@cygnus.com>
Rewritten by Mike Stump <mrs@cygnus.com>, based upon an
initial re-implementation courtesy Tad Hunt.
#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 bool decl_is_java_type (tree decl, int err);
static void initialize_handler_parm (tree, tree);
static tree do_allocate_exception (tree);
-static tree stabilize_throw_expr (tree, 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 void check_handlers_1 (tree, tree);
static tree cp_protect_cleanup_actions (void);
/* Sets up all the global eh stuff that needs to be initialized at the
return type;
}
-/* Build the address of a typeinfo decl for use in the runtime
- matching field of the exception model. */
-
-static tree
-build_eh_type_type (tree type)
+/* Return the type info for TYPE as used by EH machinery. */
+tree
+eh_type_info (tree type)
{
tree exp;
else
exp = get_tinfo_decl (type);
+ return exp;
+}
+
+/* Build the address of a typeinfo decl for use in the runtime
+ matching field of the exception model. */
+
+static tree
+build_eh_type_type (tree type)
+{
+ tree exp = eh_type_info (type);
+
+ if (!exp)
+ return NULL;
+
mark_used (exp);
- exp = build1 (ADDR_EXPR, ptr_type_node, exp);
- return exp;
+ return convert (ptr_type_node, build_address (exp));
}
tree
build_exc_ptr (void)
{
- return build (EXC_PTR_EXPR, ptr_type_node);
+ return build0 (EXC_PTR_EXPR, ptr_type_node);
}
/* Build up a call to __cxa_begin_catch, to tell the runtime that the
tree fn;
fn = get_identifier ("__cxa_begin_catch");
- if (IDENTIFIER_GLOBAL_VALUE (fn))
- fn = IDENTIFIER_GLOBAL_VALUE (fn);
- else
+ if (!get_global_value_if_present (fn, &fn))
{
/* Declare void* __cxa_begin_catch (void *). */
tree tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
tree fn, cleanup;
fn = get_identifier ("__cxa_end_catch");
- if (IDENTIFIER_GLOBAL_VALUE (fn))
- fn = IDENTIFIER_GLOBAL_VALUE (fn);
- else
+ if (!get_global_value_if_present (fn, &fn))
{
/* Declare void __cxa_end_catch (). */
fn = push_void_library_fn (fn, void_list_node);
&& TYPE_FOR_JAVA (TREE_TYPE (decl)))
{
/* Can't throw a reference. */
- error ("type `%T' is disallowed in Java `throw' or `catch'",
- decl);
+ error ("type %qT is disallowed in Java %<throw%> or %<catch%>",
+ decl);
}
if (r)
if (jthrow_node == NULL_TREE)
fatal_error
- ("call to Java `catch' or `throw' with `jthrowable' undefined");
+ ("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)))
{
/* Thrown object must be a Throwable. */
- error ("type `%T' is not derived from `java::lang::Throwable'",
- TREE_TYPE (decl));
+ error ("type %qT is not derived from %<java::lang::Throwable%>",
+ TREE_TYPE (decl));
}
}
}
return;
case chose_none:
- ; /* proceed to language selection */
+ ; /* Proceed to language selection. */
}
switch (lang)
break;
default:
- abort ();
+ gcc_unreachable ();
}
return;
adjusted by value from __cxa_begin_catch. Others are returned by
reference. */
init_type = TREE_TYPE (decl);
- if (! TYPE_PTR_P (init_type)
- && TREE_CODE (init_type) != REFERENCE_TYPE)
+ if (!POINTER_TYPE_P (init_type))
init_type = build_reference_type (init_type);
choose_personality_routine (decl_is_java_type (init_type, 0)
generic exception header. */
init = build_exc_ptr ();
init = build1 (NOP_EXPR, build_pointer_type (type), init);
- init = build (MINUS_EXPR, TREE_TYPE (init), init,
- TYPE_SIZE_UNIT (TREE_TYPE (init)));
+ init = build2 (MINUS_EXPR, TREE_TYPE (init), init,
+ TYPE_SIZE_UNIT (TREE_TYPE (init)));
init = build_indirect_ref (init, NULL);
is_java = true;
}
{
tree r = build_stmt (EH_SPEC_BLOCK, NULL_TREE, NULL_TREE);
add_stmt (r);
+ EH_SPEC_STMTS (r) = push_stmt_list ();
return r;
}
{
tree raises;
- RECHAIN_STMTS (eh_spec_block, EH_SPEC_STMTS (eh_spec_block));
+ EH_SPEC_STMTS (eh_spec_block) = pop_stmt_list (EH_SPEC_STMTS (eh_spec_block));
/* Strip cv quals, etc, from the specification types. */
for (raises = NULL_TREE;
raw_raises && TREE_VALUE (raw_raises);
raw_raises = TREE_CHAIN (raw_raises))
- raises = tree_cons (NULL_TREE, prepare_eh_type (TREE_VALUE (raw_raises)),
- 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;
}
tree fn;
fn = get_identifier ("__cxa_allocate_exception");
- if (IDENTIFIER_GLOBAL_VALUE (fn))
- fn = IDENTIFIER_GLOBAL_VALUE (fn);
- else
+ if (!get_global_value_if_present (fn, &fn))
{
/* Declare void *__cxa_allocate_exception(size_t). */
tree tmp = tree_cons (NULL_TREE, size_type_node, void_list_node);
NULL_TREE));
}
-#if 0
/* Call __cxa_free_exception from a cleanup. This is never invoked
directly, but see the comment for stabilize_throw_expr. */
tree fn;
fn = get_identifier ("__cxa_free_exception");
- if (IDENTIFIER_GLOBAL_VALUE (fn))
- fn = IDENTIFIER_GLOBAL_VALUE (fn);
- else
+ if (!get_global_value_if_present (fn, &fn))
{
/* Declare void __cxa_free_exception (void *). */
fn = push_void_library_fn (fn, tree_cons (NULL_TREE, ptr_type_node,
return build_function_call (fn, tree_cons (NULL_TREE, ptr, NULL_TREE));
}
-#endif
/* Wrap all cleanups for TARGET_EXPRs in MUST_NOT_THROW_EXPR.
Called from build_throw via walk_tree_without_duplicates. */
return NULL_TREE;
}
-/* Like stabilize_expr, but specifically for a thrown expression. When
- throwing a temporary class object, we want to construct it directly into
- the thrown exception, so we look past the TARGET_EXPR and stabilize the
- arguments of the call instead.
-
- The case where EXP is a call to a function returning a class is a bit of
- a grey area in the standard; it's unclear whether or not it should be
- allowed to throw. I'm going to say no, as that allows us to optimize
- this case without worrying about deallocating the exception object if it
- does. The alternatives would be either not optimizing this case, or
- wrapping the initialization in a TRY_CATCH_EXPR to call do_free_exception
- rather than in a MUST_NOT_THROW_EXPR, for this case only. */
-
-static tree
-stabilize_throw_expr (tree exp, tree *initp)
-{
- tree init_expr;
-
- if (TREE_CODE (exp) == TARGET_EXPR
- && TREE_CODE (TARGET_EXPR_INITIAL (exp)) == AGGR_INIT_EXPR
- && flag_elide_constructors)
- {
- tree aggr_init = AGGR_INIT_EXPR_CHECK (TARGET_EXPR_INITIAL (exp));
- tree args = TREE_OPERAND (aggr_init, 1);
- tree newargs = NULL_TREE;
- tree *p = &newargs;
-
- init_expr = void_zero_node;
- for (; args; args = TREE_CHAIN (args))
- {
- tree arg = TREE_VALUE (args);
- tree arg_init_expr;
-
- arg = stabilize_expr (arg, &arg_init_expr);
-
- if (TREE_SIDE_EFFECTS (arg_init_expr))
- init_expr = build (COMPOUND_EXPR, void_type_node, init_expr,
- arg_init_expr);
- *p = tree_cons (NULL_TREE, arg, NULL_TREE);
- p = &TREE_CHAIN (*p);
- }
- TREE_OPERAND (aggr_init, 1) = newargs;
- }
- else
- {
- exp = stabilize_expr (exp, &init_expr);
- }
-
- *initp = init_expr;
- return exp;
-}
-
/* Build a throw expression. */
tree
return exp;
if (processing_template_decl)
- return build_min (THROW_EXPR, void_type_node, exp);
+ {
+ 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 && decl_is_java_type (TREE_TYPE (exp), 1))
{
tree fn = get_identifier ("_Jv_Throw");
- if (IDENTIFIER_GLOBAL_VALUE (fn))
- fn = IDENTIFIER_GLOBAL_VALUE (fn);
- else
+ if (!get_global_value_if_present (fn, &fn))
{
/* Declare void _Jv_Throw (void *). */
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 (exp)
tree object, ptr;
tree tmp;
tree temp_expr, allocate_expr;
+ bool elided;
+ /* The CLEANUP_TYPE is the internal type of a destructor. */
+ 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 (IDENTIFIER_GLOBAL_VALUE (fn))
- fn = IDENTIFIER_GLOBAL_VALUE (fn);
- else
+ if (!get_global_value_if_present (fn, &fn))
{
- /* The CLEANUP_TYPE is the internal type of a destructor. */
- if (cleanup_type == NULL_TREE)
- {
- 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);
- }
-
/* Declare void __cxa_throw (void*, void*, void (*)(void*)). */
/* ??? Second argument is supposed to be "std::type_info*". */
tmp = void_list_node;
tmp = build_function_type (void_type_node, tmp);
fn = push_throw_library_fn (fn, tmp);
}
-
+
/* throw expression */
/* First, decay it. */
exp = decay_conversion (exp);
the call to __cxa_allocate_exception first (which doesn't
matter, since it can't throw). */
- /* Pre-evaluate the thrown expression first, since if we allocated
- the space first we would have to deal with cleaning it up if
- evaluating this expression throws. */
- exp = stabilize_throw_expr (exp, &temp_expr);
-
/* Allocate the space for the exception. */
allocate_expr = do_allocate_exception (TREE_TYPE (exp));
allocate_expr = get_target_expr (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);
+
/* And initialize the exception object. */
exp = build_init (object, exp, LOOKUP_ONLYCONVERTING);
if (exp == error_mark_node)
return error_mark_node;
}
- exp = build1 (MUST_NOT_THROW_EXPR, void_type_node, exp);
+ /* Pre-evaluate the thrown expression first, since if we allocated
+ the space first we would have to deal with cleaning it up if
+ evaluating this expression throws.
+
+ The case where EXP the initializer is a call to a constructor or a
+ function returning a class is a bit of a grey area in the
+ standard; it's unclear whether or not it should be allowed to
+ throw. We used to say no, as that allowed us to optimize this
+ case without worrying about deallocating the exception object if
+ it does. But that conflicted with expectations (PR 13944) and the
+ EDG compiler; now we wrap the initialization in a TRY_CATCH_EXPR
+ to call do_free_exception rather than in a MUST_NOT_THROW_EXPR,
+ for this case only.
+
+ Note that we don't check the return value from stabilize_init
+ because it will only return false in cases where elided is true,
+ and therefore we don't need to work around the failure to
+ preevaluate. */
+ temp_expr = NULL_TREE;
+ stabilize_init (exp, &temp_expr);
+
+ 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);
+
/* Prepend the allocation. */
- exp = build (COMPOUND_EXPR, TREE_TYPE (exp), allocate_expr, exp);
- if (temp_expr != void_zero_node)
+ exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), allocate_expr, exp);
+ if (temp_expr)
{
/* Prepend the calculation of the throw expression. Also, force
any cleanups from the expression to be evaluated here so that
them in MUST_NOT_THROW_EXPR, since they are run after the
exception object is initialized. */
walk_tree_without_duplicates (&temp_expr, wrap_cleanups_r, 0);
- exp = build (COMPOUND_EXPR, TREE_TYPE (exp), temp_expr, exp);
+ exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), temp_expr, exp);
exp = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (exp), exp);
}
cleanup = build1 (ADDR_EXPR, cleanup_type, cleanup);
}
else
- {
- cleanup = build_int_2 (0, 0);
- TREE_TYPE (cleanup) = cleanup_type;
- }
-
+ 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);
/* Tack on the initialization stuff. */
- exp = build (COMPOUND_EXPR, TREE_TYPE (tmp), exp, tmp);
+ exp = build2 (COMPOUND_EXPR, TREE_TYPE (tmp), exp, tmp);
}
else
{
/* Rethrow current exception. */
tree fn = get_identifier ("__cxa_rethrow");
- if (IDENTIFIER_GLOBAL_VALUE (fn))
- fn = IDENTIFIER_GLOBAL_VALUE (fn);
- else
+ if (!get_global_value_if_present (fn, &fn))
{
/* Declare void __cxa_rethrow (void). */
fn = push_throw_library_fn
conversion. */
else if (CLASS_TYPE_P (type) && CLASSTYPE_PURE_VIRTUALS (type))
{
- error ("expression '%E' of abstract class type '%T' cannot be used in throw-expression", expr, type);
+ error ("expression %qE of abstract class type %qT cannot "
+ "be used in throw-expression", expr, type);
return false;
}
/* Can't be a C library function. */
return 0;
- id = DECL_ASSEMBLER_NAME (fn);
+ /* Being a C library function, DECL_ASSEMBLER_NAME == DECL_NAME
+ unless the system headers are playing rename tricks, and if
+ they are, we don't want to be confused by them. */
+ id = DECL_NAME (fn);
return !!libc_name_p (IDENTIFIER_POINTER (id), IDENTIFIER_LENGTH (id));
}
if (TREE_CODE (to) == VOID_TYPE)
return 1;
- /* else fall through */
+ /* Else fall through. */
}
if (CLASS_TYPE_P (to) && CLASS_TYPE_P (from)
return 0;
}
-/* Check whether any of HANDLERS are shadowed by another handler accepting
- TYPE. Note that the shadowing may not be complete; even if an exception
- of type B would be caught by a handler for A, there could be a derived
- class C for which A is an ambiguous base but B is not, so the handler
- for B would catch an exception of type C. */
+/* Check whether any of the handlers in I are shadowed by another handler
+ accepting TYPE. Note that the shadowing may not be complete; even if
+ an exception of type B would be caught by a handler for A, there could
+ be a derived class C for which A is an ambiguous base but B is not, so
+ the handler for B would catch an exception of type C. */
static void
-check_handlers_1 (tree master, tree handlers)
+check_handlers_1 (tree master, tree_stmt_iterator i)
{
tree type = TREE_TYPE (master);
- tree handler;
- for (handler = handlers; handler; handler = TREE_CHAIN (handler))
- if (TREE_TYPE (handler)
- && can_convert_eh (type, TREE_TYPE (handler)))
- {
- input_line = STMT_LINENO (handler);
- warning ("exception of type `%T' will be caught",
- TREE_TYPE (handler));
- input_line = STMT_LINENO (master);
- warning (" by earlier handler for `%T'", type);
- break;
- }
+ 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;
+ }
+ }
}
-/* Given a chain of HANDLERs, make sure that they're OK. */
+/* Given a STATEMENT_LIST of HANDLERs, make sure that they're OK. */
void
check_handlers (tree handlers)
{
- tree handler;
- int save_line = input_line;
-
- for (handler = handlers; handler; handler = TREE_CHAIN (handler))
- {
- if (TREE_CHAIN (handler) == NULL_TREE)
- /* No more handlers; nothing to shadow. */;
- else if (TREE_TYPE (handler) == NULL_TREE)
- {
- input_line = STMT_LINENO (handler);
- pedwarn
- ("`...' handler must be the last handler for its try block");
- }
- else
- check_handlers_1 (handler, TREE_CHAIN (handler));
- }
- input_line = save_line;
+ tree_stmt_iterator i;
+
+ /* If we don't have a STATEMENT_LIST, then we've just got one
+ handler, and thus nothing to warn about. */
+ 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);
+
+ /* No more handlers; nothing to shadow. */
+ 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);
+ }
}