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