view dmd/BinExp.d @ 12:832f71e6f96c

*Exp and *AssignExp arrayOp implementation added (might be a bit incomplete) Some unittest-specific functions implemented
author korDen
date Mon, 12 Apr 2010 15:13:00 +0400
parents 10317f0c89a5
children 51605de93870
line wrap: on
line source

module dmd.BinExp;

import dmd.Expression;
import dmd.Loc;
import dmd.ClassDeclaration;
import dmd.OutBuffer;
import dmd.HdrGenState;
import dmd.TOK;
import dmd.IRState;
import dmd.Scope;
import dmd.Type;
import dmd.InterState;
import dmd.InlineCostState;
import dmd.InlineScanState;
import dmd.InlineDoState;
import dmd.AggregateDeclaration;
import dmd.Identifier;
import dmd.MATCH;
import dmd.declaration.Match;
import dmd.ArrayTypes;
import dmd.TY;
import dmd.TypeClass;
import dmd.TypeStruct;
import dmd.Dsymbol;
import dmd.FuncDeclaration;
import dmd.TemplateDeclaration;
import dmd.DotIdExp;
import dmd.ErrorExp;
import dmd.WANT;
import dmd.IntegerExp;
import dmd.MulExp;
import dmd.Token;
import dmd.PREC;
import dmd.StringValue;
import dmd.StringTable;
import dmd.Argument;
import dmd.Statement;
import dmd.ForeachRangeStatement;
import dmd.ArrayLengthExp;
import dmd.IdentifierExp;
import dmd.ExpStatement;
import dmd.CompoundStatement;
import dmd.TypeFunction;
import dmd.LINK;
import dmd.Lexer;
import dmd.ReturnStatement;
import dmd.Id;
import dmd.STC;
import dmd.PROT;
import dmd.VarExp;
import dmd.CallExp;

import dmd.expression.Util;

import dmd.backend.elem;
import dmd.backend.Util;

import dmd.backend.iasm : binary;

import std.stdio : writef;

/**************************************
 * Combine types.
 * Output:
 *	*pt	merged type, if *pt is not null
 *	*pe1	rewritten e1
 *	*pe2	rewritten e2
 * Returns:
 *	!=0	success
 *	0	failed
 */

/**************************************
 * Hash table of array op functions already generated or known about.
 */

__gshared StringTable arrayfuncs;

