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


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

#include "msgcat.h"

#include "c_types.h"
#include "exp_ops.h"
#include "graph_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "nat_ops.h"
#include "nspace_ops.h"
#include "off_ops.h"
#include "tok_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "option.h"
#include "basetype.h"
#include "check.h"
#include "chktype.h"
#include "constant.h"
#include "convert.h"
#include "derive.h"
#include "function.h"
#include "hash.h"
#include "literal.h"
#include "merge.h"
#include "namespace.h"
#include "syntax.h"
#include "tokdef.h"


/*
 *    ARE TWO OFFSETS EQUAL?
 *
 *    This routine checks whether the offsets a and b are equal.  The co
 *    argument is passed through to eq_exp.
 */

int
eq_offset(OFFSET a, OFFSET b, int co)
{
    unsigned taga, tagb;
	
    /* Check for obvious equality */
    if (EQ_off (a, b)) return (1);
    if (IS_NULL_off (a)) return (0);
    if (IS_NULL_off (b)) return (0);
	
    /* Check tags */
    taga = TAG_off (a);
    tagb = TAG_off (b);
    if (taga != tagb) return (0);
	
    /* Check individual cases */
    ASSERT (ORDER_off == 13);
    switch (TAG_off (a)) {
	case off_zero_tag : {
	    /* Zero offsets */
	    TYPE ta = DEREF_type (off_zero_type (a));
	    TYPE tb = DEREF_type (off_zero_type (b));
	    return (eq_type_offset (ta, tb));
	}
	case off_type_tag : {
	    /* Type offsets */
	    TYPE ta = DEREF_type (off_type_type (a));
	    TYPE tb = DEREF_type (off_type_type (b));
	    return (eq_type_offset (ta, tb));
	}
	case off_extra_tag : {
	    /* Extra allocator offsets */
	    TYPE ta = DEREF_type (off_extra_type (a));
	    TYPE tb = DEREF_type (off_extra_type (b));
	    int na = DEREF_int (off_extra_scale (a));
	    int nb = DEREF_int (off_extra_scale (b));
	    return (na == nb && eq_type_offset (ta, tb));
	}
	case off_array_tag : {
	    /* Array offsets */
	    TYPE ta = DEREF_type (off_array_type (a));
	    TYPE tb = DEREF_type (off_array_type (b));
	    unsigned na = DEREF_unsigned (off_array_arg (a));
	    unsigned nb = DEREF_unsigned (off_array_arg (b));
	    return (na == nb && eq_type_offset (ta, tb));
	}
	case off_base_tag : {
	    /* Base class offsets */
	    GRAPH ga = DEREF_graph (off_base_graph (a));
	    GRAPH gb = DEREF_graph (off_base_graph (b));
	    return (eq_graph (ga, gb));
	}
	case off_deriv_tag : {
	    /* Derived class offsets */
	    GRAPH ga = DEREF_graph (off_deriv_graph (a));
	    GRAPH gb = DEREF_graph (off_deriv_graph (b));
	    return (eq_graph (ga, gb));
	}
	case off_member_tag : {
	    /* Member offsets */
	    IDENTIFIER ia = DEREF_id (off_member_id (a));
	    IDENTIFIER ib = DEREF_id (off_member_id (b));
	    return (EQ_id (ia, ib));
	}
	case off_ptr_mem_tag : {
	    /* Pointer member offsets */
	    EXP xa = DEREF_exp (off_ptr_mem_arg (a));
	    EXP xb = DEREF_exp (off_ptr_mem_arg (b));
	    return (eq_exp (xa, xb, co));
	}
	case off_negate_tag : {
	    /* Offset negation */
	    OFFSET sa = DEREF_off (off_negate_arg (a));
	    OFFSET sb = DEREF_off (off_negate_arg (b));
	    return (eq_offset (sa, sb, co));
	}
	case off_plus_tag : {
	    /* Offset addition */
	    OFFSET sa = DEREF_off (off_plus_arg1 (a));
	    OFFSET sb = DEREF_off (off_plus_arg1 (b));
	    OFFSET ta = DEREF_off (off_plus_arg2 (a));
	    OFFSET tb = DEREF_off (off_plus_arg2 (b));
	    if (eq_offset (sa, sb, co) && eq_offset (ta, tb, co)) {
			return (1);
	    }
	    if (co && eq_offset (sa, tb, 1) && eq_offset (ta, sb, 1)) {
			return (1);
	    }
	    return (0);
	}
	case off_mult_tag : {
	    /* Offset multiplication */
	    OFFSET sa = DEREF_off (off_mult_arg1 (a));
	    OFFSET sb = DEREF_off (off_mult_arg1 (b));
	    EXP za = DEREF_exp (off_mult_arg2 (a));
	    EXP zb = DEREF_exp (off_mult_arg2 (b));
	    return (eq_offset (sa, sb, co) && eq_exp (za, zb, co));
	}
	case off_ptr_diff_tag : {
	    /* Pointer difference */
	    EXP xa = DEREF_exp (off_ptr_diff_ptr1 (a));
	    EXP xb = DEREF_exp (off_ptr_diff_ptr1 (b));
	    EXP za = DEREF_exp (off_ptr_diff_ptr2 (a));
	    EXP zb = DEREF_exp (off_ptr_diff_ptr2 (b));
	    return (eq_exp (xa, xb, co) && eq_exp (za, zb, co));
	}
	case off_token_tag : {
	    /* Token application */
	    IDENTIFIER ia = DEREF_id (off_token_tok (a));
	    IDENTIFIER ib = DEREF_id (off_token_tok (b));
	    LIST (TOKEN) pa = DEREF_list (off_token_args (a));
	    LIST (TOKEN) pb = DEREF_list (off_token_args (b));
	    return (eq_token_args (ia, ib, pa, pb));
	}
    }
    return (0);
}


/*
 *    ARE TWO LISTS OF EXPRESSIONS EQUAL?
 *
 *    This routine checks whether the expression lists p and q are equal,
 *    in the sense that each of their components is equal.  The co argument
 *    is passed through to eq_exp.
 */

static int
eq_exp_list(LIST (EXP) p, LIST (EXP) q, int co)
{
    unsigned np = LENGTH_list (p);
    unsigned nq = LENGTH_list (q);
    if (np != nq) return (0);
    while (!IS_NULL_list (p)) {
		EXP a = DEREF_exp (HEAD_list (p));
		EXP b = DEREF_exp (HEAD_list (q));
		if (!eq_exp (a, b, co)) return (0);
		p = TAIL_list (p);
		q = TAIL_list (q);
    }
    return (1);
}


/*
 *    CHECK EXPRESSION EQUALITY
 *
 *    This is an auxiliary routine for eq_exp which checks whether the
 *    expressions a and b, which have the same tag value, are equal.
 */

