home *** CD-ROM | disk | FTP | other *** search
- /* Handle exceptional things in C++.
- Copyright (C) 1989, 1992 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. */
-
- #define NULL 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
-
- #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
-
- 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));
- name[length + IDENTIFIER_LENGTH (prefix)] = JOINER;
- 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), 0, 0, 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, 0, 1);
- finish_decl (ctor, NULL_TREE, 0, 0);
- TREE_CHAIN (ctor) = TREE_VALUE (list_of_fieldlists);
- TREE_VALUE (list_of_fieldlists) = ctor;
-
- finish_struct (t, list_of_fieldlists, 0, 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, 0, 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), 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;
- 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. */
- }
- /* 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)));
- }
- 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)));
- 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 (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 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), 0, 0, 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), 0, 0, 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, 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, 0, 0, 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);
- }
- }
-