/* Handle exceptional things in C++.
Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010
+ 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.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to
-the Free Software Foundation, 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "tree-inline.h"
#include "tree-iterator.h"
#include "target.h"
+#include "gimple.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 (void);
static int dtor_nothrow (tree);
static tree do_end_catch (tree);
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");
- if (targetm.arm_eabi_unwinder)
- unwind_resume_libfunc = init_one_libfunc ("__cxa_end_cleanup");
- else
- default_init_unwind_resume_libfunc ();
-
- lang_eh_runtime_type = build_eh_type_type;
lang_protect_cleanup_actions = &cp_protect_cleanup_actions;
}
When the destruction of an object during stack unwinding exits
using an exception ... void terminate(); is called. */
- return build_call (terminate_node, NULL_TREE);
+ return terminate_node;
}
static tree
/* Peel off cv qualifiers. */
type = TYPE_MAIN_VARIANT (type);
+ /* Functions and arrays decay to pointers. */
+ type = type_decays_to (type);
+
return type;
}
/* Build the address of a typeinfo decl for use in the runtime
matching field of the exception model. */
-static tree
+tree
build_eh_type_type (tree type)
{
tree exp = eh_type_info (type);
tree
build_exc_ptr (void)
{
- return build0 (EXC_PTR_EXPR, ptr_type_node);
+ return build_call_n (built_in_decls [BUILT_IN_EH_POINTER],
+ 1, integer_zero_node);
+}
+
+/* Declare a function NAME, returning RETURN_TYPE, taking a single
+ parameter PARM_TYPE, with an empty exception specification.
+
+ Note that the C++ ABI document does not have a throw-specifier on
+ the routines declared below via this function. The declarations
+ are consistent with the actual implementations in libsupc++. */
+
+static tree
+declare_nothrow_library_fn (tree name, tree return_type, tree parm_type)
+{
+ tree tmp = tree_cons (NULL_TREE, parm_type, void_list_node);
+ return push_library_fn (name, build_function_type (return_type, tmp),
+ empty_except_spec);
}
/* Build up a call to __cxa_get_exception_ptr so that we can build a
fn = get_identifier ("__cxa_get_exception_ptr");
if (!get_global_value_if_present (fn, &fn))
{
- /* Declare void* __cxa_get_exception_ptr (void *). */
- tree tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
- fn = push_library_fn (fn, build_function_type (ptr_type_node, tmp));
+ /* Declare void* __cxa_get_exception_ptr (void *) throw(). */
+ fn = declare_nothrow_library_fn (fn, ptr_type_node, ptr_type_node);
}
- return build_function_call (fn, tree_cons (NULL_TREE, build_exc_ptr (),
- NULL_TREE));
+ return cp_build_function_call (fn, tree_cons (NULL_TREE, build_exc_ptr (),
+ NULL_TREE),
+ tf_warning_or_error);
}
/* Build up a call to __cxa_begin_catch, to tell the runtime that the
fn = get_identifier ("__cxa_begin_catch");
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);
- fn = push_library_fn (fn, build_function_type (ptr_type_node, tmp));
+ /* Declare void* __cxa_begin_catch (void *) throw(). */
+ fn = declare_nothrow_library_fn (fn, ptr_type_node, ptr_type_node);
}
- return build_function_call (fn, tree_cons (NULL_TREE, build_exc_ptr (),
- NULL_TREE));
+ return cp_build_function_call (fn, tree_cons (NULL_TREE, build_exc_ptr (),
+ NULL_TREE),
+ tf_warning_or_error);
}
/* Returns nonzero if cleaning up an exception of type TYPE (which can be
static int
dtor_nothrow (tree type)
{
- if (type == NULL_TREE)
+ if (type == NULL_TREE || type == error_mark_node)
return 0;
- if (!CLASS_TYPE_P (type))
+ if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
return 1;
if (CLASSTYPE_LAZY_DESTRUCTOR (type))
TREE_NOTHROW (fn) = 0;
}
- cleanup = build_function_call (fn, NULL_TREE);
+ cleanup = cp_build_function_call (fn, NULL_TREE, tf_warning_or_error);
TREE_NOTHROW (cleanup) = dtor_nothrow (type);
return cleanup;
/* Select the personality routine to be used for exception handling,
or issue an error if we need two different ones in the same
translation unit.
- ??? At present eh_personality_libfunc is set to
+ ??? At present eh_personality_decl is set to
__gxx_personality_(sj|v)0 in init_exception_processing - should it
be done here instead? */
void
case lang_java:
state = chose_java;
- eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS
- ? "__gcj_personality_sj0"
- : "__gcj_personality_v0");
+ terminate_node = built_in_decls [BUILT_IN_ABORT];
+ pragma_java_exceptions = true;
break;
default:
/* Make sure we mark the catch param as used, otherwise we'll get a
warning about an unused ((anonymous)). */
TREE_USED (decl) = 1;
+ DECL_READ_P (decl) = 1;
/* Figure out the type that the initializer is. Pointers are returned
adjusted by value from __cxa_begin_catch. Others are returned by
pointer catch parm with the address of the temporary. */
if (TREE_CODE (init_type) == REFERENCE_TYPE
&& TYPE_PTR_P (TREE_TYPE (init_type)))
- exp = build_unary_op (ADDR_EXPR, exp, 1);
+ exp = cp_build_unary_op (ADDR_EXPR, exp, 1, tf_warning_or_error);
exp = ocp_convert (init_type, exp, CONV_IMPLICIT|CONV_FORCE_TEMP, 0);
See also expand_default_init. */
init = ocp_convert (TREE_TYPE (decl), init,
CONV_IMPLICIT|CONV_FORCE_TEMP, 0);
+ /* Force cleanups now to avoid nesting problems with the
+ MUST_NOT_THROW_EXPR. */
+ init = fold_build_cleanup_point_expr (TREE_TYPE (init), init);
init = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (init), init);
}
- /* Let `cp_finish_decl' know that this initializer is ok. */
- DECL_INITIAL (decl) = error_mark_node;
decl = pushdecl (decl);
- start_decl_1 (decl);
+ start_decl_1 (decl, true);
cp_finish_decl (decl, init, /*init_const_expr_p=*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING|DIRECT_BIND);
}
expand_start_catch_block (tree decl)
{
tree exp;
- tree type;
+ tree type, init;
if (! doing_eh (1))
return NULL_TREE;
/* Make sure this declaration is reasonable. */
if (decl && !complete_ptr_ref_or_void_ptr_p (TREE_TYPE (decl), NULL_TREE))
- decl = NULL_TREE;
+ decl = error_mark_node;
if (decl)
type = prepare_eh_type (TREE_TYPE (decl));
generic exception header. */
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);
+ exp = build2 (POINTER_PLUS_EXPR, TREE_TYPE (exp), exp,
+ fold_build1_loc (input_location,
+ NEGATE_EXPR, sizetype,
+ TYPE_SIZE_UNIT (TREE_TYPE (exp))));
+ exp = cp_build_indirect_ref (exp, RO_NULL, tf_warning_or_error);
initialize_handler_parm (decl, exp);
return type;
}
/* Call __cxa_end_catch at the end of processing the exception. */
push_eh_cleanup (type);
+ init = do_begin_catch ();
+
/* If there's no decl at all, then all we need to do is make sure
to tell the runtime that we've begun handling the exception. */
- if (decl == NULL)
- finish_expr_stmt (do_begin_catch ());
+ if (decl == NULL || decl == error_mark_node || init == error_mark_node)
+ finish_expr_stmt (init);
/* If the C++ object needs constructing, we need to do that before
calling __cxa_begin_catch, so that std::uncaught_exception gets
the right value during the copy constructor. */
- else if (flag_use_cxa_get_exception_ptr
+ 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 ());
+ finish_expr_stmt (init);
}
/* Otherwise the type uses a bitwise copy, and we don't have to worry
copy with the return value of __cxa_end_catch instead. */
else
{
- tree init = do_begin_catch ();
- exp = create_temporary_var (ptr_type_node);
+ tree init_type = type;
+
+ /* Pointers are passed by values, everything else by reference. */
+ if (!TYPE_PTR_P (type))
+ init_type = build_pointer_type (type);
+ if (init_type != TREE_TYPE (init))
+ init = build1 (NOP_EXPR, init_type, init);
+ exp = create_temporary_var (init_type);
DECL_REGISTER (exp) = 1;
- cp_finish_decl (exp, init, /*init_const_expr=*/false,
+ cp_finish_decl (exp, init, /*init_const_expr=*/false,
NULL_TREE, LOOKUP_ONLYCONVERTING);
- finish_expr_stmt (build_modify_expr (exp, INIT_EXPR, init));
initialize_handler_parm (decl, exp);
}
tree
begin_eh_spec_block (void)
{
- tree r = build_stmt (EH_SPEC_BLOCK, NULL_TREE, NULL_TREE);
+ tree r;
+ /* A noexcept specification (or throw() with -fnothrow-opt) is a
+ MUST_NOT_THROW_EXPR. */
+ if (TYPE_NOEXCEPT_P (TREE_TYPE (current_function_decl)))
+ {
+ r = build_stmt (input_location, MUST_NOT_THROW_EXPR, NULL_TREE);
+ TREE_SIDE_EFFECTS (r) = 1;
+ }
+ else
+ r = build_stmt (input_location, EH_SPEC_BLOCK, NULL_TREE, NULL_TREE);
add_stmt (r);
- EH_SPEC_STMTS (r) = push_stmt_list ();
+ TREE_OPERAND (r, 0) = push_stmt_list ();
return r;
}
{
tree raises;
- EH_SPEC_STMTS (eh_spec_block) = pop_stmt_list (EH_SPEC_STMTS (eh_spec_block));
+ TREE_OPERAND (eh_spec_block, 0)
+ = pop_stmt_list (TREE_OPERAND (eh_spec_block, 0));
+
+ if (TREE_CODE (eh_spec_block) == MUST_NOT_THROW_EXPR)
+ return;
/* Strip cv quals, etc, from the specification types. */
for (raises = NULL_TREE;
fn = get_identifier ("__cxa_allocate_exception");
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);
- fn = push_library_fn (fn, build_function_type (ptr_type_node, tmp));
+ /* Declare void *__cxa_allocate_exception(size_t) throw(). */
+ fn = declare_nothrow_library_fn (fn, ptr_type_node, size_type_node);
}
- return build_function_call (fn, tree_cons (NULL_TREE, size_in_bytes (type),
- NULL_TREE));
+ return cp_build_function_call (fn,
+ tree_cons (NULL_TREE, size_in_bytes (type),
+ NULL_TREE),
+ tf_warning_or_error);
}
/* Call __cxa_free_exception from a cleanup. This is never invoked
fn = get_identifier ("__cxa_free_exception");
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,
- void_list_node));
+ /* Declare void __cxa_free_exception (void *) throw(). */
+ fn = declare_nothrow_library_fn (fn, void_type_node, ptr_type_node);
}
- return build_function_call (fn, tree_cons (NULL_TREE, ptr, NULL_TREE));
+ return cp_build_function_call (fn, tree_cons (NULL_TREE, ptr, NULL_TREE),
+ tf_warning_or_error);
}
/* Wrap all cleanups for TARGET_EXPRs in MUST_NOT_THROW_EXPR.
if (processing_template_decl)
{
- current_function_returns_abnormally = 1;
+ if (cfun)
+ current_function_returns_abnormally = 1;
return build_min (THROW_EXPR, void_type_node, exp);
}
return error_mark_node;
}
fn = OVL_CURRENT (fn);
- exp = build_function_call (fn, tree_cons (NULL_TREE, exp, NULL_TREE));
+ exp = cp_build_function_call (fn, tree_cons (NULL_TREE, exp, NULL_TREE),
+ tf_warning_or_error);
}
else if (exp)
{
tree throw_type;
+ tree temp_type;
tree cleanup;
tree object, ptr;
tree tmp;
- tree temp_expr, allocate_expr;
- bool elided;
+ tree allocate_expr;
/* The CLEANUP_TYPE is the internal type of a destructor. */
if (!cleanup_type)
fn = push_throw_library_fn (fn, tmp);
}
- /* throw expression */
- /* First, decay it. */
- exp = decay_conversion (exp);
+ /* [except.throw]
+
+ A throw-expression initializes a temporary object, the type
+ of which is determined by removing any top-level
+ cv-qualifiers from the static type of the operand of throw
+ and adjusting the type from "array of T" or "function return
+ T" to "pointer to T" or "pointer to function returning T"
+ respectively. */
+ temp_type = is_bitfield_expr_with_lowered_type (exp);
+ if (!temp_type)
+ temp_type = type_decays_to (TREE_TYPE (exp));
/* OK, this is kind of wacky. The standard says that we call
terminate when the exception handling mechanism, after
matter, since it can't throw). */
/* Allocate the space for the exception. */
- allocate_expr = do_allocate_exception (TREE_TYPE (exp));
+ allocate_expr = do_allocate_exception (temp_type);
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);
+ TARGET_EXPR_CLEANUP (allocate_expr) = do_free_exception (ptr);
+ CLEANUP_EH_ONLY (allocate_expr) = 1;
- elided = (TREE_CODE (exp) == TARGET_EXPR);
+ object = build_nop (build_pointer_type (temp_type), ptr);
+ object = cp_build_indirect_ref (object, RO_NULL, tf_warning_or_error);
/* And initialize the exception object. */
- exp = build_init (object, exp, LOOKUP_ONLYCONVERTING);
- if (exp == error_mark_node)
+ if (CLASS_TYPE_P (temp_type))
{
- error (" in thrown expression");
- return error_mark_node;
+ int flags = LOOKUP_NORMAL | LOOKUP_ONLYCONVERTING;
+ VEC(tree,gc) *exp_vec;
+
+ /* Under C++0x [12.8/16 class.copy], a thrown lvalue is sometimes
+ treated as an rvalue for the purposes of overload resolution
+ to favor move constructors over copy constructors. */
+ if (/* Must be a local, automatic variable. */
+ TREE_CODE (exp) == VAR_DECL
+ && DECL_CONTEXT (exp) == current_function_decl
+ && ! TREE_STATIC (exp)
+ /* The variable must not have the `volatile' qualifier. */
+ && !(cp_type_quals (TREE_TYPE (exp)) & TYPE_QUAL_VOLATILE))
+ flags = flags | LOOKUP_PREFER_RVALUE;
+
+ /* Call the copy constructor. */
+ exp_vec = make_tree_vector_single (exp);
+ exp = (build_special_member_call
+ (object, complete_ctor_identifier, &exp_vec,
+ TREE_TYPE (object), flags, tf_warning_or_error));
+ release_tree_vector (exp_vec);
+ if (exp == error_mark_node)
+ {
+ error (" in thrown expression");
+ return error_mark_node;
+ }
}
-
- /* 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 cast 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.
-
- BUT: Issue 475 may do away with this inconsistency by removing the
- terminate() in this situation.
-
- 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);
-
- /* Wrap the initialization in a CLEANUP_POINT_EXPR so that cleanups
- for temporaries within the initialization are run before the one
- for the exception object, preserving LIFO order. */
- exp = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (exp), 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);
+ {
+ tmp = decay_conversion (exp);
+ if (tmp == error_mark_node)
+ return error_mark_node;
+ exp = build2 (INIT_EXPR, temp_type, object, tmp);
+ }
+
+ /* Mark any cleanups from the initialization as MUST_NOT_THROW, since
+ they are run after the exception object is initialized. */
+ cp_walk_tree_without_duplicates (&exp, wrap_cleanups_r, 0);
/* Prepend the allocation. */
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
- we don't have to do them during unwinding. But first wrap
- 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 = build2 (COMPOUND_EXPR, TREE_TYPE (exp), temp_expr, exp);
- exp = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (exp), exp);
- }
+
+ /* Force all the cleanups to be evaluated here so that we don't have
+ to do them during unwinding. */
+ exp = build1 (CLEANUP_POINT_EXPR, void_type_node, exp);
throw_type = build_eh_type_type (prepare_eh_type (TREE_TYPE (object)));
tmp = tree_cons (NULL_TREE, throw_type, tmp);
tmp = tree_cons (NULL_TREE, ptr, tmp);
/* ??? Indicate that this function call throws throw_type. */
- tmp = build_function_call (fn, tmp);
+ tmp = cp_build_function_call (fn, tmp, tf_warning_or_error);
/* Tack on the initialization stuff. */
exp = build2 (COMPOUND_EXPR, TREE_TYPE (tmp), exp, tmp);
/* ??? Indicate that this function call allows exceptions of the type
of the enclosing catch block (if known). */
- exp = build_function_call (fn, NULL_TREE);
+ exp = cp_build_function_call (fn, NULL_TREE, tf_warning_or_error);
}
exp = build1 (THROW_EXPR, void_type_node, exp);
#include "cfns.h"
int
-nothrow_libfn_p (tree fn)
+nothrow_libfn_p (const_tree fn)
{
tree id;
tree handler = tsi_stmt (i);
if (TREE_TYPE (handler) && can_convert_eh (type, TREE_TYPE (handler)))
{
- warning (0, "%Hexception of type %qT will be caught",
- EXPR_LOCUS (handler), TREE_TYPE (handler));
- warning (0, "%H by earlier handler for %qT",
- EXPR_LOCUS (master), type);
+ warning_at (EXPR_LOCATION (handler), 0,
+ "exception of type %qT will be caught",
+ TREE_TYPE (handler));
+ warning_at (EXPR_LOCATION (master), 0,
+ " by earlier handler for %qT", type);
break;
}
}
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));
+ permerror (EXPR_LOCATION (handler), "%<...%>"
+ " handler must be the last handler for its try block");
else
check_handlers_1 (handler, i);
}