/*
 * 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/assign.c,v 1.7 2005/07/22 12:21:01 stefanf Exp $
 */


#include "config.h"
#include "producer.h"
#include "c_types.h"
#include "exp_ops.h"
#include "id_ops.h"
#include "off_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "assign.h"
#include "basetype.h"
#include "cast.h"
#include "construct.h"
#include "convert.h"
#include "chktype.h"
#include "expression.h"
#include "identifier.h"
#include "initialise.h"
#include "literal.h"
#include "member.h"
#include "operator.h"
#include "predict.h"
#include "syntax.h"
#include "ustring.h"


/*
 *    CONVERT BY ASSIGNMENT
 *
 *    This routine converts the expression a to the type t, as if by
 *    assignment.  The cases where t is a class type or a reference are
 *    handled elsewhere.
 */

EXP
convert_assign(TYPE t, EXP a, ERROR *err)
{
    EXP e = cast_exp (t, a, err, CAST_IMPLICIT);
    return (e);
}


/*
 *    CONVERT TO A CLASS TYPE BY ASSIGNMENT
 *
 *    This routine converts the expression a to the class t, as if by
 *    assignment.  The constructors of t are ignored, only base class
 *    conversions and conversion operators being considered.
 */

EXP
convert_class(TYPE t, EXP a, ERROR *err)
{
    EXP e;
    TYPE s = DEREF_type (exp_type (a));
    if (IS_type_compound (s)) {
		e = cast_class_class (t, a, err, CAST_IMPLICIT, 0);
		if (!IS_NULL_exp (e)) return (e);
    }
    e = convert_conv (t, a, err, CAST_IMPLICIT);
    return (e);
}


/*
 *    CONSTRUCT A POSTFIX EXPRESSION
 *
 *    This routine constructs the expressions 'a++' and 'a--'.  Note that
 *    in this and other assignment expressions, a cannot have array type,
 *    so that no bounds checks are appropriate.  The result is an rvalue.
 */

EXP
make_postfix_exp(int op, EXP a)
{
    EXP e;
    TYPE ta;
    ERROR err;
    unsigned ca;
    int is_bool;
	
    /* An assignment is a side effect */
    no_side_effects++;
	
    /* Allow for reference conversions */
    a = convert_reference (a, REF_NORMAL);
    ta = DEREF_type (exp_type (a));
    ca = type_category (&ta);
	
    /* Check for overloading */
#if LANGUAGE_CPP
    if (IS_TYPE_OVERLOAD (ca)) {
		if (overload_depth == 0) {
			/* Overloads as 'operator op (a, 0)' */
			EXP b = make_null_exp (type_sint);
			e = binary_overload (op, a, b);
			return (e);
		}
		if (IS_TYPE_CLASS (ca)) goto error_lab;
    }
#else
    if (IS_TYPE_CLASS (ca)) goto error_lab;
#endif
	
    /* Operand should be a modifiable lvalue */
    err = check_modifiable (ta, a);
    if (!IS_NULL_err (err)) {
		err = concat_error (err, ERR_expr_post_incr_mod (op));
		report (crt_loc, err);
		err = NULL_err;
    }
	
    /* Operand can be arithmetic ... */
    if (IS_TYPE_ARITH (ca)) {
		EXP b;
		TYPE t;
		TYPE tb;
		OFFSET off = NULL_off;
		
		/* Allow for bitfields */
		if (IS_TYPE_BITF (ca)) off = decons_bitf_exp (&a);
		
		/* Form the result */
		MAKE_exp_dummy (ta, a, LINK_NONE, off, 0, a);
		b = convert_lvalue (a);
		tb = DEREF_type (exp_type (b));
		t = promote_type (tb);
		if (IS_NULL_off (off)) {
			MAKE_exp_dummy (tb, b, LINK_NONE, NULL_off, 0, b);
			e = convert_promote (t, b);
		} else {
			b = convert_promote (t, b);
			MAKE_exp_dummy (t, b, LINK_NONE, NULL_off, 0, b);
			e = b;
			tb = t;
		}
		is_bool = check_int_type (ta, btype_bool);
		if (is_bool && op == lex_plus_Hplus) {
			/* bool b; b++ results in b being true. */
			report (crt_loc, ERR_expr_post_incr_bool_inc (op, ta));
			e = make_bool_exp (BOOL_TRUE, exp_int_lit_tag);
		} else {
			/* Other types and bool-- */
			EXP c;
			if (is_bool) {
				report (crt_loc, ERR_expr_post_incr_bool_dec (op, ta));
			}
			c = make_unit_exp (t);
			if (op == lex_plus_Hplus) {
				MAKE_exp_plus (t, e, c, e);
			} else {
				MAKE_exp_minus (t, e, c, e);
			}
			e = convert_assign (ta, e, &err);
			if (!IS_NULL_err (err)) {
				err = concat_warning (err, ERR_expr_ass_conv ());
				report (crt_loc, err);
			}
		}
		MAKE_exp_postinc (tb, a, b, e, e);
		return (e);
    }
	
    /* ... or pointer ... */
    if (IS_TYPE_PTR (ca)) {
		/* Pointer must be to complete object type */
		EXP b;
		OFFSET off;
		TYPE t = check_pointer (ta, &err);
		if (!IS_NULL_err (err)) {
			err = concat_error (err, ERR_expr_post_incr_incompl (op));
			report (crt_loc, err);
		}
		if (IS_type_top_etc (t)) t = type_char;
		
		/* Create the identities */
		MAKE_exp_dummy (ta, a, LINK_NONE, NULL_off, 0, a);
		b = convert_lvalue (a);
		ta = DEREF_type (exp_type (b));
		MAKE_exp_dummy (ta, b, LINK_NONE, NULL_off, 0, b);
		
		/* Form the result */
		MAKE_off_type (t, off);
		if (op == lex_minus_Hminus) MAKE_off_negate (off, off);
		e = make_add_ptr (ta, b, off);
		MAKE_exp_postinc (ta, a, b, e, e);
		return (e);
    }
	
    /* ... and nothing else */
    error_lab : {
		if (!IS_TYPE_ERROR (ca)) {
			report (crt_loc, ERR_expr_post_incr_op (op, ta));
		}
    }
    e = make_error_exp (0);
    return (e);
}