int typeMerge(Scope sc, Expression e, Type* pt, Expression* pe1, Expression* pe2)
{
    //printf("typeMerge() %s op %s\n", (*pe1).toChars(), (*pe2).toChars());
    //dump(0);

    Expression e1 = (*pe1).integralPromotions(sc);
    Expression e2 = (*pe2).integralPromotions(sc);

    Type t1 = e1.type;
    Type t2 = e2.type;
    assert(t1);
    Type t = t1;

    //if (t1) printf("\tt1 = %s\n", t1.toChars());
    //if (t2) printf("\tt2 = %s\n", t2.toChars());
debug {
    if (!t2) writef("\te2 = '%s'\n", e2.toChars());
}
    assert(t2);

    Type t1b = t1.toBasetype();
    Type t2b = t2.toBasetype();

    TY ty = cast(TY)Type.impcnvResult[t1b.ty][t2b.ty];
    if (ty != TY.Terror)
    {
		TY ty1;
		TY ty2;

		ty1 = cast(TY)Type.impcnvType1[t1b.ty][t2b.ty];
		ty2 = cast(TY)Type.impcnvType2[t1b.ty][t2b.ty];

		if (t1b.ty == ty1)	// if no promotions
		{
			if (t1 == t2)
			{
				t = t1;
				goto Lret;
			}

			if (t1b == t2b)
			{
				t = t1b;
				goto Lret;
			}
		}

		t = Type.basic[ty];

		t1 = Type.basic[ty1];
		t2 = Type.basic[ty2];

		e1 = e1.castTo(sc, t1);
		e2 = e2.castTo(sc, t2);
		//printf("after typeCombine():\n");
		//dump(0);
		//printf("ty = %d, ty1 = %d, ty2 = %d\n", ty, ty1, ty2);
		goto Lret;
    }

    t1 = t1b;
    t2 = t2b;

Lagain:
    if (t1 == t2)
    {
    }
    else if (t1.ty == TY.Tpointer && t2.ty == TY.Tpointer)
    {
		// Bring pointers to compatible type
		Type t1n = t1.nextOf();
		Type t2n = t2.nextOf();

		if (t1n == t2n) {
			;
		} else if (t1n.ty == TY.Tvoid)	{// pointers to void are always compatible
			t = t2;
		} else if (t2n.ty == TY.Tvoid) {
			;
		} else if (t1n.mod != t2n.mod) {
			t1 = t1n.mutableOf().constOf().pointerTo();
			t2 = t2n.mutableOf().constOf().pointerTo();
			t = t1;
			goto Lagain;
		} else if (t1n.ty == TY.Tclass && t2n.ty == TY.Tclass) {
			ClassDeclaration cd1 = t1n.isClassHandle();
			ClassDeclaration cd2 = t2n.isClassHandle();
			int offset;

			if (cd1.isBaseOf(cd2, &offset))
			{
			if (offset)
				e2 = e2.castTo(sc, t);
			}
			else if (cd2.isBaseOf(cd1, &offset))
			{
			t = t2;
			if (offset)
				e1 = e1.castTo(sc, t);
			}
			else
			goto Lincompatible;
		} else {
			goto Lincompatible;
		}
    }
    else if ((t1.ty == TY.Tsarray || t1.ty == TY.Tarray) &&
	     e2.op == TOK.TOKnull && t2.ty == TY.Tpointer && t2.nextOf().ty == TY.Tvoid)
    {	/*  (T[n] op void*)
		 *  (T[] op void*)
		 */
		goto Lx1;
    }
    else if ((t2.ty == TY.Tsarray || t2.ty == TY.Tarray) &&
	     e1.op == TOK.TOKnull && t1.ty == TY.Tpointer && t1.nextOf().ty == TY.Tvoid)
    {	/*  (void* op T[n])
		 *  (void* op T[])
		 */
		goto Lx2;
    }
    else if ((t1.ty == TY.Tsarray || t1.ty == TY.Tarray) && t1.implicitConvTo(t2))
    {
		goto Lt2;
    }
    else if ((t2.ty == TY.Tsarray || t2.ty == TY.Tarray) && t2.implicitConvTo(t1))
    {
		goto Lt1;
    }
    /* If one is mutable and the other invariant, then retry
     * with both of them as const
     */
    else if ((t1.ty == TY.Tsarray || t1.ty == TY.Tarray || t1.ty == TY.Tpointer) &&
	     (t2.ty == TY.Tsarray || t2.ty == TY.Tarray || t2.ty == TY.Tpointer) &&
	     t1.nextOf().mod != t2.nextOf().mod
	    )
    {
		if (t1.ty == TY.Tpointer)
			t1 = t1.nextOf().mutableOf().constOf().pointerTo();
		else
			t1 = t1.nextOf().mutableOf().constOf().arrayOf();

		if (t2.ty == TY.Tpointer)
			t2 = t2.nextOf().mutableOf().constOf().pointerTo();
		else
			t2 = t2.nextOf().mutableOf().constOf().arrayOf();
		t = t1;
		goto Lagain;
    }
    else if (t1.ty == TY.Tclass || t2.ty == TY.Tclass)
    {
		while (1)
		{
			int i1 = e2.implicitConvTo(t1);
			int i2 = e1.implicitConvTo(t2);

			if (i1 && i2)
			{
			// We have the case of class vs. void*, so pick class
			if (t1.ty == TY.Tpointer)
				i1 = 0;
			else if (t2.ty == TY.Tpointer)
				i2 = 0;
			}

			if (i2)
			{
				goto Lt2;
			}
			else if (i1)
			{
				goto Lt1;
			}
			else if (t1.ty == TY.Tclass && t2.ty == TY.Tclass)
			{	
				TypeClass tc1 = cast(TypeClass)t1;
				TypeClass tc2 = cast(TypeClass)t2;

				/* Pick 'tightest' type
				 */
				ClassDeclaration cd1 = tc1.sym.baseClass;
				ClassDeclaration cd2 = tc2.sym.baseClass;

				if (cd1 && cd2)
				{   t1 = cd1.type;
					t2 = cd2.type;
				}
				else if (cd1)
					t1 = cd1.type;
				else if (cd2)
					t2 = cd2.type;
				else
					goto Lincompatible;
			}
			else
				goto Lincompatible;
		}
    }
    else if (t1.ty == TY.Tstruct && t2.ty == TY.Tstruct)
    {
		if ((cast(TypeStruct)t1).sym != (cast(TypeStruct)t2).sym)
			goto Lincompatible;
    }
    else if ((e1.op == TOK.TOKstring || e1.op == TOK.TOKnull) && e1.implicitConvTo(t2))
    {
		goto Lt2;
    }
    else if ((e2.op == TOK.TOKstring || e2.op == TOK.TOKnull) && e2.implicitConvTo(t1))
    {
		goto Lt1;
    }
    else if (t1.ty == TY.Tsarray && t2.ty == TY.Tsarray &&
	     e2.implicitConvTo(t1.nextOf().arrayOf()))
    {
     Lx1:
		t = t1.nextOf().arrayOf();
		e1 = e1.castTo(sc, t);
		e2 = e2.castTo(sc, t);
    }
    else if (t1.ty == TY.Tsarray && t2.ty == TY.Tsarray &&
	     e1.implicitConvTo(t2.nextOf().arrayOf()))
    {
     Lx2:
		t = t2.nextOf().arrayOf();
		e1 = e1.castTo(sc, t);
		e2 = e2.castTo(sc, t);
    }
    else if (t1.isintegral() && t2.isintegral())
    {
		assert(0);
    }
    else if (e1.op == TOK.TOKslice && t1.ty == TY.Tarray &&
	     e2.implicitConvTo(t1.nextOf()))
    {	// T[] op T
		e2 = e2.castTo(sc, t1.nextOf());
		t = t1.nextOf().arrayOf();
    }
    else if (e2.op == TOK.TOKslice && t2.ty == TY.Tarray &&
	     e1.implicitConvTo(t2.nextOf()))
    {	// T op T[]
		e1 = e1.castTo(sc, t2.nextOf());
		t = t2.nextOf().arrayOf();

		//printf("test %s\n", e.toChars());
		e1 = e1.optimize(WANT.WANTvalue);
		if (e && e.isCommutative() && e1.isConst())
		{   /* Swap operands to minimize number of functions generated
			 */
			//printf("swap %s\n", e.toChars());
			Expression tmp = e1;
			e1 = e2;
			e2 = tmp;
		}
    }
    else
    {
     Lincompatible:
		return 0;
    }
Lret:
    if (!*pt)
	*pt = t;
    *pe1 = e1;
    *pe2 = e2;
static if (false) {
    printf("-typeMerge() %s op %s\n", e1.toChars(), e2.toChars());
    if (e1.type) printf("\tt1 = %s\n", e1.type.toChars());
    if (e2.type) printf("\tt2 = %s\n", e2.type.toChars());
    printf("\ttype = %s\n", t.toChars());
}
    //dump(0);
    return 1;


Lt1:
    e2 = e2.castTo(sc, t1);
    t = t1;
    goto Lret;

Lt2:
    e1 = e1.castTo(sc, t2);
    t = t2;
    goto Lret;
}

