/*
 * Copyright (c) 2003, 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, 1998
 *
 *  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/statement.c,v 1.14 2005/10/24 14:13:41 stefanf Exp $
 */


#include "config.h"
#include "producer.h"
#include "c_types.h"
#include "etype_ops.h"
#include "exp_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "loc_ext.h"
#include "member_ops.h"
#include "nspace_ops.h"
#include "str_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "option.h"
#include "assign.h"
#include "basetype.h"
#include "cast.h"
#include "check.h"
#include "chktype.h"
#include "class.h"
#include "constant.h"
#include "construct.h"
#include "convert.h"
#include "declare.h"
#include "destroy.h"
#include "dump.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 "lex.h"
#include "literal.h"
#include "member.h"
#include "namespace.h"
#include "overload.h"
#include "parse.h"
#include "predict.h"
#include "quality.h"
#include "redeclare.h"
#include "statement.h"
#include "symbols.h"
#include "syntax.h"
#include "template.h"
#include "ustring.h"
#include "variable.h"


/*
 *    UNREACHED CODE ANALYSIS
 *
 *    The detection of unreachable code is primarily by means of the simple
 *    flag, which is true for unreported statements.  The flag unreached_last
 *    is set to equal unreached_code whenever a reachability check is applied.
 *    This is to ensure that only the first statement in an unreached block
 *    is reported.  At the end of a conditional, or other complex statement,
 *    unreached_prev is set to the value of unreached_code at the start of
 *    that statement.  The flag unreached_fall is set to false immediately
 *    following a 'case' or 'default' label, but set to true after a non-
 *    trivial statement (this is included in the grammar).
 */

int unreached_code = 0;
int unreached_last = 0;
int unreached_prev = 0;
int unreached_fall = 0;
int suppress_fall = 0;


/*
 *    DOES AN EXPRESSION NOT RETURN?
 *
 *    This routine tests whether the expression e has type bottom, i.e. does
 *    not return a value.
 */

static int
is_bottom(EXP e)
{
	TYPE t;
	if (IS_NULL_exp (e)) return (0);
	t = DEREF_type (exp_type (e));
	return (IS_type_bottom (t));
}


/*
 *    FIND THE PARENT OF A STATEMENT
 *
 *    This routine returns a pointer to the parent of the statement e.  It
 *    returns the null pointer if e is a simple expression.
 */

static PTR (EXP)
parent_stmt(EXP e)
{
	PTR (EXP) ptr = NULL_ptr (EXP);
	if (!IS_NULL_exp (e)) {
		switch (TAG_exp (e)) {
		case exp_reach_tag : {
			ptr = exp_reach_parent (e);
			break;
		}
		case exp_unreach_tag : {
			ptr = exp_unreach_parent (e);
			break;
		}
		case exp_sequence_tag : {
			ptr = exp_sequence_parent (e);
			break;
		}
		case exp_solve_stmt_tag : {
			ptr = exp_solve_stmt_parent (e);
			break;
		}
		case exp_decl_stmt_tag : {
			ptr = exp_decl_stmt_parent (e);
			break;
		}
		case exp_if_stmt_tag : {
			ptr = exp_if_stmt_parent (e);
			break;
		}
		case exp_while_stmt_tag : {
			ptr = exp_while_stmt_parent (e);
			break;
		}
		case exp_do_stmt_tag : {
			ptr = exp_do_stmt_parent (e);
			break;
		}
		case exp_switch_stmt_tag : {
			ptr = exp_switch_stmt_parent (e);
			break;
		}
		case exp_hash_if_tag : {
			ptr = exp_hash_if_parent (e);
			break;
		}
		case exp_return_stmt_tag : {
			ptr = exp_return_stmt_parent (e);
			break;
		}
		case exp_goto_stmt_tag : {
			ptr = exp_goto_stmt_parent (e);
			break;
		}
		case exp_label_stmt_tag : {
			ptr = exp_label_stmt_parent (e);
			break;
		}
		case exp_try_block_tag : {
			ptr = exp_try_block_parent (e);
			break;
		}
		case exp_handler_tag : {
			ptr = exp_handler_parent (e);
			break;
		}
		case exp_token_tag : {
			ptr = exp_token_parent (e);
			break;
		}
		case exp_location_tag : {
			EXP a = DEREF_exp (exp_location_arg (e));
			ptr = parent_stmt (a);
			break;
		}
		case exp_paren_tag : {
			EXP a = DEREF_exp (exp_paren_arg (e));
			ptr = parent_stmt (a);
			break;
		}
		}
	}
	return (ptr);
}


/*
 *    GET THE PARENT OF A STATEMENT
 *
 *    This routine returns the parent statement of e.
 */

EXP
get_parent_stmt(EXP e)
{
	EXP p = NULL_exp;
	if (!IS_NULL_exp (e)) {
		PTR (EXP) ptr = parent_stmt (e);
		if (!IS_NULL_ptr (ptr)) p = DEREF_exp (ptr);
	}
	return (p);
}


/*
 *    SET THE PARENT OF A STATEMENT
 *
 *    This routine sets the parent of the statement e to be p.
 */

void
set_parent_stmt(EXP e, EXP p)
{
	if (!IS_NULL_exp (e)) {
		PTR (EXP) ptr = parent_stmt (e);
		if (!IS_NULL_ptr (ptr)) COPY_exp (ptr, p);
	}
	return;
}


/*
 *    STATEMENT LOCATION FLAG
 *
 *    The flag record_location may be set to true to force extra expressions
 *    giving the location of each statement to be inserted into the output.
 *    The variable stmt_loc records the last such location.
 */

int record_location = 0;
LOCATION stmt_loc = NULL_loc;
static int adjusted_line = 0;


/*
 *    ADJUST COLUMN NUMBER
 *
 *    This routine sets stmt_loc to point to the start of the preprocessing
 *    token p.
 */

static void
adjust_column(PPTOKEN *p)
{
	if (p) {
		int t = p->tok;
		if (t >= FIRST_SYMBOL && t <= LAST_SYMBOL) {
			/* Adjust to start of symbol */
			ulong len = (ulong) ustrlen (token_name (t));
			stmt_loc = crt_loc;
			stmt_loc.column -= (len - 1);
			return;
		}
		if (t >= FIRST_KEYWORD && t <= LAST_KEYWORD) {
			/* Map keywords to underlying identifier */
			t = lex_identifier;
		}
		if (t == lex_identifier) {
			IDENTIFIER id = p->pp_data.id.use;
			id = underlying_id (id);
			DEREF_loc (id_loc (id), stmt_loc);
			return;
		}
	}
	stmt_loc = crt_loc;
	return;
}


/*
 *    ADJUST LINE NUMBER
 *
 *    This routine is called whenever the location of a statement is
 *    recorded.  It is intended to ensure that the current location points
 *    to the start of the next statement rather than the end of the current
 *    statement.
 */

static void
adjust_line(int next)
{
	if (!adjusted_line) {
		adjusted_line = 1;
		if (next) {
			switch (crt_lex_token) {
			case lex_open_Hbrace_H1 :
			case lex_colon :
			case lex_semicolon :
			case lex_close_Hround :
			case lex_else :
			case lex_exhaustive : {
				/* Scan to next token */
				PPTOKEN *p = crt_token->next;
				if (p) {
					/* Set location from next token */
					p = read_loc_tokens (p);
					if (crt_state_depth == 0) {
						adjust_column (p);
						return;
					}
				} else {
					if (crt_state_depth == 0) {
						/* Skip white space from input file */
						unsigned long sp = skip_white (1);
						update_column ();
						stmt_loc = crt_loc;
						stmt_loc.column++;
						if (sp) patch_white (sp);
						return;
					}
				}
				break;
			}
			default : {
				/* Use current token */
				if (crt_state_depth == 0) {
					adjust_column (crt_token);
					return;
				}
				break;
			}
			}
		}
		stmt_loc = crt_loc;
	}
	return;
}


/*
 *    BLOCK NAMESPACE
 *
 *    This variable can be set to make begin_compound_stmt use an existing
 *    namespace rather than creating a new one.
 */

NAMESPACE block_namespace = NULL_nspace;


/*
 *    BEGIN A COMPOUND STATEMENT
 *
 *    These routine begins the construction of a compound statement.  If
 *    scope is true then this compound statement also establishes a scope.
 *    A compound statement consists of a list of statements (the first of
 *    which is always a dummy null expression) plus a pointer indicating
 *    where the next statement to be added to the compound statement is to
 *    go.  For simple statement lists this is the last element of the list,
 *    but if labels or declarations are introduced then any statements added
 *    subsequently will go at the end of the dummy block introduced by
 *    begin_label_stmt or make_decl_stmt.  The parent field of the main
 *    statement is used within the construction to point to the innermost
 *    block of this kind.  It is only set to its correct value at the end
 *    of the compound statement.
 */

EXP
begin_compound_stmt(int scope)
{
	TYPE t;
	NAMESPACE ns;
	EXP e = NULL_exp;
	LIST (EXP) stmts;
	NAMESPACE cns = NULL_nspace;

	/* Create block namespace */
	if (scope > 0) {
		ns = block_namespace;
		if (IS_NULL_nspace (ns)) {
			/* Create new namespace */
			unsigned tag = nspace_block_tag;
			if (scope == 2) tag = nspace_dummy_tag;
			cns = crt_namespace;
			ns = make_namespace (crt_func_id, tag, 0);
			push_namespace (ns);
		} else {
			/* Use existing namespace */
			MEMBER mem = DEREF_member (nspace_last (ns));
			COPY_member (nspace_prev (ns), mem);
			cns = ns;
			block_namespace = NULL_nspace;
		}
		IGNORE incr_value (OPT_VAL_statement_depth);
	} else {
		ns = NULL_nspace;
	}

	/* Create compound statement */
	t = (unreached_code ? type_bottom : type_void);
	if (record_location && scope >= 0) {
		/* Record start of block */
		adjust_line (scope);
		adjusted_line = 0;
		MAKE_exp_location (t, stmt_loc, e, e);
		if (do_scope) dump_begin_scope (NULL_id, ns, cns, &crt_loc);
	}
	CONS_exp (e, NULL_list (EXP), stmts);
	MAKE_exp_sequence (t, stmts, stmts, ns, 0, e);
	COPY_exp (exp_sequence_parent (e), e);
	return (e);
}


/*
 *    MARK THE START OF A COMPOUND STATEMENT
 *
 *    On occasions a compound statement may be created before the open brace
 *    which marks its start.  In this case this routine is called when the
 *    open brace is encountered to record the actual position of the start
 *    of the block.
 */

void
mark_compound_stmt(EXP prev)
{
	if (record_location) {
		LIST (EXP) stmts = DEREF_list (exp_sequence_first (prev));
		EXP stmt = DEREF_exp (HEAD_list (stmts));
		if (!IS_NULL_exp (stmt) && IS_exp_location (stmt)) {
			adjust_line (1);
			adjusted_line = 0;
			COPY_loc (exp_location_end (stmt), stmt_loc);
		}
	}
	return;
}


