/*
 * Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *    		 Crown Copyright (c) 1997
 *    
 *    This TenDRA(r) Computer Program is subject to Copyright
 *    owned by the United Kingdom Secretary of State for Defence
 *    acting through the Defence Evaluation and Research Agency
 *    (DERA).  It is made available to Recipients with a
 *    royalty-free licence for its use, reproduction, transfer
 *    to other parties and amendment for any purpose not excluding
 *    product development provided that any such use et cetera
 *    shall be deemed to be acceptance of the following conditions:-
 *    
 *        (1) Its Recipients shall ensure that this Notice is
 *        reproduced upon any copies or amended versions of it;
 *    
 *        (2) Any amended version of it shall be clearly marked to
 *        show both the nature of and the organisation responsible
 *        for the relevant amendment or amendments;
 *    
 *        (3) Its onward transfer from a recipient to another
 *        party shall be deemed to be that party's acceptance of
 *        these conditions;
 *    
 *        (4) DERA gives no warranty or assurance as to its
 *        quality or suitability for any purpose and DERA accepts
 *        no liability whatsoever in relation to any use to which
 *        it may be put.
 *
 * $TenDRA: tendra/src/producers/common/construct/exception.c,v 1.9 2005/08/04 20:22:16 stefanf Exp $
 */


#include "config.h"
#include "producer.h"

#include "msgcat.h"

#include "c_types.h"
#include "exp_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "tok_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "option.h"
#include "allocate.h"
#include "basetype.h"
#include "cast.h"
#include "chktype.h"
#include "class.h"
#include "construct.h"
#include "convert.h"
#include "declare.h"
#include "derive.h"
#include "destroy.h"
#include "exception.h"
#include "expression.h"
#include "file.h"
#include "function.h"
#include "hash.h"
#include "identifier.h"
#include "initialise.h"
#include "label.h"
#include "namespace.h"
#include "predict.h"
#include "redeclare.h"
#include "statement.h"
#include "syntax.h"
#include "template.h"
#include "tokdef.h"
#include "typeid.h"


/*
 *    THE SET OF ALL TYPES
 *
 *    The dummy list univ_type_set is used to represent the set of all
 *    types.  These sets of types are used to represent exception
 *    specifications for functions.  The list empty_type_set is used to
 *    give the exception specification for a function when none is given,
 *    by default it equals univ_type_set.
 */

LIST (TYPE) univ_type_set = NULL_list (TYPE);
LIST (TYPE) empty_type_set = NULL_list (TYPE);


/*
 *    INITIALISE THE SET OF ALL TYPES
 *
 *    This routine initialises the set of all types to a dummy unique list.
 */

void
init_exception()
{
    LIST (TYPE) p;
    CONS_type (type_any, NULL_list (TYPE), p);
    p = uniq_type_set (p);
    COPY_list (type_func_except (type_func_void), p);
    COPY_list (type_func_except (type_temp_func), p);
    empty_type_set = p;
    univ_type_set = p;
    return;
}


/*
 *    IS A TYPE IN A SET OF TYPES?
 *
 *    This routine checks whether the type t is an element of the set of
 *    types listed as p.
 */

int
in_type_set(LIST (TYPE) p, TYPE t)
{
    if (EQ_list (p, univ_type_set)) return (1);
    expand_tokdef++;
    while (!IS_NULL_list (p)) {
		TYPE s = DEREF_type (HEAD_list (p));
		if (EQ_type (t, s) || eq_type_unqual (t, s)) {
			expand_tokdef--;
			return (1);
		}
		p = TAIL_list (p);
    }
    expand_tokdef--;
    return (0);
}


/*
 *    IS A TYPE DERIVABLE FROM A SET OF TYPES?
 *
 *    This routine checks whether an exception of type t will be caught by
 *    an element of the set of types listed as p.  It returns the catching
 *    type, or the null type if no match is found.
 */

