summaryrefslogtreecommitdiff
path: root/gnu/egcs/gcc/cp/except.c
diff options
context:
space:
mode:
authorMarc Espie <espie@cvs.openbsd.org>1999-05-26 13:38:57 +0000
committerMarc Espie <espie@cvs.openbsd.org>1999-05-26 13:38:57 +0000
commit0126e157b87f137fc08dc7f46f6c291b9d06ac5d (patch)
treef8555e3e504eb82b4cd3cba5cec20ae4ce8124ff /gnu/egcs/gcc/cp/except.c
parentff8e9a4356e55ed142306c3a375fa280800abc86 (diff)
egcs projects compiler system
Exact copy of the snapshot, except for the removal of texinfo/ gcc/ch/ libchill/
Diffstat (limited to 'gnu/egcs/gcc/cp/except.c')
-rw-r--r--gnu/egcs/gcc/cp/except.c1161
1 files changed, 1161 insertions, 0 deletions
diff --git a/gnu/egcs/gcc/cp/except.c b/gnu/egcs/gcc/cp/except.c
new file mode 100644
index 00000000000..9e2d6af592a
--- /dev/null
+++ b/gnu/egcs/gcc/cp/except.c
@@ -0,0 +1,1161 @@
+/* Handle exceptional things in C++.
+ Copyright (C) 1989, 92-97, 1998, 1999 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.
+
+This file is part of GNU CC.
+
+GNU CC 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)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+
+#include "config.h"
+#include "system.h"
+#include "tree.h"
+#include "rtl.h"
+#include "cp-tree.h"
+#include "flags.h"
+#include "obstack.h"
+#include "expr.h"
+#include "output.h"
+#include "except.h"
+#include "function.h"
+#include "defaults.h"
+#include "toplev.h"
+#include "eh-common.h"
+
+rtx expand_builtin_return_addr PROTO((enum built_in_function, int, rtx));
+
+/* Holds the fndecl for __builtin_return_address. */
+tree builtin_return_address_fndecl;
+
+/* A couple of backend routines from m88k.c */
+
+static void push_eh_cleanup PROTO((void));
+static tree build_eh_type_type PROTO((tree));
+static tree build_eh_type PROTO((tree));
+static void expand_end_eh_spec PROTO((tree));
+static tree call_eh_info PROTO((void));
+static void push_eh_info PROTO((void));
+static tree get_eh_info PROTO((void));
+static tree get_eh_value PROTO((void));
+#if 0
+static tree get_eh_type PROTO((void));
+static tree get_eh_caught PROTO((void));
+static tree get_eh_handlers PROTO((void));
+#endif
+static tree do_pop_exception PROTO((void));
+static void process_start_catch_block PROTO((tree, tree));
+static tree build_eh_type_type_ref PROTO((tree));
+static tree build_terminate_handler PROTO((void));
+static tree alloc_eh_object PROTO((tree));
+
+#if 0
+/* This is the startup, and finish stuff per exception table. */
+
+/* XXX - Tad: exception handling section */
+#ifndef EXCEPT_SECTION_ASM_OP
+#define EXCEPT_SECTION_ASM_OP "section\t.gcc_except_table,\"a\",@progbits"
+#endif
+
+#ifdef EXCEPT_SECTION_ASM_OP
+
+ /* on machines which support it, the exception table lives in another section,
+ but it needs a label so we can reference it... This sets up that
+ label! */
+asm (EXCEPT_SECTION_ASM_OP);
+exception_table __EXCEPTION_TABLE__[1] = { (void*)0, (void*)0, (void*)0 };
+asm (TEXT_SECTION_ASM_OP);
+
+#endif /* EXCEPT_SECTION_ASM_OP */
+
+#ifdef EXCEPT_SECTION_ASM_OP
+
+ /* we need to know where the end of the exception table is... so this
+ is how we do it! */
+
+asm (EXCEPT_SECTION_ASM_OP);
+exception_table __EXCEPTION_END__[1] = { (void*)-1, (void*)-1, (void*)-1 };
+asm (TEXT_SECTION_ASM_OP);
+
+#endif /* EXCEPT_SECTION_ASM_OP */
+
+#endif
+
+#include "decl.h"
+#include "insn-flags.h"
+#include "obstack.h"
+
+/* ======================================================================
+ 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.
+
+ ===================================================================== */
+
+/* local globals for function calls
+ ====================================================================== */
+
+/* Used to cache "terminate" and "__throw_type_match*". */
+static tree Terminate, CatchMatch;
+
+/* Used to cache __find_first_exception_table_match for throw. */
+static tree FirstExceptionMatch;
+
+/* Used to cache a call to __unwind_function. */
+static tree Unwind;
+
+/* ====================================================================== */
+
+
+/* ========================================================================= */
+
+
+
+/* local globals - these local globals are for storing data necessary for
+ generating the exception table and code in the correct order.
+
+ ========================================================================= */
+
+extern rtx catch_clauses;
+extern tree const_ptr_type_node;
+
+/* ========================================================================= */
+
+/* sets up all the global eh stuff that needs to be initialized at the
+ start of compilation.
+
+ This includes:
+ - Setting up all the function call trees. */
+
+void
+init_exception_processing ()
+{
+ /* void vtype () */
+ tree vtype = build_function_type (void_type_node, void_list_node);
+
+ if (flag_honor_std)
+ push_namespace (get_identifier ("std"));
+ Terminate = auto_function (get_identifier ("terminate"),
+ vtype, NOT_BUILT_IN);
+ TREE_THIS_VOLATILE (Terminate) = 1;
+ if (flag_honor_std)
+ pop_namespace ();
+
+ push_lang_context (lang_name_c);
+
+ set_exception_lang_code (EH_LANG_C_plus_plus);
+ set_exception_version_code (1);
+
+ CatchMatch
+ = builtin_function (flag_rtti
+ ? "__throw_type_match_rtti"
+ : "__throw_type_match",
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE, const_ptr_type_node,
+ tree_cons (NULL_TREE, const_ptr_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ void_list_node)))),
+ NOT_BUILT_IN, NULL_PTR);
+ FirstExceptionMatch
+ = builtin_function ("__find_first_exception_table_match",
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ void_list_node)),
+ NOT_BUILT_IN, NULL_PTR);
+ Unwind
+ = builtin_function ("__unwind_function",
+ build_function_type (void_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ void_list_node)),
+ NOT_BUILT_IN, NULL_PTR);
+
+ pop_lang_context ();
+
+ /* 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. */
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+
+ /* struct cp_eh_info. This must match exception.cc. Note that this
+ type is not pushed anywhere. */
+ t1= make_lang_type (RECORD_TYPE);
+ fields[0] = build_lang_field_decl (FIELD_DECL,
+ get_identifier ("handler_label"), ptr_type_node);
+ fields[1] = build_lang_field_decl (FIELD_DECL,
+ get_identifier ("dynamic_handler_chain"), ptr_type_node);
+ fields[2] = build_lang_field_decl (FIELD_DECL,
+ get_identifier ("info"), ptr_type_node);
+ fields[3] = build_lang_field_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_lang_type (RECORD_TYPE);
+ fields[0] = build_lang_field_decl (FIELD_DECL,
+ get_identifier ("match_function"), ptr_type_node);
+ fields[1] = build_lang_field_decl (FIELD_DECL,
+ get_identifier ("language"), short_integer_type_node);
+ fields[2] = build_lang_field_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_lang_type (RECORD_TYPE);
+ fields[0] = build_lang_field_decl (FIELD_DECL,
+ get_identifier ("eh_info"), t1);
+ fields[1] = build_lang_field_decl (FIELD_DECL, get_identifier ("value"),
+ ptr_type_node);
+ fields[2] = build_lang_field_decl (FIELD_DECL, get_identifier ("type"),
+ ptr_type_node);
+ fields[3] = build_lang_field_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_lang_field_decl (FIELD_DECL, get_identifier ("caught"),
+ boolean_type_node);
+ fields[5] = build_lang_field_decl (FIELD_DECL, get_identifier ("next"),
+ build_pointer_type (t));
+ fields[6] = build_lang_field_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 = build_lang_decl (FUNCTION_DECL, fn,
+ build_function_type (t, void_list_node));
+ DECL_EXTERNAL (fn) = 1;
+ TREE_PUBLIC (fn) = 1;
+ DECL_ARTIFICIAL (fn) = 1;
+ pushdecl_top_level (fn);
+ make_function_rtl (fn);
+ pop_obstacks ();
+ }
+ mark_used (fn);
+ 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, 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);
+}
+
+/* Returns a reference to whether or not the current exception
+ has been caught. */
+
+static tree
+get_eh_caught ()
+{
+ return build_component_ref (get_eh_info (), get_identifier ("caught"),
+ NULL_TREE, 0);
+}
+
+/* Returns a reference to whether or not the current exception
+ has been caught. */
+
+static tree
+get_eh_handlers ()
+{
+ return build_component_ref (get_eh_info (), get_identifier ("handlers"),
+ NULL_TREE, 0);
+}
+#endif
+
+/* Build a type value for use at runtime for a type that is matched
+ against by the exception handling system. */
+
+static tree
+build_eh_type_type (type)
+ tree type;
+{
+ const char *typestring;
+ tree exp;
+
+ if (type == error_mark_node)
+ return error_mark_node;
+
+ /* 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 (flag_rtti)
+ return build1 (ADDR_EXPR, ptr_type_node, get_typeid_1 (type));
+
+ typestring = build_overload_name (type, 1, 1);
+ exp = combine_strings (build_string (strlen (typestring)+1, typestring));
+ return build1 (ADDR_EXPR, ptr_type_node, exp);
+}
+
+/* Build the address of a runtime type for use in the runtime matching
+ field of the new exception model */
+
+static tree
+build_eh_type_type_ref (type)
+ tree type;
+{
+ const char *typestring;
+ tree exp;
+
+ if (type == error_mark_node)
+ return error_mark_node;
+
+ /* 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);
+
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+
+ if (flag_rtti)
+ {
+ exp = get_tinfo_fn (type);
+ TREE_USED (exp) = 1;
+ mark_inline_for_output (exp);
+ exp = build1 (ADDR_EXPR, ptr_type_node, exp);
+ }
+ else
+ {
+ typestring = build_overload_name (type, 1, 1);
+ exp = combine_strings (build_string (strlen (typestring)+1, typestring));
+ exp = build1 (ADDR_EXPR, ptr_type_node, exp);
+ }
+ pop_obstacks ();
+ return (exp);
+}
+
+
+/* Build a type value for use at runtime for a exp that is thrown or
+ matched against by the exception handling system. */
+
+static tree
+build_eh_type (exp)
+ tree exp;
+{
+ if (flag_rtti)
+ {
+ exp = build_typeid (exp);
+ return build1 (ADDR_EXPR, ptr_type_node, exp);
+ }
+ return build_eh_type_type (TREE_TYPE (exp));
+}
+
+/* This routine is called to mark all the symbols representing runtime
+ type functions in the exception table as haveing been referenced.
+ This will make sure code is emitted for them. Called from finish_file. */
+void
+mark_all_runtime_matches ()
+{
+ 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++)
+ {
+ 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;
+ }
+ }
+
+ free (ptr);
+}
+
+/* Build up a call to __cp_pop_exception, to destroy the exception object
+ for the current catch block. HANDLER is either true or false, telling
+ the library whether or not it is being called from an exception handler;
+ if it is, it avoids destroying the object on rethrow. */
+
+static tree
+do_pop_exception ()
+{
+ tree fn, cleanup;
+ fn = get_identifier ("__cp_pop_exception");
+ if (IDENTIFIER_GLOBAL_VALUE (fn))
+ fn = IDENTIFIER_GLOBAL_VALUE (fn);
+ else
+ {
+ /* Declare void __cp_pop_exception (void *),
+ as defined in exception.cc. */
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ fn = build_lang_decl
+ (FUNCTION_DECL, fn,
+ build_function_type (void_type_node, tree_cons
+ (NULL_TREE, ptr_type_node, void_list_node)));
+ DECL_EXTERNAL (fn) = 1;
+ TREE_PUBLIC (fn) = 1;
+ DECL_ARTIFICIAL (fn) = 1;
+ pushdecl_top_level (fn);
+ make_function_rtl (fn);
+ pop_obstacks ();
+ }
+
+ mark_used (fn);
+ /* 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, expr_tree_cons
+ (NULL_TREE, cleanup, NULL_TREE));
+ return cleanup;
+}
+
+/* This routine creates the cleanup for the current exception. */
+
+static void
+push_eh_cleanup ()
+{
+ int yes;
+
+ yes = suspend_momentary ();
+ /* All cleanups must last longer than normal. */
+ expand_decl_cleanup (NULL_TREE, do_pop_exception ());
+ resume_momentary (yes);
+}
+
+/* Build up a call to terminate on the function obstack, for use as an
+ exception handler. */
+
+static tree
+build_terminate_handler ()
+{
+ int yes = suspend_momentary ();
+ tree term = build_function_call (Terminate, NULL_TREE);
+ resume_momentary (yes);
+ return term;
+}
+
+/* Call this to start a catch block. Typename is the typename, and identifier
+ is the variable to place the object in or NULL if the variable doesn't
+ matter. If typename is NULL, that means its a "catch (...)" or catch
+ everything. In that case we don't need to do any type checking.
+ (ie: it ends up as the "else" clause rather than an "else if" clause) */
+
+void
+expand_start_catch_block (declspecs, declarator)
+ tree declspecs, declarator;
+{
+ tree decl;
+
+ if (processing_template_decl)
+ {
+ if (declspecs)
+ {
+ decl = grokdeclarator (declarator, declspecs, CATCHPARM,
+ 1, NULL_TREE);
+ pushdecl (decl);
+ decl = build_min_nt (DECL_STMT, copy_to_permanent (declarator),
+ copy_to_permanent (declspecs),
+ NULL_TREE);
+ add_tree (decl);
+ }
+ return;
+ }
+
+ if (! doing_eh (1))
+ return;
+
+ process_start_catch_block (declspecs, declarator);
+}
+
+
+/* This function performs the expand_start_catch_block functionality for
+ exceptions implemented in the new style. __throw determines whether
+ a handler needs to be called or not, so the handler itself has to do
+ nothing additional. */
+
+static void
+process_start_catch_block (declspecs, declarator)
+ tree declspecs, declarator;
+{
+ tree decl = NULL_TREE;
+ tree init;
+
+ /* Create a binding level for the eh_info and the exception object
+ cleanup. */
+ pushlevel (0);
+ expand_start_bindings (0);
+
+
+ if (declspecs)
+ {
+ decl = grokdeclarator (declarator, declspecs, CATCHPARM, 1, NULL_TREE);
+
+ if (decl == NULL_TREE)
+ error ("invalid catch parameter");
+ }
+
+ if (decl)
+ start_catch_handler (build_eh_type_type_ref (TREE_TYPE (decl)));
+ else
+ start_catch_handler (CATCH_ALL_TYPE);
+
+ emit_line_note (input_filename, lineno);
+
+ push_eh_info ();
+
+ if (decl)
+ {
+ tree exp;
+ tree init_type;
+
+ /* 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. */
+ init_type = TREE_TYPE (decl);
+ if (TREE_CODE (init_type) != REFERENCE_TYPE
+ && TREE_CODE (init_type) != POINTER_TYPE)
+ init_type = build_reference_type (init_type);
+
+ exp = get_eh_value ();
+
+ /* 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)
+ exp = build_unary_op (ADDR_EXPR, exp, 1);
+
+ exp = ocp_convert (init_type , exp, CONV_IMPLICIT|CONV_FORCE_TEMP, 0);
+
+ push_eh_cleanup ();
+
+ /* Create a binding level for the parm. */
+ pushlevel (0);
+ expand_start_bindings (0);
+
+ init = convert_from_reference (exp);
+
+ /* If the constructor for the catch parm exits via an exception, we
+ must call terminate. See eh23.C. */
+ if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl)))
+ {
+ /* Generate the copy constructor call directly so we can wrap it.
+ 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 ());
+ }
+
+ /* Let `cp_finish_decl' know that this initializer is ok. */
+ DECL_INITIAL (decl) = init;
+ decl = pushdecl (decl);
+
+ start_decl_1 (decl);
+ cp_finish_decl (decl, init, NULL_TREE, 0,
+ LOOKUP_ONLYCONVERTING|DIRECT_BIND);
+ }
+ else
+ {
+ push_eh_cleanup ();
+
+ /* Create a binding level for the parm. */
+ pushlevel (0);
+ expand_start_bindings (0);
+
+ /* Fall into the catch all section. */
+ }
+
+ emit_line_note (input_filename, lineno);
+}
+
+
+/* Call this to end a catch block. Its responsible for emitting the
+ code to handle jumping back to the correct place, and for emitting
+ the label to jump to if this catch block didn't match. */
+
+void
+expand_end_catch_block ()
+{
+ if (! doing_eh (1))
+ return;
+
+ /* Cleanup the EH parameter. */
+ expand_end_bindings (getdecls (), kept_level_p (), 0);
+ poplevel (kept_level_p (), 1, 0);
+
+ /* Cleanup the EH object. */
+ expand_end_bindings (getdecls (), kept_level_p (), 0);
+ poplevel (kept_level_p (), 1, 0);
+
+ /* Fall to outside the try statement when done executing handler and
+ we fall off end of handler. This is jump Lresume in the
+ documentation. */
+ expand_goto (top_label_entry (&caught_return_label_stack));
+
+ end_catch_handler ();
+}
+
+/* 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. */
+
+void
+expand_start_eh_spec ()
+{
+ expand_start_try_stmts ();
+}
+
+static void
+expand_end_eh_spec (raises)
+ tree raises;
+{
+ tree tmp, fn, decl, types = NULL_TREE;
+ int count = 0;
+
+ expand_start_all_catch ();
+ expand_start_catch_block (NULL_TREE, NULL_TREE);
+
+ /* Build up an array of type_infos. */
+ for (; raises && TREE_VALUE (raises); raises = TREE_CHAIN (raises))
+ {
+ types = expr_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;
+ cp_finish_decl (decl, types, NULL_TREE, 0, 0);
+
+ decl = decay_conversion (decl);
+
+ fn = get_identifier ("__check_eh_spec");
+ if (IDENTIFIER_GLOBAL_VALUE (fn))
+ fn = IDENTIFIER_GLOBAL_VALUE (fn);
+ else
+ {
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+
+ 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 = build_lang_decl (FUNCTION_DECL, fn, tmp);
+ DECL_EXTERNAL (fn) = 1;
+ TREE_PUBLIC (fn) = 1;
+ DECL_ARTIFICIAL (fn) = 1;
+ TREE_THIS_VOLATILE (fn) = 1;
+ pushdecl_top_level (fn);
+ make_function_rtl (fn);
+ pop_obstacks ();
+ }
+
+ mark_used (fn);
+ tmp = expr_tree_cons (NULL_TREE, build_int_2 (count, 0), expr_tree_cons
+ (NULL_TREE, decl, NULL_TREE));
+ tmp = build_call (fn, TREE_TYPE (TREE_TYPE (fn)), tmp);
+ expand_expr (tmp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ expand_end_catch_block ();
+ expand_end_all_catch ();
+}
+
+/* This is called to expand all the toplevel exception handling
+ finalization for a function. It should only be called once per
+ function. */
+
+void
+expand_exception_blocks ()
+{
+ do_pending_stack_adjust ();
+ push_to_sequence (catch_clauses);
+ expand_leftover_cleanups ();
+ do_pending_stack_adjust ();
+ catch_clauses = get_insns ();
+ end_sequence ();
+
+ /* Do this after we expand leftover cleanups, so that the
+ expand_eh_region_end that expand_end_eh_spec does will match the
+ right expand_eh_region_start, and make sure it comes out before
+ the terminate protected region. */
+ if (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl)))
+ {
+ expand_end_eh_spec (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl)));
+ do_pending_stack_adjust ();
+ push_to_sequence (catch_clauses);
+ expand_leftover_cleanups ();
+ do_pending_stack_adjust ();
+ catch_clauses = get_insns ();
+ end_sequence ();
+ }
+
+ if (catch_clauses)
+ {
+ 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 = NULL_RTX;
+
+ if (exceptions_via_longjmp == 0)
+ expand_eh_region_end (build_terminate_handler ());
+
+ expand_leftover_cleanups ();
+
+ emit_label (funcend);
+ }
+}
+
+tree
+start_anon_func ()
+{
+ static int counter = 0;
+ int old_interface_unknown = interface_unknown;
+ char name[32];
+ tree params;
+ tree t;
+
+ push_cp_function_context (NULL_TREE);
+ push_to_top_level ();
+
+ /* No need to mangle this. */
+ push_lang_context (lang_name_c);
+
+ interface_unknown = 1;
+
+ params = void_list_node;
+ /* tcf stands for throw clean function. */
+ sprintf (name, "__tcf_%d", counter++);
+ t = make_call_declarator (get_identifier (name), params, NULL_TREE,
+ NULL_TREE);
+ start_function (decl_tree_cons (NULL_TREE, get_identifier ("static"),
+ void_list_node),
+ t, NULL_TREE, 0);
+ store_parm_decls ();
+ pushlevel (0);
+ clear_last_expr ();
+ push_momentary ();
+ expand_start_bindings (0);
+ emit_line_note (input_filename, lineno);
+
+ interface_unknown = old_interface_unknown;
+
+ pop_lang_context ();
+
+ return current_function_decl;
+}
+
+void
+end_anon_func ()
+{
+ expand_end_bindings (getdecls (), 1, 0);
+ poplevel (1, 0, 0);
+ pop_momentary ();
+
+ finish_function (lineno, 0, 0);
+
+ pop_from_top_level ();
+ pop_cp_function_context (NULL_TREE);
+}
+
+/* Return a pointer to a buffer for an exception object of type TYPE. */
+
+static tree
+alloc_eh_object (type)
+ tree type;
+{
+ tree fn, exp;
+
+ fn = get_identifier ("__eh_alloc");
+ if (IDENTIFIER_GLOBAL_VALUE (fn))
+ fn = IDENTIFIER_GLOBAL_VALUE (fn);
+ else
+ {
+ /* Declare __eh_alloc (size_t), as defined in exception.cc. */
+ tree tmp;
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ tmp = tree_cons (NULL_TREE, sizetype, void_list_node);
+ fn = build_lang_decl (FUNCTION_DECL, fn,
+ build_function_type (ptr_type_node, tmp));
+ DECL_EXTERNAL (fn) = 1;
+ TREE_PUBLIC (fn) = 1;
+ DECL_ARTIFICIAL (fn) = 1;
+ pushdecl_top_level (fn);
+ make_function_rtl (fn);
+ pop_obstacks ();
+ }
+
+ mark_used (fn);
+ exp = build_function_call (fn, expr_tree_cons
+ (NULL_TREE, size_in_bytes (type), NULL_TREE));
+ exp = build1 (NOP_EXPR, build_pointer_type (type), exp);
+ return exp;
+}
+
+/* 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. */
+
+void
+expand_throw (exp)
+ tree exp;
+{
+ tree fn;
+ static tree cleanup_type;
+
+ if (! doing_eh (1))
+ return;
+
+ if (exp)
+ {
+ tree throw_type;
+ tree cleanup = NULL_TREE, e;
+
+ /* throw expression */
+ /* First, decay it. */
+ exp = decay_conversion (exp);
+
+ /* cleanup_type is void (*)(void *, int),
+ the internal type of a destructor. */
+ if (cleanup_type == NULL_TREE)
+ {
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ cleanup_type = build_pointer_type
+ (build_function_type
+ (void_type_node, tree_cons
+ (NULL_TREE, ptr_type_node, tree_cons
+ (NULL_TREE, integer_type_node, void_list_node))));
+ pop_obstacks ();
+ }
+
+ if (TYPE_PTR_P (TREE_TYPE (exp)))
+ throw_type = build_eh_type (exp);
+ else
+ {
+ tree object, ptr;
+
+ /* OK, this is kind of wacky. The WP 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. We also expand the call to __eh_alloc
+ first. Since there could be temps in the expression, we need
+ to handle that, too. */
+
+ expand_start_target_temps ();
+
+#if 0
+ /* Unfortunately, this doesn't work. */
+ preexpand_calls (exp);
+#else
+ /* Store the throw expression into a temp. This can be less
+ efficient than storing it into the allocated space directly, but
+ oh well. To do this efficiently we would need to insinuate
+ ourselves into expand_call. */
+ if (TREE_SIDE_EFFECTS (exp))
+ {
+ tree temp = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (exp));
+ DECL_ARTIFICIAL (temp) = 1;
+ DECL_RTL (temp) = assign_temp (TREE_TYPE (exp), 2, 0, 1);
+ DECL_INITIAL (temp) = exp;
+ cp_finish_decl (temp, exp, NULL_TREE, 0, LOOKUP_ONLYCONVERTING);
+ exp = temp;
+ }
+#endif
+
+ /* Allocate the space for the exception. */
+ ptr = save_expr (alloc_eh_object (TREE_TYPE (exp)));
+ expand_expr (ptr, const0_rtx, VOIDmode, 0);
+
+ expand_eh_region_start ();
+
+ object = build_indirect_ref (ptr, NULL_PTR);
+ exp = build_modify_expr (object, INIT_EXPR, exp);
+
+ if (exp == error_mark_node)
+ error (" in thrown expression");
+
+ expand_expr (exp, const0_rtx, VOIDmode, 0);
+ expand_eh_region_end (build_terminate_handler ());
+ expand_end_target_temps ();
+
+ throw_type = build_eh_type (object);
+
+ if (TYPE_HAS_DESTRUCTOR (TREE_TYPE (object)))
+ {
+ cleanup = lookup_fnfields (TYPE_BINFO (TREE_TYPE (object)),
+ 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;
+ }
+
+ /* 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)
+ {
+ cleanup = build_int_2 (0, 0);
+ TREE_TYPE (cleanup) = cleanup_type;
+ }
+
+ fn = get_identifier ("__cp_push_exception");
+ 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;
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ 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 = build_lang_decl (FUNCTION_DECL, fn,
+ build_function_type (void_type_node, tmp));
+ DECL_EXTERNAL (fn) = 1;
+ TREE_PUBLIC (fn) = 1;
+ DECL_ARTIFICIAL (fn) = 1;
+ pushdecl_top_level (fn);
+ make_function_rtl (fn);
+ pop_obstacks ();
+ }
+
+ mark_used (fn);
+ e = expr_tree_cons (NULL_TREE, exp, expr_tree_cons
+ (NULL_TREE, throw_type, expr_tree_cons
+ (NULL_TREE, cleanup, NULL_TREE)));
+ e = build_function_call (fn, e);
+ expand_expr (e, const0_rtx, VOIDmode, 0);
+ }
+ else
+ {
+ /* rethrow current exception; note that it's no longer caught. */
+
+ tree fn = get_identifier ("__uncatch_exception");
+ if (IDENTIFIER_GLOBAL_VALUE (fn))
+ fn = IDENTIFIER_GLOBAL_VALUE (fn);
+ else
+ {
+ /* Declare void __uncatch_exception (void)
+ as defined in exception.cc. */
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ fn = build_lang_decl (FUNCTION_DECL, fn,
+ build_function_type (void_type_node,
+ void_list_node));
+ DECL_EXTERNAL (fn) = 1;
+ TREE_PUBLIC (fn) = 1;
+ DECL_ARTIFICIAL (fn) = 1;
+ pushdecl_top_level (fn);
+ make_function_rtl (fn);
+ pop_obstacks ();
+ }
+
+ mark_used (fn);
+ exp = build_function_call (fn, NULL_TREE);
+ expand_expr (exp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ }
+
+ expand_internal_throw ();
+}
+
+/* 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");
+
+ e = build1 (THROW_EXPR, void_type_node, e);
+ TREE_SIDE_EFFECTS (e) = 1;
+ TREE_USED (e) = 1;
+
+ return e;
+}