Mercurial > projects > ddmd
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; } }