static TYPE
from_type_set(LIST (TYPE) p, TYPE t)
{
    if (EQ_list (p, univ_type_set)) {
		/* The universal set catches everything */
		return (t);
    }
    expand_tokdef++;
    if (IS_type_ref (t)) t = DEREF_type (type_ref_sub (t));
    while (!IS_NULL_list (p)) {
		TYPE r = DEREF_type (HEAD_list (p));
		if (!IS_NULL_type (r)) {
			TYPE s = r;
			unsigned rank;
			CONVERSION conv;
			if (IS_type_ref (s)) s = DEREF_type (type_ref_sub (s));
			if (eq_type_unqual (t, s)) {
				/* Exact match is allowed */
				expand_tokdef--;
				return (r);
			}
			conv.from = t;
			conv.to = s;
			rank = std_convert_seq (&conv, NULL_exp, 0, 0);
			switch (rank) {
			case CONV_EXACT :
			case CONV_QUAL :
			case CONV_BASE :
			case CONV_PTR_BASE :
			case CONV_PTR_VOID :
			case CONV_PTR_BOTTOM : {
				/* These conversions are allowed */
				expand_tokdef--;
				return (r);
			}
			}
		}
		p = TAIL_list (p);
    }
    expand_tokdef--;
    return (NULL_type);
}


/*
 *    ARE TWO TYPE SETS EQUAL?
 *
 *    This routine checks whether the sets of types listed as p and q are
 *    equal.  It returns 2 if they are equal, 1 if p is a subset of q, and
 *    0 otherwise.  Because p and q will have been constructed not to contain
 *    duplicate elements a fair amount can be deduced from the cardinalities
 *    of the sets, also the search is optimised if the types are given in
 *    the same order in each set.  If eq is true then only equality is
 *    checked for.
 */

int
eq_type_set(LIST (TYPE) p, LIST (TYPE) q, int eq)
{
    unsigned n, m;
    LIST (TYPE) r;
	
    /* Deal with the set of all types */
    if (EQ_list (p, q)) return (2);
    if (EQ_list (q, univ_type_set) && !eq) return (1);
    if (EQ_list (p, univ_type_set)) return (0);
	
    /* Check whether p is larger than q */
    n = LENGTH_list (p);
    m = LENGTH_list (q);
    if (n > m) return (0);
    if (n < m && eq) return (0);
	
    /* Check whether p is a subset of q */
    r = q;
    while (!IS_NULL_list (p)) {
		TYPE t = DEREF_type (HEAD_list (p));
		TYPE s = DEREF_type (HEAD_list (r));
		if (!EQ_type (t, s) && !eq_type_unqual (t, s)) {
			if (!in_type_set (q, t)) return (0);
		}
		r = TAIL_list (r);
		p = TAIL_list (p);
    }
	
    /* Check for equality using set sizes */
    if (n < m) return (1);
    return (2);
}


/*
 *    ADD AN ELEMENT TO A TYPE SET
 *
 *    This routine adds the type t to the type set p if it is not already
 *    a member.
 */

LIST (TYPE)
cons_type_set(LIST (TYPE) p, TYPE t)
{
    if (!IS_NULL_type (t) && !in_type_set (p, t)) {
		CONS_type (t, p, p);
    }
    return (p);
}


/*
 *    FIND THE UNION OF TWO TYPE SETS
 *
 *    This routine adds the elements of the type set q to the type set p.
 */

LIST (TYPE)
union_type_set(LIST (TYPE) p, LIST (TYPE) q)
{
    if (!EQ_list (p, univ_type_set)) {
		if (EQ_list (q, univ_type_set)) {
			DESTROY_list (p, SIZE_type);
			p = q;
		} else {
			while (!IS_NULL_list (q)) {
				TYPE t = DEREF_type (HEAD_list (q));
				if (!IS_NULL_type (t)) {
					if (!in_type_set (p, t)) CONS_type (t, p, p);
				}
				q = TAIL_list (q);
			}
		}
    }
    return (p);
}


/*
 *    MAKE A UNIQUE COPY OF A TYPE SET
 *
 *    This routine maintains a list of type sets.  If p equals an element of
 *    this list then the copy is returned and p is destroyed.  Otherwise p
 *    is added to the list.
 */

LIST (TYPE)
uniq_type_set(LIST (TYPE) p)
{
    static LIST (LIST (TYPE)) sets = NULL_list (LIST (TYPE));
    LIST (LIST (TYPE)) s = sets;
    while (!IS_NULL_list (s)) {
		LIST (TYPE) q = DEREF_list (HEAD_list (s));
		if (eq_type_set (p, q, 1) == 2) {
			DESTROY_list (p, SIZE_type);
			return (q);
		}
		s = TAIL_list (s);
    }
    CONS_list (p, sets, sets);
    return (p);
}


