diff 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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dmd/BinExp.d	Sat Oct 24 08:42:06 2009 +0400
@@ -0,0 +1,920 @@
+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;
+	}
+}
\ No newline at end of file