/*
 *    EXTEND A COMPOUND STATEMENT
 *
 *    This routine adds the statement stmt to the end of the compound
 *    statement prev.  Note that this routine also decides where the
 *    statement after this one is to go on the basis of the rules above.
 */

static EXP
extend_compound_stmt(EXP prev, EXP stmt, int loc)
{
	EXP body;
	EXP parent;
	LIST (EXP) elem;
	LIST (EXP) elem0;
	LIST (EXP) stmts;

	/* Allow for null statements */
	if (IS_NULL_exp (stmt)) return (prev);

	/* Add statement to list */
	stmts = DEREF_list (exp_sequence_last (prev));
	CONS_exp (stmt, NULL_list (EXP), elem);
	COPY_list (PTR_TAIL_list (stmts), elem);
	elem0 = elem;

	/* Set the parent of stmt */
	parent = DEREF_exp (exp_sequence_parent (prev));
	set_parent_stmt (stmt, parent);

	/* Find location of next statement */
	switch (TAG_exp (stmt)) {
	case exp_decl_stmt_tag : {
		/* Transfer to the body of a declaration */
		unsigned tag;
		body = stmt;
		do {
			body = DEREF_exp (exp_decl_stmt_body (body));
			tag = TAG_exp (body);
		} while (tag == exp_decl_stmt_tag);
		if (tag == exp_sequence_tag) {
			elem = DEREF_list (exp_sequence_last (body));
			COPY_exp (exp_sequence_parent (prev), body);
		}
		loc = 0;
		break;
	}
	case exp_label_stmt_tag : {
		/* Transfer to the body of a labelled statement */
		body = DEREF_exp (exp_label_stmt_body (stmt));
		if (IS_exp_sequence (body)) {
			elem = DEREF_list (exp_sequence_last (body));
			COPY_exp (exp_sequence_parent (prev), body);
		}
		loc = 0;
		break;
	}
	case exp_location_tag :
	case exp_thrown_tag : {
		/* Don't record location in these cases */
		loc = 0;
		break;
	}
	}
	COPY_list (exp_sequence_last (prev), elem);

	/* Record statement location */
	if (record_location) {
		adjust_line (1);
		if (loc) {
			TYPE t = DEREF_type (exp_type (stmt));
			MAKE_exp_location (t, stmt_loc, stmt, stmt);
			COPY_exp (HEAD_list (elem0), stmt);
		}
	}

	/* Unreached code analysis */
	if (is_bottom (stmt)) {
		COPY_type (exp_type (prev), type_bottom);
	}
	return (prev);
}


/*
 *    ADD A STATEMENT TO A COMPOUND STATEMENT
 *
 *    This routine adds the statement stmt to the compound statement prev,
 *    returning the resulting compound statement.  It differs from the
 *    previous routine in taking any declarations in stmt into account.
 *    This is done using the last field of the current namespace which keeps
 *    track of the last variable in the block for which a declaration
 *    statement has been introduced.  The treatment of labelled statements
 *    is tricky - the declarations need to be inserted between the label
 *    and its body.
 */

EXP
add_compound_stmt(EXP prev, EXP stmt)
{
	EXP parent = NULL_exp;
	LIST (EXP) last = NULL_list (EXP);
	NAMESPACE ns = DEREF_nspace (exp_sequence_decl (prev));
	if (!IS_NULL_nspace (ns)) {
		MEMBER p = DEREF_member (nspace_last (ns));
		MEMBER q = DEREF_member (nspace_prev (ns));
		if (!EQ_member (p, q)) {
			/* Create declaration statement */
			int vars = 0;
			EXP decl = make_decl_stmt (p, q, &vars);
			if (!IS_NULL_exp (stmt) && IS_exp_label_stmt (stmt)) {
				/* Labels come before declarations */
				EXP body = DEREF_exp (exp_label_stmt_body (stmt));
				if (IS_exp_sequence (body)) {
					last = DEREF_list (exp_sequence_last (body));
					body = DEREF_exp (HEAD_list (last));
					if (!IS_NULL_exp (body)) {
						EXP b = body;
						if (IS_exp_location (b)) {
							b = DEREF_exp (exp_location_arg (b));
						}
						if (!IS_NULL_exp (b)) {
							COPY_exp (HEAD_list (last), NULL_exp);
						}
					}
					last = NULL_list (EXP);
					prev = extend_compound_stmt (prev, stmt, 1);
					stmt = body;
				}
			}
			if (!vars) {
				last = DEREF_list (exp_sequence_last (prev));
				parent = DEREF_exp (exp_sequence_parent (prev));
			}
			prev = extend_compound_stmt (prev, decl, 0);
			COPY_member (nspace_prev (ns), p);
		}
	}
	if (!IS_NULL_exp (stmt)) {
		/* Add the new statement */
		prev = extend_compound_stmt (prev, stmt, 1);
	}
	if (!IS_NULL_list (last)) {
		/* Restrict scope of temporaries to stmt */
		last = END_list (last);
		COPY_list (exp_sequence_last (prev), last);
		COPY_exp (exp_sequence_parent (prev), parent);
	}
	adjusted_line = 0;
	return (prev);
}


/*
 *    END A COMPOUND STATEMENT
 *
 *    This routine ends the compound statement prev.
 */

EXP
end_compound_stmt(EXP prev)
{
	/* Take local declarations out of scope */
	int blk = DEREF_int (exp_sequence_block (prev));
	NAMESPACE ns = DEREF_nspace (exp_sequence_decl (prev));
	if (!IS_NULL_nspace (ns)) {
		if (check_namespace (ns, prev, ANON_NONE, 1)) {
			if (blk == 0) {
				COPY_int (exp_sequence_block (prev), 1);
			}
		}
		if (do_scope) dump_end_scope (NULL_id, ns, &stmt_loc);
		decr_value (OPT_VAL_statement_depth);
		IGNORE pop_namespace ();
	}
	COPY_exp (exp_sequence_parent (prev), NULL_exp);
	return (prev);
}


/*
 *    CONSTRUCT A TEMPORARY DECLARATION STATEMENT
 *
 *    This routine binds any temporary variable declarations given by the
 *    namespace members p to q to the expression e.  This is part of the
 *    action of make_decl_stmt.
 */

EXP
make_temp_decl(MEMBER p, MEMBER q, EXP e)
{
	MEMBER r = p;
	while (!EQ_member (r, q)) {
		IDENTIFIER id = DEREF_id (member_id (r));
		if (!IS_NULL_id (id) && IS_id_variable (id)) {
			/* Construct declaration statement */
			DECL_SPEC ds = DEREF_dspec (id_storage (id));
			if (ds & dspec_temp) {
				/* Temporary variables */
				if (!(ds & dspec_ignore)) {
					EXP d;
					TYPE t = DEREF_type (id_variable_type (id));
					EXP term = DEREF_exp (id_variable_term (id));
					if (!IS_NULL_exp (term)) have_destructor = 1;
					MAKE_exp_decl_stmt (t, id, e, d);
					set_parent_stmt (e, d);
					e = d;
				}
			}
		}
		r = DEREF_member (member_next (r));
	}
	return (e);
}


/*
 *    CONSTRUCT A DECLARATION STATEMENT
 *
 *    This routine constructs a series of nested declaration statements
 *    corresponding to the given list of namespace members.  p gives the
 *    last member of the current block namespace defined, while q gives
 *    the last member defined before this declaration.  The body of a
 *    declaration statement consists of the rest of the statements in the
 *    enclosing block.  That is:
 *
 *			{
 *			    stmt1;
 *			    ....
 *			    decl1;
 *			    decl2;
 *			    ....
 *			    body1;
 *			    ....
 *			}
 *
 *    is transformed into:
 *
 *			{
 *			    stmt1;
 *			    ....
 *			    decl1; {
 *				decl2; {
 *				    .... {
 *					body1;
 *					....
 *				    }
 *				}
 *			    }
 *			}
 *
 *    except that the introduced blocks do not establish scopes.  Any
 *    temporary variables introduced are declared before the normal
 *    variables regardless of their actual order of declaration.
 */

EXP
make_decl_stmt(MEMBER p, MEMBER q, int *vars)
{
	MEMBER r = p;
	unsigned temps = 0;
	IDENTIFIER init = NULL_id;
	LIST (IDENTIFIER) destr = NULL_list (IDENTIFIER);
	EXP e = begin_compound_stmt (-1);

	/* Scan through members */
	while (!EQ_member (r, q)) {
		IDENTIFIER id = DEREF_id (member_id (r));
		if (!IS_NULL_id (id) && IS_id_variable (id)) {
			/* Construct declaration statement */
			DECL_SPEC ds = DEREF_dspec (id_storage (id));
			if (ds & dspec_temp) {
				/* Temporary variables */
				if (!(ds & dspec_ignore)) {
					if (ds & dspec_register) {
						EXP term = DEREF_exp (id_variable_term (id));
						if (!IS_NULL_exp (term)) {
							CONS_id (id, destr, destr);
						}
					}
					temps++;
				}
			} else if (ds & dspec_linkage) {
				/* External variables */
				/* EMPTY */
			} else if ((ds & dspec_reserve) && !is_anon_member (id)) {
				/* Injected variables */
				/* EMPTY */
			} else {
				EXP d;
				TYPE t = DEREF_type (id_variable_type (id));
				EXP def = DEREF_exp (id_variable_init (id));
				EXP term = DEREF_exp (id_variable_term (id));
				if (!IS_NULL_exp (term)) have_destructor = 1;
				if (!IS_NULL_exp (def) && !IS_exp_null (def)) {
					/* Identifier is initialised */
					init = id;
				}
				MAKE_exp_decl_stmt (t, id, e, d);
				set_parent_stmt (e, d);
				*vars = 1;
				e = d;
			}
		}
		r = DEREF_member (member_next (r));
	}

	/* Scan through again for temporary variables */
	if (temps) {
		e = make_temp_decl (p, q, e);
		if (!IS_NULL_list (destr)) {
			/* NOT YET IMPLEMENTED */
			DESTROY_list (destr, SIZE_id);
		}
	}

	/* Only report unreached declarations if they are initialised */
	if (!IS_NULL_id (init) && unreached_code) {
		if (!unreached_last) {
			LOCATION loc;
			DEREF_loc (id_loc (init), loc);
			report (loc, ERR_stmt_stmt_unreach ());
			unreached_last = 1;
		}
	}
	return (e);
}


/*
 *    BIND TEMPORARY VARIABLES TO AN EXPRESSION
 *
 *    This routine binds any temporary variables declared in e to the
 *    expression.  Reference conversions are also applied, but any
 *    parentheses are preserved for the benefit of the assignment in
 *    boolean check.
 */