/*
 *    CREATE A PRE-INCREMENT EXPRESSION
 *
 *    This routine creates a pre-increment or assignment expression for
 *    the expression a of type t given by the operation b.  If a is a
 *    bitfield then off gives the bitfield offset.  The result is an
 *    lvalue in C++, but an rvalue in C.  There is a slight problem in the
 *    latter case when t is a bitfield: the value is not b, but
 *    convert_bitfield (b).  This conversion is left implicit, and is
 *    only made explicit in the TDF output routines.
 */

static EXP
make_preinc_exp(TYPE t, EXP a, EXP b, OFFSET off, int op)
{
    EXP e;
#if LANGUAGE_C
    t = rvalue_type (t);
#endif
    if (IS_NULL_off (off)) {
		/* Simple case */
		if (op == lex_assign) {
			MAKE_exp_assign (t, a, b, e);
		} else {
			MAKE_exp_preinc (t, a, b, op, e);
		}
    } else {
		/* Bitfield case */
#if LANGUAGE_C
		MAKE_exp_preinc (t, a, b, op, e);
		t = promote_type (t);
#else
		TYPE p;
		TYPE s = find_bitfield_type (t);
		MAKE_type_ptr (cv_none, s, p);
		s = lvalue_type (s);
		MAKE_exp_preinc (s, a, b, op, e);
		MAKE_exp_address (p, e, e);
		MAKE_exp_add_ptr (p, e, off, 0, e);
		MAKE_exp_indir (t, e, e);
#endif
    }
#if LANGUAGE_C
    MAKE_exp_contents (t, e, e);
#endif
    return (e);
}


/*
 *    CONSTRUCT A PREFIX EXPRESSION
 *
 *    This routine constructs the expressions '++a' and '--a'.  The result is
 *    an lvalue in C++ but an rvalue in C.
 */