class BinExp : Expression
{
    Expression e1;
    Expression e2;

    this(Loc loc, TOK op, int size, Expression e1, Expression e2)
	{
		super(loc, op, size);
		this.e1 = e1;
		this.e2 = e2;
	}
	
    Expression syntaxCopy()
	{
		BinExp e = cast(BinExp)copy();
		e.type = null;
		e.e1 = e.e1.syntaxCopy();
		e.e2 = e.e2.syntaxCopy();

		return e;
	}

    Expression semantic(Scope sc)
	{
version (LOGSEMANTIC) {
		printf("BinExp.semantic('%s')\n", toChars());
}
		e1 = e1.semantic(sc);
		if (!e1.type && !(op == TOK.TOKassign && e1.op == TOK.TOKdottd))	// a.template = e2
		{
			error("%s has no value", e1.toChars());
			e1.type = Type.terror;
		}
		e2 = e2.semantic(sc);
		if (!e2.type)
		{
			error("%s has no value", e2.toChars());
			e2.type = Type.terror;
		}
		return this;
	}
	
    Expression semanticp(Scope sc)
	{
version (LOGSEMANTIC) {
		printf("BinExp.semantic('%s')\n", toChars());
}
		e1 = e1.semantic(sc);
		if (!e1.type &&
		!(op == TOK.TOKassign && e1.op == TOK.TOKdottd))	// a.template = e2
		{
			error("%s has no value", e1.toChars());
			e1.type = Type.terror;
		}
		e2 = e2.semantic(sc);
		if (!e2.type)
		{
			error("%s has no value", e2.toChars());
			e2.type = Type.terror;
		}
		return this;
	}
	
    Expression commonSemanticAssign(Scope sc)
	{
		assert(false);
	}

    Expression commonSemanticAssignIntegral(Scope sc)
	{
		Expression e;

		if (!type)
		{
			BinExp.semantic(sc);
			e2 = resolveProperties(sc, e2);

			e = op_overload(sc);
			if (e)
				return e;

			if (e1.op == TOK.TOKslice)
			{   // T[] op= ...
				typeCombine(sc);
				type = e1.type;
				return arrayOp(sc);
			}

			e1 = e1.modifiableLvalue(sc, e1);
			e1.checkScalar();
			type = e1.type;
			if (type.toBasetype().ty == TY.Tbool)
			{
				e2 = e2.implicitCastTo(sc, type);
			}

			typeCombine(sc);
			e1.checkIntegral();
			e2.checkIntegral();
		}

		return this;
	}
	
    bool checkSideEffect(int flag)
	{
		switch (op) {
			case TOK.TOKplusplus:
			case TOK.TOKminusminus:
			case TOK.TOKassign:
			case TOK.TOKconstruct:
			case TOK.TOKblit:
			case TOK.TOKaddass:
			case TOK.TOKminass:
			case TOK.TOKcatass:
			case TOK.TOKmulass:
			case TOK.TOKdivass:
			case TOK.TOKmodass:
			case TOK.TOKshlass:
			case TOK.TOKshrass:
			case TOK.TOKushrass:
			case TOK.TOKandass:
			case TOK.TOKorass:
			case TOK.TOKxorass:
			case TOK.TOKin:
			case TOK.TOKremove:
				return true;

			default:
				return Expression.checkSideEffect(flag);
		}
	}
	
    void toCBuffer(OutBuffer buf, HdrGenState* hgs)
	{
		expToCBuffer(buf, hgs, e1, precedence[op]);
		buf.writeByte(' ');
		buf.writestring(Token.toChars(op));
		buf.writeByte(' ');
		expToCBuffer(buf, hgs, e2, cast(PREC)(precedence[op] + 1));
	}

