Expression *BinExp::op_overload(Scope *sc) { //printf("BinExp::op_overload() (%s)\n", toChars()); Identifier *id = opId(); Identifier *id_r = opId_r(); Expressions args1; Expressions args2; int argsset = 0; AggregateDeclaration *ad1 = isAggregate(e1->type); AggregateDeclaration *ad2 = isAggregate(e2->type); Dsymbol *s = NULL; Dsymbol *s_r = NULL; #if 1 // the old D1 scheme if (ad1 && id) { s = search_function(ad1, id); } if (ad2 && id_r) { s_r = search_function(ad2, id_r); } #endif Objects *targsi = NULL; #if DMDV2 if (op == TOKplusplus || op == TOKminusminus) { // Bug4099 fix if (ad1 && search_function(ad1, Id::opUnary)) return NULL; } if (!s && !s_r && op != TOKequal && op != TOKnotequal && op != TOKassign && op != TOKplusplus && op != TOKminusminus) { /* Try the new D2 scheme, opBinary and opBinaryRight */ if (ad1) s = search_function(ad1, Id::opBinary); if (ad2) s_r = search_function(ad2, Id::opBinaryRight); // Set targsi, the template argument list, which will be the operator string if (s || s_r) { id = Id::opBinary; id_r = Id::opBinaryRight; targsi = opToArg(sc, op); } } #endif if (s || s_r) { /* Try: * a.opfunc(b) * b.opfunc_r(a) * and see which is better. */ args1.setDim(1); args1.tdata()[0] = e1; args2.setDim(1); args2.tdata()[0] = e2; argsset = 1; Match m; memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; if (s) { FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { overloadResolveX(&m, fd, NULL, &args2, sc->module); } else { TemplateDeclaration *td = s->isTemplateDeclaration(); templateResolve(&m, td, sc, loc, targsi, e1, &args2); } } FuncDeclaration *lastf = m.lastf; if (s_r) { FuncDeclaration *fd = s_r->isFuncDeclaration(); if (fd) { overloadResolveX(&m, fd, NULL, &args1, sc->module); } else { TemplateDeclaration *td = s_r->isTemplateDeclaration(); templateResolve(&m, td, sc, loc, targsi, e2, &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 == MATCHnomatch) { m.lastf = m.anyf; if (targsi) goto L1; } Expression *e; if (op == TOKplusplus || op == 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, m.lastf ? m.lastf : s); else if (lastf && m.lastf == lastf || !s_r && m.last == MATCHnomatch) // Rewrite (e1 op e2) as e1.opfunc(e2) e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); else // Rewrite (e1 op e2) as e2.opfunc_r(e1) e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s_r); return e; } L1: #if 1 // Retained for D1 compatibility if (isCommutative() && !targsi) { 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.tdata()[0] = e1; args2.setDim(1); args2.tdata()[0] = e2; } Match m; memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; if (s_r) { FuncDeclaration *fd = s_r->isFuncDeclaration(); if (fd) { overloadResolveX(&m, fd, NULL, &args2, sc->module); } else { TemplateDeclaration *td = s_r->isTemplateDeclaration(); templateResolve(&m, td, sc, loc, targsi, e1, &args2); } } FuncDeclaration *lastf = m.lastf; if (s) { FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { overloadResolveX(&m, fd, NULL, &args1, sc->module); } else { TemplateDeclaration *td = s->isTemplateDeclaration(); templateResolve(&m, td, sc, loc, targsi, e2, &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 == MATCHnomatch) { m.lastf = m.anyf; } Expression *e; if (lastf && m.lastf == lastf || !s && m.last == MATCHnomatch) // Rewrite (e1 op e2) as e1.opfunc_r(e2) e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s_r); else // Rewrite (e1 op e2) as e2.opfunc(e1) e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s); // When reversing operands of comparison operators, // need to reverse the sense of the op switch (op) { case TOKlt: op = TOKgt; break; case TOKgt: op = TOKlt; break; case TOKle: op = TOKge; break; case TOKge: op = TOKle; break; // Floating point compares case TOKule: op = TOKuge; break; case TOKul: op = TOKug; break; case TOKuge: op = TOKule; break; case TOKug: op = TOKul; break; // These are symmetric case TOKunord: case TOKlg: case TOKleg: case TOKue: break; } return e; } } #endif #if DMDV2 // Try alias this on first operand if (ad1 && ad1->aliasthis && !(op == TOKassign && ad2 && ad1 == ad2)) // See Bugzilla 2943 { /* Rewrite (e1 op e2) as: * (e1.aliasthis op e2) */ Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); Expression *e = copy(); ((BinExp *)e)->e1 = e1; e = e->trySemantic(sc); return e; } // Try alias this on second operand if (ad2 && ad2->aliasthis && /* Bugzilla 2943: make sure that when we're copying the struct, we don't * just copy the alias this member */ !(op == TOKassign && ad1 && ad1 == ad2)) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); Expression *e = copy(); ((BinExp *)e)->e2 = e2; e = e->trySemantic(sc); return e; } #endif return NULL; }
Expression *BinExp::typeCombine(Scope *sc) { Type *t1; Type *t2; Type *t; TY ty; //printf("BinExp::typeCombine()\n"); //dump(0); e1 = e1->integralPromotions(sc); e2 = e2->integralPromotions(sc); // BUG: do toBasetype() t1 = e1->type; t2 = e2->type; assert(t1); //if (t1) printf("\tt1 = %s\n", t1->toChars()); //if (t2) printf("\tt2 = %s\n", t2->toChars()); #ifdef DEBUG if (!t2) printf("\te2 = '%s'\n", e2->toChars()); #endif assert(t2); Type *t1b = t1->toBasetype(); Type *t2b = t2->toBasetype(); ty = (TY)Type::impcnvResult[t1b->ty][t2b->ty]; if (ty != Terror) { TY ty1; TY ty2; ty1 = (TY)Type::impcnvType1[t1b->ty][t2b->ty]; ty2 = (TY)Type::impcnvType2[t1b->ty][t2b->ty]; if (t1b->ty == ty1) // if no promotions { if (t1 == t2) { if (!type) type = t1; return this; } if (t1b == t2b) { if (!type) type = t1b; return this; } } if (!type) type = Type::basic[ty]; t1 = Type::basic[ty1]; t2 = Type::basic[ty2]; e1 = e1->castTo(sc, t1); e2 = e2->castTo(sc, t2); #if 0 if (type != Type::basic[ty]) { t = type; type = Type::basic[ty]; return castTo(sc, t); } #endif //printf("after typeCombine():\n"); //dump(0); //printf("ty = %d, ty1 = %d, ty2 = %d\n", ty, ty1, ty2); return this; } t = t1; if (t1 == t2) { if ((t1->ty == Tstruct || t1->ty == Tclass) && (op == TOKmin || op == TOKadd)) goto Lincompatible; } else if (t1->isintegral() && t2->isintegral()) { printf("t1 = %s, t2 = %s\n", t1->toChars(), t2->toChars()); int sz1 = t1->size(); int sz2 = t2->size(); int sign1 = t1->isunsigned() == 0; int sign2 = t2->isunsigned() == 0; if (sign1 == sign2) { if (sz1 < sz2) goto Lt2; else goto Lt1; } if (!sign1) { if (sz1 >= sz2) goto Lt1; else goto Lt2; } else { if (sz2 >= sz1) goto Lt2; else goto Lt1; } } else if (t1->ty == Tpointer && t2->ty == Tpointer) { // Bring pointers to compatible type Type *t1n = t1->next; Type *t2n = t2->next; //t1->print(); //t2->print(); //if (t1n == t2n) *(char *)0 = 0; assert(t1n != t2n); if (t1n->ty == Tvoid) // pointers to void are always compatible t = t2; else if (t2n->ty == Tvoid) ; else if (t1n->ty == Tclass && t2n->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 == Tsarray || t1->ty == Tarray) && e2->op == TOKnull && t2->ty == Tpointer && t2->nextOf()->ty == Tvoid) { /* (T[n] op void*) * (T[] op void*) */ goto Lx1; } else if ((t2->ty == Tsarray || t2->ty == Tarray) && e1->op == TOKnull && t1->ty == Tpointer && t1->nextOf()->ty == Tvoid) { /* (void* op T[n]) * (void* op T[]) */ goto Lx2; } else if ((t1->ty == Tsarray || t1->ty == Tarray) && t1->implicitConvTo(t2)) { goto Lt2; } else if ((t2->ty == Tsarray || t2->ty == Tarray) && t2->implicitConvTo(t1)) { goto Lt1; } else if (t1->ty == Tclass || t2->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 == Tpointer) i1 = 0; else if (t2->ty == Tpointer) i2 = 0; } if (i2) { goto Lt2; } else if (i1) { goto Lt1; } else if (t1->ty == Tclass && t2->ty == Tclass) { TypeClass *tc1 = (TypeClass *)t1; TypeClass *tc2 = (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 ((e1->op == TOKstring || e1->op == TOKnull) && e1->implicitConvTo(t2)) { goto Lt2; } //else if (e2->op == TOKstring) { printf("test2\n"); } else if ((e2->op == TOKstring || e2->op == TOKnull) && e2->implicitConvTo(t1)) { goto Lt1; } else if (t1->ty == Tsarray && t2->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 == Tsarray && t2->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->isArrayOperand() && t1->ty == Tarray && e2->implicitConvTo(t1->nextOf())) { // T[] op T e2 = e2->castTo(sc, t1->nextOf()); t = t1->nextOf()->arrayOf(); } else if (e2->isArrayOperand() && t2->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(WANTvalue); if (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: incompatibleTypes(); type = Type::terror; e1 = new ErrorExp(); e2 = new ErrorExp(); return new ErrorExp(); } Lret: if (!type) type = t; //dump(0); return this; Lt1: e2 = e2->castTo(sc, t1); t = t1; goto Lret; Lt2: e1 = e1->castTo(sc, t2); t = t2; goto Lret; }