/*
 *    COMPARE THE EXCEPTION SPECIFIERS OF TWO TYPES
 *
 *    This routine compares the exception specifiers of the similar types
 *    s and t.  It returns 2 if they are equal, 1 if s is more constrained
 *    than t, and 0 otherwise.
 */

int
eq_except(TYPE s, TYPE t)
{
    unsigned ns, nt;
    if (EQ_type (s, t)) return (2);
    if (IS_NULL_type (s)) return (0);
    if (IS_NULL_type (t)) return (0);
    ns = TAG_type (s);
    nt = TAG_type (t);
    if (ns != nt) return (2);
    ASSERT (ORDER_type == 18);
    switch (ns) {
		
	case type_func_tag : {
	    /* Function types */
	    LIST (TYPE) es = DEREF_list (type_func_except (s));
	    LIST (TYPE) et = DEREF_list (type_func_except (t));
	    int eq = eq_type_set (es, et, 0);
	    if (eq) {
			TYPE rs, rt;
			LIST (TYPE) ps = DEREF_list (type_func_ptypes (s));
			LIST (TYPE) pt = DEREF_list (type_func_ptypes (t));
			while (!IS_NULL_list (ps) && !IS_NULL_list (pt)) {
				rs = DEREF_type (HEAD_list (ps));
				rt = DEREF_type (HEAD_list (pt));
				if (eq_except (rs, rt) != 2) return (0);
				pt = TAIL_list (pt);
				ps = TAIL_list (ps);
			}
			rs = DEREF_type (type_func_ret (s));
			rt = DEREF_type (type_func_ret (t));
			if (eq_except (rs, rt) != 2) return (0);
	    }
	    return (eq);
	}
		
	case type_ptr_tag :
	case type_ref_tag : {
	    /* Pointer and reference types */
	    TYPE ps = DEREF_type (type_ptr_etc_sub (s));
	    TYPE pt = DEREF_type (type_ptr_etc_sub (t));
	    return (eq_except (ps, pt));
	}
		
	case type_ptr_mem_tag : {
	    /* Pointer to member types */
	    TYPE ps = DEREF_type (type_ptr_mem_sub (s));
	    TYPE pt = DEREF_type (type_ptr_mem_sub (t));
	    return (eq_except (ps, pt));
	}
		
	case type_array_tag : {
	    /* Array types */
	    TYPE ps = DEREF_type (type_array_sub (s));
	    TYPE pt = DEREF_type (type_array_sub (t));
	    return (eq_except (ps, pt));
	}
		
	case type_templ_tag : {
	    /* Template types */
	    TOKEN as = DEREF_tok (type_templ_sort (s));
	    TOKEN at = DEREF_tok (type_templ_sort (t));
	    LIST (IDENTIFIER) qs = DEREF_list (tok_templ_pids (as));
	    LIST (IDENTIFIER) qt = DEREF_list (tok_templ_pids (at));
	    int eq = eq_templ_params (qs, qt);
	    if (eq) {
			TYPE ps = DEREF_type (type_templ_defn (s));
			TYPE pt = DEREF_type (type_templ_defn (t));
			eq = eq_except (ps, pt);
	    }
	    restore_templ_params (qs);
	    return (eq);
	}
    }
    return (2);
}


/*
 *    CREATE AN EXCEPTION TYPE
 *
 *    This routine converts the exception type t to its primary form.
 *    Reference types are replaced by the referenced type and any top level
 *    type qualifiers are removed.  chk gives the context for the conversion,
 *    1 for a throw expression, 2 for a catch statement, 3 for an exception
 *    specifier and 0 otherwise.
 */

TYPE
exception_type(TYPE t, int chk)
{
    if (!IS_NULL_type (t)) {
		unsigned tag = TAG_type (t);
		if (tag == type_ref_tag) {
			t = DEREF_type (type_ref_sub (t));
			tag = TAG_type (t);
		}
		t = qualify_type (t, cv_none, 0);
		if (chk) {
			/* Check exception type */
			TYPE s = t;
			if (tag == type_ptr_tag) {
				s = DEREF_type (type_ptr_sub (s));
				tag = TAG_type (s);
			}
			if (tag == type_compound_tag) {
				ERROR err = check_incomplete (s);
				if (!IS_NULL_err (err)) {
					/* Can't have an incomplete class */
					ERROR err2 = NULL_err;
					switch (chk) {
					case 1 : err2 = ERR_except_throw_incompl () ; break;
					case 2 : err2 = ERR_except_handle_incompl () ; break;
					case 3 : err2 = ERR_except_spec_incompl () ; break;
					}
					err = concat_error (err, err2);
					report (crt_loc, err);
				}
				if (chk == 1) {
					/* Can't throw a type with an ambiguous base */
					CLASS_TYPE cs = DEREF_ctype (type_compound_defn (s));
					err = class_info (cs, cinfo_ambiguous, 1);
					if (!IS_NULL_err (err)) {
						ERROR err2 = ERR_except_throw_ambig ();
						err = concat_error (err, err2);
						report (crt_loc, err);
					}
				}
			}
		}
    }
    return (t);
}


