Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (c) 2002-2006 The TenDRA Project <http://www.tendra.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,
* 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.
* 3. Neither the name of The TenDRA Project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific, prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``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 COPYRIGHT HOLDERS OR
* CONTRIBUTORS 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.
*
* $Id$
*/
/*
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.
*/
#include "config.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(void)
{
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 contruction 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 contruction 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