EXP
bind_temporary(EXP e)
{
	if (!IS_NULL_exp (e)) {
		NAMESPACE ns = crt_namespace;
		unsigned tag = TAG_exp (e);
		e = convert_reference (e, REF_NORMAL);
		if (!IS_NULL_nspace (ns) && IS_nspace_block_etc (ns)) {
			MEMBER p = DEREF_member (nspace_last (ns));
			MEMBER q = DEREF_member (nspace_prev (ns));
			if (!EQ_member (p, q)) {
				e = make_temp_decl (p, q, e);
				COPY_member (nspace_prev (ns), p);
			}
		}
		if (tag == exp_paren_tag && option (OPT_bool_assign)) {
			e = make_paren_exp (e);
		}
	}
	return (e);
}


/*
 *    DISCARD AN EXPRESSION
 *
 *    This routine discards the expression e.  If e is an lvalue then the
 *    lvalue conversions are checked but not performed.
 */

EXP
make_discard_exp(EXP e)
{
	if (!IS_NULL_exp (e)) {
		unsigned tag = TAG_exp (e);
		TYPE t = DEREF_type (exp_type (e));
		switch (TAG_type (t)) {
		case type_top_tag :
		case type_bottom_tag : {
			/* Void types */
			break;
		}
		case type_bitfield_tag : {
			/* Remove bitfield components */
			if (tag == exp_contents_tag) {
				e = DEREF_exp (exp_contents_ptr (e));
				tag = TAG_exp (e);
			}
			if (tag == exp_indir_tag) {
				e = DEREF_exp (exp_indir_ptr (e));
				tag = TAG_exp (e);
				if (tag == exp_add_ptr_tag) {
					e = DEREF_exp (exp_add_ptr_ptr (e));
					tag = TAG_exp (e);
					if (tag == exp_address_tag) {
						e = DEREF_exp (exp_address_arg (e));
						tag = TAG_exp (e);
					}
				}
			}
			break;
		}
		case type_array_tag :
		case type_func_tag :
		case type_templ_tag : {
			/* Check for overloaded functions */
			e = convert_lvalue (e);
			tag = TAG_exp (e);
			break;
		}
		case type_token_tag : {
			if (is_templ_type (t)) {
				/* Mark template parameters */
				MAKE_exp_op (t, lex_void, e, NULL_exp, e);
				tag = exp_op_tag;
				break;
			}
			goto default_lab;
		}
		default :
			default_lab : {
				/* Check lvalue conversions */
				ERROR err;
				CV_SPEC qual = DEREF_cv (type_qual (t));
				if (!(qual & cv_lvalue)) break;
				if (qual == (cv_lvalue | cv_const)) {
					if (tag == exp_identifier_tag) {
						e = convert_const (e);
						tag = TAG_exp (e);
						break;
					}
				}
				err = check_incomplete (t);
				if (!IS_NULL_err (err)) {
					err = concat_error (err, ERR_stmt_expr_incompl ());
					report (crt_loc, err);
				}
				break;
			}
		}
		switch (tag) {
		case exp_postinc_tag : {
			/* Discard postincrement expressions */
			COPY_exp (exp_postinc_value (e), NULL_exp);
			break;
		}
		case exp_address_tag :
		case exp_address_mem_tag : {
			/* Check for overloaded functions */
			e = convert_lvalue (e);
			break;
		}
		case exp_constr_tag : {
			/* Introduce temporary for constructor */
			e = convert_none (e);
			break;
		}
		}
	}
	return (e);
}


/*
 *    CHECK A DISCARDED EXPRESSION
 *
 *    This routine checks whether discarding the expression e should be
 *    warned about.
 */

static int
check_discard_exp(EXP e)
{
	if (!IS_NULL_exp (e)) {
		switch (TAG_exp (e)) {
		case exp_indir_tag : {
			/* Ignore indirection expressions */
			EXP a = DEREF_exp (exp_indir_ptr (e));
			return (check_discard_exp (a));
		}
		case exp_contents_tag : {
			/* Ignore contents expressions */
			EXP a = DEREF_exp (exp_contents_ptr (e));
			return (check_discard_exp (a));
		}
		case exp_comma_tag : {
			/* Examine final component of comma expression */
			LIST (EXP) p = DEREF_list (exp_comma_args (e));
			EXP a = DEREF_exp (HEAD_list (END_list (p)));
			return (check_discard_exp (a));
		}
		case exp_postinc_tag : {
			/* Discard postincrement expressions */
			COPY_exp (exp_postinc_value (e), NULL_exp);
			goto assign_lab;
		}
		case exp_assign_tag :
		case exp_init_tag :
		case exp_preinc_tag :
		case exp_decl_stmt_tag :
			assign_lab : {
				/* Assignments and declarations are allowed */
				if (!IS_NULL_id (made_temporary)) {
					report (crt_loc, ERR_stmt_expr_discard_val ());
				}
				break;
			}
		case exp_func_tag : {
			/* Discarded function return */
			report (crt_loc, ERR_stmt_expr_discard_func ());
			break;
		}
		case exp_func_id_tag : {
			/* Discarded function return */
			DECL_SPEC ds;
			IDENTIFIER id = DEREF_id (exp_func_id_id (e));
			if (IS_id_token (id)) {
				id = DEREF_id (id_token_alt (id));
			}
			ds = DEREF_dspec (id_storage (id));
			if (!(ds & dspec_ignore)) {
				ERROR err = ERR_expr_call_func (id);
				ERROR err2 = ERR_stmt_expr_discard_func ();
				err = concat_error (err, err2);
				report (crt_loc, err);
			}
			break;
		}
		default : {
			/* Discarded value */
			report (crt_loc, ERR_stmt_expr_discard_val ());
			return (1);
		}
		}
	}
	return (0);
}


/*
 *    CONSTRUCT AN EXPRESSION STATEMENT
 *
 *    This routine constructs an expression statement from the expression e.
 *    This is basically an identity operation, however various reachability
 *    and discarded value checks (which need to take account of certain
 *    special forms) are applied.
 */

EXP
make_exp_stmt(EXP e)
{
	TYPE t;

	/* Perform operand conversion */
	if (IS_NULL_exp (e)) return (e);
	e = convert_reference (e, REF_NORMAL);
	made_temporary = NULL_id;
	e = make_discard_exp (e);

	/* Check the type of e */
	t = DEREF_type (exp_type (e));
	switch (TAG_type (t)) {
	case type_bottom_tag : {
		/* Unreached code */
		unreached_code = 1;
		return (e);
	}
	case type_top_tag :
	case type_error_tag : {
		/* No effect */
		return (e);
	}
	case type_token_tag : {
		/* Check for template types */
		if (is_templ_type (t)) {
			MAKE_exp_op (t, lex_discard, e, NULL_exp, e);
			return (e);
		}
		break;
	}
	}

	/* Check discarded value */
	if (check_discard_exp (e)) {
		MAKE_exp_cast (type_void, CONV_ELLIPSIS, e, e);
	}
	return (e);
}


/*
 *    CURRENT CONDITION
 *
 *    This value is used to hold an indicating of the value of the condition
 *    in the current conditional or iteration statement.  There are four
 *    possible values corresponding to true, false and indeterminate constant
 *    conditions, plus non-constant conditions.
 */

unsigned crt_condition = BOOL_INVALID;


/*
 *    CHECK A CONDITION
 *
 *    This routine converts the condition expression cond to a boolean,
 *    including setting the value of crt_condition.  Note that the condition
 *    can be a condition declaration or the null expression (indicating an
 *    absent condition in a for-statement).  In the former case the
 *    declaration is returned via pd.  op denotes the context for the
 *    condition (an if-statement etc.).
 */

EXP
check_cond(EXP cond, EXP *pd, int op)
{
	EXP c;
	TYPE tc;
	unsigned ca;
	unsigned cc;
	unsigned tag;
	EXP a = NULL_exp;
	ERROR err = NULL_err;

	/* Check for null expressions */
	if (IS_NULL_exp (cond)) {
		c = make_bool_exp (BOOL_TRUE, exp_int_lit_tag);
		crt_condition = BOOL_TRUE;
		if (record_location && op != lex_cond_Hop) {
			/* Mark position of condition */
			TYPE t = DEREF_type (exp_type (c));
			MAKE_exp_location (t, crt_loc, c, c);
		}
		return (c);
	}

	/* Check for condition declarations */
	tag = TAG_exp (cond);
	if (tag == exp_paren_tag) {
		TYPE t;
		DESTROY_exp_paren (destroy, t, cond, cond);
		UNUSED (t);
	}
	if (IS_exp_decl_stmt (cond)) {
		DECL_SPEC ds;
		IDENTIFIER id;
		*pd = cond;
		do {
			/* Step over temporary variables */
			id = DEREF_id (exp_decl_stmt_id (cond));
			cond = DEREF_exp (exp_decl_stmt_body (cond));
		} while (IS_exp_decl_stmt (cond));
		ds = DEREF_dspec (id_storage (id));
		if (IS_id_variable (id) && !(ds & dspec_temp)) {
			/* This doesn't count as a use of id */
			EXP b = DEREF_exp (id_variable_init (id));
			TYPE t = DEREF_type (id_variable_type (id));
			if (!IS_NULL_exp (b)) {
				if (!IS_type_compound (t)) {
					/* Condition value is used in constant checks */
					a = convert_boolean (b, exp_paren_tag, KILL_err);
				}
				if (op == lex_while || op == lex_for) {
					/* Move initialisation into loop condition */
					MAKE_exp_init (t, id, b, cond);
					COPY_exp (id_variable_init (id), NULL_exp);
					ds |= dspec_explicit;
					COPY_dspec (id_storage (id), ds);
				} else {
					MAKE_exp_identifier (t, id, qual_none, cond);
				}
			} else {
				MAKE_exp_identifier (t, id, qual_none, cond);
			}
		}
	}

	/* Convert the condition to a boolean */
	c = convert_reference (cond, REF_NORMAL);
	tc = DEREF_type (exp_type (c));
	ca = type_category (&tc);
	if (IS_TYPE_CLASS (ca)) {
		/* Allow for conversion functions */
		c = convert_conv (type_bool, c, &err, CAST_IMPLICIT);
	} else if (IS_TYPE_TEMPL (ca)) {
		/* Allow for template parameters */
		MAKE_exp_op (type_bool, op, c, NULL_exp, c);
	} else {
		/* Simple conversions */
		c = convert_lvalue (c);
		c = convert_boolean (c, tag, &err);
	}
	if (!IS_NULL_err (err)) {
		ERROR err2;
		switch (op) {
		case lex_if : err2 = ERR_stmt_if_cond (); break;
		case lex_do : err2 = ERR_stmt_do_cond (); break;
		case lex_for : err2 = ERR_stmt_for_cond (); break;
		case lex_while : err2 = ERR_stmt_while_cond (); break;
		case lex_cond_Hop : err2 = ERR_expr_cond_bool (); break;
		default : err2 = NULL_err; break;
		}
		err = concat_error (err, err2);
		report (crt_loc, err);
	}

	/* Check for constant conditions */
	if (IS_NULL_exp (a)) a = c;
	cc = eval_const_cond (a);
	if (cc != BOOL_INVALID) {
		if (op == lex_if) {
			/* Report determinate constants in if statements */
			if (cc != BOOL_UNKNOWN) {
				report (crt_loc, ERR_stmt_if_const ());
			}
		} else if (op == lex_cond_Hop) {
			/* Similarly for conditional expressions */
			if (cc != BOOL_UNKNOWN) {
				report (crt_loc, ERR_expr_cond_const ());
			}
		} else {
			/* Report non-literal constants in iteration statements */
			if (!is_literal (a)) {
				ERROR err2;
				switch (op) {
				case lex_do : err2 = ERR_stmt_do_const (); break;
				case lex_for : err2 = ERR_stmt_for_const (); break;
				case lex_while : err2 = ERR_stmt_while_const (); break;
				default : err2 = NULL_err; break;
				}
				report (crt_loc, err2);
			}
		}
	}
	crt_condition = cc;
	if (record_location && op != lex_cond_Hop) {
		/* Mark position of condition */
		TYPE t = DEREF_type (exp_type (c));
		MAKE_exp_location (t, crt_loc, c, c);
	}
	return (c);
}