/*
 *    CHECK AN EXCEPTION SPECIFIER TYPE
 *
 *    This routine checks the type t, which forms part of an exception
 *    specification for a function.  The argument n gives the number of types
 *    defined in t.
 */

TYPE
check_except_type(TYPE t, int n)
{
    if (n) report (crt_loc, ERR_except_spec_typedef ());
    IGNORE exception_type (t, 3);
    return (t);
}


/*
 *    STACK OF CURRENTLY ACTIVE TRY BLOCKS
 *
 *    The stack crt_try_block is used to hold all the currently active try
 *    blocks and exception handlers.  The flag in_func_handler is set to
 *    1 (or 2 for constructors and destructors) in the handler of a function
 *    try block.
 */

STACK (EXP) crt_try_blocks = NULL_stack (EXP);
static STACK (STACK (EXP)) past_try_blocks = NULL_stack (STACK (EXP));
int in_func_handler = 0;


/*
 *    CHECK A THROWN TYPE
 *
 *    This routine checks the type t thrown from an explicit throw expression
 *    (if expl is true) or a function call.  The null type is used to
 *    indicate an unknown type.  The routine returns true if the exception
 *    is caught by an enclosing handler.
 */

int
check_throw(TYPE t, int expl)
{
    IDENTIFIER fn;
    LIST (EXP) p = LIST_stack (crt_try_blocks);
    while (!IS_NULL_list (p)) {
		EXP e = DEREF_exp (HEAD_list (p));
		if (IS_exp_try_block (e)) {
			/* Add to list of thrown types */
			LIST (TYPE) q;
			q = DEREF_list (exp_try_block_ttypes (e));
			if (!EQ_list (q, univ_type_set)) {
				LIST (LOCATION) ql;
				ql = DEREF_list (exp_try_block_tlocs (e));
				if (IS_NULL_type (t)) {
					DESTROY_list (q, SIZE_type);
					DESTROY_list (ql, SIZE_loc);
					q = univ_type_set;
					ql = NULL_list (LOCATION);
					CONS_loc (crt_loc, ql, ql);
				} else {
					if (!in_type_set (q, t)) {
						CONS_type (t, q, q);
						CONS_loc (crt_loc, ql, ql);
					}
				}
				COPY_list (exp_try_block_ttypes (e), q);
				COPY_list (exp_try_block_tlocs (e), ql);
			}
			return (1);
		}
		if (IS_NULL_type (t) && expl && IS_exp_handler (e)) {
			/* Can deduce type of 'throw' inside a handler */
			IDENTIFIER ex = DEREF_id (exp_handler_except (e));
			if (!IS_NULL_id (ex)) {
				t = DEREF_type (id_variable_etc_type (ex));
				t = exception_type (t, 0);
			}
		}
		p = TAIL_list (p);
    }
	
    /* Exception not caught by any try block */
    fn = crt_func_id;
    if (IS_NULL_type (t)) t = type_any;
    if (IS_NULL_id (fn)) {
		report (crt_loc, ERR_except_spec_throw (t));
    } else {
		report (crt_loc, ERR_except_spec_call (fn, t));
    }
    return (0);
}


/*
 *    CHECK THE EXCEPTIONS THROWN IN A TRY BLOCK
 *
 *    This routine checks the exceptions thrown in the try block e.  Any
 *    which are not caught by the handlers of e are passed to the enclosing
 *    block or reported if this is the outermost block.  The routine
 *    returns true if all the exceptions are handled by an enclosing block.
 */