EXP
make_prefix_exp(int op, EXP a)
{
    EXP e;
    TYPE ta;
    ERROR err;
    unsigned ca;
    int is_bool;
	
    /* An assignment is a side effect */
    no_side_effects++;
	
    /* Allow for reference conversions */
    a = convert_reference (a, REF_NORMAL);
    ta = DEREF_type (exp_type (a));
    ca = type_category (&ta);
	
    /* Check for overloading */
#if LANGUAGE_CPP
    if (IS_TYPE_OVERLOAD (ca)) {
		if (overload_depth == 0) {
			/* Overloads as 'operator op (a)' */
			e = unary_overload (op, a);
			return (e);
		}
		if (IS_TYPE_CLASS (ca)) goto error_lab;
    }
#else
    if (IS_TYPE_CLASS (ca)) goto error_lab;
#endif
	
    /* Operand should be a modifiable lvalue */
    err = check_modifiable (ta, a);
    if (!IS_NULL_err (err)) {
		err = concat_error (err, ERR_expr_pre_incr_mod (op));
		report (crt_loc, err);
		err = NULL_err;
    }
	
    /* Operand can be arithmetic ... */
    if (IS_TYPE_ARITH (ca)) {
		EXP c;
		TYPE t;
		OFFSET off = NULL_off;
		
		is_bool = check_int_type (ta, btype_bool);
		if (is_bool && op == lex_plus_Hplus) {
			/* bool b; ++b results in b being true. */
			report (crt_loc, ERR_expr_pre_incr_bool_inc (op, ta));
			c = make_bool_exp (BOOL_TRUE, exp_int_lit_tag);
			e = make_preinc_exp (ta, a, c, NULL_off, lex_assign);
			return (e);
		} else if (is_bool) {
			report (crt_loc, ERR_expr_pre_incr_bool_dec (op, ta));
		}

		/* Allow for bitfields */
		if (IS_TYPE_BITF (ca)) off = decons_bitf_exp (&a);
		
		/* Form the result */
		MAKE_exp_dummy (ta, a, LINK_NONE, off, 0, a);
		e = convert_lvalue (a);
		t = DEREF_type (exp_type (e));
		t = promote_type (t);
		e = convert_promote (t, e);
		c = make_unit_exp (t);
		if (op == lex_plus_Hplus) {
			MAKE_exp_plus (t, e, c, e);
		} else {
			MAKE_exp_minus (t, e, c, e);
		}
		e = convert_assign (ta, e, &err);
		if (!IS_NULL_err (err)) {
			err = concat_warning (err, ERR_expr_ass_conv ());
			report (crt_loc, err);
		}
		e = make_preinc_exp (ta, a, e, off, op);
		return (e);
    }
	
    /* ... or pointer ... */
    if (IS_TYPE_PTR (ca)) {
		/* Pointer must be to complete object type */
		OFFSET off;
		TYPE t = check_pointer (ta, &err);
		if (!IS_NULL_err (err)) {
			err = concat_error (err, ERR_expr_pre_incr_incompl (op));
			report (crt_loc, err);
		}
		if (IS_type_top_etc (t)) t = type_char;
		
		/* Form the result */
		MAKE_exp_dummy (ta, a, LINK_NONE, NULL_off, 0, a);
		e = convert_lvalue (a);
		if (!IS_type_ptr (ta)) ta = DEREF_type (exp_type (e));
		MAKE_off_type (t, off);
		if (op == lex_minus_Hminus) MAKE_off_negate (off, off);
		e = make_add_ptr (ta, e, off);
		e = make_preinc_exp (ta, a, e, NULL_off, op);
		return (e);
    }
	
    /* ... and nothing else */
    error_lab : {
		if (!IS_TYPE_ERROR (ca)) {
			report (crt_loc, ERR_expr_pre_incr_op (op, ta));
		}
		e = make_error_exp (LANGUAGE_CPP);
		return (e);
    }
}


/*
 *    CONSTRUCT AN ASSIGNMENT EXPRESSION
 *
 *    This routine constructs the expression 'a = b'.  If c is true then
 *    assignment of classes is done directly rather than via an assignment
 *    operator.  The result is an lvalue in C++ but an rvalue in C.
 */

