aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/except.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/cp/except.c')
-rw-r--r--gcc/cp/except.c1021
1 files changed, 386 insertions, 635 deletions
diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index bcca6863bcb..f558f5a539b 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -34,276 +34,78 @@ Boston, MA 02111-1307, USA. */
#include "output.h"
#include "except.h"
#include "toplev.h"
-#include "eh-common.h"
static void push_eh_cleanup PARAMS ((tree));
+static tree prepare_eh_type PARAMS ((tree));
static tree build_eh_type_type PARAMS ((tree));
-static tree call_eh_info PARAMS ((void));
-static void push_eh_info PARAMS ((void));
-static tree get_eh_info PARAMS ((void));
-static tree get_eh_value PARAMS ((void));
-#if 0
-static tree get_eh_type PARAMS ((void));
-static tree get_eh_caught PARAMS ((void));
-static tree get_eh_handlers PARAMS ((void));
-#endif
+static tree do_begin_catch PARAMS ((void));
static int dtor_nothrow PARAMS ((tree));
-static tree do_pop_exception PARAMS ((tree));
-static tree build_eh_type_type_ref PARAMS ((tree));
-static tree build_terminate_handler PARAMS ((void));
-static tree alloc_eh_object PARAMS ((tree));
+static tree do_end_catch PARAMS ((tree));
+static void push_eh_cleanup PARAMS ((tree));
+static bool decl_is_java_type PARAMS ((tree decl, int err));
+static void initialize_handler_parm PARAMS ((tree, tree));
+static tree do_allocate_exception PARAMS ((tree));
static int complete_ptr_ref_or_void_ptr_p PARAMS ((tree, tree));
+static bool is_admissible_throw_operand PARAMS ((tree));
static int can_convert_eh PARAMS ((tree, tree));
static void check_handlers_1 PARAMS ((tree, tree));
-static void initialize_handler_parm PARAMS ((tree));
-static tree expand_throw PARAMS ((tree));
-static int decl_is_java_type PARAMS ((tree decl, int err));
+static tree cp_protect_cleanup_actions PARAMS ((void));
#include "decl.h"
-#include "insn-flags.h"
#include "obstack.h"
-/* In a given translation unit we are constrained to catch only C++
- types or only Java types. `catch_language' holds the current type,
- and `catch_language_init' registers whether `catch_language' has
- been set. */
-
-static int catch_language_init = 0;
-static int catch_language;
-
-/* ======================================================================
- Briefly the algorithm works like this:
-
- When a constructor or start of a try block is encountered,
- push_eh_entry (&eh_stack) is called. Push_eh_entry () creates a
- new entry in the unwind protection stack and returns a label to
- output to start the protection for that block.
-
- When a destructor or end try block is encountered, pop_eh_entry
- (&eh_stack) is called. Pop_eh_entry () returns the eh_entry it
- created when push_eh_entry () was called. The eh_entry structure
- contains three things at this point. The start protect label,
- the end protect label, and the exception handler label. The end
- protect label should be output before the call to the destructor
- (if any). If it was a destructor, then its parse tree is stored
- in the finalization variable in the eh_entry structure. Otherwise
- the finalization variable is set to NULL to reflect the fact that
- it is the end of a try block. Next, this modified eh_entry node
- is enqueued in the finalizations queue by calling
- enqueue_eh_entry (&queue,entry).
-
- +---------------------------------------------------------------+
- |XXX: Will need modification to deal with partially |
- | constructed arrays of objects |
- | |
- | Basically, this consists of keeping track of how many |
- | of the objects have been constructed already (this |
- | should be in a register though, so that shouldn't be a |
- | problem. |
- +---------------------------------------------------------------+
-
- When a catch block is encountered, there is a lot of work to be
- done.
-
- Since we don't want to generate the catch block inline with the
- regular flow of the function, we need to have some way of doing
- so. Luckily, we can use sequences to defer the catch sections.
- When the start of a catch block is encountered, we start the
- sequence. After the catch block is generated, we end the
- sequence.
-
- Next we must insure that when the catch block is executed, all
- finalizations for the matching try block have been completed. If
- any of those finalizations throw an exception, we must call
- terminate according to the ARM (section r.15.6.1). What this
- means is that we need to dequeue and emit finalizations for each
- entry in the eh_queue until we get to an entry with a NULL
- finalization field. For any of the finalization entries, if it
- is not a call to terminate (), we must protect it by giving it
- another start label, end label, and exception handler label,
- setting its finalization tree to be a call to terminate (), and
- enqueue'ing this new eh_entry to be output at an outer level.
- Finally, after all that is done, we can get around to outputting
- the catch block which basically wraps all the "catch (...) {...}"
- statements in a big if/then/else construct that matches the
- correct block to call.
-
- ===================================================================== */
-
-/* ====================================================================== */
-
-/* sets up all the global eh stuff that needs to be initialized at the
+/* Sets up all the global eh stuff that needs to be initialized at the
start of compilation. */
void
init_exception_processing ()
{
- /* void vtype () */
- tree vtype = build_function_type (void_type_node, void_list_node);
-
+ tree tmp;
+
if (flag_honor_std)
push_namespace (std_identifier);
- terminate_node = build_cp_library_fn_ptr ("terminate", vtype);
+
+ /* void std::terminate (); */
+ 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;
if (flag_honor_std)
pop_namespace ();
- set_exception_lang_code (EH_LANG_C_plus_plus);
- set_exception_version_code (1);
-
- /* If we use setjmp/longjmp EH, arrange for all cleanup actions to
- be protected with __terminate. */
- protect_cleanup_actions_with_terminate = 1;
-}
-
-/* Retrieve a pointer to the cp_eh_info node for the current exception. */
-
-static tree
-call_eh_info ()
-{
- tree fn;
-
- fn = get_identifier ("__start_cp_handler");
- if (IDENTIFIER_GLOBAL_VALUE (fn))
- fn = IDENTIFIER_GLOBAL_VALUE (fn);
- else
- {
- tree t1, t, fields[7];
-
- /* Declare cp_eh_info * __start_cp_handler (void),
- as defined in exception.cc. */
-
- /* struct cp_eh_info. This must match exception.cc. Note that this
- type is not pushed anywhere. */
- t1= make_aggr_type (RECORD_TYPE);
- fields[0] = build_decl (FIELD_DECL,
- get_identifier ("handler_label"), ptr_type_node);
- fields[1] = build_decl (FIELD_DECL,
- get_identifier ("dynamic_handler_chain"), ptr_type_node);
- fields[2] = build_decl (FIELD_DECL,
- get_identifier ("info"), ptr_type_node);
- fields[3] = build_decl (FIELD_DECL,
- get_identifier ("table_index"), ptr_type_node);
- /* N.B.: The fourth field LEN is expected to be
- the number of fields - 1, not the total number of fields. */
- finish_builtin_type (t1, "eh_context", fields, 3, ptr_type_node);
- t1 = build_pointer_type (t1);
-
- t1= make_aggr_type (RECORD_TYPE);
- fields[0] = build_decl (FIELD_DECL,
- get_identifier ("match_function"), ptr_type_node);
- fields[1] = build_decl (FIELD_DECL,
- get_identifier ("language"), short_integer_type_node);
- fields[2] = build_decl (FIELD_DECL,
- get_identifier ("version"), short_integer_type_node);
- /* N.B.: The fourth field LEN is expected to be
- the number of fields - 1, not the total number of fields. */
- finish_builtin_type (t1, "__eh_info", fields, 2, ptr_type_node);
- t = make_aggr_type (RECORD_TYPE);
- fields[0] = build_decl (FIELD_DECL,
- get_identifier ("eh_info"), t1);
- fields[1] = build_decl (FIELD_DECL, get_identifier ("value"),
- ptr_type_node);
- fields[2] = build_decl (FIELD_DECL, get_identifier ("type"),
- ptr_type_node);
- fields[3] = build_decl
- (FIELD_DECL, get_identifier ("cleanup"),
- build_pointer_type (build_function_type
- (ptr_type_node, tree_cons
- (NULL_TREE, ptr_type_node, void_list_node))));
- fields[4] = build_decl (FIELD_DECL, get_identifier ("caught"),
- boolean_type_node);
- fields[5] = build_decl (FIELD_DECL, get_identifier ("next"),
- build_pointer_type (t));
- fields[6] = build_decl
- (FIELD_DECL, get_identifier ("handlers"), long_integer_type_node);
- /* N.B.: The fourth field LEN is expected to be
- the number of fields - 1, not the total number of fields. */
- finish_builtin_type (t, "cp_eh_info", fields, 6, ptr_type_node);
- t = build_pointer_type (t);
-
- /* And now the function. */
- fn = push_library_fn (fn, build_function_type (t, void_list_node));
- }
- return build_function_call (fn, NULL_TREE);
-}
-
-/* Retrieve a pointer to the cp_eh_info node for the current exception
- and save it in the current binding level. */
-
-static void
-push_eh_info ()
-{
- tree decl, fn = call_eh_info ();
-
- /* Remember the pointer to the current exception info; it won't change
- during this catch block. */
- decl = build_decl (VAR_DECL, get_identifier ("__exception_info"),
- TREE_TYPE (fn));
- DECL_ARTIFICIAL (decl) = 1;
- DECL_INITIAL (decl) = fn;
- decl = pushdecl (decl);
- cp_finish_decl (decl, fn, NULL_TREE, 0);
-}
-
-/* Returns a reference to the cp_eh_info node for the current exception. */
-
-static tree
-get_eh_info ()
-{
- /* Look for the pointer pushed in push_eh_info. */
- tree t = lookup_name (get_identifier ("__exception_info"), 0);
- return build_indirect_ref (t, NULL_PTR);
-}
-
-/* Returns a reference to the current exception object. */
-
-static tree
-get_eh_value ()
-{
- return build_component_ref (get_eh_info (), get_identifier ("value"),
- NULL_TREE, 0);
-}
-
-/* Returns a reference to the current exception type. */
-
-#if 0
-static tree
-get_eh_type ()
-{
- return build_component_ref (get_eh_info (), get_identifier ("type"),
- NULL_TREE, 0);
-}
+ /* void __cxa_call_unexpected(void *); */
+ 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);
-/* Returns a reference to whether or not the current exception
- has been caught. */
+ eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS
+ ? "__gxx_personality_sj0"
+ : "__gxx_personality_v0");
-static tree
-get_eh_caught ()
-{
- return build_component_ref (get_eh_info (), get_identifier ("caught"),
- NULL_TREE, 0);
+ lang_eh_runtime_type = build_eh_type_type;
+ lang_protect_cleanup_actions = &cp_protect_cleanup_actions;
}
-/* Returns a reference to whether or not the current exception
- has been caught. */
+/* Returns an expression to be executed if an unhandled exception is
+ propogated out of a cleanup region. */
static tree
-get_eh_handlers ()
+cp_protect_cleanup_actions ()
{
- return build_component_ref (get_eh_info (), get_identifier ("handlers"),
- NULL_TREE, 0);
-}
-#endif
+ /* [except.terminate]
-/* Build a type value for use at runtime for a type that is matched
- against by the exception handling system. */
+ When the destruction of an object during stack unwinding exits
+ using an exception ... void terminate(); is called. */
+ return build_call (terminate_node, NULL_TREE);
+}
static tree
-build_eh_type_type (type)
+prepare_eh_type (type)
tree type;
{
+ if (type == NULL_TREE)
+ return type;
if (type == error_mark_node)
return error_mark_node;
@@ -314,14 +116,14 @@ build_eh_type_type (type)
/* Peel off cv qualifiers. */
type = TYPE_MAIN_VARIANT (type);
- return build1 (ADDR_EXPR, ptr_type_node, get_typeid_1 (type));
+ return type;
}
/* Build the address of a typeinfo decl for use in the runtime
- matching field of the new exception model */
+ matching field of the exception model. */
static tree
-build_eh_type_type_ref (type)
+build_eh_type_type (type)
tree type;
{
tree exp;
@@ -329,47 +131,43 @@ build_eh_type_type_ref (type)
if (type == NULL_TREE || type == error_mark_node)
return type;
- /* peel back references, so they match. */
- if (TREE_CODE (type) == REFERENCE_TYPE)
- type = TREE_TYPE (type);
-
- /* Peel off cv qualifiers. */
- type = TYPE_MAIN_VARIANT (type);
+ if (decl_is_java_type (type, 0))
+ exp = build_java_class_ref (TREE_TYPE (type));
+ else
+ exp = get_tinfo_decl (type);
- exp = get_tinfo_decl (type);
mark_used (exp);
exp = build1 (ADDR_EXPR, ptr_type_node, exp);
- return (exp);
+ return exp;
+}
+
+tree
+build_exc_ptr ()
+{
+ return build (EXC_PTR_EXPR, ptr_type_node);
}
-/* This routine is called to mark all the symbols representing runtime
- type functions in the exception table as having been referenced.
- This will make sure code is emitted for them. Called from finish_file. */
+/* Build up a call to __cxa_begin_catch, to tell the runtime that the
+ exception has been handled. */
-void
-mark_all_runtime_matches ()
+static tree
+do_begin_catch ()
{
- int x,num;
- void **ptr;
- tree exp;
-
- num = find_all_handler_type_matches (&ptr);
- if (num == 0 || ptr == NULL)
- return;
-
- for (x=0; x <num; x++)
+ tree fn;
+
+ fn = get_identifier ("__cxa_begin_catch");
+ if (IDENTIFIER_GLOBAL_VALUE (fn))
+ fn = IDENTIFIER_GLOBAL_VALUE (fn);
+ else
{
- exp = (tree) ptr[x];
- if (TREE_CODE (exp) == ADDR_EXPR)
- {
- exp = TREE_OPERAND (exp, 0);
- if (TREE_CODE (exp) == FUNCTION_DECL)
- TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (exp)) = 1;
- }
+ /* 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));
}
-
- free (ptr);
+
+ return build_function_call (fn, tree_cons (NULL_TREE, build_exc_ptr (),
+ NULL_TREE));
}
/* Returns nonzero if cleaning up an exception of type TYPE (which can be
@@ -392,32 +190,29 @@ dtor_nothrow (type)
return TREE_NOTHROW (fn);
}
-/* Build up a call to __cp_pop_exception, to destroy the exception object
+/* Build up a call to __cxa_end_catch, to destroy the exception object
for the current catch block if no others are currently using it. */
static tree
-do_pop_exception (type)
+do_end_catch (type)
tree type;
{
tree fn, cleanup;
- fn = get_identifier ("__cp_pop_exception");
+
+ fn = get_identifier ("__cxa_end_catch");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
- /* Declare void __cp_pop_exception (void *),
- as defined in exception.cc. */
- fn = push_void_library_fn
- (fn, tree_cons (NULL_TREE, ptr_type_node, void_list_node));
+ /* Declare void __cxa_end_catch (). */
+ fn = push_void_library_fn (fn, void_list_node);
/* This can throw if the destructor for the exception throws. */
TREE_NOTHROW (fn) = 0;
}
- /* Arrange to do a dynamically scoped cleanup upon exit from this region. */
- cleanup = lookup_name (get_identifier ("__exception_info"), 0);
- cleanup = build_function_call (fn, tree_cons
- (NULL_TREE, cleanup, NULL_TREE));
+ cleanup = build_function_call (fn, NULL_TREE);
TREE_NOTHROW (cleanup) = dtor_nothrow (type);
+
return cleanup;
}
@@ -427,29 +222,20 @@ static void
push_eh_cleanup (type)
tree type;
{
- finish_decl_cleanup (NULL_TREE, do_pop_exception (type));
-}
-
-/* Build up a call to terminate on the function obstack, for use as an
- exception handler. */
-
-static tree
-build_terminate_handler ()
-{
- return build_function_call (terminate_node, NULL_TREE);
+ finish_decl_cleanup (NULL_TREE, do_end_catch (type));
}
/* Return nonzero value if DECL is a Java type suitable for catch or
throw. */
-static int
+static bool
decl_is_java_type (decl, err)
tree decl;
int err;
{
- int r = (TREE_CODE (decl) == POINTER_TYPE
- && TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE
- && TYPE_FOR_JAVA (TREE_TYPE (decl)));
+ bool r = (TREE_CODE (decl) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE
+ && TYPE_FOR_JAVA (TREE_TYPE (decl)));
if (err)
{
@@ -485,71 +271,97 @@ decl_is_java_type (decl, err)
return r;
}
+/* 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
+ __gxx_personality_(sj|v)0 in init_exception_processing - should it
+ be done here instead? */
+void
+choose_personality_routine (lang)
+ 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:
+ ; /* proceed to language selection */
+ }
+
+ 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:
+ abort ();
+ }
+ return;
+
+ give_error:
+ error ("mixing C++ and Java catches in a single translation unit");
+ state = gave_error;
+}
+
/* Initialize the catch parameter DECL. */
static void
-initialize_handler_parm (decl)
+initialize_handler_parm (decl, exp)
tree decl;
+ tree exp;
{
- tree exp;
tree init;
tree init_type;
- int lang;
/* Make sure we mark the catch param as used, otherwise we'll get a
warning about an unused ((anonymous)). */
TREE_USED (decl) = 1;
- /* Figure out the type that the initializer is. */
+ /* Figure out the type that the initializer is. Pointers are returned
+ adjusted by value from __cxa_begin_catch. Others are returned by
+ reference. */
init_type = TREE_TYPE (decl);
- if (TREE_CODE (init_type) != REFERENCE_TYPE
- && TREE_CODE (init_type) != POINTER_TYPE)
+ if (! TYPE_PTR_P (init_type)
+ && TREE_CODE (init_type) != REFERENCE_TYPE)
init_type = build_reference_type (init_type);
- if (decl_is_java_type (init_type, 0))
- {
- tree fn
- = builtin_function ("_Jv_exception_info",
- build_function_type (ptr_type_node,
- tree_cons (NULL_TREE,
- void_type_node,
- NULL_TREE)),
- 0, NOT_BUILT_IN, NULL_PTR);
-
- exp = build (CALL_EXPR, ptr_type_node,
- build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)),
- fn),
- NULL_TREE, NULL_TREE);
- TREE_SIDE_EFFECTS (exp) = 1;
- lang = EH_LANG_Java;
-
- set_exception_lang_code (EH_LANG_Java);
- set_exception_version_code (1);
- }
- else
- {
- exp = get_eh_value ();
- lang = EH_LANG_C_plus_plus;
- }
-
- if (catch_language_init)
- {
- if (lang != catch_language)
- error ("mixing C++ and Java `catch'es in single translation unit");
- }
- else
- {
- catch_language_init = 1;
- catch_language = lang;
- }
+ choose_personality_routine (decl_is_java_type (init_type, 0)
+ ? lang_java : lang_cplusplus);
/* Since pointers are passed by value, initialize a reference to
- pointer catch parm with the address of the value slot. */
- if (TREE_CODE (init_type) == REFERENCE_TYPE
- && TREE_CODE (TREE_TYPE (init_type)) == POINTER_TYPE)
+ 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 = ocp_convert (init_type , exp, CONV_IMPLICIT|CONV_FORCE_TEMP, 0);
+ exp = ocp_convert (init_type, exp, CONV_IMPLICIT|CONV_FORCE_TEMP, 0);
init = convert_from_reference (exp);
@@ -561,8 +373,7 @@ initialize_handler_parm (decl)
See also expand_default_init. */
init = ocp_convert (TREE_TYPE (decl), init,
CONV_IMPLICIT|CONV_FORCE_TEMP, 0);
- init = build (TRY_CATCH_EXPR, TREE_TYPE (init), init,
- build_terminate_handler ());
+ init = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (init), init);
}
/* Let `cp_finish_decl' know that this initializer is ok. */
@@ -582,6 +393,9 @@ expand_start_catch_block (decl)
{
tree compound_stmt_1;
tree compound_stmt_2;
+ tree exp = NULL_TREE;
+ tree type;
+ bool is_java;
if (! doing_eh (1))
return NULL_TREE;
@@ -593,35 +407,56 @@ expand_start_catch_block (decl)
/* Create a binding level for the eh_info and the exception object
cleanup. */
compound_stmt_1 = begin_compound_stmt (/*has_no_scope=*/0);
+ note_level_for_catch ();
+
+ if (decl)
+ type = prepare_eh_type (TREE_TYPE (decl));
+ else
+ type = NULL_TREE;
+ begin_catch_block (type);
- if (! decl || ! decl_is_java_type (TREE_TYPE (decl), 1))
+ is_java = false;
+ if (decl)
{
- /* The ordinary C++ case. */
- tree type;
+ tree init;
- if (decl)
- type = TREE_TYPE (decl);
+ if (decl_is_java_type (type, 1))
+ {
+ /* Java only passes object via pointer and doesn't require
+ adjusting. The java object is immediately before the
+ 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 = build_indirect_ref (init, NULL);
+ is_java = true;
+ }
else
- type = NULL_TREE;
- begin_catch_block (build_eh_type_type_ref (type));
-
- push_eh_info ();
- push_eh_cleanup (type);
+ {
+ /* C++ requires that we call __cxa_begin_catch to get the
+ pointer to the actual object. */
+ init = do_begin_catch ();
+ }
+
+ 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));
}
else
- {
- /* The Java case. In this case, the match_info is a pointer to
- the Java class object. We assume that the class is a
- compiled class. */
- tree ref = build_java_class_ref (TREE_TYPE (TREE_TYPE (decl)));
- begin_catch_block (build1 (ADDR_EXPR, jclass_node, ref));
- }
+ finish_expr_stmt (do_begin_catch ());
+
+ /* C++ requires that we call __cxa_end_catch at the end of
+ processing the exception. */
+ if (! is_java)
+ push_eh_cleanup (type);
/* Create a binding level for the parm. */
compound_stmt_2 = begin_compound_stmt (/*has_no_scope=*/0);
if (decl)
- initialize_handler_parm (decl);
+ initialize_handler_parm (decl, exp);
return build_tree_list (compound_stmt_1, compound_stmt_2);
}
@@ -654,204 +489,152 @@ expand_end_catch_block (blocks)
finish_compound_stmt (/*has_no_scope=*/0, compound_stmt_1);
}
-/* An exception spec is implemented more or less like:
-
- try {
- function body;
- } catch (...) {
- void *p[] = { typeid(raises) };
- __check_eh_spec (p, count);
- }
-
- __check_eh_spec in exception.cc handles all the details. */
-
tree
-expand_start_eh_spec ()
+begin_eh_spec_block ()
{
- return begin_try_block ();
+ tree r = build_stmt (EH_SPEC_BLOCK, NULL_TREE, NULL_TREE);
+ add_stmt (r);
+ return r;
}
void
-expand_end_eh_spec (raises, try_block)
- tree raises;
- tree try_block;
+finish_eh_spec_block (raw_raises, eh_spec_block)
+ tree raw_raises;
+ tree eh_spec_block;
{
- tree tmp, fn, decl, types = NULL_TREE;
- tree blocks;
- tree handler;
- int count = 0;
+ tree raises;
- finish_try_block (try_block);
- handler = begin_handler ();
- blocks = finish_handler_parms (NULL_TREE, handler);
-
- if (TREE_VALUE (raises) == NULL_TREE)
- {
- fn = get_identifier ("__check_null_eh_spec");
- if (IDENTIFIER_GLOBAL_VALUE (fn))
- fn = IDENTIFIER_GLOBAL_VALUE (fn);
- else
- {
- tmp = build_function_type (void_type_node, void_list_node);
- fn = push_throw_library_fn (fn, tmp);
- /* Since the spec doesn't allow any exceptions, this call will
- never throw. We use push_throw_library_fn because we do want
- TREE_THIS_VOLATILE to be set. */
- TREE_NOTHROW (fn) = 1;
- }
- tmp = NULL_TREE;
- }
- else
- {
- /* Build up an array of type_infos. */
- for (; raises && TREE_VALUE (raises); raises = TREE_CHAIN (raises))
- {
- types = tree_cons
- (NULL_TREE, build_eh_type_type (TREE_VALUE (raises)), types);
- ++count;
- }
-
- types = build_nt (CONSTRUCTOR, NULL_TREE, types);
- TREE_HAS_CONSTRUCTOR (types) = 1;
-
- /* We can't pass the CONSTRUCTOR directly, so stick it in a variable. */
- tmp = build_cplus_array_type (const_ptr_type_node, NULL_TREE);
- decl = build_decl (VAR_DECL, NULL_TREE, tmp);
- DECL_ARTIFICIAL (decl) = 1;
- DECL_INITIAL (decl) = types;
- DECL_CONTEXT (decl) = current_function_decl;
- cp_finish_decl (decl, types, NULL_TREE, 0);
-
- decl = decay_conversion (decl);
-
- fn = get_identifier ("__check_eh_spec");
- if (IDENTIFIER_GLOBAL_VALUE (fn))
- fn = IDENTIFIER_GLOBAL_VALUE (fn);
- else
- {
- tmp = tree_cons
- (NULL_TREE, integer_type_node, tree_cons
- (NULL_TREE, TREE_TYPE (decl), void_list_node));
- tmp = build_function_type (void_type_node, tmp);
-
- fn = push_throw_library_fn (fn, tmp);
- }
-
- tmp = tree_cons (NULL_TREE, build_int_2 (count, 0),
- tree_cons (NULL_TREE, decl, NULL_TREE));
- }
+ RECHAIN_STMTS (eh_spec_block, EH_SPEC_STMTS (eh_spec_block));
- tmp = build_call (fn, tmp);
- finish_expr_stmt (tmp);
+ /* 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);
- finish_handler (blocks, handler);
- finish_handler_sequence (try_block);
+ EH_SPEC_RAISES (eh_spec_block) = raises;
}
-/* This is called to expand all the toplevel exception handling
- finalization for a function. It should only be called once per
- function. */
+/* Return a pointer to a buffer for an exception object of type TYPE. */
-void
-expand_exception_blocks ()
+static tree
+do_allocate_exception (type)
+ tree type;
{
- do_pending_stack_adjust ();
+ tree fn;
- if (catch_clauses)
+ fn = get_identifier ("__cxa_allocate_exception");
+ if (IDENTIFIER_GLOBAL_VALUE (fn))
+ fn = IDENTIFIER_GLOBAL_VALUE (fn);
+ else
{
- rtx funcend = gen_label_rtx ();
- emit_jump (funcend);
-
- /* We cannot protect n regions this way if we must flow into the
- EH region through the top of the region, as we have to with
- the setjmp/longjmp approach. */
- if (exceptions_via_longjmp == 0)
- expand_eh_region_start ();
-
- emit_insns (catch_clauses);
- catch_clauses = catch_clauses_last = NULL_RTX;
-
- if (exceptions_via_longjmp == 0)
- expand_eh_region_end (build_terminate_handler ());
-
- emit_insns (catch_clauses);
- catch_clauses = catch_clauses_last = NULL_RTX;
- emit_label (funcend);
+ /* Declare void *__cxa_allocate_exception(size_t). */
+ tree tmp = tree_cons (NULL_TREE, c_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));
}
-/* Return a pointer to a buffer for an exception object of type TYPE. */
+#if 0
+/* Call __cxa_free_exception from a cleanup. This is never invoked
+ directly. */
static tree
-alloc_eh_object (type)
- tree type;
+do_free_exception (ptr)
+ tree ptr;
{
- tree fn, exp;
+ tree fn;
- fn = get_identifier ("__eh_alloc");
+ fn = get_identifier ("__cxa_free_exception");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
- /* Declare __eh_alloc (size_t), as defined in exception.cc. */
- tree tmp = tree_cons (NULL_TREE, sizetype, void_list_node);
- fn = push_library_fn (fn, build_function_type (ptr_type_node, tmp));
+ /* Declare void __cxa_free_exception (void *). */
+ fn = push_void_library_fn (fn, tree_cons (NULL_TREE, ptr_type_node,
+ void_list_node));
}
- exp = build_function_call (fn, tree_cons
- (NULL_TREE, size_in_bytes (type), NULL_TREE));
- exp = build1 (NOP_EXPR, build_pointer_type (type), exp);
- return exp;
+ return build_function_call (fn, tree_cons (NULL_TREE, ptr, NULL_TREE));
}
+#endif
-/* Expand a throw statement. This follows the following
- algorithm:
-
- 1. Allocate space to save the current PC onto the stack.
- 2. Generate and emit a label and save its address into the
- newly allocated stack space since we can't save the pc directly.
- 3. If this is the first call to throw in this function:
- generate a label for the throw block
- 4. jump to the throw block label. */
+/* Build a throw expression. */
-static tree
-expand_throw (exp)
+tree
+build_throw (exp)
tree exp;
{
tree fn;
+ if (exp == error_mark_node)
+ return exp;
+
+ if (processing_template_decl)
+ return build_min (THROW_EXPR, void_type_node, exp);
+
+ if (exp == null_node)
+ cp_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))
+ if (exp && decl_is_java_type (TREE_TYPE (exp), 1))
{
- /* A Java `throw' statement. */
- tree args = tree_cons (NULL_TREE, exp, NULL);
-
- fn = get_identifier (exceptions_via_longjmp
- ? "_Jv_Sjlj_Throw"
- : "_Jv_Throw");
+ tree fn = get_identifier ("_Jv_Throw");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
- /* Declare _Jv_Throw (void *), as defined in Java's
- exception.cc. */
+ /* 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);
}
- exp = build_function_call (fn, args);
+ exp = build_function_call (fn, tree_cons (NULL_TREE, exp, NULL_TREE));
}
else if (exp)
{
tree throw_type;
- tree cleanup = NULL_TREE, e;
+ tree cleanup;
tree stmt_expr;
tree compound_stmt;
- tree try_block;
+ tree object, ptr;
+ tree tmp;
+
+ fn = get_identifier ("__cxa_throw");
+ if (IDENTIFIER_GLOBAL_VALUE (fn))
+ fn = IDENTIFIER_GLOBAL_VALUE (fn);
+ else
+ {
+ /* 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 = 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);
+ }
begin_init_stmts (&stmt_expr, &compound_stmt);
@@ -859,165 +642,100 @@ expand_throw (exp)
/* First, decay it. */
exp = decay_conversion (exp);
- /* The CLEANUP_TYPE is the internal type of a destructor. Under
- the old ABI, destructors are two-argument functions; under
- the new ABI they take only one argument. */
- if (cleanup_type == NULL_TREE)
+ /* OK, this is kind of wacky. The standard says that we call
+ terminate when the exception handling mechanism, after
+ completing evaluation of the expression to be thrown but
+ before the exception is caught (_except.throw_), calls a
+ user function that exits via an uncaught exception.
+
+ So we have to protect the actual initialization of the
+ exception object with terminate(), but evaluate the
+ expression first. Since there could be temps in the
+ expression, we need to handle that, too. We also expand
+ the call to __cxa_allocate_exception first (which doesn't
+ matter, since it can't throw). */
+
+ my_friendly_assert (stmts_are_full_exprs_p () == 1, 19990926);
+
+ /* Store the throw expression into a temp. This can be less
+ efficient than storing it into the allocated space directly, but
+ if we allocated the space first we would have to deal with
+ cleaning it up if evaluating this expression throws. */
+ if (TREE_SIDE_EFFECTS (exp))
{
- tree arg_types;
-
- arg_types = void_list_node;
- if (!flag_new_abi)
- arg_types = tree_cons (NULL_TREE, integer_type_node, arg_types);
- arg_types = tree_cons (NULL_TREE, ptr_type_node, arg_types);
- cleanup_type = (build_pointer_type
- (build_function_type (void_type_node, arg_types)));
+ tmp = create_temporary_var (TREE_TYPE (exp));
+ DECL_INITIAL (tmp) = exp;
+ cp_finish_decl (tmp, exp, NULL_TREE, LOOKUP_ONLYCONVERTING);
+ exp = tmp;
}
- if (TYPE_PTR_P (TREE_TYPE (exp)))
- throw_type = build_eh_type_type (TREE_TYPE (exp));
- else
- {
- tree object, ptr;
-
- /* OK, this is kind of wacky. The standard says that we call
- terminate when the exception handling mechanism, after
- completing evaluation of the expression to be thrown but
- before the exception is caught (_except.throw_), calls a
- user function that exits via an uncaught exception.
-
- So we have to protect the actual initialization of the
- exception object with terminate(), but evaluate the
- expression first. Since there could be temps in the
- expression, we need to handle that, too. We also expand
- the call to __eh_alloc first (which doesn't matter, since
- it can't throw). */
-
- my_friendly_assert (stmts_are_full_exprs_p () == 1, 19990926);
-
- /* Store the throw expression into a temp. This can be less
- efficient than storing it into the allocated space directly, but
- if we allocated the space first we would have to deal with
- cleaning it up if evaluating this expression throws. */
- if (TREE_SIDE_EFFECTS (exp))
- {
- tree temp = create_temporary_var (TREE_TYPE (exp));
- DECL_INITIAL (temp) = exp;
- cp_finish_decl (temp, exp, NULL_TREE, LOOKUP_ONLYCONVERTING);
- exp = temp;
- }
-
- /* Allocate the space for the exception. */
- ptr = save_expr (alloc_eh_object (TREE_TYPE (exp)));
- finish_expr_stmt (ptr);
+ /* Allocate the space for the exception. */
+ ptr = create_temporary_var (ptr_type_node);
+ DECL_REGISTER (ptr) = 1;
+ cp_finish_decl (ptr, NULL_TREE, NULL_TREE, LOOKUP_ONLYCONVERTING);
+ tmp = do_allocate_exception (TREE_TYPE (exp));
+ tmp = build_modify_expr (ptr, INIT_EXPR, tmp);
+ finish_expr_stmt (tmp);
- try_block = begin_try_block ();
- object = build_indirect_ref (ptr, NULL_PTR);
- exp = build_modify_expr (object, INIT_EXPR, exp);
+ object = build1 (NOP_EXPR, build_pointer_type (TREE_TYPE (exp)), ptr);
+ object = build_indirect_ref (object, NULL);
- if (exp == error_mark_node)
- error (" in thrown expression");
+ exp = build_modify_expr (object, INIT_EXPR, exp);
+ if (exp == error_mark_node)
+ error (" in thrown expression");
- finish_expr_stmt (exp);
- finish_cleanup_try_block (try_block);
- finish_cleanup (build_terminate_handler (), try_block);
+ exp = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (exp), exp);
+ finish_expr_stmt (exp);
- throw_type = build_eh_type_type (TREE_TYPE (object));
+ throw_type = build_eh_type_type (prepare_eh_type (TREE_TYPE (object)));
- if (TYPE_HAS_DESTRUCTOR (TREE_TYPE (object)))
- {
- cleanup = lookup_fnfields (TYPE_BINFO (TREE_TYPE (object)),
- (flag_new_abi
- ? complete_dtor_identifier
- : dtor_identifier),
- 0);
- cleanup = TREE_VALUE (cleanup);
- mark_used (cleanup);
- mark_addressable (cleanup);
- /* Pretend it's a normal function. */
- cleanup = build1 (ADDR_EXPR, cleanup_type, cleanup);
- }
-
- exp = ptr;
+ if (TYPE_HAS_DESTRUCTOR (TREE_TYPE (object)))
+ {
+ cleanup = lookup_fnfields (TYPE_BINFO (TREE_TYPE (object)),
+ complete_dtor_identifier, 0);
+ cleanup = TREE_VALUE (cleanup);
+ mark_used (cleanup);
+ mark_addressable (cleanup);
+ /* Pretend it's a normal function. */
+ cleanup = build1 (ADDR_EXPR, cleanup_type, cleanup);
}
-
- /* Cast EXP to `void *' so that it will match the prototype for
- __cp_push_exception. */
- exp = convert (ptr_type_node, exp);
-
- if (cleanup == NULL_TREE)
+ else
{
cleanup = build_int_2 (0, 0);
TREE_TYPE (cleanup) = cleanup_type;
}
- fn = cp_push_exception_identifier;
- if (IDENTIFIER_GLOBAL_VALUE (fn))
- fn = IDENTIFIER_GLOBAL_VALUE (fn);
- else
- {
- /* Declare __cp_push_exception (void*, void*, void (*)(void*, int)),
- as defined in exception.cc. */
- tree tmp;
- tmp = tree_cons
- (NULL_TREE, ptr_type_node, tree_cons
- (NULL_TREE, ptr_type_node, tree_cons
- (NULL_TREE, cleanup_type, void_list_node)));
- fn = push_void_library_fn (fn, tmp);
- }
+ 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);
+
+ /* ??? Indicate that this function call throws throw_type. */
- e = tree_cons (NULL_TREE, exp, tree_cons
- (NULL_TREE, throw_type, tree_cons
- (NULL_TREE, cleanup, NULL_TREE)));
- finish_expr_stmt (build_function_call (fn, e));
+ finish_expr_stmt (tmp);
exp = finish_init_stmts (stmt_expr, compound_stmt);
}
else
{
- /* rethrow current exception; note that it's no longer caught. */
+ /* Rethrow current exception. */
- tree fn = get_identifier ("__uncatch_exception");
+ tree fn = get_identifier ("__cxa_rethrow");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
- /* Declare void __uncatch_exception (void)
- as defined in exception.cc. */
- fn = push_void_library_fn (fn, void_list_node);
+ {
+ /* Declare void __cxa_rethrow (void). */
+ fn = push_throw_library_fn
+ (fn, build_function_type (void_type_node, void_list_node));
+ }
exp = build_function_call (fn, NULL_TREE);
}
- return exp;
-}
-
-/* Build a throw expression. */
-
-tree
-build_throw (e)
- tree e;
-{
- if (e == error_mark_node)
- return e;
-
- if (processing_template_decl)
- return build_min (THROW_EXPR, void_type_node, e);
-
- if (e == null_node)
- cp_warning ("throwing NULL, which has integral, not pointer type");
-
- if (e != NULL_TREE)
- {
- if (!complete_ptr_ref_or_void_ptr_p (TREE_TYPE (e), e))
- return error_mark_node;
- }
-
- e = expand_throw (e);
- e = build1 (THROW_EXPR, void_type_node, e);
- TREE_SIDE_EFFECTS (e) = 1;
- TREE_USED (e) = 1;
+ exp = build1 (THROW_EXPR, void_type_node, exp);
- return e;
+ return exp;
}
/* Make sure TYPE is complete, pointer to complete, reference to
@@ -1051,6 +769,38 @@ complete_ptr_ref_or_void_ptr_p (type, from)
return 1;
}
+/* Return truth-value if EXPRESSION is admissible in throw-expression,
+ i.e. if it is not of incomplete type or a pointer/reference to such
+ a type or of an abstract class type. */
+
+static bool
+is_admissible_throw_operand (expr)
+ tree expr;
+{
+ tree type = TREE_TYPE (expr);
+
+ /* 15.1/4 [...] The type of the throw-expression shall not be an
+ incomplete type, or a pointer or a reference to an incomplete
+ type, other than void*, const void*, volatile void*, or
+ const volatile void*. Except for these restriction and the
+ restrictions on type matching mentioned in 15.3, the operand
+ of throw is treated exactly as a function argument in a call
+ (5.2.2) or the operand of a return statement. */
+ if (!complete_ptr_ref_or_void_ptr_p (type, expr))
+ return false;
+
+ /* 10.4/3 An abstract class shall not be used as a parameter type,
+ as a function return type or as type of an explicit
+ conversion. */
+ else if (CLASS_TYPE_P (type) && CLASSTYPE_PURE_VIRTUALS (type))
+ {
+ cp_error ("Expression '%E' of abstract class type '%T' cannot be used in throw-expression", expr, type);
+ return false;
+ }
+
+ return true;
+}
+
/* Returns nonzero if FN is a declaration of a standard C library
function which is known not to throw.
@@ -1069,6 +819,7 @@ nothrow_libfn_p (fn)
if (TREE_PUBLIC (fn)
&& DECL_EXTERNAL (fn)
+ && DECL_NAMESPACE_SCOPE_P (fn)
&& DECL_EXTERN_C_P (fn))
/* OK */;
else
@@ -1105,7 +856,7 @@ can_convert_eh (to, from)
/* else fall through */
}
- if (IS_AGGR_TYPE (to) && IS_AGGR_TYPE (from)
+ if (CLASS_TYPE_P (to) && CLASS_TYPE_P (from)
&& PUBLICLY_UNIQUELY_DERIVED_P (to, from))
return 1;