/*
 *    CHECK A CONDITIONAL BODY
 *
 *    This routine is called following a conditional or loop statement
 *    (given by op) to check for suspicious empty bodies which may be
 *    due to a misplaced semicolon.
 */

void
check_empty_stmt(int op)
{
	if (!suppress_quality) {
		int t = crt_lex_token;
		if (t == lex_semicolon) {
			PPTOKEN *p = crt_token;
			NAMESPACE ns = crt_lookup;
			t = next_token ();
			if (t == lex_open_Hbrace_H1) {
				/* Have 'op; {', probably mean 'op {' */
				report (crt_loc, ERR_stmt_stmt_empty (op));
			}
			crt_lookup = ns;
			crt_token = p;
		}
	}
	return;
}


/*
 *    CREATE A CONDITION DECLARATION
 *
 *    This routine combines the condition declaration d with its associated
 *    condition e.  If tmp is true a temporary variable is introduced for
 *    the value of e.
 */

static EXP
make_cond_decl(EXP d, EXP e, int tmp)
{
	EXP b;
	EXP c = d;
	if (tmp) {
		/* Introduce temporary variable */
		ERROR err = NULL_err;
		TYPE t = DEREF_type (exp_type (e));
		e = make_temporary (t, e, NULL_exp, 0, &err);
		if (!IS_NULL_err (err)) report (crt_loc, err);
	}
	for (;;) {
		b = DEREF_exp (exp_decl_stmt_body (c));
		if (!IS_exp_decl_stmt (b)) break;
		c = b;
	}
	if (IS_exp_sequence (b)) free_exp (b, 0);
	COPY_exp (exp_decl_stmt_body (c), e);
	set_parent_stmt (e, c);
	if (tmp) {
		/* Result is contents of temporary variable */
		e = make_id_exp (made_temporary);
		e = convert_reference (e, REF_NORMAL);
		e = convert_lvalue (e);
		d = join_exp (d, e);
	}
	return (d);
}


/*
 *    FIND A CONDITIONAL STATEMENT
 *
 *    This routine finds the conditional statement associated with e.
 *    e may contain an enclosing condition declaration.
 */

static EXP
find_cond_stmt(EXP e)
{
	unsigned tag = TAG_exp (e);
	while (tag == exp_decl_stmt_tag) {
		e = DEREF_exp (exp_decl_stmt_body (e));
		tag = TAG_exp (e);
	}
	while (tag == exp_sequence_tag) {
		LIST (EXP) p = DEREF_list (exp_sequence_first (e));
		p = TAIL_list (p);
		e = DEREF_exp (HEAD_list (p));
		tag = TAG_exp (e);
		if (tag == exp_location_tag) {
			e = DEREF_exp (exp_location_arg (e));
			tag = TAG_exp (e);
		}
	}
	return (e);
}


/*
 *    BEGIN AN IF STATEMENT
 *
 *    This routine begins the construction of an if statement with condition
 *    cond.  In this and all routines involving a condition, a constant true
 *    condition is replaced by the null expression.
 */

EXP
begin_if_stmt(EXP cond)
{
	EXP e;
	EXP d = NULL_exp;
	EXP b = begin_label_stmt (NULL_id, lex_if);
	IDENTIFIER lab = DEREF_id (exp_label_stmt_label (b));

	/* Check the condition */
	cond = check_cond (cond, &d, lex_if);
	if (crt_condition == BOOL_FALSE) unreached_code = 1;

	/* Construct the result */
	MAKE_exp_if_stmt (type_void, cond, NULL_exp, NULL_exp, lab, e);
	check_empty_stmt (lex_if);
	set_parent_stmt (b, e);
	if (!IS_NULL_exp (d)) e = make_cond_decl (d, e, 0);
	return (e);
}


/*
 *    CONTINUE AN IF STATEMENT
 *
 *    This routine continues the construction of an if statement by adding
 *    the statement, right, which is evaluated if the condition is true.
 *    This is called just before any else clause is processed.
 */

EXP
cont_if_stmt(EXP prev, EXP right)
{
	/* Do unreached code analysis */
	EXP e;
	if (crt_condition == BOOL_TRUE) {
		unreached_code = 1;
	} else {
		unreached_code = unreached_prev;
	}

	/* Copy the right code into the conditional */
	e = find_cond_stmt (prev);
	COPY_exp (exp_if_stmt_true_code (e), right);
	set_parent_stmt (right, e);
	return (prev);
}


/*
 *    COMPLETE AN IF STATEMENT
 *
 *    This routine completes the construction of an if statement by adding
 *    the statement, wrong, which is evaluated if the condition is false.
 */

EXP
end_if_stmt(EXP prev, EXP wrong)
{
	/* Do unreached code analysis */
	EXP e = find_cond_stmt (prev);
	EXP right = DEREF_exp (exp_if_stmt_true_code (e));
	if (is_bottom (wrong)) {
		if (is_bottom (right)) {
			/* Don't reach the end of either branch */
			COPY_type (exp_type (prev), type_bottom);
			COPY_type (exp_type (e), type_bottom);
			unreached_code = 1;
		} else if (crt_condition == BOOL_FALSE) {
			/* Don't reach the end of the taken branch */
			COPY_type (exp_type (e), type_bottom);
			unreached_code = 1;
		} else {
			/* Reach the end of one branch */
			unreached_code = unreached_prev;
		}
	} else if (is_bottom (right)) {
		if (crt_condition == BOOL_TRUE) {
			/* Don't reach the end of the taken branch */
			COPY_type (exp_type (prev), type_bottom);
			COPY_type (exp_type (e), type_bottom);
			unreached_code = 1;
		} else {
			/* Reach the end of one branch */
			unreached_code = unreached_prev;
		}
	} else {
		/* Reach the end of both branches */
		unreached_code = unreached_prev;
	}

	/* Copy the wrong code into the conditional */
	COPY_exp (exp_if_stmt_false_code (e), wrong);
	set_parent_stmt (wrong, e);
	return (prev);
}


/*
 *    STACK OF CURRENTLY ACTIVE ITERATION AND SWITCH STATEMENTS
 *
 *    This stack is used to record a nested list of iteration and switch
 *    statements in order to determine which statement a break, continue,
 *    case or default refers to.
 */

STACK(EXP) crt_loop_stack = NULL_stack (EXP);


/*
 *    BEGIN A DO STATEMENT
 *
 *    This routine begins the construction of a do statement.
 */

EXP
begin_do_stmt(void)
{
	EXP e;

	/* Construct the break and continue destinations */
	EXP bk = begin_label_stmt (NULL_id, lex_break);
	EXP cn = begin_label_stmt (NULL_id, lex_continue);
	EXP lp = begin_label_stmt (NULL_id, lex_do);
	IDENTIFIER bk_lab = DEREF_id (exp_label_stmt_label (bk));
	IDENTIFIER cn_lab = DEREF_id (exp_label_stmt_label (cn));
	IDENTIFIER lp_lab = DEREF_id (exp_label_stmt_label (lp));

	/* Construct the do statement */
	MAKE_exp_do_stmt (type_void, NULL_exp, bk_lab, cn_lab, lp_lab, e);
	set_parent_stmt (bk, e);
	set_parent_stmt (cn, e);
	set_parent_stmt (lp, e);

	/* Add statement to loop stack */
	PUSH_exp (e, crt_loop_stack);
	return (e);
}


/*
 *    COMPLETE A DO STATEMENT
 *
 *    This routine completes the construction of the do statement prev
 *    using the statement body and the condition cond.
 */

EXP
end_do_stmt(EXP prev, EXP body, EXP cond)
{
	int uc;
	IDENTIFIER bk;
	EXP d = NULL_exp;

	/* Remove statement from loop stack */
	EXP e;
	POP_exp (e, crt_loop_stack);
	UNUSED (e);

	/* Convert the condition to a boolean */
	cond = check_cond (cond, &d, lex_do);
	if (!IS_NULL_exp (d)) cond = make_cond_decl (d, cond, 1);

	/* Find whether the condition is reached */
	uc = unreached_code;
	if (uc) {
		IDENTIFIER cn = DEREF_id (exp_do_stmt_cont_lab (prev));
		if (used_label (cn) == 1) {
			/* Reached using a continue */
			uc = unreached_prev;
		} else {
			/* Report unreached code */
			if (!unreached_last) {
				report (crt_loc, ERR_stmt_stmt_unreach ());
				unreached_last = 1;
			}
		}
	}

	/* Find whether the following statement is reached */
	bk = DEREF_id (exp_do_stmt_break_lab (prev));
	COPY_loc (id_loc (bk), crt_loc);
	if (crt_condition == BOOL_TRUE) uc = 1;
	if (uc) {
		if (used_label (bk) == 1) {
			/* Can reach next statement using break */
			uc = unreached_prev;
		} else {
			COPY_type (exp_type (prev), type_bottom);
		}
	}
	unreached_code = uc;

	/* Fill in the gaps in the do statement */
	COPY_exp (exp_do_stmt_cond (prev), cond);
	COPY_exp (exp_do_stmt_body (prev), body);
	set_parent_stmt (body, prev);
	return (prev);
}


