view dmd/BinExp.d @ 0:10317f0c89a5

Initial commit
author korDen
date Sat, 24 Oct 2009 08:42:06 +0400
parents
children 832f71e6f96c
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.expression.Util;

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

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
 */

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();
	}

    Expression arrayOp(Scope sc)
	{
		assert(false);
	}

    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;
	}
}