aboutsummaryrefslogtreecommitdiff
path: root/gnu/usr.bin/cc/cc1plus/cp-except.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/cc/cc1plus/cp-except.c')
-rw-r--r--gnu/usr.bin/cc/cc1plus/cp-except.c1221
1 files changed, 1221 insertions, 0 deletions
diff --git a/gnu/usr.bin/cc/cc1plus/cp-except.c b/gnu/usr.bin/cc/cc1plus/cp-except.c
new file mode 100644
index 000000000000..6f5dc3b09796
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/cp-except.c
@@ -0,0 +1,1221 @@
+/* Handle exceptional things in C++.
+ Copyright (C) 1989, 1992, 1993 Free Software Foundation, Inc.
+ Contributed by Michael Tiemann (tiemann@cygnus.com)
+
+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, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* High-level class interface. */
+
+#include "config.h"
+#include "tree.h"
+#include "rtl.h"
+#include "cp-tree.h"
+#include "flags.h"
+/* On Suns this can get you to the right definition if you
+ set the right value for TARGET. */
+#include <setjmp.h>
+#ifdef sequent
+/* Can you believe they forgot this? */
+#define _JBLEN 11
+#endif
+
+#ifndef _JBLEN
+#define _JBLEN (sizeof(jmp_buf)/sizeof(int))
+#endif
+
+#undef NULL
+#define NULL (char *)0
+
+/* This should be part of `ansi_opname', or at least be defined by the std. */
+#define EXCEPTION_NAME_PREFIX "__ex"
+#define EXCEPTION_NAME_LENGTH 4
+
+void init_exception_processing ();
+void init_exception_processing_1 ();
+
+/* If non-zero, a VAR_DECL whose cleanup will cause a throw to the
+ next exception handler. Its value says whether to throw or not.
+ In the case of functions which do not issue a RAISE, it should be
+ possible to optimize away this VAR_DECL (and overhead associated
+ with it). */
+tree exception_throw_decl;
+/* Use this to know that we did not set `exception_throw_decl',
+ until GCC optimizer is smart enough to figure it out for itself. */
+int sets_exception_throw_decl;
+
+/* The exception `type' currently in scope, or NULL_TREE if none. */
+tree current_exception_type;
+
+/* The exception handler object for the given scope. */
+tree current_exception_decl;
+rtx current_exception_name_as_rtx;
+rtx current_exception_parms_as_rtx;
+
+/* The ``object'' view of the current exception parameters.
+ We cast up from the `parms' field to `current_exception_type'. */
+tree current_exception_object;
+
+/* Cache `setjmp', `longjmp', `raise_exception', and `unhandled_exception'
+ after default conversion. Maybe later they will get built-in. */
+static tree BISJ, BILJ, BIR, BIUE;
+
+/* Local variables which give the appearance that exception
+ handling is part of the language and the execution model. */
+
+/* The type of the exception handler stack. */
+static tree EHS_type;
+
+/* The global handler stack. */
+tree EHS_decl;
+
+/* Cached component refs to fields of `EHS_decl'. */
+static tree EHS_prev, EHS_handler, EHS_parms, EHS_name;
+static rtx EHS_parms_as_rtx, EHS_name_as_rtx;
+
+/* The parameter names of this exception type. */
+
+static tree last_exception_fields;
+static tree last_exception_field_types;
+
+/* When ID is VOID_TYPE_NODE, it means ``raise all''.
+ Cannot be inline, since it uses `alloca', and that
+ breaks code which pushes the result of this function
+ on the stack. */
+static tree
+exception_object_name (prefix, id)
+ tree prefix;
+ tree id;
+{
+ /* First, cons up the `name' of this exception. */
+ char *name;
+ int length = (id == void_type_node ? 3 : IDENTIFIER_LENGTH (id)) + EXCEPTION_NAME_LENGTH;
+
+ if (prefix)
+ length += IDENTIFIER_LENGTH (prefix) + 2;
+
+ name = (char *)alloca (length);
+ strcpy (name, EXCEPTION_NAME_PREFIX);
+ length = EXCEPTION_NAME_LENGTH;
+ if (prefix)
+ {
+ strcpy (name + length, IDENTIFIER_POINTER (prefix));
+#ifdef JOINER
+ name[length + IDENTIFIER_LENGTH (prefix)] = JOINER;
+#else
+ name[length + IDENTIFIER_LENGTH (prefix)] = '_';
+#endif
+ length += IDENTIFIER_LENGTH (prefix) + 1;
+ }
+ if (id == void_type_node)
+ strcpy (name + length, "all");
+ else
+ strcpy (name + length, IDENTIFIER_POINTER (id));
+ return get_identifier (name);
+}
+
+tree
+lookup_exception_cname (ctype, cname, raise_id)
+ tree ctype, cname;
+ tree raise_id;
+{
+ tree this_cname = TREE_PURPOSE (raise_id);
+ if (this_cname == NULL_TREE)
+ {
+ if (cname)
+ {
+ tree name = TREE_VALUE (raise_id);
+ if (purpose_member (name, CLASSTYPE_TAGS (ctype)))
+ this_cname = cname;
+ }
+ }
+ else if (this_cname == void_type_node)
+ this_cname = NULL_TREE;
+ else if (TREE_CODE (this_cname) != IDENTIFIER_NODE)
+ {
+ sorry ("multiple scope refs in `cplus_expand_raise_stmt'");
+ this_cname = error_mark_node;
+ }
+ return this_cname;
+}
+
+tree
+lookup_exception_tname (oname)
+ tree oname;
+{
+ return get_identifier (IDENTIFIER_POINTER (oname) + EXCEPTION_NAME_LENGTH);
+}
+
+tree
+lookup_exception_object (cname, name, complain)
+ tree cname, name;
+ int complain;
+{
+ tree oname;
+ tree decl;
+
+ if (cname == void_type_node)
+ cname = NULL_TREE;
+ else if (cname && TREE_CODE (cname) != IDENTIFIER_NODE)
+ {
+ sorry ("multiple scope refs in `lookup_exception_object'");
+ cname = NULL_TREE;
+ }
+ oname = exception_object_name (cname, name);
+ decl = IDENTIFIER_GLOBAL_VALUE (oname);
+ if (decl == NULL_TREE || TREE_CODE (decl) != VAR_DECL)
+ {
+ if (complain)
+ {
+ push_obstacks_nochange ();
+
+ if (cname)
+ error ("no exception name object for name `%s::%s'",
+ IDENTIFIER_POINTER (cname),
+ IDENTIFIER_POINTER (name));
+ else
+ error ("no exception name object for name `%s'",
+ IDENTIFIER_POINTER (name));
+ end_temporary_allocation ();
+ /* Avoid further error messages. */
+ pushdecl_top_level (build_lang_field_decl (VAR_DECL,
+ exception_object_name (cname, name),
+ error_mark_node));
+ pop_obstacks ();
+ }
+ return NULL_TREE;
+ }
+ return decl;
+}
+
+tree
+lookup_exception_type (ctype, cname, raise_id)
+ tree ctype, cname;
+ tree raise_id;
+{
+ tree name = TREE_VALUE (raise_id);
+ tree purpose = TREE_PURPOSE (raise_id);
+
+ if (cname && purpose == NULL_TREE)
+ purpose = cname;
+
+ if (purpose && purpose != void_type_node)
+ {
+ tree link = NULL_TREE;
+
+ if (TREE_CODE (purpose) != IDENTIFIER_NODE)
+ {
+ sorry ("multiple scope refs in `lookup_exception_type'");
+ TREE_PURPOSE (raise_id) = NULL_TREE;
+ return NULL_TREE;
+ }
+ if (! is_aggr_typedef (purpose, 1))
+ return NULL_TREE;
+ ctype = IDENTIFIER_TYPE_VALUE (purpose);
+ link = purpose_member (name, CLASSTYPE_TAGS (ctype));
+ if (link)
+ return TREE_VALUE (link);
+ }
+
+ ctype = lookup_name (name, 1);
+ if (ctype && TREE_CODE (ctype) == TYPE_DECL)
+ ctype = TREE_TYPE (ctype);
+ if (ctype && TREE_CODE (ctype) == RECORD_TYPE
+ && CLASSTYPE_DECLARED_EXCEPTION (ctype))
+ return ctype;
+ return NULL_TREE;
+}
+
+tree
+finish_exception (e, list_of_fieldlists)
+ tree e;
+ tree list_of_fieldlists;
+{
+ tree parmtypes = NULL_TREE, name_field;
+ tree cname = TYPE_NAME (e);
+
+ if (TREE_CODE (cname) == TYPE_DECL)
+ cname = DECL_NAME (cname);
+
+ if (last_exception_fields)
+ error ("cannot declare exceptions within exceptions");
+ if (list_of_fieldlists && ! ANON_AGGRNAME_P (cname))
+ error_with_aggr_type (e, "exception name `%s' must follow body declaration");
+ if (list_of_fieldlists)
+ {
+ tree prev, field;
+
+ /* Note: no public, private, or protected allowed. */
+ if (TREE_CHAIN (list_of_fieldlists))
+ error ("visibility declarations invalid in exception declaration");
+ else if (TREE_PURPOSE (list_of_fieldlists) != (tree)visibility_default)
+ error ("visibility declarations invalid in exception declaration");
+ TREE_PURPOSE (list_of_fieldlists) = (tree)visibility_default;
+
+ /* Note also: no member function declarations allowed. */
+ for (prev = 0, field = TREE_VALUE (list_of_fieldlists);
+ field; prev = field, field = TREE_CHAIN (field))
+ {
+ switch (TREE_CODE (field))
+ {
+ case FIELD_DECL:
+ /* ok. */
+ parmtypes = tree_cons (NULL_TREE, TREE_TYPE (field), parmtypes);
+ continue;
+ case FUNCTION_DECL:
+ error_with_decl (field, "declaration of function `%s' in exception invalid");
+ break;
+ case VAR_DECL:
+ if (TREE_STATIC (field))
+ error_with_decl (field, "declaration of static variable `%s' in exception invalid");
+ else
+ error_with_decl (field, "declaration of constant field `%s' in exception invalid");
+ break;
+ case CONST_DECL:
+ error_with_decl (field, "declaration of enum value `%s' in exception invalid");
+ break;
+ case SCOPE_REF:
+ error ("use of `::' in exception context invalid");
+ break;
+ }
+ if (prev)
+ TREE_CHAIN (prev) = TREE_CHAIN (field);
+ else
+ TREE_VALUE (list_of_fieldlists) = TREE_CHAIN (field);
+ }
+ }
+
+ /* Now that we've cleaned up the fields, add a name identifier at front. */
+ name_field = build_lang_field_decl (FIELD_DECL, get_identifier ("__name"),
+ ptr_type_node);
+ if (list_of_fieldlists)
+ {
+ TREE_CHAIN (name_field) = TREE_VALUE (list_of_fieldlists);
+ TREE_VALUE (list_of_fieldlists) = name_field;
+ }
+ else
+ list_of_fieldlists = build_tree_list (NULL_TREE, name_field);
+
+ last_exception_fields = TREE_VALUE (list_of_fieldlists);
+ if (parmtypes)
+ {
+ last_exception_field_types = nreverse (parmtypes);
+ /* Set the TREE_CHAIN of what is now at the end of the
+ list to `void_list_node'. */
+ TREE_CHAIN (parmtypes) = void_list_node;
+ }
+ else
+ last_exception_field_types = void_list_node;
+
+ popclass (0);
+
+#if 0
+ /* Remove aggregate types from the list of tags,
+ since these appear at global scope. */
+ while (x && IS_AGGR_TYPE (TREE_VALUE (x)))
+ x = TREE_CHAIN (x);
+ CLASSTYPE_TAGS (t) = x;
+ y = x;
+ while (x)
+ {
+ if (IS_AGGR_TYPE (TREE_VALUE (x)))
+ TREE_CHAIN (y) = TREE_CHAIN (x);
+ x = TREE_CHAIN (x);
+ }
+#endif
+
+ if (flag_cadillac)
+ cadillac_finish_exception (e);
+
+ return e;
+}
+
+void
+finish_exception_decl (cname, decl)
+ tree cname, decl;
+{
+ /* In cp-decl.h. */
+ extern tree last_function_parms;
+
+ /* An exception declaration. */
+ tree t, ctor;
+ tree parmdecls = NULL_TREE, fields;
+ tree list_of_fieldlists = temp_tree_cons (NULL_TREE,
+ copy_list (last_exception_fields),
+ NULL_TREE);
+ tree edecl = build_lang_field_decl (VAR_DECL,
+ exception_object_name (cname, DECL_NAME (decl)),
+ ptr_type_node);
+
+ DECL_LANGUAGE (edecl) = lang_c;
+ TREE_STATIC (edecl) = 1;
+ TREE_PUBLIC (edecl) = 1;
+ finish_decl (pushdecl (edecl), NULL_TREE, NULL_TREE, 0);
+
+ /* Now instantiate the exception decl. */
+ t = xref_tag (exception_type_node, DECL_NAME (decl), NULL_TREE);
+
+ /* finish_struct will pop this. */
+ pushclass (t, 0);
+
+ /* Now add a constructor which takes as parameters all the types we
+ just defined. */
+ ctor = build_lang_decl (FUNCTION_DECL, DECL_NAME (decl),
+ build_cplus_method_type (t, TYPE_POINTER_TO (t),
+ last_exception_field_types));
+ /* Don't take `name'. The constructor handles that. */
+ fields = TREE_CHAIN (TREE_VALUE (list_of_fieldlists));
+ while (fields)
+ {
+ tree parm = build_decl (PARM_DECL, DECL_NAME (fields), TREE_TYPE (fields));
+ /* Since there is a prototype, args are passed in their own types. */
+ DECL_ARG_TYPE (parm) = TREE_TYPE (parm);
+#ifdef PROMOTE_PROTOTYPES
+ if (TREE_CODE (TREE_TYPE (fields)) == INTEGER_TYPE
+ && TYPE_PRECISION (TREE_TYPE (fields)) < TYPE_PRECISION (integer_type_node))
+ DECL_ARG_TYPE (parm) = integer_type_node;
+#endif
+ TREE_CHAIN (parm) = parmdecls;
+ parmdecls = parm;
+ fields = TREE_CHAIN (fields);
+ }
+ fields = TREE_VALUE (list_of_fieldlists);
+ last_function_parms = nreverse (parmdecls);
+
+ DECL_CONSTRUCTOR_P (ctor) = 1;
+ TYPE_HAS_CONSTRUCTOR (t) = 1;
+ grokclassfn (t, DECL_NAME (decl), ctor, NO_SPECIAL, NULL_TREE);
+ DECL_EXTERNAL (ctor) = 1;
+ TREE_STATIC (ctor) = 1;
+ TREE_PUBLIC (ctor) = 0;
+ DECL_INLINE (ctor) = 1;
+ make_decl_rtl (ctor, NULL_PTR, 1);
+ finish_decl (ctor, NULL_TREE, NULL_TREE, 0);
+ TREE_CHAIN (ctor) = TREE_VALUE (list_of_fieldlists);
+ TREE_VALUE (list_of_fieldlists) = ctor;
+
+ finish_struct (t, list_of_fieldlists, 0);
+
+ if (current_function_decl)
+ error ("cannot define exception inside function scope");
+ else
+ {
+ enum debug_info_type old_write_symbols = write_symbols;
+ write_symbols = NO_DEBUG;
+
+ /* Now build the constructor for this exception. */
+ parmdecls = DECL_ARGUMENTS (ctor);
+ start_function (NULL_TREE, ctor, 0, 1);
+ store_parm_decls ();
+ pushlevel (0);
+ clear_last_expr ();
+ push_momentary ();
+ expand_start_bindings (0);
+
+ /* Move all the parameters to the fields, skipping `this'. */
+ parmdecls = TREE_CHAIN (parmdecls);
+ /* Install `name' of this exception handler. */
+ DECL_INITIAL (fields) = build_unary_op (ADDR_EXPR, edecl, 0);
+ fields = TREE_CHAIN (fields);
+ /* Install all the values. */
+ while (fields)
+ {
+ /* Set up the initialization for this field. */
+ DECL_INITIAL (fields) = parmdecls;
+ fields = TREE_CHAIN (fields);
+ parmdecls = TREE_CHAIN (parmdecls);
+ }
+ emit_base_init (t, 0);
+
+ finish_function (DECL_SOURCE_LINE (ctor), 1);
+ write_symbols = old_write_symbols;
+ }
+}
+
+void
+end_exception_decls ()
+{
+ last_exception_field_types = NULL_TREE;
+ last_exception_fields = NULL_TREE;
+}
+
+/* Statement-level exception semantics. */
+
+void
+cplus_expand_start_try (implicit)
+ int implicit;
+{
+ tree call_to_setjmp;
+ tree handler, ref;
+
+ /* Start a new block enclosing the whole handler. */
+ if (implicit)
+ {
+ pushlevel_temporary (1);
+ }
+ else
+ {
+ pushlevel (0);
+ clear_last_expr ();
+ push_momentary ();
+
+ /* Encompass whole exception handler in one big binding contour.
+ If RAISE should throw out of the whole TRY/EXCEPT block, call
+ `expand_start_bindings' with argument of 1. */
+ expand_start_bindings (0);
+ }
+
+ /* Allocate handler in that block. It's real name will come later.
+ Note that it will be the first name in this binding contour. */
+ handler = get_temp_name (EHS_type, 0);
+ DECL_INITIAL (handler) = error_mark_node;
+ finish_decl (handler, NULL_TREE, NULL_TREE, 0);
+
+ /* Must come after call to `finish_decl', else the cleanup for the temp
+ for the handler will cause the contour we just created to be popped. */
+ if (implicit)
+ declare_implicit_exception ();
+
+ /* Catch via `setjmp'. */
+ ref = build_component_ref (handler, get_identifier ("handler"), NULL_TREE, 0);
+ call_to_setjmp = build_function_call (BISJ, build_tree_list (NULL_TREE, ref));
+
+ /* RAISE throws to EXCEPT part. */
+ expand_start_try (build_binary_op (EQ_EXPR, call_to_setjmp, integer_zero_node, 1), 0, 1);
+}
+
+/* If KEEP is 1, then declarations in the TRY statement are worth keeping.
+ If KEEP is 2, then the TRY statement was generated by the compiler.
+ If KEEP is 0, the declarations in the TRY statement contain errors. */
+
+tree
+cplus_expand_end_try (keep)
+ int keep;
+{
+ tree decls, decl, block;
+
+ if (keep < 2)
+ pop_implicit_try_blocks (NULL_TREE);
+
+ decls = getdecls ();
+
+ /* Emit code to avoid falling through into a default
+ handler that might come later. */
+ expand_end_try ();
+
+ /* Pops binding contour local to TRY, and get the exception handler
+ object built by `...start_try'. */
+ switch (keep)
+ {
+ case 0:
+ expand_end_bindings (decls, 0, 1);
+ block = poplevel (0, 0, 0);
+ pop_momentary ();
+ decl = getdecls ();
+ break;
+
+ case 1:
+ expand_end_bindings (decls, 1, 1);
+ block = poplevel (1, 1, 0);
+ pop_momentary ();
+ decl = getdecls ();
+ break;
+
+ default:
+ decl = tree_last (decls);
+ block = NULL_TREE;
+ break;
+ }
+
+ my_friendly_assert (TREE_CODE (decl) == VAR_DECL
+ && TREE_TYPE (decl) == EHS_type, 203);
+ if (block)
+ {
+ BLOCK_HANDLER_BLOCK (block) = 1;
+ TREE_USED (block) = 1;
+ }
+
+ /* Pass it back so that its rtl can be bound to its name
+ (or vice versa). */
+ return decl;
+}
+
+void
+cplus_expand_start_except (name, decl)
+ tree name, decl;
+{
+ int yes;
+ tree tmp, init;
+
+ expand_start_except (0, 1);
+
+ /* This is internal `eh'. */
+ current_exception_decl = decl;
+ current_exception_name_as_rtx
+ = expand_expr (build (COMPONENT_REF, ptr_type_node,
+ current_exception_decl, TREE_OPERAND (EHS_name, 1)),
+ 0, 0, 0);
+ init = build (COMPONENT_REF, ptr_type_node, decl, TREE_OPERAND (EHS_parms, 1));
+ current_exception_parms_as_rtx = expand_expr (init, 0, 0, 0);
+
+ if (name)
+ {
+ /* Get the exception object into scope (user declared `ex'). */
+ tmp = pushdecl (build_decl (VAR_DECL, name, ptr_type_node));
+ DECL_INITIAL (tmp) = error_mark_node;
+ finish_decl (tmp, init, 0, 0);
+ }
+ current_exception_type = NULL_TREE;
+ yes = suspend_momentary ();
+ if (name)
+ {
+ /* From now on, send the user to our faked-up object. */
+ current_exception_object = build1 (INDIRECT_REF, void_type_node, tmp);
+ IDENTIFIER_LOCAL_VALUE (name) = current_exception_object;
+ }
+ resume_momentary (yes);
+
+ /* Pop exception handler stack. */
+ expand_assignment (EHS_decl, EHS_prev, 0, 0);
+}
+
+/* Generate the call to `unhandled_exception' that is appropriate
+ for this particular unhandled exception. */
+static tree
+call_to_unhandled_exception ()
+{
+ extern int lineno;
+ extern tree combine_strings ();
+ tree parms = tree_cons (NULL_TREE,
+ combine_strings (build_string (strlen (input_filename + 1), input_filename)),
+ build_tree_list (NULL_TREE, build_int_2 (lineno, 0)));
+ return build_function_call (BIUE, parms);
+}
+
+/* Note that this must be mirror image of `...start_try'.
+ DFAULT is the default clause, if there was one.
+ DFAULT is ERROR_MARK_NODE when this ends an implicit handler. */
+void
+cplus_expand_end_except (dfault)
+ tree dfault;
+{
+ extern tree expand_end_except (); /* stmt.c. */
+ tree decls, raised;
+
+ if (dfault == NULL_TREE)
+ {
+ /* Uncaught exception at outermost level. If raised locally,
+ reraise the exception. Otherwise, generate code to call `abort'. */
+ if (in_try_block (1) == 0)
+ {
+ expand_start_cond (build (EQ_EXPR, integer_type_node,
+ exception_throw_decl, integer_zero_node), 0);
+ expand_expr (call_to_unhandled_exception (), 0, VOIDmode, 0);
+ expand_end_cond ();
+ }
+ /* Try the next handler. */
+ if (! expand_escape_except ())
+ compiler_error ("except nesting botch");
+ }
+
+ raised = expand_end_except ();
+
+ decls = getdecls ();
+ expand_end_bindings (decls, decls != 0, 1);
+ poplevel (decls != 0, 1, 0);
+
+ /* Implicit handlers do not use the momentary obstack. */
+ if (dfault != error_mark_node)
+ pop_momentary ();
+
+ if (! in_try_block (1))
+ {
+ /* Check that this function is not raising exceptions
+ it is not supposed to. */
+ while (raised)
+ {
+ error_with_decl (TREE_VALUE (raised), "exception `%s' raised but not declared raisable");
+ raised = TREE_CHAIN (raised);
+ }
+ }
+ else if (dfault == NULL_TREE || dfault == error_mark_node)
+ {
+ expand_start_cond (build (NE_EXPR, integer_type_node,
+ exception_throw_decl,
+ integer_zero_node), 0);
+ /* We fell off the end of this try block. Try going to the next.
+ The escape_label will be the beginning of the next try block. */
+ if (! expand_escape_except ())
+ compiler_error ("except nesting botch");
+ expand_end_cond ();
+ }
+}
+
+/* Generate code to raise exception RAISE_ID.
+ If EXP is NULL_TREE, then PARMS is the list of parameters to use
+ for constructing this exception.
+ If EXP is non-NULL, then it is an already constructed object
+ of the kind that we want.
+
+ FOR_RERAISE is non-zero if this raise is called by reraise. In
+ this case we do not need to emit extra gotos to avoid warning messages;
+ the caller will do that once after all the exceptions it reraises
+ are handled and raised. */
+void
+cplus_expand_raise (raise_id, parms, exp, for_reraise)
+ tree raise_id;
+ tree parms;
+ tree exp;
+ int for_reraise;
+{
+ /* Allocate new exception of appropriate type, passing
+ PARMS to its constructor. */
+ tree cname, name;
+ tree decl;
+ tree xexp = exp;
+
+ cname = lookup_exception_cname (current_class_type, current_class_name, raise_id);
+ if (cname == error_mark_node)
+ return;
+ name = TREE_VALUE (raise_id);
+
+ decl = lookup_exception_object (cname, name, 1);
+ if (decl == NULL_TREE)
+ return;
+
+ if (exp == NULL_TREE)
+ {
+ exp = build_method_call (NULL_TREE, name, parms, NULL_TREE, LOOKUP_COMPLAIN);
+ if (exp == error_mark_node)
+ return;
+ }
+
+ if (in_try_block (1))
+ {
+ expand_raise (decl);
+ }
+ else if (! current_function_decl)
+ {
+ if (xexp == NULL_TREE)
+ error_with_decl (decl, "invalid raise of `%s' outside of functions");
+ else
+ error_with_decl (decl, "invalid reraise of `%s' outside of functions");
+ }
+ else
+ {
+ /* Test this raise against what this function permits. */
+ tree names = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl));
+ while (names)
+ {
+ if (decl == TREE_TYPE (names))
+ break;
+ names = TREE_CHAIN (names);
+ }
+ if (names == NULL_TREE)
+ {
+ error ("current function not declared to raise exception `%s'",
+ IDENTIFIER_POINTER (name));
+ return;
+ }
+ }
+
+ store_expr (exp, EHS_parms_as_rtx, 0);
+
+ /* Set the global exception handler stack's NAME field
+ to the `name' of this exception. The global exception
+ handler stack is the container for the exception object
+ we just built.
+
+ We go through a function call to make life easier when debugging. */
+#if 0
+ expand_assignment (EHS_name, build_unary_op (ADDR_EXPR, decl, 0), 0, 0);
+#else
+ parms = tree_cons (NULL_TREE, build_unary_op (ADDR_EXPR, EHS_name, 0),
+ build_tree_list (NULL_TREE,
+ build_unary_op (ADDR_EXPR, decl, 0)));
+ expand_expr (build_function_call (BIR, parms), 0, 0, 0);
+#endif
+
+ /* Activate thrower. If we are inside a TRY statement,
+ we can cheat and not do this, saving a longjmp. */
+ if (in_try_block (1) == 0)
+ {
+ sets_exception_throw_decl = 1;
+ emit_move_insn (DECL_RTL (exception_throw_decl), const1_rtx);
+ }
+
+ if (xexp == NULL_TREE)
+ {
+ /* Invoke destructors for current procedure or handler. */
+ if (! expand_escape_except ())
+ compiler_error ("except nesting botch");
+ /* Throw via `longjmp'... Done as side-effect of goto. */
+ }
+ /* To avoid spurious warning messages, we add a goto to the end
+ of the function. This code is dead, and the compiler should
+ know how to delete it, but for now, we are stuck with it. */
+ if (! for_reraise
+ && TREE_TYPE (DECL_RESULT (current_function_decl)) != void_type_node)
+ expand_null_return ();
+}
+
+extern tree cplus_exception_name ();
+
+tree
+ansi_exception_object_lookup (type)
+ tree type;
+{
+ tree raise_id = cplus_exception_name (type);
+ tree decl;
+
+ decl = IDENTIFIER_GLOBAL_VALUE (raise_id);
+ if (decl == NULL_TREE || TREE_CODE (decl) != VAR_DECL)
+ {
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ decl = build_decl (VAR_DECL, raise_id, ptr_type_node);
+ TREE_PUBLIC (decl) = 1;
+ TREE_STATIC (decl) = 1;
+ pushdecl_top_level (decl);
+ make_decl_rtl (decl, (char*)0, 1);
+ pop_obstacks ();
+ }
+ return decl;
+}
+
+/* Generate code to throw an exception using EXP.
+ Usng ANSI syntax and semantics.
+ If EXP is NULL_TREE< re-raise instead. */
+
+void
+cplus_expand_throw (exp)
+ tree exp;
+{
+ tree parms;
+ int for_reraise;
+ /* Allocate new exception of appropriate type, passing
+ PARMS to its constructor. */
+ tree decl = ansi_exception_object_lookup (TREE_TYPE (exp));
+ tree xexp = exp;
+
+ if (in_try_block (1))
+ {
+#if 1
+ my_friendly_abort (35);
+#else
+ expand_raise (decl);
+#endif
+ }
+ else if (! current_function_decl)
+ error ("invalid throw outside of functions");
+ else
+ {
+#if 0
+ /* Test this raise against what this function permits. */
+ tree names = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl));
+ while (names)
+ {
+ if (decl == TREE_TYPE (names))
+ break;
+ names = TREE_CHAIN (names);
+ }
+ if (names == NULL_TREE)
+ {
+ error ("current function not declared to raise exception `%s'",
+ IDENTIFIER_POINTER (name));
+ return;
+ }
+#endif
+ }
+
+ store_expr (exp, EHS_parms_as_rtx, 0);
+
+ /* Set the global exception handler stack's NAME field
+ to the `name' of this exception. The global exception
+ handler stack is the container for the exception object
+ we just built.
+
+ We go through a function call to make life easier when debugging. */
+#if 0
+ expand_assignment (EHS_name, build_unary_op (ADDR_EXPR, decl, 0), 0, 0);
+#else
+ parms = tree_cons (NULL_TREE, build_unary_op (ADDR_EXPR, EHS_name, 0),
+ build_tree_list (NULL_TREE,
+ build_unary_op (ADDR_EXPR, decl, 0)));
+ expand_expr (build_function_call (BIR, parms), 0, 0, 0);
+#endif
+
+ /* Activate thrower. If we are inside a TRY statement,
+ we can cheat and not do this, saving a longjmp. */
+ if (in_try_block (1) == 0)
+ {
+ sets_exception_throw_decl = 1;
+ emit_move_insn (DECL_RTL (exception_throw_decl), const1_rtx);
+ }
+
+ if (xexp == NULL_TREE)
+ {
+ /* Invoke destructors for current procedure or handler. */
+ if (! expand_escape_except ())
+ compiler_error ("except nesting botch");
+ /* Throw via `longjmp'... Done as side-effect of goto. */
+ }
+
+ /* XXX: for_reraise is never set above here. */
+ /* To avoid spurious warning messages, we add a goto to the end
+ of the function. This code is dead, and the compiler should
+ know how to delete it, but for now, we are stuck with it. */
+ if (! for_reraise
+ && TREE_TYPE (DECL_RESULT (current_function_decl)) != void_type_node)
+ expand_null_return ();
+}
+
+tree
+cplus_expand_start_catch (raise_id)
+ tree raise_id;
+{
+ tree cname = lookup_exception_cname (current_class_type, current_class_name, raise_id);
+ tree decl;
+ tree cond;
+
+ if (cname == error_mark_node)
+ {
+ decl = error_mark_node;
+ cond = error_mark_node;
+ }
+ else
+ {
+ decl = lookup_exception_object (cname, TREE_VALUE (raise_id), 1);
+ if (decl == NULL_TREE)
+ cond = error_mark_node;
+ else
+ cond = build_binary_op (EQ_EXPR, build_unary_op (ADDR_EXPR, decl, 0),
+ build (COMPONENT_REF, ptr_type_node,
+ current_exception_decl,
+ TREE_OPERAND (EHS_name, 1)),
+ 1);
+ }
+ expand_start_cond (cond, 0);
+
+ /* Does nothing right now. */
+ expand_catch (decl);
+ if (current_exception_type
+ && TYPE_NEEDS_DESTRUCTOR (current_exception_type))
+ {
+ /* Make a cleanup for the name-specific exception object now in scope. */
+ tree cleanup = maybe_build_cleanup (current_exception_object);
+ expand_start_bindings (0);
+ expand_decl_cleanup (NULL_TREE, cleanup);
+ }
+ return decl;
+}
+tree
+ansi_expand_start_catch (raise_type)
+ tree raise_type;
+{
+ tree decl = ansi_exception_object_lookup (raise_type);
+ tree cond;
+
+ if (decl == NULL_TREE)
+ cond = error_mark_node;
+ else
+ cond = build_binary_op (EQ_EXPR, build_unary_op (ADDR_EXPR, decl, 0),
+ build (COMPONENT_REF, ptr_type_node,
+ current_exception_decl,
+ TREE_OPERAND (EHS_name, 1)),
+ 1);
+ expand_start_cond (cond, 0);
+
+ /* Does nothing right now. */
+ expand_catch (decl);
+ return decl;
+}
+
+void
+cplus_expand_end_catch (for_reraise)
+ int for_reraise;
+{
+ if (current_exception_type
+ && TYPE_NEEDS_DESTRUCTOR (current_exception_type))
+ {
+ /* Destroy the specific exception object now in scope. */
+ expand_end_bindings (getdecls (), 0, 1);
+ }
+ if (for_reraise)
+ {
+ if (! expand_escape_except ())
+ my_friendly_abort (36);
+ }
+ else
+ {
+ if (! expand_end_catch ())
+ my_friendly_abort (37);
+ }
+ expand_end_cond ();
+}
+
+/* Reraise an exception.
+ If EXCEPTIONS is NULL_TREE, it means reraise whatever exception was caught.
+ If EXCEPTIONS is an IDENTIFIER_NODE, it means reraise the exception
+ object named by EXCEPTIONS. This must be a variable declared in
+ an `except' clause.
+ If EXCEPTIONS is a TREE_LIST, it is the list of exceptions we are
+ willing to reraise. */
+
+void
+cplus_expand_reraise (exceptions)
+ tree exceptions;
+{
+ tree ex_ptr;
+ tree ex_object = current_exception_object;
+ rtx ex_ptr_as_rtx;
+
+ if (exceptions && TREE_CODE (exceptions) == IDENTIFIER_NODE)
+ {
+ /* Don't get tripped up if its TREE_TYPE is `error_mark_node'. */
+ ex_object = IDENTIFIER_LOCAL_VALUE (exceptions);
+ if (ex_object == NULL_TREE || TREE_CODE (ex_object) != INDIRECT_REF)
+ {
+ error ("`%s' is not an exception decl", IDENTIFIER_POINTER (exceptions));
+ return;
+ }
+ my_friendly_assert (TREE_CODE (TREE_OPERAND (ex_object, 0)) == VAR_DECL,
+ 204);
+ exceptions = NULL_TREE;
+ }
+
+ ex_ptr = build1 (NOP_EXPR, ptr_type_node, TREE_OPERAND (ex_object, 0));
+ ex_ptr_as_rtx = expand_expr (ex_ptr, 0, 0, 0);
+
+ /* reraise ALL, used by compiler. */
+ if (exceptions == NULL_TREE)
+ {
+ /* Now treat reraise like catch/raise. */
+ expand_catch (error_mark_node);
+ expand_raise (error_mark_node);
+ emit_move_insn (EHS_name_as_rtx, current_exception_name_as_rtx);
+ store_expr ((tree) EHS_parms_as_rtx, current_exception_parms_as_rtx, 0);
+ if (in_try_block (1) == 0)
+ {
+ sets_exception_throw_decl = 1;
+ emit_move_insn (DECL_RTL (exception_throw_decl), const1_rtx);
+ }
+ /* Set to zero so that destructor will not be called. */
+ emit_move_insn (ex_ptr_as_rtx, const0_rtx);
+ if (! expand_escape_except ())
+ my_friendly_abort (38);
+
+ /* To avoid spurious warning messages, we add a goto to the end
+ of the function. This code is dead, and the compiler should
+ know how to delete it, but for now, we are stuck with it. */
+ if (TREE_TYPE (DECL_RESULT (current_function_decl)) != void_type_node)
+ expand_null_return ();
+
+ return;
+ }
+
+ /* reraise from a list of exceptions. */
+ while (exceptions)
+ {
+ tree type = lookup_exception_type (current_class_type, current_class_name,
+ exceptions);
+ if (type == NULL_TREE)
+ {
+ error ("`%s' is not an exception type",
+ IDENTIFIER_POINTER (TREE_VALUE (exceptions)));
+ current_exception_type = NULL_TREE;
+ TREE_TYPE (ex_object) = error_mark_node;
+ TREE_TYPE (ex_ptr) = error_mark_node;
+ }
+ else
+ {
+ current_exception_type = type;
+ /* In-place union. */
+ TREE_TYPE (ex_object) = type;
+ TREE_TYPE (ex_ptr) = TYPE_POINTER_TO (type);
+ }
+
+ /* Now treat reraise like catch/raise. */
+ cplus_expand_start_catch (exceptions);
+ cplus_expand_raise (exceptions, NULL_TREE, ex_ptr, 1);
+ /* Set to zero so that destructor will not be called. */
+ if (TREE_TYPE (ex_ptr) != error_mark_node)
+ emit_move_insn (ex_ptr_as_rtx, const0_rtx);
+ cplus_expand_end_catch (1);
+ exceptions = TREE_CHAIN (exceptions);
+ }
+ /* Don't propagate any unhandled exceptions. */
+ expand_expr (call_to_unhandled_exception (), 0, VOIDmode, 0);
+
+ /* To avoid spurious warning messages, we add a goto to the end
+ of the function. This code is dead, and the compiler should
+ know how to delete it, but for now, we are stuck with it. */
+ if (TREE_TYPE (DECL_RESULT (current_function_decl)) != void_type_node)
+ expand_null_return ();
+}
+
+void
+setup_exception_throw_decl ()
+{
+ tree call_to_longjmp, parms;
+
+ int old = suspend_momentary ();
+
+ exception_throw_decl = build_decl (VAR_DECL, get_identifier (THROW_NAME), integer_type_node);
+ pushdecl (exception_throw_decl);
+ parms = tree_cons (NULL_TREE, EHS_handler,
+ build_tree_list (0, integer_one_node));
+ call_to_longjmp = build_function_call (BILJ, parms);
+
+ expand_decl (exception_throw_decl);
+ expand_decl_cleanup (exception_throw_decl,
+ build (COND_EXPR, void_type_node,
+ exception_throw_decl,
+ call_to_longjmp, integer_zero_node));
+ DECL_INITIAL (exception_throw_decl) = integer_zero_node;
+ sets_exception_throw_decl = 0;
+ resume_momentary (old);
+
+ /* Cache these, since they won't change throughout the function. */
+ EHS_parms_as_rtx = expand_expr (EHS_parms, 0, 0, 0);
+ EHS_name_as_rtx = expand_expr (EHS_name, 0, 0, 0);
+}
+
+void
+init_exception_processing ()
+{
+ extern tree build_function_type (), define_function ();
+ extern tree unhandled_exception_fndecl;
+ tree cname = get_identifier ("ExceptionHandler");
+ tree field, chain;
+ tree ctor, dtor;
+ tree jmp_buf_type = build_array_type (integer_type_node,
+ build_index_type (build_int_2 (_JBLEN-1, 0)));
+ tree jmp_buf_arg_type = build_pointer_type (integer_type_node);
+
+ tree parmtypes = hash_tree_chain (jmp_buf_arg_type, void_list_node);
+ tree setjmp_fndecl, longjmp_fndecl, raise_fndecl;
+
+ int old_interface_only = interface_only;
+ int old_interface_unknown = interface_unknown;
+ interface_only = 1;
+ interface_unknown = 0;
+ EHS_type = xref_tag (record_type_node, cname, NULL_TREE);
+ push_lang_context (lang_name_c);
+ setjmp_fndecl = define_function ("setjmp",
+ build_function_type (integer_type_node,
+ parmtypes),
+ NOT_BUILT_IN, pushdecl, 0);
+ BISJ = default_conversion (setjmp_fndecl);
+ parmtypes = hash_tree_chain (jmp_buf_arg_type,
+ hash_tree_chain (integer_type_node, void_list_node));
+ longjmp_fndecl = define_function ("longjmp",
+ build_function_type (void_type_node, parmtypes),
+ NOT_BUILT_IN, pushdecl, 0);
+ raise_fndecl = define_function ("__raise_exception",
+ build_function_type (void_type_node,
+ hash_tree_chain (ptr_type_node,
+ hash_tree_chain (build_pointer_type (ptr_type_node), void_list_node))),
+ NOT_BUILT_IN, pushdecl, 0);
+ BILJ = default_conversion (longjmp_fndecl);
+ BIR = default_conversion (raise_fndecl);
+ BIUE = default_conversion (unhandled_exception_fndecl);
+
+ pop_lang_context ();
+
+ /* finish_struct will pop this. */
+ pushclass (EHS_type, 0);
+ field = build_lang_field_decl (FIELD_DECL, get_identifier ("parms"), ptr_type_node);
+ chain = field;
+ field = build_lang_field_decl (FIELD_DECL, get_identifier ("name"),
+ build_pointer_type (default_function_type));
+ TREE_CHAIN (field) = chain;
+ chain = field;
+ field = build_lang_field_decl (FIELD_DECL, get_identifier ("handler"), jmp_buf_type);
+ TREE_CHAIN (field) = chain;
+ chain = field;
+ field = build_lang_field_decl (FIELD_DECL, get_identifier ("prev"),
+ TYPE_POINTER_TO (EHS_type));
+ TREE_CHAIN (field) = chain;
+ chain = field;
+
+ ctor = build_lang_decl (FUNCTION_DECL, cname,
+ build_cplus_method_type (EHS_type, TYPE_POINTER_TO (EHS_type), void_list_node));
+ DECL_CONSTRUCTOR_P (ctor) = 1;
+ TREE_STATIC (ctor) = 1;
+ TREE_PUBLIC (ctor) = 1;
+ DECL_EXTERNAL (ctor) = 1;
+ grokclassfn (EHS_type, cname, ctor, NO_SPECIAL, 0);
+ grok_ctor_properties (EHS_type, ctor);
+ finish_decl (pushdecl (ctor), NULL_TREE, NULL_TREE, 0);
+ /* Must copy the node here because the FUNCTION_DECL
+ used inside the struct ain't the same as the
+ FUNCTION_DECL we stick into the global binding
+ contour. */
+ ctor = copy_node (ctor);
+ TREE_CHAIN (ctor) = chain;
+ chain = ctor;
+ dtor = build_lang_decl (FUNCTION_DECL, cname,
+ build_cplus_method_type (EHS_type, TYPE_POINTER_TO (EHS_type), void_list_node));
+ TREE_STATIC (dtor) = 1;
+ TREE_PUBLIC (dtor) = 1;
+ DECL_EXTERNAL (dtor) = 1;
+ grokclassfn (EHS_type, cname, dtor, DTOR_FLAG, 0);
+ finish_decl (pushdecl (dtor), NULL_TREE, NULL_TREE, 0);
+ /* Copy for the same reason as copying ctor. */
+ dtor = copy_node (dtor);
+ TREE_CHAIN (dtor) = chain;
+ chain = dtor;
+ TYPE_HAS_CONSTRUCTOR (EHS_type) = 1;
+ TYPE_HAS_DESTRUCTOR (EHS_type) = 1;
+ finish_struct (EHS_type, temp_tree_cons (NULL_TREE, chain, NULL_TREE), 0);
+ interface_only = old_interface_only;
+ interface_unknown = old_interface_unknown;
+}
+
+void
+init_exception_processing_1 ()
+{
+ register tree EHS_id = get_identifier ("exceptionHandlerStack");
+
+ EHS_decl = IDENTIFIER_GLOBAL_VALUE (EHS_id);
+
+ /* If we have no other definition, default to library implementation. */
+ if (EHS_decl == NULL_TREE)
+ {
+ EHS_decl = build_decl (VAR_DECL, EHS_id, TYPE_POINTER_TO (EHS_type));
+ /* If we don't push this, its definition, should it be encountered,
+ will not be seen. */
+ EHS_decl = pushdecl (EHS_decl);
+ DECL_EXTERNAL (EHS_decl) = 1;
+ TREE_STATIC (EHS_decl) = 1;
+ TREE_PUBLIC (EHS_decl) = 1;
+ finish_decl (EHS_decl, NULL_TREE, NULL_TREE, 0);
+ }
+ else if (TREE_CODE (EHS_decl) != VAR_DECL
+ || TREE_TYPE (EHS_decl) != TYPE_POINTER_TO (EHS_type))
+ fatal ("exception handling declarations conflict with compiler's internal model");
+
+ if (EHS_prev == NULL_TREE)
+ {
+ register tree EHS_DECL = build1 (INDIRECT_REF, EHS_type, EHS_decl);
+ EHS_prev = build_component_ref (EHS_DECL, get_identifier ("prev"), 0, 0);
+ EHS_handler = build_component_ref (EHS_DECL, get_identifier ("handler"), 0, 0);
+ EHS_parms = build_component_ref (EHS_DECL, get_identifier ("parms"), 0, 0);
+ EHS_name = build_component_ref (EHS_DECL, get_identifier ("name"), 0, 0);
+ }
+}