/*
 *    BEGIN A FOR STATEMENT
 *
 *    The construction of a for statement is in four stages, given by the
 *    following four routines.  A statement of the form:
 *
 *		    for (init; cond; step) body;
 *
 *    is translated to:
 *
 *		    {
 *			init;
 *			while (cond) {
 *			    body;
 *			    cn : step;
 *			}
 *		    }
 *
 *    where an absent cond is replaced by 'true', except that 'continue' is
 *    mapped to 'goto cn' (for the pre-ISO scoping rules the outer braces
 *    do not form a scope - or rather they don't until after the init).
 *    This routine, begin_for_stmt, creates the initial compound statement.
 *    The next, init_for_stmt, adds init to this compound.  The third,
 *    cond_for_stmt, creates the while loop from cond and body.  Finally
 *    end_for_stmt fills in the body and completes the construction.
 */

EXP
begin_for_stmt(void)
{
	EXP e;
	int scope = 0;
	if (option (OPT_for_scope) == OPTION_ON) scope = 1;
	e = begin_compound_stmt (scope);
	return (e);
}


/*
 *    ADD AN INITIAL STATEMENT TO A FOR STATEMENT
 *
 *    This routine adds the initial statement pointed to by init to the for
 *    statement prev (see above).  If the initial statement is a declaration
 *    then the declaration statement is only created during this routine
 *    (init is the null expression).  In this case init is set to point to
 *    the declaration statement created.
 */

EXP
init_for_stmt(EXP prev, EXP *init)
{
	EXP stmt = *init;
	NAMESPACE ns = DEREF_nspace (exp_sequence_decl (prev));
	if (!IS_NULL_nspace (ns)) {
		/* Add any declarations to the block */
		MEMBER p = DEREF_member (nspace_last (ns));
		MEMBER q = DEREF_member (nspace_prev (ns));
		if (!EQ_member (p, q)) {
			/* Create declaration statement */
			int vars = 0;
			EXP decl = make_decl_stmt (p, q, &vars);
			prev = extend_compound_stmt (prev, decl, 0);
			COPY_member (nspace_prev (ns), p);
			*init = decl;
		}
	} else {
		/* Defer declarations to enclosing block */
		NAMESPACE cns = crt_namespace;
		ns = make_namespace (crt_func_id, nspace_block_tag, 0);
		push_namespace (ns);
		if (do_scope) dump_begin_scope (NULL_id, ns, cns, &crt_loc);
		COPY_nspace (exp_sequence_decl (prev), ns);
		IGNORE incr_value (OPT_VAL_statement_depth);
	}
	if (!IS_NULL_exp (stmt)) {
		/* Add the new statement */
		prev = extend_compound_stmt (prev, stmt, 1);
	}
	adjusted_line = 0;
	return (prev);
}


/*
 *    ADD A CONDITION TO A FOR STATEMENT
 *
 *    This routine adds the condition and step statements, cond and step, to
 *    the for statement prev (see above).  Note that cond is wrapped in a
 *    location expression.
 */

EXP
cond_for_stmt(EXP prev, EXP cond, EXP step)
{
	EXP e;
	TYPE t;
	LOCATION loc;
	EXP d = NULL_exp;

	/* Construct the break and continue destinations */
	EXP bk = begin_label_stmt (NULL_id, lex_break);
	EXP cn = begin_label_stmt (NULL_id, lex_continue);
	EXP lp = begin_label_stmt (NULL_id, lex_for);
	IDENTIFIER bk_lab = DEREF_id (exp_label_stmt_label (bk));
	IDENTIFIER cn_lab = DEREF_id (exp_label_stmt_label (cn));
	IDENTIFIER lp_lab = DEREF_id (exp_label_stmt_label (lp));

	/* Convert condition to a boolean */
	bad_crt_loc++;
	loc = crt_loc;
	DESTROY_exp_location (destroy, t, crt_loc, cond, cond);
	cond = check_cond (cond, &d, lex_for);
	if (crt_condition == BOOL_FALSE) unreached_code = 1;
	crt_loc = loc;
	bad_crt_loc--;
	UNUSED (t);

	/* Deal with the step statement */
	if (!IS_NULL_exp (step)) {
		if (record_location) {
			t = DEREF_type (exp_type (step));
			MAKE_exp_location (t, stmt_loc, step, step);
		}
		IGNORE end_label_stmt (cn, step);
	}

	/* Construct the while statement */
	MAKE_exp_while_stmt (type_void, cond, bk_lab, cn_lab, lp_lab, e);
	set_parent_stmt (bk, e);
	set_parent_stmt (cn, e);
	set_parent_stmt (lp, e);
	check_empty_stmt (lex_for);

	/* Add statement to loop stack */
	PUSH_exp (e, crt_loop_stack);

	/* Add to for statement */
	if (!IS_NULL_exp (d)) {
		EXP c = d;
		LIST (IDENTIFIER) cids = NULL_list (IDENTIFIER);
		while (!IS_NULL_exp (c) && IS_exp_decl_stmt (c)) {
			IDENTIFIER cid = DEREF_id (exp_decl_stmt_id (c));
			CONS_id (cid, cids, cids);
			c = DEREF_exp (exp_decl_stmt_body (c));
		}
		COPY_list (exp_while_stmt_cond_id (e), cids);
		d = make_cond_decl (d, e, 0);
		e = d;
	}
	e = add_compound_stmt (prev, e);
	return (e);
}


/*
 *    END A FOR STATEMENT
 *
 *    This routine completes the construction of the for statement prev by
 *    adding the body statement, body (see above).
 */

EXP
end_for_stmt(EXP prev, EXP body)
{
	TYPE t;
	LIST (EXP) stmts = DEREF_list (exp_sequence_last (prev));
	EXP stmt = DEREF_exp (HEAD_list (stmts));
	if (IS_exp_location (stmt)) {
		EXP e = DEREF_exp (exp_location_arg (stmt));
		e = end_while_stmt (e, body);
		t = DEREF_type (exp_type (e));
		COPY_exp (exp_location_arg (stmt), e);
	} else {
		EXP e = end_while_stmt (stmt, body);
		t = DEREF_type (exp_type (e));
		COPY_exp (HEAD_list (stmts), e);
	}
	prev = end_compound_stmt (prev);
	COPY_type (exp_type (prev), t);

	/* Mark for-init variables as private */
	if (option (OPT_for_scope) == OPTION_WARN) {
		NAMESPACE ns = crt_namespace;
		MEMBER p = DEREF_member (nspace_last (ns));
		MEMBER q = DEREF_member (nspace_prev (ns));
		while (!EQ_member (p, q)) {
			IDENTIFIER id = DEREF_id (member_id (p));
			if (!IS_NULL_id (id) && IS_id_variable (id)) {
				DECL_SPEC ds = DEREF_dspec (id_storage (id));
				if (ds & dspec_auto) {
					ds |= dspec_private;
					COPY_dspec (id_storage (id), ds);
				}
			}
			p = DEREF_member (member_next (p));
		}
	}
	return (prev);
}


/*
 *    BEGIN A WHILE STATEMENT
 *
 *    This routine begins the construction of a while statement with
 *    condition cond.
 */

EXP
begin_while_stmt(EXP cond)
{
	EXP e;
	EXP d = NULL_exp;

	/* Construct the break and continue destinations */
	EXP bk = begin_label_stmt (NULL_id, lex_break);
	EXP cn = begin_label_stmt (NULL_id, lex_continue);
	EXP lp = begin_label_stmt (NULL_id, lex_while);
	IDENTIFIER bk_lab = DEREF_id (exp_label_stmt_label (bk));
	IDENTIFIER cn_lab = DEREF_id (exp_label_stmt_label (cn));
	IDENTIFIER lp_lab = DEREF_id (exp_label_stmt_label (lp));

	/* Convert the condition to a boolean */
	cond = check_cond (cond, &d, lex_while);
	if (crt_condition == BOOL_FALSE) unreached_code = 1;

	/* Construct the while statement */
	MAKE_exp_while_stmt (type_void, cond, bk_lab, cn_lab, lp_lab, e);
	set_parent_stmt (bk, e);
	set_parent_stmt (cn, e);
	set_parent_stmt (lp, e);
	check_empty_stmt (lex_while);

	/* Add statement to loop stack */
	PUSH_exp (e, crt_loop_stack);

	/* Allow for condition declarations */
	if (!IS_NULL_exp (d)) {
		EXP c = d;
		LIST (IDENTIFIER) cids = NULL_list (IDENTIFIER);
		while (!IS_NULL_exp (c) && IS_exp_decl_stmt (c)) {
			IDENTIFIER cid = DEREF_id (exp_decl_stmt_id (c));
			CONS_id (cid, cids, cids);
			c = DEREF_exp (exp_decl_stmt_body (c));
		}
		COPY_list (exp_while_stmt_cond_id (e), cids);
		d = make_cond_decl (d, e, 0);
		e = d;
	}
	return (e);
}


/*
 *    COMPLETE A WHILE STATEMENT
 *
 *    This routine completes the construction of the while statement prev
 *    using the statement body.
 */

EXP
end_while_stmt(EXP prev, EXP body)
{
	/* Remove statement from loop stack */
	EXP e;
	IDENTIFIER bk;
	POP_exp (e, crt_loop_stack);
	UNUSED (e);

	/* Find whether the end of the body is reached */
	e = find_cond_stmt (prev);
	if (unreached_code) {
		IDENTIFIER cn = DEREF_id (exp_while_stmt_cont_lab (e));
		if (used_label (cn) == 1) {
			/* Reached using a continue */
			unreached_code = unreached_prev;
		} else {
			/* Check for unreached continuation statement */
			LIST (EXP) step;
			EXP cont = DEREF_exp (id_label_stmt (cn));
			cont = DEREF_exp (exp_label_stmt_body (cont));
			step = DEREF_list (exp_sequence_first (cont));
			step = TAIL_list (step);
			if (!IS_NULL_list (step)) {
				LOCATION loc;
				DEREF_loc (id_loc (cn), loc);
				report (loc, ERR_stmt_stmt_unreach ());
			}
		}
	}

	/* Find whether the following statement is reached */
	bk = DEREF_id (exp_while_stmt_break_lab (e));
	COPY_loc (id_loc (bk), crt_loc);
	unreached_code = unreached_prev;
	if (crt_condition == BOOL_TRUE) {
		if (used_label (bk) != 1) {
			/* Infinite loop and no breaks */
			COPY_type (exp_type (prev), type_bottom);
			COPY_type (exp_type (e), type_bottom);
			unreached_code = 1;
		}
	}

	/* Copy the body into the result */
	COPY_exp (exp_while_stmt_body (e), body);
	set_parent_stmt (body, e);
	return (prev);
}


/*
 *    CONSTRUCT A BREAK STATEMENT
 *
 *    This routine constructs a break statement.  Note that this must appear
 *    inside an iteration or a switch statement.  It is implemented as a jump
 *    to the break label.
 */