int
check_try_block(EXP e)
{
    int res = 1;
    if (IS_exp_try_block (e)) {
		LOCATION loc;
		LIST (LOCATION) ql;
		LIST (TYPE) p = DEREF_list (exp_try_block_htypes (e));
		LIST (TYPE) q = DEREF_list (exp_try_block_ttypes (e));
		EXP a = DEREF_exp (exp_try_block_ellipsis (e));
		if (EQ_list (p, univ_type_set)) {
			/* Have handlers for any type */
			return (1);
		}
		if (!IS_NULL_exp (a) && IS_exp_handler (a)) {
			/* Have a ... handler */
			return (1);
		}
		bad_crt_loc++;
		loc = crt_loc;
		ql = DEREF_list (exp_try_block_tlocs (e));
		if (EQ_list (q, univ_type_set)) {
			/* Can throw any type */
			DEREF_loc (HEAD_list (ql), crt_loc);
			res = check_throw (NULL_type, 0);
		} else {
			/* Can throw a finite set of types */
			q = REVERSE_list (q);
			ql = REVERSE_list (ql);
			COPY_list (exp_try_block_ttypes (e), q);
			COPY_list (exp_try_block_tlocs (e), ql);
			while (!IS_NULL_list (q)) {
				TYPE t = DEREF_type (HEAD_list (q));
				TYPE u = from_type_set (p, t);
				if (IS_NULL_type (u)) {
					/* Throw uncaught type to enclosing block */
					DEREF_loc (HEAD_list (ql), crt_loc);
					if (!check_throw (t, 0)) res = 0;
				}
				ql = TAIL_list (ql);
				q = TAIL_list (q);
			}
		}
		crt_loc = loc;
		bad_crt_loc--;
    }
    return (res);
}


/*
 *    CHECK THE EXCEPTIONS THROWN BY A FUNCTION CALL
 *
 *    This routine checks the possible exceptions thrown by a call to a
 *    function of type fn.  When known the function name is given by fid.
 *    The routine returns true if the exception is handled by an enclosing
 *    try-block.
 */

int
check_func_throw(TYPE fn, IDENTIFIER fid)
{
    int res = 1;
    if (IS_type_func (fn)) {
		LIST (TYPE) p = DEREF_list (type_func_except (fn));
		if (IS_NULL_list (p)) return (1);
		if (EQ_list (p, univ_type_set)) {
			/* Can throw any type */
			res = check_throw (NULL_type, 0);
		} else {
			/* Can throw a finite set of types */
			while (!IS_NULL_list (p)) {
				TYPE t = DEREF_type (HEAD_list (p));
				if (!IS_NULL_type (t)) {
					if (!check_throw (t, 0)) res = 0;
				}
				p = TAIL_list (p);
			}
		}
    } else {
		res = check_throw (NULL_type, 0);
    }
    UNUSED (fid);
    return (res);
}


/*
 *    START THE EXCEPTION CHECKS FOR A FUNCTION DEFINITION
 *
 *    This routine starts the exception specification checks for a function
 *    which throws the types p.
 */

void
start_try_check(LIST (TYPE) p)
{
    EXP e;
    MAKE_exp_try_block (type_void, NULL_exp, 0, e);
    COPY_list (exp_try_block_htypes (e), p);
    PUSH_stack (crt_try_blocks, past_try_blocks);
    crt_try_blocks = NULL_stack (EXP);
    PUSH_exp (e, crt_try_blocks);
    return;
}


/*
 *    END THE EXCEPTION CHECKS FOR A FUNCTION DEFINITION
 *
 *    This routine ends the exception specification checks for the function
 *    id with definition a.
 */

EXP
end_try_check(IDENTIFIER id, EXP a)
{
    EXP e;
    POP_exp (e, crt_try_blocks);
    POP_stack (crt_try_blocks, past_try_blocks);
    if (!IS_NULL_exp (e) && IS_exp_try_block (e)) {
		IDENTIFIER fid = crt_func_id;
		crt_func_id = id;
		IGNORE check_try_block (e);
		if (EQ_id (fid, id)) {
			LIST (TYPE) p = DEREF_list (exp_try_block_ttypes (e));
			if (IS_NULL_list (p) && !in_template_decl) {
				/* Function can't throw an exception */
				DECL_SPEC ds = DEREF_dspec (id_storage (id));
				ds |= dspec_friend;
				COPY_dspec (id_storage (id), ds);
			}
		}
		COPY_list (exp_try_block_htypes (e), NULL_list (TYPE));
		free_exp (e, 1);
		crt_func_id = fid;
    }
    return (a);
}