EXP
make_assign_exp(EXP a, EXP b, int c)
{
    EXP e;
    ERROR err;
    TYPE ta, tb;
    unsigned ca, cb;
    int to_class = 0;
    int op = lex_assign;
    OFFSET off = NULL_off;
	
    /* An assignment is a side effect */
    no_side_effects++;
	
    /* Apply reference conversion on first operand */
    a = convert_reference (a, REF_NORMAL);
    ta = DEREF_type (exp_type (a));
    ca = type_category (&ta);
	
    /* Apply reference conversion on second operand */
    b = convert_reference (b, REF_ASSIGN);
    tb = DEREF_type (exp_type (b));
    cb = type_category (&tb);
	
    /* Check for template parameters */
#if LANGUAGE_CPP
    if (IS_TYPE_TEMPL (ca) || IS_TYPE_TEMPL (cb)) {
		if (overload_depth == 0) {
			e = binary_overload (op, a, b);
			return (e);
		}
    }
#endif
	
    /* Check for overloading (classes only) */
    if (IS_TYPE_CLASS (ca)) {
#if LANGUAGE_CPP
		if (c == 0) {
			if (overload_depth == 0) {
				e = binary_overload (op, a, b);
				return (e);
			}
			if (!IS_TYPE_ERROR (cb)) {
				/* Find reason for failure */
				err = check_incomplete (ta);
				if (IS_NULL_err (err) && IS_type_compound (ta)) {
					CLASS_TYPE ct;
					IDENTIFIER id;
					ct = DEREF_ctype (type_compound_defn (ta));
					id = find_operator (ct, op);
					if (!IS_NULL_id (id)) {
						err = ERR_over_match_viable_none (id);
					}
				}
				err = concat_error (err, ERR_expr_ass_op (op, ta, tb));
				report (crt_loc, err);
			}
			e = make_error_exp (LANGUAGE_CPP);
			return (e);
		}
#else
		UNUSED (c);
		UNUSED (cb);
#endif
		to_class = 1;
    }
	
    /* First operand should be a modifiable lvalue */
    err = check_modifiable (ta, a);
    if (!IS_NULL_err (err)) {
		err = concat_error (err, ERR_expr_ass_mod (op));
		report (crt_loc, err);
    }
	
    /* Do operand conversion */
    err = NULL_err;
    if (to_class) {
		b = convert_none (b);
		b = convert_class (ta, b, &err);
		b = remove_temporary (b, a);
    } else {
		b = convert_assign (ta, b, &err);
    }
    if (!IS_NULL_err (err)) {
		err = concat_warning (err, ERR_expr_ass_conv ());
		report (crt_loc, err);
    }
	
    /* Construct the result */
    if (IS_type_bitfield (ta)) {
		off = decons_bitf_exp (&a);
		MAKE_exp_dummy (ta, a, LINK_NONE, off, 0, a);
    }
    e = make_preinc_exp (ta, a, b, off, op);
    return (e);
}


/*
 *    CONSTRUCT A BECOMES EXPRESSION
 *
 *    This routine constructs the expression 'a op b' where op is one of the
 *    assignment operators, '*=', '/=' etc.  The result is an lvalue in
 *    C++, but an rvalue in C.
 */