EXP
make_break_stmt(void)
{
	LIST (EXP) st = LIST_stack (crt_loop_stack);
	if (!IS_NULL_list (st)) {
		IDENTIFIER lab;
		EXP stmt = DEREF_exp (HEAD_list (st));
		unsigned tag = TAG_exp (stmt);
		if (tag == exp_while_stmt_tag) {
			lab = DEREF_id (exp_while_stmt_break_lab (stmt));
		} else if (tag == exp_do_stmt_tag) {
			lab = DEREF_id (exp_do_stmt_break_lab (stmt));
		} else {
			lab = DEREF_id (exp_switch_stmt_break_lab (stmt));
		}
		return (make_jump_stmt (lab, stmt));
	}
	report (crt_loc, ERR_stmt_break_bad ());
	return (NULL_exp);
}


/*
 *    CONSTRUCT A CONTINUE STATEMENT
 *
 *    This routine constructs a continue statement.  Note that this must
 *    appear inside an iteration statement.  It is implemented as a jump to
 *    the continue label.
 */

EXP
make_continue_stmt(void)
{
	LIST (EXP) st = LIST_stack (crt_loop_stack);
	while (!IS_NULL_list (st)) {
		EXP stmt = DEREF_exp (HEAD_list (st));
		unsigned tag = TAG_exp (stmt);
		if (tag == exp_switch_stmt_tag) {
			/* Switch statements don't count for continues */
			/* EMPTY */
		} else {
			/* Find continue destination label */
			IDENTIFIER lab;
			if (tag == exp_while_stmt_tag) {
				lab = DEREF_id (exp_while_stmt_cont_lab (stmt));
			} else {
				lab = DEREF_id (exp_do_stmt_cont_lab (stmt));
			}
			return (make_jump_stmt (lab, stmt));
		}
		st = TAIL_list (st);
	}
	report (crt_loc, ERR_stmt_cont_bad ());
	return (NULL_exp);
}


/*
 *    CHECK A RETURN EXPRESSION
 *
 *    This routine checks the return expression a.  In particular returning
 *    a reference to a local variable is detected.  It is also used to check
 *    a throw expression (as indicated by op).
 */

EXP
check_return_exp(EXP a, int op)
{
	if (option (OPT_ptr_operator)) {
		EXP b = NULL_exp;
		DECL_SPEC ds = find_exp_linkage (a, &b, 1);
		if (ds & dspec_auto) {
			if (IS_exp_identifier (b)) {
				IDENTIFIER id = DEREF_id (exp_identifier_id (b));
				if (IS_id_variable (id)) {
					TYPE t = DEREF_type (id_variable_type (id));
					if (!IS_type_ref (t)) {
						report (crt_loc, ERR_stmt_return_auto (id, op));
					}
				}
			}
		}
	}
	return (a);
}


/*
 *    CONSTRUCT A RETURN EXPRESSION
 *
 *    This routine constructs the value for the statement 'return a'.  a can
 *    be the null expression to indicate a plain 'return'.  op is lex_return
 *    to indicate explicit returns rather than the implicit return at the
 *    end of the function.  If the return is via a jump then the label is
 *    assigned to lab.
 */

EXP
find_return_exp(EXP a, IDENTIFIER *lab, int op)
{
	TYPE r = crt_func_return;
	if (!IS_NULL_exp (a)) {
		/* Apply reference conversion */
		a = convert_reference (a, REF_NORMAL);
	}
	if (IS_NULL_type (r)) {
		/* Not in a function definition */
		IDENTIFIER id = crt_func_id;
		report (crt_loc, ERR_token_stmt_ret (id));
		if (IS_NULL_exp (a)) {
			r = type_void;
		} else {
			a = convert_lvalue (a);
			r = DEREF_type (exp_type (a));
		}
	}
	switch (TAG_type (r)) {

	case type_top_tag : {
		/* Function returns no value */
		if (!IS_NULL_exp (a)) {
			IDENTIFIER id = crt_func_id;
			HASHID nm = DEREF_hashid (id_name (id));
			unsigned tag = TAG_hashid (nm);
			if (tag == hashid_constr_tag) {
				report (crt_loc, ERR_class_ctor_result (id));
			} else if (tag == hashid_destr_tag) {
				report (crt_loc, ERR_class_dtor_result (id));
				*lab = find_postlude_label ();
			} else {
				TYPE ta = DEREF_type (exp_type (a));
				/* Special case for void because C++ allows return (void)0
				 * and the like. */
				if (eq_type_qual (ta, type_void, 1))
					report (crt_loc, ERR_stmt_return_void_expr (id));
				else
					report (crt_loc, ERR_stmt_return_none (id, r));
			}
		}
		if (in_func_handler == 2) {
			/* In function-try-block handler */
			IDENTIFIER id = crt_func_id;
			report (crt_loc, ERR_except_handle_return (id));
		}
		break;
	}

	case type_bottom_tag : {
		/* Function should not return */
		IDENTIFIER id = crt_func_id;
		if (IS_NULL_exp (a)) {
			report (crt_loc, ERR_stmt_return_bottom (id, r));
		} else {
			report (crt_loc, ERR_stmt_return_none (id, r));
		}
		break;
	}

	case type_token_tag : {
		/* Check for template types */
		if (is_templ_type (r)) {
			MAKE_exp_op (r, op, a, NULL_exp, a);
			break;
		}
		goto default_lab;
	}

	default :
		default_lab : {
			/* Function returns a value */
			if (IS_NULL_exp (a)) {
				IDENTIFIER id = crt_func_id;
				if (op == lex_return) {
					/* Explicit return statement */
					report (crt_loc, ERR_stmt_return_void (id, r));
					MAKE_exp_value (r, a);
				} else {
					/* Implicit fall out of function */
					HASHID nm = DEREF_hashid (id_name (id));
					DECL_SPEC ds = DEREF_dspec (id_storage (id));
					if ((ds & dspec_main) && IS_hashid_name (nm)) {
						/* Implicit 'return 0' in main */
						ERROR err = ERR_basic_start_main_fall (id, r);
						report (crt_loc, err);
						MAKE_exp_null (r, a);
					} else {
						report (crt_loc, ERR_stmt_return_fall (id, r));
						MAKE_exp_value (r, a);
					}
				}
			} else {
				/* Convert to result type by initialisation */
				ERROR err = NULL_err;
				a = init_assign (r, cv_none, a, &err);
				if (!IS_NULL_err (err)) {
					/* Bad return type */
					err = init_error (err, 0);
					err = concat_error (err, ERR_stmt_return_conv ());
					report (crt_loc, err);
				}
				if (crt_func_complex) {
					a = remove_temporary (a, NULL_exp);
				}
				a = check_return_exp (a, lex_return);
			}
		}
	}
	return (a);
}


/*
 *    CONSTRUCT A RETURN STATEMENT
 *
 *    This routine constructs the return statement 'return a'.  op is as in
 *    find_return_exp.
 */

EXP
make_return_stmt(EXP a, int op)
{
	IDENTIFIER lab = NULL_id;
	EXP e = find_return_exp (a, &lab, op);
	if (IS_NULL_id (lab)) {
		/* Construct return statement */
		MAKE_exp_return_stmt (type_bottom, e, e);
	} else {
		/* Jump to postlude label */
		e = make_jump_stmt (lab, NULL_exp);
	}
	unreached_code = 1;
	unreached_last = 0;
	return (e);
}


/*
 *    FALL OUT OF A FUNCTION DEFINITION
 *
 *    This routine is called at the end of a function definition to check
 *    the implicit 'return' statement caused by falling out of the function.
 */

EXP
fall_return_stmt(void)
{
	EXP e = NULL_exp;
	if (!unreached_code) {
		TYPE ret = crt_func_return;
		if (!IS_NULL_type (ret) && !IS_type_top (ret)) {
			e = make_return_stmt (NULL_exp, lex_fall);
		}
	}
	return (e);
}


/*
 *    CHECK A SWITCH CONTROL EXPRESSION
 *
 *    This routine checks the switch control expression cont.  pd is as in
 *    check_cond and pb is used to return an enumeration or boolean control
 *    expression.
 */

EXP
check_control(EXP cont, EXP *pd, EXP *pb)
{
	TYPE t;
	int ok = 1;
	unsigned c;
	EXP a = NULL_exp;

	/* Check for condition declarations */
	if (IS_exp_decl_stmt (cont)) {
		IDENTIFIER id = DEREF_id (exp_decl_stmt_id (cont));
		if (IS_id_variable (id)) {
			*pd = cont;
			t = DEREF_type (id_variable_type (id));
			a = DEREF_exp (id_variable_init (id));
			/* This doesn't count as a use of id */
			MAKE_exp_identifier (t, id, qual_none, cont);
		}
	}

	/* Apply reference conversions to control */
	cont = convert_reference (cont, REF_NORMAL);
	t = DEREF_type (exp_type (cont));
	c = type_category (&t);
	if (IS_TYPE_INT (c)) {
		/* Integral types are allowed */
		/* EMPTY */
	} else if (IS_TYPE_TEMPL (c)) {
		/* Allow for template parameters */
		MAKE_exp_op (t, lex_switch, cont, NULL_exp, cont);
		ok = 0;
	} else {
		/* Allow for overloading */
		ok = 0;
		if (IS_TYPE_CLASS (c)) {
			ERROR err = NULL_err;
			a = convert_gen (CTYPE_INT, cont, &err);
			if (!IS_NULL_exp (a)) {
				if (!IS_NULL_err (err)) {
					err = concat_error (err, ERR_stmt_switch_conv ());
					report (crt_loc, err);
				}
				cont = a;
				t = DEREF_type (exp_type (cont));
				c = type_category (&t);
				ok = 1;
			}
		}
		if (!ok && !IS_TYPE_ERROR (c)) {
			report (crt_loc, ERR_stmt_switch_control (t));
		}
	}

	/* Promote control construct */
	if (ok) {
		if (IS_TYPE_ADDRESS (c)) {
			cont = convert_lvalue (cont);
			t = DEREF_type (exp_type (cont));
		}
		if (IS_NULL_exp (a)) a = cont;
		if (is_const_exp (a, -1)) {
			/* Report constant control expressions */
			report (crt_loc, ERR_stmt_switch_const ());
		}
		if (IS_type_enumerate (t) || check_int_type (t, btype_bool)) {
			/* Store enumeration control expression */
			*pb = cont;
		}
		t = promote_type (t);
		cont = convert_promote (t, cont);
		if (record_location) {
			/* Mark position of control expression */
			MAKE_exp_location (t, crt_loc, cont, cont);
		}
	}
	return (cont);
}


/*
 *    BEGIN A SWITCH STATEMENT
 *
 *    This routine begins the construction of a switch statement with
 *    controlling expression cont.  During construction an enumeration or
 *    boolean control expression is held in the body field.
 */