/*
 *    EXCEPTION HANDLING ROUTINES
 *
 *    The exception handling routines are only included in the C++ producer.
 */

#if LANGUAGE_CPP


/*
 *    BEGIN THE CONSTRUCTION OF A TRY STATEMENT
 *
 *    This routine begins the construction of the statement 'try { body }
 *    handlers'.  It is called immediately after the 'try'.  func is true
 *    for a function-try-block.
 */

EXP
begin_try_stmt(int func)
{
    EXP e;
    if (func) {
		/* Check function try blocks */
		IDENTIFIER fn = crt_func_id;
		if (!IS_NULL_id (fn)) {
			HASHID nm = DEREF_hashid (id_name (fn));
			unsigned tag = TAG_hashid (nm);
			if (tag == hashid_constr_tag || tag == hashid_destr_tag) {
				/* Constructors and destructors are marked */
				func = 2;
			}
		} else {
			func = 0;
		}
    }
    MAKE_exp_try_block (type_void, NULL_exp, func, e);
    CONS_exp (e, all_try_blocks, all_try_blocks);
    PUSH_exp (e, crt_try_blocks);
    return (e);
}


/*
 *    INJECT FUNCTION PARAMETERS INTO A HANDLER
 *
 *    It is not allowed to redeclare a function parameter in the body or
 *    the handler of a function-try-block.  This routine ensures this by
 *    injecting the function parameters into the current scope when prev
 *    is a function-try-block.
 */

void
inject_try_stmt(EXP prev)
{
    int func = DEREF_int (exp_try_block_func (prev));
    if (func) {
		IDENTIFIER id = crt_func_id;
		if (!IS_NULL_id (id) && IS_id_function_etc (id)) {
			LIST (IDENTIFIER) pids;
			NAMESPACE ns = crt_namespace;
			TYPE t = DEREF_type (id_function_etc_type (id));
			while (IS_type_templ (t)) {
				t = DEREF_type (type_templ_defn (t));
			}
			pids = DEREF_list (type_func_pids (t));
			while (!IS_NULL_list (pids)) {
				IDENTIFIER pid = DEREF_id (HEAD_list (pids));
				IGNORE redeclare_id (ns, pid);
				pids = TAIL_list (pids);
			}
		}
    }
    return;
}


/*
 *    CONTINUE THE CONSTRUCTION OF A TRY STATEMENT
 *
 *    This routine continues the construction of the try statement prev by
 *    filling in the given body statement.
 */

EXP
cont_try_stmt(EXP prev, EXP body)
{
    EXP e;
    int func = DEREF_int (exp_try_block_func (prev));
    if (func) in_func_handler = func;
    COPY_exp (exp_try_block_body (prev), body);
    set_parent_stmt (body, prev);
    POP_exp (e, crt_try_blocks);
    UNUSED (e);
    return (prev);
}


/*
 *    COMPLETE THE CONSTRUCTION OF A TRY STATEMENT
 *
 *    This routine completes the construction of the try statement prev.  It
 *    checks whether it contains at least one handler and determines the
 *    reachability of the following statement.
 */

EXP
end_try_stmt(EXP prev, int empty)
{
    EXP e;
    TYPE t;
    int all_bottom = 1;
    int func = DEREF_int (exp_try_block_func (prev));
	
    /* Check handler list */
    EXP ell = DEREF_exp (exp_try_block_ellipsis (prev));
    LIST (EXP) hs = DEREF_list (exp_try_block_handlers (prev));
    LIST (TYPE) ps = DEREF_list (exp_try_block_ttypes (prev));
    unsigned nh = LENGTH_list (hs);
    if (IS_NULL_exp (ell)) {
		/* Create default handler if necessary */
		if (IS_NULL_list (hs) && !empty) {
			/* Check that there is at least one handler */
			report (crt_loc, ERR_except_handlers ());
		}
		MAKE_exp_exception (type_bottom, ell, NULL_exp, NULL_exp, 0, ell);
		COPY_exp (exp_try_block_ellipsis (prev), ell);
    } else {
		nh++;
    }
    IGNORE check_value (OPT_VAL_exception_handlers, (ulong) nh);
	
    /* Do unreached code analysis */
    e = DEREF_exp (exp_try_block_body (prev));
    t = DEREF_type (exp_type (e));
    if (IS_type_bottom (t)) {
		/* Don't reach end of try block */
		t = DEREF_type (exp_type (ell));
		if (!IS_type_bottom (t)) all_bottom = 0;
		while (!IS_NULL_list (hs) && all_bottom) {
			/* Check the other handlers */
			e = DEREF_exp (HEAD_list (hs));
			t = DEREF_type (exp_type (e));
			if (!IS_type_bottom (t)) all_bottom = 0;
			hs = TAIL_list (hs);
		}
    } else {
		/* Reach end of try block */
		all_bottom = 0;
    }
    if (all_bottom) {
		COPY_type (exp_type (prev), type_bottom);
		unreached_code = 1;
		unreached_last = 0;
    } else {
		unreached_code = unreached_prev;
    }
    if (IS_NULL_list (ps) && !empty && !in_template_decl) {
		report (crt_loc, ERR_except_not ());
    }
    if (func) in_func_handler = 0;
    IGNORE check_try_block (prev);
    return (prev);
}