EXP
make_become_exp(int op, EXP a, EXP b)
{
    EXP e;
    EXP d;
    TYPE td;
    ERROR err;
    TYPE ta, tb;
    unsigned tag;
    unsigned ca, cb;
    OFFSET off = NULL_off;
	
    /* An assignment is a side effect */
    no_side_effects++;
	
    /* Apply reference conversion on first operand */
    a = convert_reference (a, REF_NORMAL);
    ta = DEREF_type (exp_type (a));
    ca = type_category (&ta);
	
    /* Apply reference conversion on second operand */
    b = convert_reference (b, REF_NORMAL);
    tb = DEREF_type (exp_type (b));
    cb = type_category (&tb);
	
    /* Allow for overloading */
#if LANGUAGE_CPP
    if (IS_TYPE_OVERLOAD (ca) || IS_TYPE_OVERLOAD (cb)) {
		if (overload_depth == 0) {
			e = binary_overload (op, a, b);
			return (e);
		}
		if (IS_TYPE_CLASS (ca)) goto error_lab;
    }
#else
    if (IS_TYPE_CLASS (ca)) goto error_lab;
#endif
	
    /* First operand should be a modifiable lvalue */
    err = check_modifiable (ta, a);
    if (!IS_NULL_err (err)) {
		err = concat_error (err, ERR_expr_ass_mod (op));
		report (crt_loc, err);
		err = NULL_err;
    }
	
    /* Allow for bitfields */
    if (IS_TYPE_BITF (ca)) off = decons_bitf_exp (&a);
	
    /* Introduce identity for assignment variable */
    td = ta;
    MAKE_exp_dummy (td, a, LINK_NONE, off, 0, d);
    a = convert_lvalue (d);
    ta = DEREF_type (exp_type (a));
    ca = type_category (&ta);
	
    /* Do lvalue conversions */
    if (IS_TYPE_ADDRESS (cb)) {
		b = convert_lvalue (b);
		tb = DEREF_type (exp_type (b));
		cb = type_category (&tb);
    }
	
    /* Weed out booleans immediately */
    if (IS_TYPE_INT (ca) && check_int_type (ta, btype_bool)) {
		report (crt_loc, ERR_expr_ass_op (op, ta, tb));
		e = make_error_exp (LANGUAGE_CPP);
		return (e);
    }
	
    /* Find the operation type */
    switch (op) {
	case lex_and_Heq_H1 : tag = exp_and_tag ; goto integral_lab;
	case lex_div_Heq : tag = exp_div_tag ; goto arithmetic_lab;
	case lex_lshift_Heq : tag = exp_lshift_tag ; goto shift_lab;
	case lex_minus_Heq : tag = exp_minus_tag ; goto pointer_lab;
	case lex_or_Heq_H1 : tag = exp_or_tag ; goto integral_lab;
	case lex_plus_Heq : tag = exp_plus_tag ; goto pointer_lab;
	case lex_rem_Heq : tag = exp_rem_tag ; goto integral_lab;
	case lex_rshift_Heq : tag = exp_rshift_tag ; goto shift_lab;
	case lex_star_Heq : tag = exp_mult_tag ; goto arithmetic_lab;
	case lex_xor_Heq_H1 : tag = exp_xor_tag ; goto integral_lab;
	default : goto error_lab;
    }
	
    integral_lab : {
		/* Integral operations */
		if (IS_TYPE_INT (ca) && IS_TYPE_INT (cb)) {
			TYPE t = arith_type (ta, tb, a, b);
			a = convert_arith (t, a, op, 1);
			b = convert_arith (t, b, op, 2);
			if (op == lex_rem_Heq) {
				IGNORE check_div_exp (op, a, b);
			}
			MAKE_exp_plus_etc (tag, t, a, b, e);
			e = convert_assign (td, e, &err);
			if (!IS_NULL_err (err)) {
				err = concat_warning (err, ERR_expr_ass_conv ());
				report (crt_loc, err);
			}
			e = make_preinc_exp (td, d, e, off, op);
			return (e);
		}
		goto error_lab;
    }
	
    arithmetic_lab : {
		/* Arithmetic operations */
		if (IS_TYPE_ARITH (ca) && IS_TYPE_ARITH (cb)) {
			TYPE t = arith_type (ta, tb, a, b);
			a = convert_arith (t, a, op, 1);
			b = convert_arith (t, b, op, 2);
			if (op == lex_div_Heq) {
				IGNORE check_div_exp (op, a, b);
			}
			MAKE_exp_plus_etc (tag, t, a, b, e);
			e = convert_assign (td, e, &err);
			if (!IS_NULL_err (err)) {
				err = concat_warning (err, ERR_expr_ass_conv ());
				report (crt_loc, err);
			}
			e = make_preinc_exp (td, d, e, off, op);
			return (e);
		}
		goto error_lab;
    }
	
    shift_lab : {
		/* Shift operations */
		if (IS_TYPE_INT (ca) && IS_TYPE_INT (cb)) {
			TYPE pta = promote_type (ta);
			TYPE ptb = promote_type (tb);
			a = convert_promote (pta, a);
			b = convert_promote (ptb, b);
			IGNORE check_shift_exp (op, pta, a, b);
			MAKE_exp_plus_etc (tag, pta, a, b, e);
			e = convert_assign (td, e, &err);
			if (!IS_NULL_err (err)) {
				err = concat_warning (err, ERR_expr_ass_conv ());
				report (crt_loc, err);
			}
			e = make_preinc_exp (td, d, e, off, op);
			return (e);
		}
		goto error_lab;
    }
	
    pointer_lab : {
		/* Pointer or arithmetic operations */
		if (IS_TYPE_PTR (ca) && IS_TYPE_INT (cb)) {
			OFFSET off1;
			int neg = 0;
			TYPE t = check_pointer (ta, &err);
			if (!IS_NULL_err (err)) {
				err = concat_error (err, ERR_expr_ass_incompl (op));
				report (crt_loc, err);
			}
			if (op == lex_minus_Heq) neg = 1;
			off1 = make_off_mult (t, b, neg);
			e = make_add_ptr (ta, a, off1);
			e = make_preinc_exp (td, d, e, NULL_off, op);
			return (e);
		}
		goto arithmetic_lab;
    }
	
    error_lab : {
		/* Bad operations */
		if (!IS_TYPE_ERROR (ca) && !IS_TYPE_ERROR (cb)) {
			report (crt_loc, ERR_expr_ass_op (op, ta, tb));
		}
		e = make_error_exp (LANGUAGE_CPP);
		return (e);
    }
}