EXP
begin_switch_stmt(EXP cont)
{
	EXP e;
	EXP d = NULL_exp;
	EXP body = NULL_exp;
	EXP bk = begin_label_stmt (NULL_id, lex_break);
	IDENTIFIER bk_lab = DEREF_id (exp_label_stmt_label (bk));
	cont = check_control (cont, &d, &body);
	MAKE_exp_switch_stmt (type_void, cont, body, 0, bk_lab, e);
	check_empty_stmt (lex_switch);

	/* Add statement to loop stack */
	PUSH_exp (e, crt_loop_stack);

	/* Allow for condition declarations */
	if (!IS_NULL_exp (d)) e = make_cond_decl (d, e, 0);

	/* The following statement is never reached */
	unreached_code = 1;
	unreached_last = 0;
	unreached_fall = 1;
	return (e);
}


/*
 *    DOES A VALUE APPEAR IN A CASE LIST?
 *
 *    This routine checks whether the integer constant n appears in the list
 *    of cases p.  If so it returns the label corresponding to the case
 *    statement drawn from the list of labels q.
 */

IDENTIFIER
find_case(LIST (NAT) p, LIST (IDENTIFIER) q, NAT n)
{
	while (!IS_NULL_list (p)) {
		NAT m = DEREF_nat (HEAD_list (p));
		if (EQ_nat (n, m) || eq_nat (n, m)) {
			IDENTIFIER lab = DEREF_id (HEAD_list (q));
			return (lab);
		}
		q = TAIL_list (q);
		p = TAIL_list (p);
	}
	return (NULL_id);
}


/*
 *    COMPLETE A SWITCH STATEMENT
 *
 *    This routine completes the construction of the switch statement prev
 *    using the statement body.  exhaust is true if the switch statement
 *    is declared to be exhaustive.
 */

EXP
end_switch_stmt(EXP prev, EXP body, int exhaust)
{
	EXP bk;
	EXP cont;
	EXP stmt;
	unsigned ncases;
	IDENTIFIER bk_lab;
	LIST (NAT) cases;
	IDENTIFIER default_lab;
	LIST (IDENTIFIER) case_labs;

	/* Remove statement from loop stack */
	EXP e;
	POP_exp (e, crt_loop_stack);
	UNUSED (e);

	/* Copy the body into the result */
	stmt = find_cond_stmt (prev);
	cont = DEREF_exp (exp_switch_stmt_body (stmt));
	bk_lab = DEREF_id (exp_switch_stmt_break_lab (stmt));
	bk = DEREF_exp (id_label_stmt (bk_lab));
	if (!unreached_code) {
		/* Add break statement to end if necessary */
		if (!IS_NULL_exp (body) && IS_exp_sequence (body)) {
			EXP b = make_jump_stmt (bk_lab, stmt);
			body = add_compound_stmt (body, b);
		}
	}
	MAKE_exp_solve_stmt (type_void, body, e);
	CONS_exp (e, all_solve_stmts, all_solve_stmts);
	COPY_exp (exp_solve_stmt_parent (e), stmt);
	COPY_exp (exp_switch_stmt_body (stmt), e);
	set_parent_stmt (body, e);
	set_parent_stmt (bk, e);

	/* Check lists of cases */
	cases = DEREF_list (exp_switch_stmt_cases (stmt));
	cases = REVERSE_list (cases);
	ncases = LENGTH_list (cases);
	if (ncases == 0) {
		ERROR err;
		if (exhaust) {
			err = ERR_stmt_switch_exhaust_none ();
		} else {
			err = ERR_stmt_switch_case_none ();
		}
		report (crt_loc, err);
	}
	IGNORE check_value (OPT_VAL_switch_cases, (ulong) ncases);
	COPY_list (exp_switch_stmt_cases (stmt), cases);
	case_labs = DEREF_list (exp_switch_stmt_case_labs (stmt));
	case_labs = REVERSE_list (case_labs);
	COPY_list (exp_switch_stmt_case_labs (stmt), case_labs);

	/* Check default label */
	default_lab = DEREF_id (exp_switch_stmt_default_lab (stmt));
	if (IS_NULL_id (default_lab)) {
		report (crt_loc, ERR_stmt_switch_no_default ());
	} else {
		exhaust = 1;
	}

	/* Check switch jumps */
	stmt = solve_switch (stmt);

	/* Check switches on enumerations and booleans */
	if (!IS_NULL_exp (cont) && !exhaust) {
		TYPE t = DEREF_type (exp_type (cont));
		if (IS_type_enumerate (t)) {
			ENUM_TYPE et = DEREF_etype (type_enumerate_defn (t));
			LIST (IDENTIFIER) evals = DEREF_list (etype_values (et));
			exhaust = 1;
			while (!IS_NULL_list (evals)) {
				IDENTIFIER eid = DEREF_id (HEAD_list (evals));
				EXP en = DEREF_exp (id_enumerator_value (eid));
				NAT n = DEREF_nat (exp_int_lit_nat (en));
				IDENTIFIER lid = find_case (cases, case_labs, n);
				if (IS_NULL_id (lid)) {
					report (crt_loc, ERR_stmt_switch_case_enum (eid));
					exhaust = 0;
				}
				evals = TAIL_list (evals);
			}
		} else {
			NAT n = small_nat [BOOL_FALSE];
			IDENTIFIER lid = find_case (cases, case_labs, n);
			if (!IS_NULL_id (lid)) {
				n = small_nat [BOOL_TRUE];
				lid = find_case (cases, case_labs, n);
				if (!IS_NULL_id (lid)) exhaust = 1;
			}
		}
	}

	/* Unreached code analysis */
	if (unreached_code) {
		if (used_label (bk_lab) == 1) {
			/* Can reach the end using break */
			unreached_code = unreached_prev;
		} else if (exhaust) {
			/* All cases covered by switch */
			COPY_type (exp_type (stmt), type_bottom);
			COPY_type (exp_type (prev), type_bottom);
		} else {
			/* Not all cases covered by switch */
			unreached_code = unreached_prev;
		}
	}
	COPY_int (exp_switch_stmt_exhaust (stmt), exhaust);
	return (prev);
}


/*
 *    BEGIN A CASE STATEMENT
 *
 *    This routine begins the construction of a case statement (or a jump
 *    to a case statement if jump is true) with labelling value val.  Note
 *    that a case statement must appear inside a switch statement.
 */

EXP
begin_case_stmt(EXP val, int jump)
{
	/* Search for enclosing switch statement */
	LIST (EXP) st = LIST_stack (crt_loop_stack);
	while (!IS_NULL_list (st)) {
		EXP stmt = DEREF_exp (HEAD_list (st));

		if (IS_exp_switch_stmt (stmt)) {
			/* Switch statement found */
			NAT n;
			EXP e;
			IDENTIFIER lab;
			IDENTIFIER old_lab;
			LIST (NAT) cases;
			ERROR err = NULL_err;
			int uc = unreached_code;
			LIST (IDENTIFIER) lbls;
			unsigned etag = null_tag;
			unsigned tag = TAG_exp (val);
			TYPE ta = DEREF_type (exp_type (val));
			EXP cont = DEREF_exp (exp_switch_stmt_control (stmt));
			TYPE tc = DEREF_type (exp_type (cont));

			/* Cast val to control type */
			while (tag == exp_paren_tag) {
				val = DEREF_exp (exp_paren_arg (val));
				tag = TAG_exp (val);
			}
			if (tag == exp_int_lit_tag) {
				TYPE tb = ta;
				if (IS_type_enumerate (ta)) tb = promote_type (ta);
				etag = DEREF_unsigned (exp_int_lit_etag (val));
				if (!eq_type (tb, tc)) {
					val = make_cast_nat (tc, val, &err, CAST_IMPLICIT);
				}
			}

			/* Check that val is a constant */
			n = make_nat_exp (val, &err);
			if (!IS_NULL_err (err)) {
				err = concat_error (err, ERR_stmt_switch_case_const ());
				report (crt_loc, err);
			}

			/* Check whether this value has been used previously */
			lbls = DEREF_list (exp_switch_stmt_case_labs (stmt));
			cases = DEREF_list (exp_switch_stmt_cases (stmt));
			old_lab = find_case (cases, lbls, n);
			if (!IS_NULL_id (old_lab) && !jump) {
				DECL_SPEC info = DEREF_dspec (id_storage (old_lab));
				if ((info & dspec_defn) && !is_error_nat (n)) {
					/* Duplicate case */
					PTR (LOCATION) loc = id_loc (old_lab);
					report (crt_loc, ERR_stmt_switch_case_dup (n, loc));
					old_lab = NULL_id;
				}
			}

			/* Construct the case statement */
			if (jump) {
				e = make_goto_stmt (old_lab);
				lab = DEREF_id (exp_goto_stmt_label (e));
				COPY_int (id_label_op (lab), lex_case);
			} else {
				e = begin_label_stmt (old_lab, lex_case);
				lab = DEREF_id (exp_label_stmt_label (e));
			}
			COPY_exp (id_label_gotos (lab), stmt);
			if (IS_NULL_id (old_lab)) {
				CONS_id (lab, lbls, lbls);
				CONS_nat (n, cases, cases);
				COPY_list (exp_switch_stmt_case_labs (stmt), lbls);
				COPY_list (exp_switch_stmt_cases (stmt), cases);
			}
			if (jump) return (e);

			/* Check enumeration switches */
			cont = DEREF_exp (exp_switch_stmt_body (stmt));
			if (!IS_NULL_exp (cont)) {
				int ok = 0;
				TYPE t = DEREF_type (exp_type (cont));
				if (IS_type_enumerate (t)) {
					/* Enumeration switch */
					if (etag == exp_identifier_tag && eq_type (t, ta)) {
						ok = 1;
					} else {
						ENUM_TYPE et;
						IDENTIFIER eid;
						et = DEREF_etype (type_enumerate_defn (t));
						eid = find_enumerator (et, n);
						if (!IS_NULL_id (eid)) ok = 1;
					}
				} else {
					/* Boolean switch */
					unsigned long v = get_nat_value (n);
					if (v == 0 || v == 1) ok = 1;
				}
				if (!ok && !is_error_nat (n)) {
					err = ERR_stmt_switch_case_extra (n, t);
					report (crt_loc, err);
				}
			}

			/* Check for falling into a case */
			if (!uc && unreached_fall && !suppress_fall) {
				report (crt_loc, ERR_stmt_label_fall (lex_case));
			}
			suppress_fall = 0;
			return (e);
		}

		/* Iteration statements are ignored for cases */
		st = TAIL_list (st);
	}
	report (crt_loc, ERR_stmt_label_case ());
	return (NULL_exp);
}


/*
 *    COMPLETE A CASE STATEMENT
 *
 *    This routine completes the construction of the case statement prev
 *    using the statement body.  This is just a call to end_label_stmt.
 */

EXP
end_case_stmt(EXP prev, EXP body)
{
	return (end_label_stmt (prev, body));
}