static int
eq_exp_aux(EXP a, EXP b, int co)
{
    /* Check expressions */
    ASSERT (ORDER_exp == 88);
    switch (TAG_exp (a)) {
	case exp_identifier_tag :
	case exp_member_tag :
	case exp_ambiguous_tag :
	case exp_undeclared_tag : {
	    /* Identifier expressions */
	    IDENTIFIER ia = DEREF_id (exp_identifier_etc_id (a));
	    IDENTIFIER ib = DEREF_id (exp_identifier_etc_id (b));
	    return (EQ_id (ia, ib));
	}
	case exp_int_lit_tag : {
	    /* Integer literal expressions */
	    TYPE ta = DEREF_type (exp_type (a));
	    TYPE tb = DEREF_type (exp_type (b));
	    NAT na = DEREF_nat (exp_int_lit_nat (a));
	    NAT nb = DEREF_nat (exp_int_lit_nat (b));
	    if (!eq_type (ta, tb)) return (0);
	    if (EQ_nat (na, nb) || eq_nat (na, nb)) return (1);
	    return (0);
	}
	case exp_float_lit_tag : {
	    /* Floating literal expressions */
	    TYPE ta = DEREF_type (exp_type (a));
	    TYPE tb = DEREF_type (exp_type (b));
	    FLOAT fa = DEREF_flt (exp_float_lit_flt (a));
	    FLOAT fb = DEREF_flt (exp_float_lit_flt (b));
	    if (!eq_type (ta, tb)) return (0);
	    if (EQ_flt (fa, fb)) return (1);
	    return (eq_float_lit (fa, fb));
	}
	case exp_char_lit_tag : {
	    /* Character literal expressions */
	    TYPE ta = DEREF_type (exp_type (a));
	    TYPE tb = DEREF_type (exp_type (b));
	    STRING ca = DEREF_str (exp_char_lit_str (a));
	    STRING cb = DEREF_str (exp_char_lit_str (b));
	    if (!eq_type (ta, tb)) return (0);
	    if (EQ_str (ca, cb)) return (1);
	    return (eq_string_lit (ca, cb));
	}
	case exp_string_lit_tag : {
	    /* String literal expressions */
	    TYPE ta = DEREF_type (exp_type (a));
	    TYPE tb = DEREF_type (exp_type (b));
	    STRING ca = DEREF_str (exp_string_lit_str (a));
	    STRING cb = DEREF_str (exp_string_lit_str (b));
	    if (!eq_type (ta, tb)) return (0);
	    if (EQ_str (ca, cb)) return (1);
	    return (eq_string_lit (ca, cb));
	}
	case exp_null_tag :
	case exp_zero_tag :
	case exp_value_tag : {
	    /* Null expressions */
	    TYPE ta = DEREF_type (exp_type (a));
	    TYPE tb = DEREF_type (exp_type (b));
	    if (eq_type (ta, tb) == 1) return (1);
	    return (0);
	}
	case exp_paren_tag :
	case exp_copy_tag : {
	    /* Parenthesised expressions */
	    EXP sa = DEREF_exp (exp_paren_etc_arg (a));
	    EXP sb = DEREF_exp (exp_paren_etc_arg (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_assign_tag : {
	    /* Assignment expressions */
	    EXP ra = DEREF_exp (exp_assign_ref (a));
	    EXP rb = DEREF_exp (exp_assign_ref (b));
	    EXP sa = DEREF_exp (exp_assign_arg (a));
	    EXP sb = DEREF_exp (exp_assign_arg (b));
	    return (eq_exp (ra, rb, co) && eq_exp (sa, sb, co));
	}
	case exp_init_tag : {
	    /* Initialisation expressions */
	    IDENTIFIER ia = DEREF_id (exp_init_id (a));
	    IDENTIFIER ib = DEREF_id (exp_init_id (b));
	    EXP sa = DEREF_exp (exp_init_arg (a));
	    EXP sb = DEREF_exp (exp_init_arg (b));
	    return (EQ_id (ia, ib) && eq_exp (sa, sb, co));
	}
	case exp_preinc_tag : {
	    /* Pre-increment expressions */
	    EXP sa = DEREF_exp (exp_preinc_op (a));
	    EXP sb = DEREF_exp (exp_preinc_op (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_postinc_tag : {
	    /* Post-increment expressions */
	    EXP sa = DEREF_exp (exp_postinc_op (a));
	    EXP sb = DEREF_exp (exp_postinc_op (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_indir_tag : {
	    /* Indirection expressions */
	    EXP sa = DEREF_exp (exp_indir_ptr (a));
	    EXP sb = DEREF_exp (exp_indir_ptr (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_contents_tag : {
	    /* Contents expressions */
	    EXP sa = DEREF_exp (exp_contents_ptr (a));
	    EXP sb = DEREF_exp (exp_contents_ptr (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_address_tag : {
	    /* Address expressions */
	    EXP sa = DEREF_exp (exp_address_arg (a));
	    EXP sb = DEREF_exp (exp_address_arg (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_address_mem_tag : {
	    /* Address expressions */
	    EXP sa = DEREF_exp (exp_address_mem_arg (a));
	    EXP sb = DEREF_exp (exp_address_mem_arg (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_func_tag : {
	    /* Function expressions */
	    EXP sa = DEREF_exp (exp_func_fn (a));
	    EXP sb = DEREF_exp (exp_func_fn (b));
	    LIST (EXP) pa = DEREF_list (exp_func_args (a));
	    LIST (EXP) pb = DEREF_list (exp_func_args (b));
	    return (eq_exp (sa, sb, co) && eq_exp_list (pa, pb, co));
	}
	case exp_func_id_tag : {
	    /* Function expressions */
	    IDENTIFIER ia = DEREF_id (exp_func_id_id (a));
	    IDENTIFIER ib = DEREF_id (exp_func_id_id (b));
	    LIST (EXP) pa = DEREF_list (exp_func_id_args (a));
	    LIST (EXP) pb = DEREF_list (exp_func_id_args (b));
	    return (EQ_id (ia, ib) && eq_exp_list (pa, pb, co));
	}
	case exp_call_tag : {
	    /* Member function call expressions */
	    EXP ra = DEREF_exp (exp_call_ptr (a));
	    EXP rb = DEREF_exp (exp_call_ptr (b));
	    EXP sa = DEREF_exp (exp_call_arg (a));
	    EXP sb = DEREF_exp (exp_call_arg (b));
	    return (eq_exp (ra, rb, co) && eq_exp (sa, sb, co));
	}
	case exp_negate_tag :
	case exp_compl_tag :
	case exp_not_tag :
	case exp_abs_tag : {
	    /* Unary expressions */
	    EXP sa = DEREF_exp (exp_negate_etc_arg (a));
	    EXP sb = DEREF_exp (exp_negate_etc_arg (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_plus_tag :
	case exp_mult_tag :
	case exp_and_tag :
	case exp_or_tag :
	case exp_xor_tag :
	case exp_log_and_tag :
	case exp_log_or_tag :
	case exp_max_tag :
	case exp_min_tag : {
	    /* Commutative binary expressions */
	    EXP ra = DEREF_exp (exp_plus_etc_arg1 (a));
	    EXP rb = DEREF_exp (exp_plus_etc_arg1 (b));
	    EXP sa = DEREF_exp (exp_plus_etc_arg2 (a));
	    EXP sb = DEREF_exp (exp_plus_etc_arg2 (b));
	    if (eq_exp (ra, rb, co) && eq_exp (sa, sb, co)) {
			return (1);
	    }
	    if (co && eq_exp (ra, sb, 1) && eq_exp (sa, rb, 1)) {
			return (1);
	    }
	    return (0);
	}
	case exp_minus_tag :
	case exp_div_tag :
	case exp_rem_tag :
	case exp_lshift_tag :
	case exp_rshift_tag : {
	    /* Non-commutative binary expressions */
	    EXP ra = DEREF_exp (exp_plus_etc_arg1 (a));
	    EXP rb = DEREF_exp (exp_plus_etc_arg1 (b));
	    EXP sa = DEREF_exp (exp_plus_etc_arg2 (a));
	    EXP sb = DEREF_exp (exp_plus_etc_arg2 (b));
	    return (eq_exp (ra, rb, co) && eq_exp (sa, sb, co));
	}
	case exp_test_tag : {
	    /* Test expressions */
	    NTEST ca = DEREF_ntest (exp_test_tst (a));
	    NTEST cb = DEREF_ntest (exp_test_tst (b));
	    EXP sa = DEREF_exp (exp_test_arg (a));
	    EXP sb = DEREF_exp (exp_test_arg (b));
	    if (ca != cb) return (0);
	    return (eq_exp (sa, sb, co));
	}
	case exp_compare_tag : {
	    /* Comparison expressions */
	    NTEST ca = DEREF_ntest (exp_compare_tst (a));
	    NTEST cb = DEREF_ntest (exp_compare_tst (b));
	    EXP ra = DEREF_exp (exp_compare_arg1 (a));
	    EXP rb = DEREF_exp (exp_compare_arg1 (b));
	    EXP sa = DEREF_exp (exp_compare_arg2 (a));
	    EXP sb = DEREF_exp (exp_compare_arg2 (b));
	    if (ca != cb) return (0);
	    if (eq_exp (ra, rb, co) && eq_exp (sa, sb, co)) {
			return (1);
	    }
	    if (co && (ca == ntest_eq || ca == ntest_not_eq)) {
			/* Commutative comparisons */
			if (eq_exp (ra, sb, 1) && eq_exp (sa, rb, 1)) {
				return (1);
			}
	    }
	    return (0);
	}
	case exp_cast_tag : {
	    /* Cast expressions */
	    EXP sa = DEREF_exp (exp_cast_arg (a));
	    EXP sb = DEREF_exp (exp_cast_arg (b));
	    unsigned va = DEREF_unsigned (exp_cast_conv (a));
	    unsigned vb = DEREF_unsigned (exp_cast_conv (b));
	    return (va == vb && eq_exp (sa, sb, co));
	}
	case exp_base_cast_tag : {
	    /* Base cast expressions */
	    EXP ra = DEREF_exp (exp_base_cast_arg (a));
	    EXP rb = DEREF_exp (exp_base_cast_arg (b));
	    OFFSET za = DEREF_off (exp_base_cast_off (a));
	    OFFSET zb = DEREF_off (exp_base_cast_off (b));
	    unsigned va = DEREF_unsigned (exp_base_cast_conv (a));
	    unsigned vb = DEREF_unsigned (exp_base_cast_conv (b));
	    if (va != vb) return (0);
	    return (eq_exp (ra, rb, co) && eq_offset (za, zb, co));
	}
	case exp_dyn_cast_tag : {
	    /* Dynamic cast expressions */
	    EXP sa = DEREF_exp (exp_dyn_cast_arg (a));
	    EXP sb = DEREF_exp (exp_dyn_cast_arg (b));
	    EXP ra = DEREF_exp (exp_dyn_cast_except (a));
	    EXP rb = DEREF_exp (exp_dyn_cast_except (b));
	    return (eq_exp (sa, sb, co) && eq_exp (ra, rb, co));
	}
	case exp_add_ptr_tag : {
	    /* Pointer addition expressions */
	    EXP ra = DEREF_exp (exp_add_ptr_ptr (a));
	    EXP rb = DEREF_exp (exp_add_ptr_ptr (b));
	    OFFSET za = DEREF_off (exp_add_ptr_off (a));
	    OFFSET zb = DEREF_off (exp_add_ptr_off (b));
	    return (eq_exp (ra, rb, co) && eq_offset (za, zb, co));
	}
	case exp_offset_size_tag : {
	    /* Offset size expressions */
	    OFFSET xa = DEREF_off (exp_offset_size_off (a));
	    OFFSET xb = DEREF_off (exp_offset_size_off (b));
	    TYPE sa = DEREF_type (exp_offset_size_step (a));
	    TYPE sb = DEREF_type (exp_offset_size_step (b));
	    return (eq_offset (xa, xb, co) && eq_type_offset (sa, sb));
	}
	case exp_constr_tag : {
	    /* Constructors */
	    EXP sa = DEREF_exp (exp_constr_call (a));
	    EXP sb = DEREF_exp (exp_constr_call (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_destr_tag : {
	    /* Destructors */
	    EXP sa = DEREF_exp (exp_destr_call (a));
	    EXP sb = DEREF_exp (exp_destr_call (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_alloc_tag : {
	    /* Allocators */
	    EXP sa = DEREF_exp (exp_alloc_call (a));
	    EXP sb = DEREF_exp (exp_alloc_call (b));
	    EXP ra = DEREF_exp (exp_alloc_init (a));
	    EXP rb = DEREF_exp (exp_alloc_init (b));
	    return (eq_exp (sa, sb, co) && eq_exp (ra, rb, co));
	}
	case exp_dealloc_tag : {
	    /* Deallocators */
	    EXP sa = DEREF_exp (exp_dealloc_call (a));
	    EXP sb = DEREF_exp (exp_dealloc_call (b));
	    EXP ra = DEREF_exp (exp_dealloc_term (a));
	    EXP rb = DEREF_exp (exp_dealloc_term (b));
	    return (eq_exp (sa, sb, co) && eq_exp (ra, rb, co));
	}
	case exp_rtti_tag : {
	    /* Run-time type information */
	    EXP sa = DEREF_exp (exp_rtti_arg (a));
	    EXP sb = DEREF_exp (exp_rtti_arg (b));
	    EXP ra = DEREF_exp (exp_rtti_except (a));
	    EXP rb = DEREF_exp (exp_rtti_except (b));
	    int ia = DEREF_int (exp_rtti_op (a));
	    int ib = DEREF_int (exp_rtti_op (b));
	    if (ia != ib) return (0);
	    return (eq_exp (sa, sb, co) && eq_exp (ra, rb, co));
	}
	case exp_rtti_type_tag : {
	    /* Run-time type information */
	    TYPE sa = DEREF_type (exp_rtti_type_arg (a));
	    TYPE sb = DEREF_type (exp_rtti_type_arg (b));
	    int ia = DEREF_int (exp_rtti_type_op (a));
	    int ib = DEREF_int (exp_rtti_type_op (b));
	    if (ia != ib) return (0);
	    if (eq_type (sa, sb) == 1) return (1);
	    return (0);
	}
	case exp_rtti_no_tag : {
	    /* Link-time type information */
	    TYPE sa = DEREF_type (exp_rtti_no_arg (a));
	    TYPE sb = DEREF_type (exp_rtti_no_arg (b));
	    if (eq_type (sa, sb) == 1) return (1);
	    return (0);
	}
	case exp_dynamic_tag : {
	    /* Dynamic initialisers */
	    EXP sa = DEREF_exp (exp_dynamic_arg (a));
	    EXP sb = DEREF_exp (exp_dynamic_arg (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_aggregate_tag : {
	    /* Aggregate initialisers */
	    LIST (EXP) pa = DEREF_list (exp_aggregate_args (a));
	    LIST (EXP) pb = DEREF_list (exp_aggregate_args (b));
	    return (eq_exp_list (pa, pb, co));
	}
	case exp_initialiser_tag : {
	    /* Function style initialisers */
	    LIST (EXP) pa = DEREF_list (exp_initialiser_args (a));
	    LIST (EXP) pb = DEREF_list (exp_initialiser_args (b));
	    return (eq_exp_list (pa, pb, co));
	}
	case exp_nof_tag : {
	    /* Array initialisers */
	    EXP ra = DEREF_exp (exp_nof_start (a));
	    EXP rb = DEREF_exp (exp_nof_start (b));
	    EXP sa = DEREF_exp (exp_nof_pad (a));
	    EXP sb = DEREF_exp (exp_nof_pad (b));
	    NAT na = DEREF_nat (exp_nof_size (a));
	    NAT nb = DEREF_nat (exp_nof_size (b));
	    EXP ua = DEREF_exp (exp_nof_end (a));
	    EXP ub = DEREF_exp (exp_nof_end (b));
	    if (!EQ_nat (na, nb)) {
			if (!eq_nat (na, nb)) return (0);
	    }
	    if (!eq_exp (sa, sb, co)) return (0);
	    return (eq_exp (ra, rb, co) && eq_exp (ua, ub, co));
	}
	case exp_comma_tag : {
	    /* Comma expressions */
	    LIST (EXP) pa = DEREF_list (exp_comma_args (a));
	    LIST (EXP) pb = DEREF_list (exp_comma_args (b));
	    return (eq_exp_list (pa, pb, co));
	}
	case exp_set_tag :
	case exp_unused_tag : {
	    /* Flow analysis expressions */
	    EXP sa = DEREF_exp (exp_set_etc_arg (a));
	    EXP sb = DEREF_exp (exp_set_etc_arg (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_if_stmt_tag : {
	    /* Conditional expressions */
	    EXP ca = DEREF_exp (exp_if_stmt_cond (a));
	    EXP cb = DEREF_exp (exp_if_stmt_cond (b));
	    EXP ra = DEREF_exp (exp_if_stmt_true_code (a));
	    EXP rb = DEREF_exp (exp_if_stmt_true_code (b));
	    EXP sa = DEREF_exp (exp_if_stmt_false_code (a));
	    EXP sb = DEREF_exp (exp_if_stmt_false_code (b));
	    if (!eq_exp (ca, cb, co)) return (0);
	    return (eq_exp (ra, rb, co) && eq_exp (sa, sb, co));
	}
	case exp_exception_tag : {
	    /* Throw expressions */
	    EXP sa = DEREF_exp (exp_exception_arg (a));
	    EXP sb = DEREF_exp (exp_exception_arg (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_thrown_tag : {
	    /* Thrown expressions */
	    TYPE ta = DEREF_type (exp_type (a));
	    TYPE tb = DEREF_type (exp_type (b));
	    if (eq_type (ta, tb) == 1) return (1);
	    return (0);
	}
	case exp_op_tag : {
	    /* Undetermined expressions */
	    int ca = DEREF_int (exp_op_lex (a));
	    int cb = DEREF_int (exp_op_lex (b));
	    EXP ra = DEREF_exp (exp_op_arg1 (a));
	    EXP rb = DEREF_exp (exp_op_arg1 (b));
	    EXP sa = DEREF_exp (exp_op_arg2 (a));
	    EXP sb = DEREF_exp (exp_op_arg2 (b));
	    if (ca != cb) return (0);
	    return (eq_exp (ra, rb, co) && eq_exp (sa, sb, co));
	}
	case exp_opn_tag : {
	    /* Undetermined expressions */
	    int ca = DEREF_int (exp_opn_lex (a));
	    int cb = DEREF_int (exp_opn_lex (b));
	    LIST (EXP) pa = DEREF_list (exp_opn_args (a));
	    LIST (EXP) pb = DEREF_list (exp_opn_args (b));
	    if (ca != cb) return (0);
	    return (eq_exp_list (pa, pb, co));
	}
	case exp_assembler_tag : {
	    /* Assembler expressions */
	    STRING ca = DEREF_str (exp_assembler_op (a));
	    STRING cb = DEREF_str (exp_assembler_op (b));
	    if (EQ_str (ca, cb) || eq_string_lit (ca, cb)) {
			LIST (EXP) pa = DEREF_list (exp_assembler_args (a));
			LIST (EXP) pb = DEREF_list (exp_assembler_args (b));
			return (eq_exp_list (pa, pb, co));
	    }
	    break;
	}
	case exp_location_tag : {
	    /* Location expressions */
	    EXP sa = DEREF_exp (exp_location_arg (a));
	    EXP sb = DEREF_exp (exp_location_arg (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_token_tag : {
	    /* Token application */
	    IDENTIFIER ia = DEREF_id (exp_token_tok (a));
	    IDENTIFIER ib = DEREF_id (exp_token_tok (b));
	    LIST (TOKEN) pa = DEREF_list (exp_token_args (a));
	    LIST (TOKEN) pb = DEREF_list (exp_token_args (b));
	    return (eq_token_args (ia, ib, pa, pb));
	}
	case exp_dummy_tag : {
	    /* Dummy expressions */
	    EXP sa = DEREF_exp (exp_dummy_value (a));
	    EXP sb = DEREF_exp (exp_dummy_value (b));
	    return (eq_exp (sa, sb, co));
	}
	case exp_reach_tag :
	case exp_unreach_tag :
	case exp_sequence_tag :
	case exp_solve_stmt_tag :
	case exp_decl_stmt_tag :
	case exp_while_stmt_tag :
	case exp_do_stmt_tag :
	case exp_switch_stmt_tag :
	case exp_hash_if_tag :
	case exp_return_stmt_tag :
	case exp_goto_stmt_tag :
	case exp_label_stmt_tag :
	case exp_try_block_tag :
	case exp_handler_tag :
	case exp_uncompiled_tag :
	case exp_fail_tag : {
	    /* Statements are never equal */
	    return (0);
	}
    }
    return (0);
}


/*
 *    UNIFY TWO EXPRESSIONS
 *
 *    This routine unifies the expressions a and b by defining tokens as
 *    necessary.  It returns true if a value is assigned to a token.  Note
 *    that it is necessary to consider integer constant tokens as well as
 *    expression tokens.
 */

static int
unify_exp(EXP a, EXP b)
{
    IDENTIFIER id;
    LIST (TOKEN) args;
    switch (TAG_exp (a)) {
	case exp_token_tag : {
	    id = DEREF_id (exp_token_tok (a));
	    args = DEREF_list (exp_token_args (a));
	    break;
	}
	case exp_int_lit_tag : {
	    NAT n = DEREF_nat (exp_int_lit_nat (a));
	    if (!IS_nat_token (n)) return (0);
	    id = DEREF_id (nat_token_tok (n));
	    args = DEREF_list (nat_token_args (n));
	    break;
	}
	default : {
	    return (0);
	}
    }
    if (IS_NULL_list (args) && defining_token (id)) {
		return (define_exp_token (id, b, 1));
    }
    return (0);
}


/*
 *    ARE TWO EXPRESSIONS EQUAL?
 *
 *    This routine checks whether the expressions a and b are equal.  If
 *    co is true then commutativity and other such relations are taken
 *    into account.
 */

int
eq_exp(EXP a, EXP b, int co)
{
    /* Check for obvious equality */
    unsigned ta, tb;
    if (EQ_exp (a, b)) return (1);
    if (IS_NULL_exp (a)) return (0);
    if (IS_NULL_exp (b)) return (0);
	
    /* Allow for parentheses */
    ta = TAG_exp (a);
    while (ta == exp_paren_tag || ta == exp_copy_tag) {
		a = DEREF_exp (exp_paren_etc_arg (a));
		ta = TAG_exp (a);
    }
    tb = TAG_exp (b);
    while (tb == exp_paren_tag || tb == exp_copy_tag) {
		b = DEREF_exp (exp_paren_etc_arg (b));
		tb = TAG_exp (b);
    }
	
    /* Check equality of expressions */
    if (ta == tb && eq_exp_aux (a, b, co)) return (1);
    if (force_tokdef || force_template || expand_tokdef) {
		TYPE sa = DEREF_type (exp_type (a));
		TYPE sb = DEREF_type (exp_type (b));
		if (eq_type (sa, sb) == 1) {
			if (unify_exp (a, b)) return (1);
			if (unify_exp (b, a)) return (1);
		}
    }
    return (0);
}


/*
 *    ARE TWO EXPRESSIONS PRECISELY EQUAL?
 *
 *    This routine is similar to eq_exp, but it disallows token and template
 *    unification and does not allow for commutativity relations.
 */

int
eq_exp_exact(EXP a, EXP b)
{
    int eq;
    int tok = force_tokdef;
    int templ = force_template;
    force_tokdef = 0;
    force_template = 0;
    eq = eq_exp (a, b, 0);
    force_template = templ;
    force_tokdef = tok;
    return (eq);
}


/*
 *    ARE TWO TOKENS EQUAL?
 *
 *    This routine checks whether the tokens a and b are equal.
 */

int
eq_token(TOKEN a, TOKEN b)
{
    unsigned na, nb;
    if (EQ_tok (a, b)) return (1);
    if (IS_NULL_tok (a)) return (0);
    if (IS_NULL_tok (b)) return (0);
    na = TAG_tok (a);
    nb = TAG_tok (b);
    if (na != nb) return (0);
    switch (na) {
	case tok_exp_tag : {
	    EXP va = DEREF_exp (tok_exp_value (a));
	    EXP vb = DEREF_exp (tok_exp_value (b));
	    return (eq_exp (va, vb, 0));
	}
	case tok_stmt_tag : {
	    EXP va = DEREF_exp (tok_stmt_value (a));
	    EXP vb = DEREF_exp (tok_stmt_value (b));
	    return (eq_exp (va, vb, 0));
	}
	case tok_nat_tag :
	case tok_snat_tag : {
	    NAT va = DEREF_nat (tok_nat_etc_value (a));
	    NAT vb = DEREF_nat (tok_nat_etc_value (b));
	    if (compare_nat (va, vb) == 0) return (1);
	    break;
	}
	case tok_type_tag : {
	    TYPE va = DEREF_type (tok_type_value (a));
	    TYPE vb = DEREF_type (tok_type_value (b));
	    if (eq_type (va, vb) == 1) return (1);
	    return (0);
	}
	case tok_member_tag : {
	    OFFSET va = DEREF_off (tok_member_value (a));
	    OFFSET vb = DEREF_off (tok_member_value (b));
	    return (eq_offset (va, vb, 0));
	}
	case tok_class_tag : {
	    IDENTIFIER ia = DEREF_id (tok_class_value (a));
	    IDENTIFIER ib = DEREF_id (tok_class_value (b));
	    return (EQ_id (ia, ib));
	}
    }
    return (0);
}


/*
 *    ARE TWO TOKEN APPLICATIONS EQUAL?
 *
 *    This routine checks whether the token applications ia (pa) and
 *    ib (pb) are equal.
 */

int
eq_token_args(IDENTIFIER ia, IDENTIFIER ib, LIST (TOKEN) pa, LIST (TOKEN) pb)
{
    if (!EQ_id (ia, ib)) {
		/* Check that tokens are the same */
		ia = DEREF_id (id_alias (ia));
		ib = DEREF_id (id_alias (ib));
		if (!EQ_id (ia, ib)) {
			if (!force_merge || !merge_type (ia, ib)) {
				return (0);
			}
		}
    }
    if (EQ_list (pa, pb)) return (1);
    if (LENGTH_list (pa) != LENGTH_list (pb)) return (0);
    while (!IS_NULL_list (pa)) {
		TOKEN a, b;
		if (EQ_list (pa, pb)) return (1);
		a = DEREF_tok (HEAD_list (pa));
		b = DEREF_tok (HEAD_list (pb));
		if (!eq_token (a, b)) return (0);
		pb = TAIL_list (pb);
		pa = TAIL_list (pa);
    }
    return (1);
}


/*
 *    IS A TOKEN APPLICATION A CONSTANT?
 *
 *    This routine checks whether the token id applied to the arguments args
 *    is a constant.
 */

static int
is_const_token(IDENTIFIER id, LIST (TOKEN) args, int c)
{
    TOKEN tok = DEREF_tok (id_token_sort (id));
    if (IS_tok_proc (tok)) {
		/* Allow for procedure tokens */
		tok = DEREF_tok (tok_proc_res (tok));
    }
    switch (TAG_tok (tok)) {
	case tok_exp_tag : {
	    /* Check for constant expression tokens */
	    int cn = DEREF_int (tok_exp_constant (tok));
	    if (cn) {
			/* Constant token */
			return (1);
	    }
	    if (c >= 0) {
			/* Constant expression expected */
			report (crt_loc, ERR_token_const (id));
	    }
	    if (option (OPT_token_const) == OPTION_DISALLOW) {
			/* Non-constant tokens not allowed */
			return (0);
	    }
	    break;
	}
	case tok_nat_tag : {
	    /* Integer constants */
	    return (1);
	}
	case tok_stmt_tag : {
	    /* Statements are not constant */
	    return (0);
	}
    }
	
    /* Check all token arguments */
    while (!IS_NULL_list (args)) {
		TOKEN a = DEREF_tok (HEAD_list (args));
		if (!IS_NULL_tok (a)) {
			switch (TAG_tok (a)) {
			case tok_exp_tag : {
				EXP e = DEREF_exp (tok_exp_value (a));
				if (!is_const_exp (e, c)) return (0);
				break;
			}
			case tok_stmt_tag : {
				EXP e = DEREF_exp (tok_stmt_value (a));
				if (!is_const_exp (e, c)) return (0);
				break;
			}
			case tok_member_tag : {
				OFFSET off = DEREF_off (tok_member_value (a));
				if (!is_const_offset (off, c, 1)) return (0);
				break;
			}
			}
		}
		args = TAIL_list (args);
    }
    return (1);
}


/*
 *    IS AN OFFSET A CONSTANT?
 *
 *    This routine checks whether the offset off is a constant offset.  virt
 *    is true if virtual base classes are to be taken into account.
 */

int
is_const_offset(OFFSET off, int c, int virt)
{
    ASSERT (ORDER_off == 13);
    switch (TAG_off (off)) {
	case off_zero_tag :
	case off_type_tag :
	case off_extra_tag :
	case off_array_tag :
	case off_member_tag : {
	    /* Constant offsets */
	    return (1);
	}
	case off_base_tag : {
	    /* Base class offsets */
	    if (virt) {
			GRAPH gr = DEREF_graph (off_base_graph (off));
			DECL_SPEC acc = DEREF_dspec (graph_access (gr));
			if (!(acc & dspec_mutable)) return (1);
	    } else {
			return (1);
	    }
	    break;
	}
	case off_deriv_tag : {
	    /* Derived class offsets */
	    if (virt) {
			GRAPH gr = DEREF_graph (off_deriv_graph (off));
			DECL_SPEC acc = DEREF_dspec (graph_access (gr));
			if (!(acc & dspec_mutable)) return (1);
	    } else {
			return (1);
	    }
	    break;
	}
	case off_ptr_mem_tag : {
	    /* Pointer member offsets */
	    EXP a = DEREF_exp (off_ptr_mem_arg (off));
	    return (is_const_exp (a, c));
	}
	case off_negate_tag : {
	    /* Offset negation */
	    OFFSET off1 = DEREF_off (off_negate_arg (off));
	    return (is_const_offset (off1, c, 1));
	}
	case off_plus_tag : {
	    /* Offset addition */
	    OFFSET off1 = DEREF_off (off_plus_arg1 (off));
	    OFFSET off2 = DEREF_off (off_plus_arg2 (off));
	    if (!is_const_offset (off1, c, 1)) return (0);
	    return (is_const_offset (off2, c, 1));
	}
	case off_mult_tag : {
	    /* Offset multiplication */
	    OFFSET off1 = DEREF_off (off_mult_arg1 (off));
	    EXP a = DEREF_exp (off_mult_arg2 (off));
	    if (!is_const_offset (off1, c, 1)) return (0);
	    return (is_const_exp (a, c));
	}
	case off_ptr_diff_tag : {
	    /* Pointer difference */
	    EXP a = DEREF_exp (off_ptr_diff_ptr1 (off));
	    EXP b = DEREF_exp (off_ptr_diff_ptr2 (off));
	    if (is_const_exp (a, c) && is_const_exp (b, c)) {
			/* Only allow pointers from same array */
			EXP pa = NULL_exp;
			EXP pb = NULL_exp;
			IGNORE find_exp_linkage (a, &pa, 1);
			IGNORE find_exp_linkage (b, &pb, 1);
			if (!IS_NULL_exp (pa) && !IS_exp_string_lit (pa)) {
				return (eq_exp (pa, pb, 0));
			}
	    }
	    break;
	}
	case off_token_tag : {
	    /* All member tokens are constant */
	    IDENTIFIER id = DEREF_id (off_token_tok (off));
	    LIST (TOKEN) args = DEREF_list (off_token_args (off));
	    return (is_const_token (id, args, c));
	}
    }
    return (0);
}


/*
 *    IS AN EXPRESSION AN ADDRESS CONSTANT?
 *
 *    This routine checks whether the address of e is a constant expression.
 *    c will be nonzero.
 */

static int
is_const_addr(EXP e, int c)
{
    if (IS_NULL_exp (e)) return (1);
    switch (TAG_exp (e)) {
	case exp_identifier_tag : {
	    /* Identifier expressions */
	    IDENTIFIER id = DEREF_id (exp_identifier_id (e));
	    DECL_SPEC ds = DEREF_dspec (id_storage (id));
	    if (!(ds & dspec_auto)) {
			TYPE t = DEREF_type (exp_type (e));
			if (!IS_type_ref (t)) return (1);
	    }
	    break;
	}
	case exp_indir_tag : {
	    /* Indirection expressions */
	    EXP a = DEREF_exp (exp_indir_ptr (e));
	    return (is_const_exp (a, c));
	}
	case exp_member_tag :
	case exp_string_lit_tag :
	case exp_rtti_type_tag : {
	    /* lvalue expressions */
	    return (1);
	}
	case exp_token_tag : {
	    /* Tokenised expressions */
	    return (is_const_exp (e, c));
	}
	case exp_comma_tag : {
	    /* Comma expressions (not allowed) */
	    if (c < 0) {
			LIST (EXP) p = DEREF_list (exp_comma_args (e));
			while (!IS_NULL_list (p)) {
				EXP a = DEREF_exp (HEAD_list (p));
				p = TAIL_list (p);
				if (IS_NULL_list (p)) {
					return (is_const_addr (a, c));
				}
				if (!is_const_exp (a, c)) break;
			}
	    }
	    break;
	}
	case exp_if_stmt_tag : {
	    /* Conditional expressions */
	    EXP d = DEREF_exp (exp_if_stmt_cond (e));
	    EXP a = DEREF_exp (exp_if_stmt_true_code (e));
	    EXP b = DEREF_exp (exp_if_stmt_false_code (e));
	    if (!is_const_exp (d, c)) break;
	    if (!is_const_addr (a, c)) break;
	    return (is_const_addr (b, c));
	}
    }
    return (0);
}


/*
 *    IS AN EXPRESSION CONSTANT?
 *
 *    This routine checks whether e is a constant expression.  Note that
 *    most integer constant expressions are dealt with in a bottom-up
 *    fashion by means of the constant evaluation routines.  Other
 *    constants are dealt with in a top-down fashion by this routine.  If
 *    c is 0 then only valid integer constant expressions are allowed; c
 *    is -1 then only a check is intended.
 */

int
is_const_exp(EXP e, int c)
{
    TYPE t;
    if (IS_NULL_exp (e)) return (1);
    ASSERT (ORDER_exp == 88);
    switch (TAG_exp (e)) {
	case exp_int_lit_tag :
	case exp_char_lit_tag : {
	    /* Integer literals */
	    return (1);
	}
	case exp_identifier_tag : {
	    /* Identifiers */
	    if (c) {
			IDENTIFIER id = DEREF_id (exp_identifier_id (e));
			DECL_SPEC ds = DEREF_dspec (id_storage (id));
			if (!(ds & dspec_auto)) {
				t = DEREF_type (exp_type (e));
				if (!IS_type_ref (t)) return (1);
			}
	    }
	    break;
	}
	case exp_string_lit_tag :
	case exp_float_lit_tag :
	case exp_null_tag :
	case exp_zero_tag :
	case exp_value_tag :
	case exp_rtti_type_tag :
	case exp_rtti_no_tag :
	case exp_set_tag :
	case exp_unused_tag : {
	    /* Floating literals, null pointers etc. */
	    if (c) return (1);
	    break;
	}
	case exp_paren_tag :
	case exp_copy_tag : {
	    /* Parenthesised expressions */
	    EXP a = DEREF_exp (exp_paren_etc_arg (e));
	    return (is_const_exp (a, c));
	}
	case exp_indir_tag : {
	    /* Indirection expressions */
	    if (c) {
			EXP a = DEREF_exp (exp_indir_ptr (e));
			return (is_const_exp (a, c));
	    }
	    break;
	}
	case exp_address_tag : {
	    /* Address expressions */
	    if (c) {
			EXP a = DEREF_exp (exp_address_arg (e));
			return (is_const_addr (a, c));
	    }
	    break;
	}
	case exp_address_mem_tag : {
	    /* Address expressions */
	    if (c) {
			EXP a = DEREF_exp (exp_address_mem_arg (e));
			if (IS_exp_member (a)) return (1);
	    }
	    break;
	}
	case exp_negate_tag :
	case exp_compl_tag :
	case exp_not_tag :
	case exp_abs_tag : {
	    /* Unary expressions */
	    EXP a = DEREF_exp (exp_negate_etc_arg (e));
	    return (is_const_exp (a, c));
	}
	case exp_plus_tag :
	case exp_minus_tag :
	case exp_mult_tag :
	case exp_and_tag :
	case exp_or_tag :
	case exp_xor_tag :
	case exp_log_and_tag :
	case exp_log_or_tag :
	case exp_lshift_tag :
	case exp_rshift_tag :
	case exp_max_tag :
	case exp_min_tag : {
	    /* Binary expressions */
	    EXP a = DEREF_exp (exp_plus_etc_arg1 (e));
	    EXP b = DEREF_exp (exp_plus_etc_arg2 (e));
	    return (is_const_exp (a, c) && is_const_exp (b, c));
	}
	case exp_div_tag :
	case exp_rem_tag : {
	    /* Division expressions */
	    EXP a = DEREF_exp (exp_plus_etc_arg1 (e));
	    EXP b = DEREF_exp (exp_plus_etc_arg2 (e));
	    if (c == 0 && is_zero_exp (b)) {
			/* Division by zero doesn't count */
			break;
	    }
	    return (is_const_exp (a, c) && is_const_exp (b, c));
	}
	case exp_test_tag : {
	    /* Test expressions */
	    EXP a = DEREF_exp (exp_test_arg (e));
	    return (is_const_exp (a, c));
	}
	case exp_compare_tag : {
	    /* Comparison expressions */
	    EXP a = DEREF_exp (exp_compare_arg1 (e));
	    EXP b = DEREF_exp (exp_compare_arg2 (e));
	    return (is_const_exp (a, c) && is_const_exp (b, c));
	}
	case exp_cast_tag : {
	    /* Cast expressions */
	    EXP a = DEREF_exp (exp_cast_arg (e));
	    if (!c) {
			unsigned tc;
			t = DEREF_type (exp_type (a));
			tc = type_category (&t);
			if (!IS_TYPE_INT (tc)) break;
	    }
	    return (is_const_exp (a, c));
	}
	case exp_base_cast_tag : {
	    /* Base cast expressions */
	    if (c) {
			EXP a = DEREF_exp (exp_base_cast_arg (e));
			OFFSET off = DEREF_off (exp_base_cast_off (e));
			unsigned conv = DEREF_unsigned (exp_base_cast_conv (e));
			if (conv & CONV_PTR_MEM_BASE) {
				/* Pointer to member conversions */
				if (conv & CONV_REVERSE) return (0);
			} else {
				/* Pointer conversions */
				if (!is_zero_offset (off)) return (0);
			}
			return (is_const_exp (a, c));
	    }
	    break;
	}
	case exp_add_ptr_tag : {
	    /* Pointer addition */
	    if (c) {
			t = DEREF_type (exp_type (e));
			if (!IS_type_ref (t)) {
				EXP a = DEREF_exp (exp_add_ptr_ptr (e));
				OFFSET b = DEREF_off (exp_add_ptr_off (e));
				int v = DEREF_int (exp_add_ptr_virt (e));
				if (!is_const_exp (a, c)) return (0);
				return (is_const_offset (b, c, v));
			}
	    }
	    break;
	}
	case exp_offset_size_tag : {
	    /* Offset size */
	    OFFSET a = DEREF_off (exp_offset_size_off (e));
	    if (IS_off_type (a)) {
			/* Allow for sizeof expressions */
			TYPE s = DEREF_type (exp_offset_size_step (e));
			if (EQ_type (s, type_char)) return (1);
	    }
	    if (c) return (is_const_offset (a, c, c));
	    break;
	}
	case exp_aggregate_tag : {
	    /* Aggregate initialisers */
	    if (c) {
			LIST (EXP) p = DEREF_list (exp_aggregate_args (e));
			while (!IS_NULL_list (p)) {
				EXP a = DEREF_exp (HEAD_list (p));
				if (!is_const_exp (a, c)) return (0);
				p = TAIL_list (p);
			}
			return (1);
	    }
	    break;
	}
	case exp_nof_tag : {
	    /* Array initialisers */
	    if (c) {
			EXP a = DEREF_exp (exp_nof_start (e));
			EXP b = DEREF_exp (exp_nof_pad (e));
			EXP d = DEREF_exp (exp_nof_end (e));
			if (!is_const_exp (a, c)) return (0);
			if (!is_const_exp (b, c)) return (0);
			return (is_const_exp (d, c));
	    }
	    break;
	}
	case exp_comma_tag : {
	    /* Comma expressions (not allowed) */
	    if (c < 0) {
			LIST (EXP) p = DEREF_list (exp_comma_args (e));
			while (!IS_NULL_list (p)) {
				EXP a = DEREF_exp (HEAD_list (p));
				if (!is_const_exp (a, c)) return (0);
				p = TAIL_list (p);
			}
			return (1);
	    }
	    break;
	}
	case exp_if_stmt_tag : {
	    /* Conditional expressions */
	    if (c) {
			EXP d = DEREF_exp (exp_if_stmt_cond (e));
			EXP a = DEREF_exp (exp_if_stmt_true_code (e));
			EXP b = DEREF_exp (exp_if_stmt_false_code (e));
			if (!is_const_exp (d, c)) break;
			if (!is_const_exp (a, c)) break;
			return (is_const_exp (b, c));
	    }
	    break;
	}
	case exp_op_tag : {
	    /* Undetermined expressions */
	    EXP a = DEREF_exp (exp_op_arg1 (e));
	    EXP b = DEREF_exp (exp_op_arg2 (e));
	    return (is_const_exp (a, c) && is_const_exp (b, c));
	}
	case exp_opn_tag : {
	    /* Undetermined nary expressions */
	    LIST (EXP) p = DEREF_list (exp_opn_args (e));
	    while (!IS_NULL_list (p)) {
			EXP a = DEREF_exp (HEAD_list (p));
			if (!is_const_exp (a, c)) return (0);
			p = TAIL_list (p);
	    }
	    return (1);
	}
	case exp_token_tag : {
	    /* Tokenised expressions (C compatibility) */
	    IDENTIFIER id = DEREF_id (exp_token_tok (e));
	    LIST (TOKEN) args = DEREF_list (exp_token_args (e));
	    if (!c) {
			unsigned tc;
			t = DEREF_type (exp_type (e));
			tc = type_category (&t);
			if (!IS_TYPE_INT (tc)) break;
	    }
	    return (is_const_token (id, args, c));
	}
	case exp_location_tag : {
	    /* Location expressions */
	    EXP a = DEREF_exp (exp_location_arg (e));
	    return (is_const_exp (a, c));
	}
	case exp_dummy_tag : {
	    /* Dummy expressions */
	    EXP a = DEREF_exp (exp_dummy_value (e));
	    return (is_const_exp (a, c));
	}
    }
	
    /* Allow for errors */
    t = DEREF_type (exp_type (e));
    if (IS_type_error (t)) return (1);
    return (0);
}


/*
 *    DOES AN EXPRESSION CONTAIN AN OVERFLOW?
 *
 *    Certain evaluations on integer constant expressions are only valid
 *    if it can be shown that the operands cannot raise an exception.
 *    For example, 'a - a' can only safely be replaced by zero if the
 *    evaluation of a does not overflow.  This routine returns true if
 *    the evaluation of the expression a may raise an overflow exception
 *    or contains some other side effect.
 */

int
overflow_exp(EXP a)
{
    if (IS_NULL_exp (a)) return (0);
    switch (TAG_exp (a)) {
	case exp_int_lit_tag : {
	    /* Check integer constant expressions */
	    NAT n = DEREF_nat (exp_int_lit_nat (a));
	    unsigned etag = DEREF_unsigned (exp_int_lit_etag (a));
	    switch (etag) {
		case exp_char_lit_tag :
		case exp_offset_size_tag : {
		    /* These never overflow */
		    return (0);
		}
	    }
	    return (is_calc_nat (n));
	}
	case exp_identifier_tag :
	case exp_member_tag :
	case exp_char_lit_tag :
	case exp_value_tag :
	case exp_null_tag :
	case exp_zero_tag : {
	    return (0);
	}
	case exp_string_lit_tag : {
	    /* String literals deliberately excluded */
	    return (1);
	}
	case exp_paren_tag :
	case exp_copy_tag : {
	    EXP b = DEREF_exp (exp_paren_etc_arg (a));
	    return (overflow_exp (b));
	}
	case exp_address_tag : {
	    EXP b = DEREF_exp (exp_address_arg (a));
	    return (overflow_exp (b));
	}
	case exp_address_mem_tag : {
	    EXP b = DEREF_exp (exp_address_mem_arg (a));
	    return (overflow_exp (b));
	}
	case exp_cast_tag : {
	    /* Cast expressions */
	    unsigned c = DEREF_unsigned (exp_cast_conv (a));
	    if (c == CONV_ELLIPSIS) {
			EXP b = DEREF_exp (exp_cast_arg (a));
			return (overflow_exp (b));
	    }
	    break;
	}
	case exp_comma_tag : {
	    LIST (EXP) p = DEREF_list (exp_comma_args (a));
	    while (!IS_NULL_list (p)) {
			EXP b = DEREF_exp (HEAD_list (p));
			if (overflow_exp (b)) return (1);
			p = TAIL_list (p);
	    }
	    return (0);
	}
	case exp_aggregate_tag : {
	    LIST (EXP) p = DEREF_list (exp_aggregate_args (a));
	    while (!IS_NULL_list (p)) {
			EXP b = DEREF_exp (HEAD_list (p));
			if (overflow_exp (b)) return (1);
			p = TAIL_list (p);
	    }
	    return (0);
	}
	case exp_nof_tag : {
	    EXP b = DEREF_exp (exp_nof_start (a));
	    EXP c = DEREF_exp (exp_nof_pad (a));
	    EXP d = DEREF_exp (exp_nof_end (a));
	    NAT n = DEREF_nat (exp_nof_size (a));
	    if (overflow_exp (b)) return (1);
	    if (overflow_exp (c)) return (1);
	    if (overflow_exp (d)) return (1);
	    return (is_calc_nat (n));
	}
    }
    return (1);
}


/*
 *    FIND THE LINKAGE OF AN EXPRESSION
 *
 *    This routine checks the linkage of the expression a.  If vol is true
 *    then the result is or-ed with dspec_implicit if a is derived from
 *    an implicitly volatile external identifier and or-ed with dspec_pure
 *    if a is derived from an implicitly const string literal.  The
 *    component of a which determines the linkage is returned via pa.
 */

DECL_SPEC
find_exp_linkage(EXP e, EXP *pa, int vol)
{
    if (!IS_NULL_exp (e)) {
		ASSERT (ORDER_exp == 88);
		switch (TAG_exp (e)) {
	    case exp_identifier_tag :
	    case exp_member_tag : {
			/* Identifier expressions */
			IDENTIFIER id = DEREF_id (exp_identifier_etc_id (e));
			DECL_SPEC ds = DEREF_dspec (id_storage (id));
			ds &= (dspec_storage | dspec_temp);
			if (vol && IS_id_variable_etc (id)) {
				TYPE t = DEREF_type (id_variable_etc_type (id));
				CV_SPEC cv = find_cv_qual (t);
				if (!(cv & cv_volatile)) ds |= dspec_implicit;
			}
			*pa = e;
			return (ds);
	    }
	    case exp_string_lit_tag : {
			/* String literals have internal linkage */
			*pa = e;
			return (dspec_static | dspec_pure);
	    }
	    case exp_paren_tag :
	    case exp_copy_tag : {
			/* Parenthesised expressions */
			EXP a = DEREF_exp (exp_paren_etc_arg (e));
			return (find_exp_linkage (a, pa, vol));
	    }
	    case exp_indir_tag : {
			/* Indirection expressions */
			EXP a = DEREF_exp (exp_indir_ptr (e));
			return (find_exp_linkage (a, pa, vol));
	    }
	    case exp_address_tag : {
			/* Address expressions */
			EXP a = DEREF_exp (exp_address_arg (e));
			return (find_exp_linkage (a, pa, vol));
	    }
	    case exp_address_mem_tag : {
			/* Address expressions */
			EXP a = DEREF_exp (exp_address_mem_arg (e));
			if (IS_exp_member (a)) {
				return (find_exp_linkage (a, pa, vol));
			}
			break;
	    }
	    case exp_cast_tag : {
			/* Cast expressions */
			EXP a = DEREF_exp (exp_cast_arg (e));
			DECL_SPEC ds = find_exp_linkage (a, pa, vol);
			ds &= ~(dspec_implicit | dspec_pure);
			return (ds);
	    }
	    case exp_add_ptr_tag : {
			/* Pointer offset expressions */
			EXP a = DEREF_exp (exp_add_ptr_ptr (e));
			DECL_SPEC ds = find_exp_linkage (a, pa, vol);
			OFFSET off = DEREF_off (exp_add_ptr_off (e));
			if (vol) {
				/* Check for volatile members */
				while (IS_off_plus (off)) {
					off = DEREF_off (off_plus_arg2 (off));
				}
				if (IS_off_member (off)) {
					TYPE t;
					CV_SPEC cv;
					IDENTIFIER id;
					id = DEREF_id (off_member_id (off));
					t = DEREF_type (id_member_type (id));
					cv = find_cv_qual (t);
					if (cv & cv_volatile) ds &= ~dspec_implicit;
				}
			} else {
				/* Only base class conversions allowed */
				if (!IS_off_base (off) && !IS_off_deriv (off)) {
					ds = dspec_none;
				}
			}
			return (ds);
	    }
	    case exp_base_cast_tag : {
			/* Base cast expressions */
			EXP a = DEREF_exp (exp_base_cast_arg (e));
			return (find_exp_linkage (a, pa, vol));
	    }
	    case exp_location_tag : {
			/* Location expressions */
			EXP a = DEREF_exp (exp_location_arg (e));
			return (find_exp_linkage (a, pa, vol));
	    }
	    case exp_int_lit_tag :
	    case exp_char_lit_tag :
	    case exp_null_tag :
	    case exp_zero_tag :
	    case exp_value_tag :
	    case exp_token_tag : {
			/* These count as external linkage */
			*pa = e;
			return (dspec_extern);
	    }
		}
    }
    return (dspec_none);
}


/*
 *    IS AN OFFSET ZERO?
 *
 *    This routine checks whether the offset off is zero.  The only
 *    non-trivial case is for base class offsets where the single
 *    inheritance (zero offset) cases are marked using dspec_ignore.
 */

int
is_zero_offset(OFFSET off)
{
    if (IS_NULL_off (off)) return (1);
    ASSERT (ORDER_off == 13);
    switch (TAG_off (off)) {
	case off_zero_tag : {
	    /* Zero offsets */
	    return (1);
	}
	case off_array_tag : {
	    /* Array offsets */
	    unsigned n = DEREF_unsigned (off_array_arg (off));
	    if (n == 0) return (1);
	    break;
	}
	case off_base_tag : {
	    /* Base class offsets */
	    GRAPH gr = DEREF_graph (off_base_graph (off));
	    DECL_SPEC acc = DEREF_dspec (graph_access (gr));
	    if (acc & dspec_ignore) return (1);
	    break;
	}
	case off_deriv_tag : {
	    /* Derived class offsets */
	    GRAPH gr = DEREF_graph (off_deriv_graph (off));
	    DECL_SPEC acc = DEREF_dspec (graph_access (gr));
	    if (acc & dspec_ignore) return (1);
	    break;
	}
	case off_negate_tag : {
	    /* Offset negations */
	    OFFSET a = DEREF_off (off_negate_arg (off));
	    return (is_zero_offset (a));
	}
	case off_plus_tag : {
	    /* Offset additions */
	    OFFSET a = DEREF_off (off_plus_arg1 (off));
	    OFFSET b = DEREF_off (off_plus_arg2 (off));
	    return (is_zero_offset (a) && is_zero_offset (b));
	}
	case off_mult_tag : {
	    /* Offset multiplications */
	    OFFSET a = DEREF_off (off_mult_arg1 (off));
	    return (is_zero_offset (a));
	}
    }
    return (0);
}


/*
 *    IS THE TYPE OF AN OFFSET STATICALLY DETERMINED?
 *
 *    This routine checks whether the type of a pointer plus the offset off
 *    can be statically determined.  It returns 2 if the type is known
 *    independent of the value of the pointer, 1 if it is known if the
 *    type of the pointer is known, and 0 otherwise.
 */

static int
know_offset(OFFSET off)
{
    if (!IS_NULL_off (off)) {
		switch (TAG_off (off)) {
	    case off_base_tag :
	    case off_deriv_tag :
	    case off_ptr_mem_tag : {
			/* Base class offsets */
			return (0);
	    }
	    case off_member_tag : {
			/* Member offsets */
			return (2);
	    }
	    case off_plus_tag : {
			/* Check for derived member offsets */
			off = DEREF_off (off_plus_arg2 (off));
			if (IS_off_member (off)) return (2);
			break;
	    }
		}
    }
    return (1);
}


/*
 *    IS THE TYPE OF AN EXPRESSION STATICALLY DETERMINED?
 *
 *    This routine checks whether the expression of class type or pointer to
 *    class type, e, is derived from an object so that its type can be
 *    statically determined.  It is used to check whether the virtual call
 *    mechanism and virtual base class conversions can be bypassed for e.
 *    A value of 2 is returned for non-obvious known types.
 */

int
know_type(EXP e)
{
    if (!IS_NULL_exp (e)) {
		unsigned tag = TAG_exp (e);
		switch (tag) {
	    case exp_address_tag : {
			EXP a = DEREF_exp (exp_address_arg (e));
			tag = TAG_exp (a);
			if (tag == exp_identifier_tag) {
				return (1);
			}
			if (tag == exp_indir_tag) {
				EXP b = DEREF_exp (exp_indir_ptr (a));
				return (know_type (b));
			}
			if (tag == exp_dummy_tag) {
				EXP b = DEREF_exp (exp_dummy_value (a));
				if (IS_NULL_exp (b)) {
					int v = DEREF_int (exp_dummy_virt (a));
					if (!v) return (1);
				}
			}
			break;
	    }
	    case exp_indir_tag : {
			EXP a = DEREF_exp (exp_indir_ptr (e));
			return (know_type (a));
	    }
	    case exp_add_ptr_tag : {
			OFFSET off = DEREF_off (exp_add_ptr_off (e));
			int k = know_offset (off);
			if (k == 2) {
				return (1);
			}
			if (k == 1) {
				EXP a = DEREF_exp (exp_add_ptr_ptr (e));
				return (know_type (a));
			}
			break;
	    }
	    case exp_contents_tag : {
			e = DEREF_exp (exp_contents_ptr (e));
			if (IS_exp_identifier (e)) goto identifier_lab;
			break;
	    }
	    case exp_identifier_tag :
			identifier_lab : {
				IDENTIFIER id = DEREF_id (exp_identifier_id (e));
				HASHID nm = DEREF_hashid (id_name (id));
				if (EQ_KEYWORD (nm, lex_this_Hname)) {
					/* A 'this' expression */
					NAMESPACE ns = DEREF_nspace (id_parent (id));
					id = DEREF_id (nspace_name (ns));
					nm = DEREF_hashid (id_name (id));
					if (IS_hashid_constr (nm)) {
						/* Function is a constructor */
						return (2);
					}
					if (IS_hashid_destr (nm)) {
						/* Function is a destructor */
						return (2);
					}
				}
				break;
			}
		}
    }
    return (0);
}