/*
 *    MARK ALL VARIABLES ENCLOSING A TRY BLOCK
 *
 *    This routine marks all the local variables of the function id which
 *    contain a try block within their scope.
 */

void
end_try_blocks(IDENTIFIER id)
{
    LIST (EXP) p = all_try_blocks;
    if (!IS_NULL_list (p)) {
		if (!IS_NULL_id (id)) {
			/* Mark function */
			DECL_SPEC ds = DEREF_dspec (id_storage (id));
			ds |= dspec_mutable;
			COPY_dspec (id_storage (id), ds);
		}
		while (!IS_NULL_list (p)) {
			EXP a = DEREF_exp (HEAD_list (p));
			while (!IS_NULL_exp (a)) {
				if (IS_exp_decl_stmt (a)) {
					IDENTIFIER pid = DEREF_id (exp_decl_stmt_id (a));
					DECL_SPEC ds = DEREF_dspec (id_storage (pid));
					if (ds & dspec_auto) {
						/* Mark local variable */
						ds |= dspec_mutable;
						COPY_dspec (id_storage (pid), ds);
					}
				}
				a = get_parent_stmt (a);
			}
			p = TAIL_list (p);
		}
    }
    return;
}


/*
 *    DECLARE AN EXCEPTION HANDLER
 *
 *    This routine declares an exception handler named id with type t and
 *    declaration specifiers ds (which should always be empty).  n gives
 *    the number of types defined in t.
 */

IDENTIFIER
make_except_decl(DECL_SPEC ds, TYPE t, IDENTIFIER id, int n)
{
    /* Declare id as a local variable */
    EXP e;
    if (crt_id_qualifier == qual_nested || crt_templ_qualifier) {
		/* Other illegal identifiers are caught elsewhere */
		report (crt_loc, ERR_dcl_meaning_id (qual_nested, id));
    }
    if (n) report (crt_loc, ERR_except_handle_typedef ());
    t = make_param_type (t, CONTEXT_PARAMETER);
    id = make_object_decl (ds, t, id, 0);
	
    /* The initialising value is the current exception */
    if (IS_type_ref (t)) {
		t = DEREF_type (type_ref_sub (t));
    }
    t = lvalue_type (t);
    MAKE_exp_thrown (t, 0, e);
    IGNORE init_object (id, e);
    return (id);
}


/*
 *    BEGIN THE CONSTRUCTION OF A CATCH STATEMENT
 *
 *    This routine begins the construction of the handler 'catch (ex)
 *    { body }' associated with the try block block.  It is called after the
 *    declaration of ex.  Note that ex can be the null identifier, indicating
 *    that the handler is '...'.
 */