/*
 *    BEGIN A DEFAULT STATEMENT
 *
 *    This routine begins the construction of a default statement (or a jump
 *    to a default statement if jump is true).  Note that a default statement
 *    must appear inside a switch statement.
 */

EXP
begin_default_stmt(int jump)
{
	/* Search for enclosing switch statement */
	LIST (EXP) st = LIST_stack (crt_loop_stack);
	while (!IS_NULL_list (st)) {
		EXP stmt = DEREF_exp (HEAD_list (st));

		if (IS_exp_switch_stmt (stmt)) {
			/* Switch statement found */
			EXP e;
			IDENTIFIER lab;
			int uc = unreached_code;

			/* Check for previous default statements */
			lab = DEREF_id (exp_switch_stmt_default_lab (stmt));
			if (!IS_NULL_id (lab) && !jump) {
				DECL_SPEC info = DEREF_dspec (id_storage (lab));
				if (info & dspec_defn) {
					/* Duplicate default statement */
					PTR (LOCATION) loc = id_loc (lab);
					report (crt_loc, ERR_stmt_switch_default_dup (loc));
					lab = NULL_id;
				}
			}

			/* Construct the default statement */
			if (jump) {
				e = make_goto_stmt (lab);
				lab = DEREF_id (exp_goto_stmt_label (e));
				COPY_int (id_label_op (lab), lex_default);
			} else {
				int exhaust;
				exhaust = DEREF_int (exp_switch_stmt_exhaust (stmt));
				if (exhaust) {
					report (crt_loc, ERR_stmt_switch_exhaust_default ());
				}
				e = begin_label_stmt (lab, lex_default);
				lab = DEREF_id (exp_label_stmt_label (e));
			}
			COPY_exp (id_label_gotos (lab), stmt);
			COPY_id (exp_switch_stmt_default_lab (stmt), lab);

			/* Check for falling into default */
			if (!jump) {
				if (!uc && unreached_fall && !suppress_fall) {
					report (crt_loc, ERR_stmt_label_fall (lex_default));
				}
				suppress_fall = 0;
			}
			return (e);
		}

		/* Iteration statements are ignored for defaults */
		st = TAIL_list (st);
	}
	report (crt_loc, ERR_stmt_label_default ());
	return (NULL_exp);
}


/*
 *    COMPLETE A DEFAULT STATEMENT
 *
 *    This routine completes the construction of the default statement prev
 *    using the statement body.  This is just a call to end_label_stmt.
 */

EXP
end_default_stmt(EXP prev, EXP body)
{
	return (end_label_stmt (prev, body));
}


/*
 *    CURRENT #IF CONDITION
 *
 *    This variable is used to keep track of the cumulative target dependent
 *    condition at any point in the compilation.  It takes the form
 *    'c1 && c2 && ... && cn' where c1, c2, ..., cn are the target dependent
 *    conditions currently in scope.  The case n = 0 gives the null expression.
 */

EXP crt_hash_cond = NULL_exp;


/*
 *    ADD A CONDITION TO A LIST OF CONDITIONS
 *
 *    This routine adds the condition a to the list of conditions b by
 *    forming 'b && a'.
 */

EXP
make_if_cond(EXP a, EXP b)
{
	if (!IS_NULL_exp (b)) {
		MAKE_exp_log_and (type_bool, b, a, a);
	}
	return (a);
}


/*
 *    NEGATE THE LAST CONDITION IN A LIST
 *
 *    This routine negates the last condition in the list of conditions a,
 *    that is, it maps 'a1 && a2 && ... && an' to 'a1 && a2 && ... && !an'.
 */

EXP
make_else_cond(EXP a)
{
	if (!IS_NULL_exp (a)) {
		if (IS_exp_log_and (a)) {
			EXP b = DEREF_exp (exp_log_and_arg1 (a));
			EXP c = DEREF_exp (exp_log_and_arg2 (a));
			MAKE_exp_not (type_bool, c, c);
			MAKE_exp_log_and (type_bool, b, c, a);
		} else {
			MAKE_exp_not (type_bool, a, a);
		}
	}
	return (a);
}


/*
 *    BEGIN A #IF STATEMENT
 *
 *    This routine begins the construction of a #if statement.  cond gives
 *    the condition, which is already a boolean value.  The case where cond
 *    is a target independent constant have already been handled by the
 *    preprocessor.  right gives the code which is evaluated if this condition
 *    is true.
 */

EXP
begin_hash_if_stmt(EXP cond, EXP right)
{
	/* Construct the result */
	EXP e;
	MAKE_exp_hash_if (type_void, cond, right, NULL_exp, e);
	COPY_exp (exp_hash_if_last (e), e);
	set_parent_stmt (right, e);

	/* Unreached code analysis */
	if (is_bottom (right)) {
		COPY_type (exp_type (e), type_bottom);
	}
	/* Next branch is reached */
	unreached_code = unreached_prev;
	return (e);
}


/*
 *    CONTINUE A #IF STATEMENT
 *
 *    This routine continues the construction of a #if statement, prev, by
 *    adding a #elif statement to the condition.  cond gives the condition,
 *    which is already a boolean value.  The case where cond is a target
 *    independent constant have already been handled by the preprocessor.
 *    right gives the code which is evaluated if this condition, and none of
 *    the previous conditions in prev, is true.
 */

EXP
cont_hash_if_stmt(EXP prev, EXP cond, EXP right)
{
	/* Map '#elif' to '#else #if' */
	EXP e;
	EXP last = DEREF_exp (exp_hash_if_last (prev));
	MAKE_exp_hash_if (type_void, cond, right, NULL_exp, e);
	COPY_exp (exp_hash_if_parent (e), last);
	COPY_exp (exp_hash_if_false_code (last), e);
	COPY_exp (exp_hash_if_last (prev), e);
	set_parent_stmt (right, e);

	/* Unreached code analysis */
	if (!is_bottom (right)) {
		/* Reaching the end of a condition */
		COPY_type (exp_type (prev), type_void);
	}
	/* Next branch is reached */
	unreached_code = unreached_prev;
	return (prev);
}


/*
 *    COMPLETE A #IF STATEMENT
 *
 *    This routine completes the construction of a #if statement, prev.
 *    wrong gives the code which is evaluated if none of the conditions in
 *    prev is true.
 */

EXP
end_hash_if_stmt(EXP prev, EXP wrong)
{
	/* Copy wrong expression into result */
	EXP last = DEREF_exp (exp_hash_if_last (prev));
	COPY_exp (exp_hash_if_false_code (last), wrong);
	set_parent_stmt (wrong, last);

	/* Unreached code analysis */
	if (is_bottom (wrong)) {
		/* Reachability is determined by the previous branches */
		if (is_bottom (prev)) {
			unreached_code = 1;
		} else {
			unreached_code = unreached_prev;
		}
	} else {
		/* End of statement is reached */
		COPY_type (exp_type (prev), type_void);
		unreached_code = unreached_prev;
	}
	return (prev);
}


/*
 *    CREATE A FLOW CONTROL STATEMENT
 *
 *    This routine creates a flow control statement indicating that the
 *    statement body is either reached or unreached, depending on the
 *    value of reach.
 */

EXP
make_reach_stmt(EXP body, int reach)
{
	EXP e;
	TYPE t;
	if (IS_NULL_exp (body)) {
		t = type_void;
	} else {
		unsigned tag = TAG_exp (body);
		if (tag == exp_decl_stmt_tag || tag == exp_label_stmt_tag) {
			/* Don't bother in these cases */
			return (body);
		}
		t = DEREF_type (exp_type (body));
	}
	if (reach) {
		MAKE_exp_reach (t, body, e);
	} else {
		MAKE_exp_unreach (t, body, e);
	}
	set_parent_stmt (body, e);
	return (e);
}


/*
 *    CHECK A CONDITION DECLARATION TYPE
 *
 *    This routine checks the type t of a condition declaration.
 */

TYPE
make_cond_type(TYPE t)
{
	int td = have_type_declaration;
	if (td != TYPE_DECL_NONE) {
		/* Check for type declarations */
		if (td == TYPE_DECL_ELABORATE && found_elaborate_type) {
			/* This is allowed */
			/* EMPTY */
		} else {
			report (crt_loc, ERR_stmt_select_typedef ());
		}
	}
	switch (TAG_type (t)) {
	case type_func_tag : {
		member_func_type (NULL_ctype, id_variable_tag, t);
		check_weak_func (t, 0);
		report (crt_loc, ERR_stmt_select_type (t));
		MAKE_type_ptr (cv_none, t, t);
		break;
	}
	case type_array_tag : {
		report (crt_loc, ERR_stmt_select_type (t));
		t = DEREF_type (type_array_sub (t));
		MAKE_type_ptr (cv_none, t, t);
		break;
	}
	}
	return (t);
}


/*
 *    BEGIN A CONDITION DECLARATION
 *
 *    Condition declarations are declared in their own local scope and
 *    brought into the outermost scope of the statements they control
 *    using inject_cond.  This routine begins the construction of such a
 *    local scope.
 */

void
begin_cond(void)
{
	NAMESPACE ns = make_namespace (crt_func_id, nspace_dummy_tag, 0);
	push_namespace (ns);
	return;
}


/*
 *    END A CONDITION DECLARATION
 *
 *    This routine ends the local scope for a condition declaration, returning
 *    a corresponding declaration statement.
 */

EXP
end_cond(void)
{
	int vars = 0;
	NAMESPACE ns = pop_namespace ();
	MEMBER p = DEREF_member (nspace_last (ns));
	EXP cond = make_decl_stmt (p, NULL_member, &vars);
	return (cond);
}


/*
 *    INJECT A CONDITION DECLARATION INTO THE CURRENT SCOPE
 *
 *    This routine injects the condition declaration cond into the current
 *    scope, prev, returning the result.  It is also used to deal with
 *    declarations in for-init statements.
 */

EXP
inject_cond(EXP prev, EXP cond)
{
	if (!IS_NULL_exp (cond)) {
		NAMESPACE ns = crt_namespace;
		while (IS_exp_decl_stmt (cond)) {
			IDENTIFIER id = DEREF_id (exp_decl_stmt_id (cond));
			IGNORE redeclare_id (ns, id);
			cond = DEREF_exp (exp_decl_stmt_body (cond));
		}
	}
	return (prev);
}


/*
 *    CONSTRUCT AN ASM STATEMENT
 *
 *    This routine constructs an asm statement from the string literal
 *    expression e.  Note that the semantics of asm are totally implementation
 *    dependent, so anything goes.
 */

EXP
make_asm(EXP e, LIST (EXP) args)
{
	STRING s = DEREF_str (exp_string_lit_str (e));
	if (!IS_NULL_list (args)) {
		report (crt_loc, ERR_dcl_asm_args ());
		args = convert_args (args);
	}
	MAKE_exp_assembler (type_void, s, args, e);
	report (crt_loc, ERR_dcl_asm_ti ());
	return (e);
}