	/****************************************
	 * Scale addition/subtraction to/from pointer.
	 */
    Expression scaleFactor(Scope sc)
	{
		ulong stride;
		Type t1b = e1.type.toBasetype();
		Type t2b = e2.type.toBasetype();

		if (t1b.ty == Tpointer && t2b.isintegral())
		{   
			// Need to adjust operator by the stride
			// Replace (ptr + int) with (ptr + (int * stride))
			Type t = Type.tptrdiff_t;

			stride = t1b.nextOf().size(loc);
			if (!t.equals(t2b))
				e2 = e2.castTo(sc, t);
			e2 = new MulExp(loc, e2, new IntegerExp(Loc(0), stride, t));
			e2.type = t;
			type = e1.type;
		}
		else if (t2b.ty == Tpointer && t1b.isintegral())
		{   
			// Need to adjust operator by the stride
			// Replace (int + ptr) with (ptr + (int * stride))
			Type t = Type.tptrdiff_t;
			Expression e;

			stride = t2b.nextOf().size(loc);
			if (!t.equals(t1b))
				e = e1.castTo(sc, t);
			else
				e = e1;
			e = new MulExp(loc, e, new IntegerExp(Loc(0), stride, t));
			e.type = t;
			type = e2.type;
			e1 = e2;
			e2 = e;
		}
		return this;
	}
	
	/************************************
	 * Bring leaves to common type.
	 */
    Expression typeCombine(Scope sc)
	{
		Type t1 = e1.type.toBasetype();
		Type t2 = e2.type.toBasetype();

		if (op == TOK.TOKmin || op == TOK.TOKadd)
		{
			if (t1.ty == TY.Tstruct)
			{
				if (t2.ty == TY.Tstruct && (cast(TypeStruct)t1).sym is (cast(TypeStruct)t2).sym)
					goto Lerror;
			}
			else if (t1.ty == TY.Tclass)
			{
				if (t2.ty == TY.Tclass)
					goto Lerror;
			}
		}

		if (!typeMerge(sc, this, &type, &e1, &e2))
			goto Lerror;
		return this;

	Lerror:
		incompatibleTypes();
		type = Type.terror;
		e1 = new ErrorExp();
		e2 = new ErrorExp();
		return this;
	}

    Expression optimize(int result)
	{
		//printf("BinExp.optimize(result = %d) %s\n", result, toChars());
		if (op != TOK.TOKconstruct && op != TOK.TOKblit)	// don't replace const variable with its initializer
			e1 = e1.optimize(result);

		e2 = e2.optimize(result);
		if (op == TOK.TOKshlass || op == TOK.TOKshrass || op == TOK.TOKushrass)
		{
			if (e2.isConst() == 1)
			{
				long  i2 = e2.toInteger();
				ulong sz = e1.type.size() * 8;

				if (i2 < 0 || i2 > sz)
				{   
					error("shift assign by %jd is outside the range 0..%zu", i2, sz);
					e2 = new IntegerExp(0);
				}
			}
		}

		return this;
	}

    bool isunsigned()
	{
		assert(false);
	}
	
    void incompatibleTypes()
	{
		error("incompatible types for ((%s) %s (%s)): '%s' and '%s'",
         e1.toChars(), Token.toChars(op), e2.toChars(),
         e1.type.toChars(), e2.type.toChars());
	}

    void dump(int indent)
	{
		assert(false);
	}

    void scanForNestedRef(Scope *sc)
	{
		assert(false);
	}
	
    Expression interpretCommon(InterState *istate, Expression *(*fp)(Type *, Expression *, Expression *))
	{
		assert(false);
	}

    Expression interpretCommon2(InterState *istate, Expression *(*fp)(TOK, Type *, Expression *, Expression *))
	{
		assert(false);
	}
	
    Expression interpretAssignCommon(InterState *istate, Expression *(*fp)(Type *, Expression *, Expression *), int post = 0)
	{
		assert(false);
	}
	
    bool canThrow()
	{
		return e1.canThrow() || e2.canThrow();
	}