EXP
begin_catch_stmt(EXP block, IDENTIFIER ex)
{
    /* Construct the result */
    EXP e, d;
    MAKE_exp_handler (type_void, ex, NULL_exp, e);
    COPY_exp (exp_handler_parent (e), block);
    unreached_code = 0;
    unreached_last = 0;
	
    /* Check for '...' handlers */
    d = DEREF_exp (exp_try_block_ellipsis (block));
    if (!IS_NULL_exp (d)) {
		/* Already have a '...' handler */
		report (crt_loc, ERR_except_handle_ellipsis ());
		unreached_code = 1;
    } else if (IS_NULL_id (ex)) {
		/* Set the '...' handler if necessary */
		COPY_exp (exp_try_block_ellipsis (block), e);
    } else {
		/* Add to list of other handlers */
		TYPE t0;
		TYPE t, s;
		LIST (EXP) p, q;
		LIST (TYPE) u, v;
		
		/* Check list of handler types */
		u = DEREF_list (exp_try_block_htypes (block));
		t0 = DEREF_type (id_variable_etc_type (ex));
		t = exception_type (t0, 2);
		s = from_type_set (u, t);
		if (!IS_NULL_type (s)) {
			report (crt_loc, ERR_except_handle_unreach (t0, s));
			unreached_code = 1;
		}
		CONS_type (t, NULL_list (TYPE), v);
		u = APPEND_list (u, v);
		COPY_list (exp_try_block_htypes (block), u);
		
		/* Add ex to list of handler expressions */
		p = DEREF_list (exp_try_block_handlers (block));
		CONS_exp (e, NULL_list (EXP), q);
		p = APPEND_list (p, q);
		COPY_list (exp_try_block_handlers (block), p);
    }
    PUSH_exp (e, crt_try_blocks);
    return (e);
}


/*
 *    COMPLETE THE CONSTRUCTION OF A CATCH STATEMENT
 *
 *    This routine completes the construction of the catch statement prev by
 *    filling in the given body statement.
 */

EXP
end_catch_stmt(EXP prev, EXP body)
{
    EXP e;
    if (unreached_code) {
		/* Mark unreached statements */
		COPY_type (exp_type (prev), type_bottom);
    } else {
		/* Control reaches end of handler */
		int func;
		e = DEREF_exp (exp_handler_parent (prev));
		func = DEREF_int (exp_try_block_func (e));
		if (func == 2) {
			/* Re-throw current exception */
			e = make_throw_exp (NULL_exp, 0);
			body = add_compound_stmt (body, e);
			COPY_type (exp_type (prev), type_bottom);
		}
    }
    COPY_exp (exp_handler_body (prev), body);
    set_parent_stmt (body, prev);
    POP_exp (e, crt_try_blocks);
    UNUSED (e);
    return (prev);
}


/*
 *    CONSTRUCT A THROW ARGUMENT FROM A TYPE
 *
 *    The syntax 'throw t' for a type t is exactly equivalent to 'throw t ()'.
 *    This routine constructs the argument 't ()'.  n gives the number of types
 *    defined in t.
 */

EXP
make_throw_arg(TYPE t, int n)
{
    EXP e;
    report (crt_loc, ERR_except_throw_type ());
    if (n) report (crt_loc, ERR_except_throw_typedef ());
    e = make_func_cast_exp (t, NULL_list (EXP));
    return (e);
}


/*
 *    CONSTRUCT A THROW EXPRESSION
 *
 *    This routine constructs the expressions 'throw a' and 'throw' (if a is
 *    the null expression).  Note that a is assigned to a temporary variable
 *    of its own type.
 */

EXP
make_throw_exp(EXP a, int expl)
{
    EXP e;
    EXP b = NULL_exp;
    EXP d = NULL_exp;
    if (!IS_NULL_exp (a)) {
		/* Perform operand conversions on a */
		TYPE t;
		ERROR err;
		a = convert_reference (a, REF_NORMAL);
		t = DEREF_type (exp_type (a));
		if (!IS_type_compound (t)) {
			a = convert_lvalue (a);
			t = DEREF_type (exp_type (a));
		}
		t = exception_type (t, 1);
		IGNORE check_throw (t, 1);
		b = sizeof_exp (t);
		err = check_complete (t);
		if (IS_NULL_err (err)) {
			/* Exception is assigned to temporary variable */
			a = init_assign (t, cv_none, a, &err);
			d = init_default (t, &d, DEFAULT_DESTR, EXTRA_DESTR, &err);
			if (!IS_NULL_err (err)) err = init_error (err, 0);
		}
		if (!IS_NULL_err (err)) {
			/* Report type errors */
			err = concat_error (err, ERR_except_throw_copy ());
			report (crt_loc, err);
		}
		a = check_return_exp (a, lex_throw);
    } else {
		/* Check thrown type */
		IGNORE check_throw (NULL_type, 1);
    }
    MAKE_exp_exception (type_bottom, a, b, d, expl, e);
    return (e);
}


#endif