	/***********************************
	 * Construct the array operation expression.
	 */
    Expression arrayOp(Scope sc)
	{
		//printf("BinExp.arrayOp() %s\n", toChars());
		Expressions arguments = new Expressions();

		/* The expression to generate an array operation for is mangled
		 * into a name to use as the array operation function name.
		 * Mangle in the operands and operators in RPN order, and type.
		 */
		scope OutBuffer buf = new OutBuffer();
		buf.writestring("_array");
		buildArrayIdent(buf, arguments);
		buf.writeByte('_');

		/* Append deco of array element type
		 */
version (DMDV2) {
		buf.writestring(type.toBasetype().nextOf().toBasetype().mutableOf().deco);
} else {
		buf.writestring(type.toBasetype().nextOf().toBasetype().deco);
}

		size_t namelen = buf.offset;
		buf.writeByte(0);
		immutable(char)* name = cast(immutable(char)*)buf.extractData();

		/* Look up name in hash table
		 */
		if (arrayfuncs is null) arrayfuncs = new StringTable(); /// HACK!

		StringValue* sv = arrayfuncs.update(name[0..namelen]);
		FuncDeclaration fd = cast(FuncDeclaration)sv.ptrvalue;
		if (!fd)
		{
			/* Some of the array op functions are written as library functions,
			 * presumably to optimize them with special CPU vector instructions.
			 * List those library functions here, in alpha order.
			 */
			static const(char)*[] libArrayopFuncs =
			[
				"_arrayExpSliceAddass_a",
				"_arrayExpSliceAddass_d",		// T[]+=T
				"_arrayExpSliceAddass_f",		// T[]+=T
				"_arrayExpSliceAddass_g",
				"_arrayExpSliceAddass_h",
				"_arrayExpSliceAddass_i",
				"_arrayExpSliceAddass_k",
				"_arrayExpSliceAddass_s",
				"_arrayExpSliceAddass_t",
				"_arrayExpSliceAddass_u",
				"_arrayExpSliceAddass_w",

				"_arrayExpSliceDivass_d",		// T[]/=T
				"_arrayExpSliceDivass_f",		// T[]/=T

				"_arrayExpSliceMinSliceAssign_a",
				"_arrayExpSliceMinSliceAssign_d",	// T[]=T-T[]
				"_arrayExpSliceMinSliceAssign_f",	// T[]=T-T[]
				"_arrayExpSliceMinSliceAssign_g",
				"_arrayExpSliceMinSliceAssign_h",
				"_arrayExpSliceMinSliceAssign_i",
				"_arrayExpSliceMinSliceAssign_k",
				"_arrayExpSliceMinSliceAssign_s",
				"_arrayExpSliceMinSliceAssign_t",
				"_arrayExpSliceMinSliceAssign_u",
				"_arrayExpSliceMinSliceAssign_w",

				"_arrayExpSliceMinass_a",
				"_arrayExpSliceMinass_d",		// T[]-=T
				"_arrayExpSliceMinass_f",		// T[]-=T
				"_arrayExpSliceMinass_g",
				"_arrayExpSliceMinass_h",
				"_arrayExpSliceMinass_i",
				"_arrayExpSliceMinass_k",
				"_arrayExpSliceMinass_s",
				"_arrayExpSliceMinass_t",
				"_arrayExpSliceMinass_u",
				"_arrayExpSliceMinass_w",

				"_arrayExpSliceMulass_d",		// T[]*=T
				"_arrayExpSliceMulass_f",		// T[]*=T
				"_arrayExpSliceMulass_i",
				"_arrayExpSliceMulass_k",
				"_arrayExpSliceMulass_s",
				"_arrayExpSliceMulass_t",
				"_arrayExpSliceMulass_u",
				"_arrayExpSliceMulass_w",

				"_arraySliceExpAddSliceAssign_a",
				"_arraySliceExpAddSliceAssign_d",	// T[]=T[]+T
				"_arraySliceExpAddSliceAssign_f",	// T[]=T[]+T
				"_arraySliceExpAddSliceAssign_g",
				"_arraySliceExpAddSliceAssign_h",
				"_arraySliceExpAddSliceAssign_i",
				"_arraySliceExpAddSliceAssign_k",
				"_arraySliceExpAddSliceAssign_s",
				"_arraySliceExpAddSliceAssign_t",
				"_arraySliceExpAddSliceAssign_u",
				"_arraySliceExpAddSliceAssign_w",

				"_arraySliceExpDivSliceAssign_d",	// T[]=T[]/T
				"_arraySliceExpDivSliceAssign_f",	// T[]=T[]/T

				"_arraySliceExpMinSliceAssign_a",
				"_arraySliceExpMinSliceAssign_d",	// T[]=T[]-T
				"_arraySliceExpMinSliceAssign_f",	// T[]=T[]-T
				"_arraySliceExpMinSliceAssign_g",
				"_arraySliceExpMinSliceAssign_h",
				"_arraySliceExpMinSliceAssign_i",
				"_arraySliceExpMinSliceAssign_k",
				"_arraySliceExpMinSliceAssign_s",
				"_arraySliceExpMinSliceAssign_t",
				"_arraySliceExpMinSliceAssign_u",
				"_arraySliceExpMinSliceAssign_w",

				"_arraySliceExpMulSliceAddass_d",	// T[] += T[]*T
				"_arraySliceExpMulSliceAddass_f",
				"_arraySliceExpMulSliceAddass_r",

				"_arraySliceExpMulSliceAssign_d",	// T[]=T[]*T
				"_arraySliceExpMulSliceAssign_f",	// T[]=T[]*T
				"_arraySliceExpMulSliceAssign_i",
				"_arraySliceExpMulSliceAssign_k",
				"_arraySliceExpMulSliceAssign_s",
				"_arraySliceExpMulSliceAssign_t",
				"_arraySliceExpMulSliceAssign_u",
				"_arraySliceExpMulSliceAssign_w",

				"_arraySliceExpMulSliceMinass_d",	// T[] -= T[]*T
				"_arraySliceExpMulSliceMinass_f",
				"_arraySliceExpMulSliceMinass_r",

				"_arraySliceSliceAddSliceAssign_a",
				"_arraySliceSliceAddSliceAssign_d",	// T[]=T[]+T[]
				"_arraySliceSliceAddSliceAssign_f",	// T[]=T[]+T[]
				"_arraySliceSliceAddSliceAssign_g",
				"_arraySliceSliceAddSliceAssign_h",
				"_arraySliceSliceAddSliceAssign_i",
				"_arraySliceSliceAddSliceAssign_k",
				"_arraySliceSliceAddSliceAssign_r",	// T[]=T[]+T[]
				"_arraySliceSliceAddSliceAssign_s",
				"_arraySliceSliceAddSliceAssign_t",
				"_arraySliceSliceAddSliceAssign_u",
				"_arraySliceSliceAddSliceAssign_w",

				"_arraySliceSliceAddass_a",
				"_arraySliceSliceAddass_d",		// T[]+=T[]
				"_arraySliceSliceAddass_f",		// T[]+=T[]
				"_arraySliceSliceAddass_g",
				"_arraySliceSliceAddass_h",
				"_arraySliceSliceAddass_i",
				"_arraySliceSliceAddass_k",
				"_arraySliceSliceAddass_s",
				"_arraySliceSliceAddass_t",
				"_arraySliceSliceAddass_u",
				"_arraySliceSliceAddass_w",

				"_arraySliceSliceMinSliceAssign_a",
				"_arraySliceSliceMinSliceAssign_d",	// T[]=T[]-T[]
				"_arraySliceSliceMinSliceAssign_f",	// T[]=T[]-T[]
				"_arraySliceSliceMinSliceAssign_g",
				"_arraySliceSliceMinSliceAssign_h",
				"_arraySliceSliceMinSliceAssign_i",
				"_arraySliceSliceMinSliceAssign_k",
				"_arraySliceSliceMinSliceAssign_r",	// T[]=T[]-T[]
				"_arraySliceSliceMinSliceAssign_s",
				"_arraySliceSliceMinSliceAssign_t",
				"_arraySliceSliceMinSliceAssign_u",
				"_arraySliceSliceMinSliceAssign_w",

				"_arraySliceSliceMinass_a",
				"_arraySliceSliceMinass_d",		// T[]-=T[]
				"_arraySliceSliceMinass_f",		// T[]-=T[]
				"_arraySliceSliceMinass_g",
				"_arraySliceSliceMinass_h",
				"_arraySliceSliceMinass_i",
				"_arraySliceSliceMinass_k",
				"_arraySliceSliceMinass_s",
				"_arraySliceSliceMinass_t",
				"_arraySliceSliceMinass_u",
				"_arraySliceSliceMinass_w",

				"_arraySliceSliceMulSliceAssign_d",	// T[]=T[]*T[]
				"_arraySliceSliceMulSliceAssign_f",	// T[]=T[]*T[]
				"_arraySliceSliceMulSliceAssign_i",
				"_arraySliceSliceMulSliceAssign_k",
				"_arraySliceSliceMulSliceAssign_s",
				"_arraySliceSliceMulSliceAssign_t",
				"_arraySliceSliceMulSliceAssign_u",
				"_arraySliceSliceMulSliceAssign_w",

				"_arraySliceSliceMulass_d",		// T[]*=T[]
				"_arraySliceSliceMulass_f",		// T[]*=T[]
				"_arraySliceSliceMulass_i",
				"_arraySliceSliceMulass_k",
				"_arraySliceSliceMulass_s",
				"_arraySliceSliceMulass_t",
				"_arraySliceSliceMulass_u",
				"_arraySliceSliceMulass_w",
			];

			int i = binary(name, libArrayopFuncs.ptr, libArrayopFuncs.length);
			if (i == -1)
			{
debug {			// Make sure our array is alphabetized
				for (i = 0; i < libArrayopFuncs.length; i++)
				{
					if (strcmp(name, libArrayopFuncs[i]) == 0)
						assert(false);
				}
}
				/* Not in library, so generate it.
				 * Construct the function body:
				 *	foreach (i; 0 .. p.length)    for (size_t i = 0; i < p.length; i++)
				 *	    loopbody;
				 *	return p;
				 */

				Arguments fparams = new Arguments();
				Expression loopbody = buildArrayLoop(fparams);
				Argument p = cast(Argument)fparams.data[0 /*fparams.dim - 1*/];
version (DMDV1) {
				// for (size_t i = 0; i < p.length; i++)
				Initializer init = new ExpInitializer(0, new IntegerExp(0, 0, Type.tsize_t));
				Dsymbol d = new VarDeclaration(0, Type.tsize_t, Id.p, init);
				Statement s1 = new ForStatement(0,
					new DeclarationStatement(0, d),
					new CmpExp(TOKlt, 0, new IdentifierExp(0, Id.p), new ArrayLengthExp(0, new IdentifierExp(0, p.ident))),
					new PostExp(TOKplusplus, 0, new IdentifierExp(0, Id.p)),
					new ExpStatement(0, loopbody));
} else {
				// foreach (i; 0 .. p.length)
				Statement s1 = new ForeachRangeStatement(Loc(0), TOKforeach,
					new Argument(STC.STCundefined, null, Id.p, null),
					new IntegerExp(Loc(0), 0, Type.tint32),
					new ArrayLengthExp(Loc(0), new IdentifierExp(Loc(0), p.ident)),
					new ExpStatement(Loc(0), loopbody));
}
				Statement s2 = new ReturnStatement(Loc(0), new IdentifierExp(Loc(0), p.ident));
				//printf("s2: %s\n", s2.toChars());
				Statement fbody = new CompoundStatement(Loc(0), s1, s2);

				/* Construct the function
				 */
				TypeFunction ftype = new TypeFunction(fparams, type, 0, LINKc);
				//printf("ftype: %s\n", ftype.toChars());
				fd = new FuncDeclaration(Loc(0), Loc(0), Lexer.idPool(name[0..namelen]), STCundefined, ftype);
				fd.fbody = fbody;
				fd.protection = PROT.PROTpublic;
				fd.linkage = LINKc;

				sc.module_.importedFrom.members.push(cast(void*)fd);

				sc = sc.push();
				sc.parent = sc.module_.importedFrom;
				sc.stc = STCundefined;
				sc.linkage = LINKc;
				fd.semantic(sc);
				sc.pop();
			}
			else
			{   /* In library, refer to it.
				 */
				fd = FuncDeclaration.genCfunc(type, name[0..namelen]);
			}
			sv.ptrvalue = cast(void*)fd;	// cache symbol in hash table
		}

		/* Call the function fd(arguments)
		 */
		Expression ec = new VarExp(Loc(0), fd);
		Expression e = new CallExp(loc, ec, arguments);
		e.type = type;
		return e;
	}

    int inlineCost(InlineCostState* ics)
	{
		return 1 + e1.inlineCost(ics) + e2.inlineCost(ics);
	}
	
    Expression doInline(InlineDoState ids)
	{
		BinExp be = cast(BinExp)copy();

		be.e1 = e1.doInline(ids);
		be.e2 = e2.doInline(ids);
		return be;
	}
	
    Expression inlineScan(InlineScanState* iss)
	{
		e1 = e1.inlineScan(iss);
		e2 = e2.inlineScan(iss);
		return this;
	}

    Expression op_overload(Scope sc)
	{
		//printf("BinExp.op_overload() (%s)\n", toChars());

		AggregateDeclaration ad;
		Type t1 = e1.type.toBasetype();
		Type t2 = e2.type.toBasetype();
		Identifier id = opId();
		Identifier id_r = opId_r();

		Match m;
		scope Expressions args1 = new Expressions();
		scope Expressions args2 = new Expressions();
		int argsset = 0;

		AggregateDeclaration ad1;
		if (t1.ty == TY.Tclass)
			ad1 = (cast(TypeClass)t1).sym;
		else if (t1.ty == TY.Tstruct)
			ad1 = (cast(TypeStruct)t1).sym;
		else
			ad1 = null;

		AggregateDeclaration ad2;
		if (t2.ty == TY.Tclass)
			ad2 = (cast(TypeClass)t2).sym;
		else if (t2.ty == TY.Tstruct)
			ad2 = (cast(TypeStruct)t2).sym;
		else
			ad2 = null;

		Dsymbol s = null;
		Dsymbol s_r = null;
		FuncDeclaration fd = null;
		TemplateDeclaration td = null;
		if (ad1 && id)
		{
			s = search_function(ad1, id);
		}
		if (ad2 && id_r)
		{
			s_r = search_function(ad2, id_r);
		}

		if (s || s_r)
		{
			/* Try:
			 *	a.opfunc(b)
			 *	b.opfunc_r(a)
			 * and see which is better.
			 */
			Expression e;
			FuncDeclaration lastf;

			args1.setDim(1);
			args1.data[0] = cast(void*) e1;
			args2.setDim(1);
			args2.data[0] = cast(void*) e2;
			argsset = 1;

			///memset(&m, 0, sizeof(m));
			m.last = MATCH.MATCHnomatch;

			if (s)
			{
				fd = s.isFuncDeclaration();
				if (fd)
				{
					overloadResolveX(&m, fd, null, args2);
				}
				else
				{   
					td = s.isTemplateDeclaration();
					templateResolve(&m, td, sc, loc, null, null, args2);
				}
			}
			
			lastf = m.lastf;

			if (s_r)
			{
				fd = s_r.isFuncDeclaration();
				if (fd)
				{
					overloadResolveX(&m, fd, null, args1);
				}
				else
				{  
					td = s_r.isTemplateDeclaration();
					templateResolve(&m, td, sc, loc, null, null, args1);
				}
			}

			if (m.count > 1)
			{
				// Error, ambiguous
				error("overloads %s and %s both match argument list for %s",
					m.lastf.type.toChars(),
					m.nextf.type.toChars(),
					m.lastf.toChars());
			}
			else if (m.last == MATCH.MATCHnomatch)
			{
				m.lastf = m.anyf;
			}

			if (op == TOK.TOKplusplus || op == TOK.TOKminusminus)
				// Kludge because operator overloading regards e++ and e--
				// as unary, but it's implemented as a binary.
				// Rewrite (e1 ++ e2) as e1.postinc()
				// Rewrite (e1 -- e2) as e1.postdec()
				e = build_overload(loc, sc, e1, null, id);
			else if (lastf && m.lastf == lastf || m.last == MATCH.MATCHnomatch)
				// Rewrite (e1 op e2) as e1.opfunc(e2)
				e = build_overload(loc, sc, e1, e2, id);
			else
				// Rewrite (e1 op e2) as e2.opfunc_r(e1)
				e = build_overload(loc, sc, e2, e1, id_r);
			return e;
		}

		if (isCommutative())
		{
			s = null;
			s_r = null;
			if (ad1 && id_r)
			{
				s_r = search_function(ad1, id_r);
			}
			if (ad2 && id)
			{
				s = search_function(ad2, id);
			}

			if (s || s_r)
			{
				/* Try:
				 *	a.opfunc_r(b)
				 *	b.opfunc(a)
				 * and see which is better.
				 */

				if (!argsset)
				{	
					args1.setDim(1);
					args1.data[0] = cast(void*) e1;
					args2.setDim(1);
					args2.data[0] = cast(void*) e2;
				}

				///memset(&m, 0, sizeof(m));
				m.last = MATCH.MATCHnomatch;

				if (s_r)
				{
					fd = s_r.isFuncDeclaration();
					if (fd)
					{
						overloadResolveX(&m, fd, null, args2);
					}
					else
					{   td = s_r.isTemplateDeclaration();
						templateResolve(&m, td, sc, loc, null, null, args2);
					}
				}
				FuncDeclaration lastf = m.lastf;

				if (s)
				{
					fd = s.isFuncDeclaration();
					if (fd)
					{
						overloadResolveX(&m, fd, null, args1);
					}
					else
					{   td = s.isTemplateDeclaration();
						templateResolve(&m, td, sc, loc, null, null, args1);
					}
				}

				if (m.count > 1)
				{
					// Error, ambiguous
					error("overloads %s and %s both match argument list for %s",
						m.lastf.type.toChars(),
						m.nextf.type.toChars(),
						m.lastf.toChars());
				}
				else if (m.last == MATCH.MATCHnomatch)
				{
					m.lastf = m.anyf;
				}

				Expression e;
				if (lastf && m.lastf == lastf || id_r && m.last == MATCH.MATCHnomatch)
					// Rewrite (e1 op e2) as e1.opfunc_r(e2)
					e = build_overload(loc, sc, e1, e2, id_r);
				else
					// Rewrite (e1 op e2) as e2.opfunc(e1)
					e = build_overload(loc, sc, e2, e1, id);

				// When reversing operands of comparison operators,
				// need to reverse the sense of the op
				switch (op)
				{
					case TOK.TOKlt:		op = TOK.TOKgt;	break;
					case TOK.TOKgt:		op = TOK.TOKlt;	break;
					case TOK.TOKle:		op = TOK.TOKge;	break;
					case TOK.TOKge:		op = TOK.TOKle;	break;

					// Floating point compares
					case TOK.TOKule:	op = TOK.TOKuge; break;
					case TOK.TOKul:		op = TOK.TOKug;	 break;
					case TOK.TOKuge:	op = TOK.TOKule; break;
					case TOK.TOKug:		op = TOK.TOKul;	 break;

					// These are symmetric
					case TOK.TOKunord:
					case TOK.TOKlg:
					case TOK.TOKleg:
					case TOK.TOKue:
						break;
				}

				return e;
			}
		}

version (DMDV2) {
		// Try alias this on first operand
		if (ad1 && ad1.aliasthis)
		{
			/* Rewrite (e1 op e2) as:
			 *	(e1.aliasthis op e2)
			 */
			Expression e1 = new DotIdExp(loc, this.e1, ad1.aliasthis.ident);
			Expression e = copy();
			(cast(BinExp)e).e1 = e1;
			e = e.semantic(sc);
			return e;
		}

		// Try alias this on second operand
		if (ad2 && ad2.aliasthis)
		{
			/* Rewrite (e1 op e2) as:
			 *	(e1 op e2.aliasthis)
			 */
			Expression e2 = new DotIdExp(loc, this.e2, ad2.aliasthis.ident);
			Expression e = copy();
			(cast(BinExp)e).e2 = e2;
			e = e.semantic(sc);
			return e;
		}
}
		return null;
	}

    elem* toElemBin(IRState* irs, int op)
	{
		//printf("toElemBin() '%s'\n", toChars());

		tym_t tym = type.totym();

		elem* el = e1.toElem(irs);
		elem* er = e2.toElem(irs);
		elem* e = el_bin(op,tym,el,er);
		el_setLoc(e,loc);

		return e;
	}
	
	final void Exp_buildArrayIdent(OutBuffer buf, Expressions arguments, string Str)
	{									
		/* Evaluate assign expressions left to right			
		 */									
		e1.buildArrayIdent(buf, arguments);				
		e2.buildArrayIdent(buf, arguments);				
		buf.writestring(Str);						
	}
	
	final void AssignExp_buildArrayIdent(OutBuffer buf, Expressions arguments, string Str)
	{							
		/* Evaluate assign expressions right to left	
		 */							
		e2.buildArrayIdent(buf, arguments);		
		e1.buildArrayIdent(buf, arguments);		
		buf.writestring(Str);				
		buf.writestring("ass");				
	}
}