1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(DMDSRC _opover.d)
9  */
10 
11 module ddmd.opover;
12 
13 import core.stdc.stdio;
14 import ddmd.aggregate;
15 import ddmd.aliasthis;
16 import ddmd.arraytypes;
17 import ddmd.dclass;
18 import ddmd.declaration;
19 import ddmd.dscope;
20 import ddmd.dstruct;
21 import ddmd.dsymbol;
22 import ddmd.dtemplate;
23 import ddmd.errors;
24 import ddmd.expression;
25 import ddmd.func;
26 import ddmd.globals;
27 import ddmd.id;
28 import ddmd.identifier;
29 import ddmd.mtype;
30 import ddmd.statement;
31 import ddmd.tokens;
32 import ddmd.visitor;
33 
34 /***********************************
35  * Determine if operands of binary op can be reversed
36  * to fit operator overload.
37  */
38 extern (C++) bool isCommutative(TOK op)
39 {
40     switch (op)
41     {
42     case TOKadd:
43     case TOKmul:
44     case TOKand:
45     case TOKor:
46     case TOKxor:
47     // EqualExp
48     case TOKequal:
49     case TOKnotequal:
50     // CmpExp
51     case TOKlt:
52     case TOKle:
53     case TOKgt:
54     case TOKge:
55         return true;
56     default:
57         break;
58     }
59     return false;
60 }
61 
62 /***********************************
63  * Get Identifier for operator overload.
64  */
65 extern (C++) static Identifier opId(Expression e)
66 {
67     extern (C++) final class OpIdVisitor : Visitor
68     {
69         alias visit = super.visit;
70     public:
71         Identifier id;
72 
73         override void visit(Expression e)
74         {
75             assert(0);
76         }
77 
78         override void visit(UAddExp e)
79         {
80             id = Id.uadd;
81         }
82 
83         override void visit(NegExp e)
84         {
85             id = Id.neg;
86         }
87 
88         override void visit(ComExp e)
89         {
90             id = Id.com;
91         }
92 
93         override void visit(CastExp e)
94         {
95             id = Id._cast;
96         }
97 
98         override void visit(InExp e)
99         {
100             id = Id.opIn;
101         }
102 
103         override void visit(PostExp e)
104         {
105             id = (e.op == TOKplusplus) ? Id.postinc : Id.postdec;
106         }
107 
108         override void visit(AddExp e)
109         {
110             id = Id.add;
111         }
112 
113         override void visit(MinExp e)
114         {
115             id = Id.sub;
116         }
117 
118         override void visit(MulExp e)
119         {
120             id = Id.mul;
121         }
122 
123         override void visit(DivExp e)
124         {
125             id = Id.div;
126         }
127 
128         override void visit(ModExp e)
129         {
130             id = Id.mod;
131         }
132 
133         override void visit(PowExp e)
134         {
135             id = Id.pow;
136         }
137 
138         override void visit(ShlExp e)
139         {
140             id = Id.shl;
141         }
142 
143         override void visit(ShrExp e)
144         {
145             id = Id.shr;
146         }
147 
148         override void visit(UshrExp e)
149         {
150             id = Id.ushr;
151         }
152 
153         override void visit(AndExp e)
154         {
155             id = Id.iand;
156         }
157 
158         override void visit(OrExp e)
159         {
160             id = Id.ior;
161         }
162 
163         override void visit(XorExp e)
164         {
165             id = Id.ixor;
166         }
167 
168         override void visit(CatExp e)
169         {
170             id = Id.cat;
171         }
172 
173         override void visit(AssignExp e)
174         {
175             id = Id.assign;
176         }
177 
178         override void visit(AddAssignExp e)
179         {
180             id = Id.addass;
181         }
182 
183         override void visit(MinAssignExp e)
184         {
185             id = Id.subass;
186         }
187 
188         override void visit(MulAssignExp e)
189         {
190             id = Id.mulass;
191         }
192 
193         override void visit(DivAssignExp e)
194         {
195             id = Id.divass;
196         }
197 
198         override void visit(ModAssignExp e)
199         {
200             id = Id.modass;
201         }
202 
203         override void visit(AndAssignExp e)
204         {
205             id = Id.andass;
206         }
207 
208         override void visit(OrAssignExp e)
209         {
210             id = Id.orass;
211         }
212 
213         override void visit(XorAssignExp e)
214         {
215             id = Id.xorass;
216         }
217 
218         override void visit(ShlAssignExp e)
219         {
220             id = Id.shlass;
221         }
222 
223         override void visit(ShrAssignExp e)
224         {
225             id = Id.shrass;
226         }
227 
228         override void visit(UshrAssignExp e)
229         {
230             id = Id.ushrass;
231         }
232 
233         override void visit(CatAssignExp e)
234         {
235             id = Id.catass;
236         }
237 
238         override void visit(PowAssignExp e)
239         {
240             id = Id.powass;
241         }
242 
243         override void visit(EqualExp e)
244         {
245             id = Id.eq;
246         }
247 
248         override void visit(CmpExp e)
249         {
250             id = Id.cmp;
251         }
252 
253         override void visit(ArrayExp e)
254         {
255             id = Id.index;
256         }
257 
258         override void visit(PtrExp e)
259         {
260             id = Id.opStar;
261         }
262     }
263 
264     scope OpIdVisitor v = new OpIdVisitor();
265     e.accept(v);
266     return v.id;
267 }
268 
269 /***********************************
270  * Get Identifier for reverse operator overload,
271  * NULL if not supported for this operator.
272  */
273 extern (C++) static Identifier opId_r(Expression e)
274 {
275     extern (C++) final class OpIdRVisitor : Visitor
276     {
277         alias visit = super.visit;
278     public:
279         Identifier id;
280 
281         override void visit(Expression e)
282         {
283             id = null;
284         }
285 
286         override void visit(InExp e)
287         {
288             id = Id.opIn_r;
289         }
290 
291         override void visit(AddExp e)
292         {
293             id = Id.add_r;
294         }
295 
296         override void visit(MinExp e)
297         {
298             id = Id.sub_r;
299         }
300 
301         override void visit(MulExp e)
302         {
303             id = Id.mul_r;
304         }
305 
306         override void visit(DivExp e)
307         {
308             id = Id.div_r;
309         }
310 
311         override void visit(ModExp e)
312         {
313             id = Id.mod_r;
314         }
315 
316         override void visit(PowExp e)
317         {
318             id = Id.pow_r;
319         }
320 
321         override void visit(ShlExp e)
322         {
323             id = Id.shl_r;
324         }
325 
326         override void visit(ShrExp e)
327         {
328             id = Id.shr_r;
329         }
330 
331         override void visit(UshrExp e)
332         {
333             id = Id.ushr_r;
334         }
335 
336         override void visit(AndExp e)
337         {
338             id = Id.iand_r;
339         }
340 
341         override void visit(OrExp e)
342         {
343             id = Id.ior_r;
344         }
345 
346         override void visit(XorExp e)
347         {
348             id = Id.ixor_r;
349         }
350 
351         override void visit(CatExp e)
352         {
353             id = Id.cat_r;
354         }
355     }
356 
357     scope OpIdRVisitor v = new OpIdRVisitor();
358     e.accept(v);
359     return v.id;
360 }
361 
362 /************************************
363  * If type is a class or struct, return the symbol for it,
364  * else NULL
365  */
366 extern (C++) AggregateDeclaration isAggregate(Type t)
367 {
368     t = t.toBasetype();
369     if (t.ty == Tclass)
370     {
371         return (cast(TypeClass)t).sym;
372     }
373     else if (t.ty == Tstruct)
374     {
375         return (cast(TypeStruct)t).sym;
376     }
377     return null;
378 }
379 
380 /*******************************************
381  * Helper function to turn operator into template argument list
382  */
383 extern (C++) Objects* opToArg(Scope* sc, TOK op)
384 {
385     /* Remove the = from op=
386      */
387     switch (op)
388     {
389     case TOKaddass:
390         op = TOKadd;
391         break;
392     case TOKminass:
393         op = TOKmin;
394         break;
395     case TOKmulass:
396         op = TOKmul;
397         break;
398     case TOKdivass:
399         op = TOKdiv;
400         break;
401     case TOKmodass:
402         op = TOKmod;
403         break;
404     case TOKandass:
405         op = TOKand;
406         break;
407     case TOKorass:
408         op = TOKor;
409         break;
410     case TOKxorass:
411         op = TOKxor;
412         break;
413     case TOKshlass:
414         op = TOKshl;
415         break;
416     case TOKshrass:
417         op = TOKshr;
418         break;
419     case TOKushrass:
420         op = TOKushr;
421         break;
422     case TOKcatass:
423         op = TOKcat;
424         break;
425     case TOKpowass:
426         op = TOKpow;
427         break;
428     default:
429         break;
430     }
431     Expression e = new StringExp(Loc(), cast(char*)Token.toChars(op));
432     e = e.semantic(sc);
433     auto tiargs = new Objects();
434     tiargs.push(e);
435     return tiargs;
436 }
437 
438 /************************************
439  * Operator overload.
440  * Check for operator overload, if so, replace
441  * with function call.
442  * Return NULL if not an operator overload.
443  */
444 extern (C++) Expression op_overload(Expression e, Scope* sc)
445 {
446     extern (C++) final class OpOverload : Visitor
447     {
448         alias visit = super.visit;
449     public:
450         Scope* sc;
451         Expression result;
452 
453         extern (D) this(Scope* sc)
454         {
455             this.sc = sc;
456         }
457 
458         override void visit(Expression e)
459         {
460             assert(0);
461         }
462 
463         override void visit(UnaExp e)
464         {
465             //printf("UnaExp::op_overload() (%s)\n", e->toChars());
466             if (e.e1.op == TOKarray)
467             {
468                 ArrayExp ae = cast(ArrayExp)e.e1;
469                 ae.e1 = ae.e1.semantic(sc);
470                 ae.e1 = resolveProperties(sc, ae.e1);
471                 Expression ae1old = ae.e1;
472                 const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOKinterval);
473                 IntervalExp ie = null;
474                 if (maybeSlice && ae.arguments.dim)
475                 {
476                     assert((*ae.arguments)[0].op == TOKinterval);
477                     ie = cast(IntervalExp)(*ae.arguments)[0];
478                 }
479                 while (true)
480                 {
481                     if (ae.e1.op == TOKerror)
482                     {
483                         result = ae.e1;
484                         return;
485                     }
486                     Expression e0 = null;
487                     Expression ae1save = ae.e1;
488                     ae.lengthVar = null;
489                     Type t1b = ae.e1.type.toBasetype();
490                     AggregateDeclaration ad = isAggregate(t1b);
491                     if (!ad)
492                         break;
493                     if (search_function(ad, Id.opIndexUnary))
494                     {
495                         // Deal with $
496                         result = resolveOpDollar(sc, ae, &e0);
497                         if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j)
498                             goto Lfallback;
499                         if (result.op == TOKerror)
500                             return;
501                         /* Rewrite op(a[arguments]) as:
502                          *      a.opIndexUnary!(op)(arguments)
503                          */
504                         Expressions* a = ae.arguments.copy();
505                         Objects* tiargs = opToArg(sc, e.op);
506                         result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexUnary, tiargs);
507                         result = new CallExp(e.loc, result, a);
508                         if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)()
509                             result = result.trySemantic(sc);
510                         else
511                             result = result.semantic(sc);
512                         if (result)
513                         {
514                             result = Expression.combine(e0, result);
515                             return;
516                         }
517                     }
518                 Lfallback:
519                     if (maybeSlice && search_function(ad, Id.opSliceUnary))
520                     {
521                         // Deal with $
522                         result = resolveOpDollar(sc, ae, ie, &e0);
523                         if (result.op == TOKerror)
524                             return;
525                         /* Rewrite op(a[i..j]) as:
526                          *      a.opSliceUnary!(op)(i, j)
527                          */
528                         auto a = new Expressions();
529                         if (ie)
530                         {
531                             a.push(ie.lwr);
532                             a.push(ie.upr);
533                         }
534                         Objects* tiargs = opToArg(sc, e.op);
535                         result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceUnary, tiargs);
536                         result = new CallExp(e.loc, result, a);
537                         result = result.semantic(sc);
538                         result = Expression.combine(e0, result);
539                         return;
540                     }
541                     // Didn't find it. Forward to aliasthis
542                     if (ad.aliasthis && t1b != ae.att1)
543                     {
544                         if (!ae.att1 && t1b.checkAliasThisRec())
545                             ae.att1 = t1b;
546                         /* Rewrite op(a[arguments]) as:
547                          *      op(a.aliasthis[arguments])
548                          */
549                         ae.e1 = resolveAliasThis(sc, ae1save, true);
550                         if (ae.e1)
551                             continue;
552                     }
553                     break;
554                 }
555                 ae.e1 = ae1old; // recovery
556                 ae.lengthVar = null;
557             }
558             e.e1 = e.e1.semantic(sc);
559             e.e1 = resolveProperties(sc, e.e1);
560             if (e.e1.op == TOKerror)
561             {
562                 result = e.e1;
563                 return;
564             }
565             AggregateDeclaration ad = isAggregate(e.e1.type);
566             if (ad)
567             {
568                 Dsymbol fd = null;
569                 version (all)
570                 {
571                     // Old way, kept for compatibility with D1
572                     if (e.op != TOKpreplusplus && e.op != TOKpreminusminus)
573                     {
574                         fd = search_function(ad, opId(e));
575                         if (fd)
576                         {
577                             // Rewrite +e1 as e1.add()
578                             result = build_overload(e.loc, sc, e.e1, null, fd);
579                             return;
580                         }
581                     }
582                 }
583                 /* Rewrite as:
584                  *      e1.opUnary!(op)()
585                  */
586                 fd = search_function(ad, Id.opUnary);
587                 if (fd)
588                 {
589                     Objects* tiargs = opToArg(sc, e.op);
590                     result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
591                     result = new CallExp(e.loc, result);
592                     result = result.semantic(sc);
593                     return;
594                 }
595                 // Didn't find it. Forward to aliasthis
596                 if (ad.aliasthis && e.e1.type != e.att1)
597                 {
598                     /* Rewrite op(e1) as:
599                      *      op(e1.aliasthis)
600                      */
601                     //printf("att una %s e1 = %s\n", Token::toChars(op), this->e1->type->toChars());
602                     Expression e1 = new DotIdExp(e.loc, e.e1, ad.aliasthis.ident);
603                     UnaExp ue = cast(UnaExp)e.copy();
604                     if (!ue.att1 && e.e1.type.checkAliasThisRec())
605                         ue.att1 = e.e1.type;
606                     ue.e1 = e1;
607                     result = ue.trySemantic(sc);
608                     return;
609                 }
610             }
611         }
612 
613         override void visit(ArrayExp ae)
614         {
615             //printf("ArrayExp::op_overload() (%s)\n", ae->toChars());
616             ae.e1 = ae.e1.semantic(sc);
617             ae.e1 = resolveProperties(sc, ae.e1);
618             Expression ae1old = ae.e1;
619             const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOKinterval);
620             IntervalExp ie = null;
621             if (maybeSlice && ae.arguments.dim)
622             {
623                 assert((*ae.arguments)[0].op == TOKinterval);
624                 ie = cast(IntervalExp)(*ae.arguments)[0];
625             }
626             while (true)
627             {
628                 if (ae.e1.op == TOKerror)
629                 {
630                     result = ae.e1;
631                     return;
632                 }
633                 Expression e0 = null;
634                 Expression ae1save = ae.e1;
635                 ae.lengthVar = null;
636                 Type t1b = ae.e1.type.toBasetype();
637                 AggregateDeclaration ad = isAggregate(t1b);
638                 if (!ad)
639                 {
640                     // If the non-aggregate expression ae->e1 is indexable or sliceable,
641                     // convert it to the corresponding concrete expression.
642                     if (t1b.ty == Tpointer || t1b.ty == Tsarray || t1b.ty == Tarray || t1b.ty == Taarray || t1b.ty == Ttuple || ae.e1.op == TOKtype)
643                     {
644                         // Convert to SliceExp
645                         if (maybeSlice)
646                         {
647                             result = new SliceExp(ae.loc, ae.e1, ie);
648                             result = result.semantic(sc);
649                             return;
650                         }
651                         // Convert to IndexExp
652                         if (ae.arguments.dim == 1)
653                         {
654                             result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]);
655                             result = result.semantic(sc);
656                             return;
657                         }
658                     }
659                     break;
660                 }
661                 if (search_function(ad, Id.index))
662                 {
663                     // Deal with $
664                     result = resolveOpDollar(sc, ae, &e0);
665                     if (!result) // a[i..j] might be: a.opSlice(i, j)
666                         goto Lfallback;
667                     if (result.op == TOKerror)
668                         return;
669                     /* Rewrite e1[arguments] as:
670                      *      e1.opIndex(arguments)
671                      */
672                     Expressions* a = ae.arguments.copy();
673                     result = new DotIdExp(ae.loc, ae.e1, Id.index);
674                     result = new CallExp(ae.loc, result, a);
675                     if (maybeSlice) // a[] might be: a.opSlice()
676                         result = result.trySemantic(sc);
677                     else
678                         result = result.semantic(sc);
679                     if (result)
680                     {
681                         result = Expression.combine(e0, result);
682                         return;
683                     }
684                 }
685             Lfallback:
686                 if (maybeSlice && ae.e1.op == TOKtype)
687                 {
688                     result = new SliceExp(ae.loc, ae.e1, ie);
689                     result = result.semantic(sc);
690                     result = Expression.combine(e0, result);
691                     return;
692                 }
693                 if (maybeSlice && search_function(ad, Id.slice))
694                 {
695                     // Deal with $
696                     result = resolveOpDollar(sc, ae, ie, &e0);
697                     if (result.op == TOKerror)
698                         return;
699                     /* Rewrite a[i..j] as:
700                      *      a.opSlice(i, j)
701                      */
702                     auto a = new Expressions();
703                     if (ie)
704                     {
705                         a.push(ie.lwr);
706                         a.push(ie.upr);
707                     }
708                     result = new DotIdExp(ae.loc, ae.e1, Id.slice);
709                     result = new CallExp(ae.loc, result, a);
710                     result = result.semantic(sc);
711                     result = Expression.combine(e0, result);
712                     return;
713                 }
714                 // Didn't find it. Forward to aliasthis
715                 if (ad.aliasthis && t1b != ae.att1)
716                 {
717                     if (!ae.att1 && t1b.checkAliasThisRec())
718                         ae.att1 = t1b;
719                     //printf("att arr e1 = %s\n", this->e1->type->toChars());
720                     /* Rewrite op(a[arguments]) as:
721                      *      op(a.aliasthis[arguments])
722                      */
723                     ae.e1 = resolveAliasThis(sc, ae1save, true);
724                     if (ae.e1)
725                         continue;
726                 }
727                 break;
728             }
729             ae.e1 = ae1old; // recovery
730             ae.lengthVar = null;
731         }
732 
733         /***********************************************
734          * This is mostly the same as UnaryExp::op_overload(), but has
735          * a different rewrite.
736          */
737         override void visit(CastExp e)
738         {
739             //printf("CastExp::op_overload() (%s)\n", e->toChars());
740             AggregateDeclaration ad = isAggregate(e.e1.type);
741             if (ad)
742             {
743                 Dsymbol fd = null;
744                 /* Rewrite as:
745                  *      e1.opCast!(T)()
746                  */
747                 fd = search_function(ad, Id._cast);
748                 if (fd)
749                 {
750                     version (all)
751                     {
752                         // Backwards compatibility with D1 if opCast is a function, not a template
753                         if (fd.isFuncDeclaration())
754                         {
755                             // Rewrite as:  e1.opCast()
756                             result = build_overload(e.loc, sc, e.e1, null, fd);
757                             return;
758                         }
759                     }
760                     auto tiargs = new Objects();
761                     tiargs.push(e.to);
762                     result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
763                     result = new CallExp(e.loc, result);
764                     result = result.semantic(sc);
765                     return;
766                 }
767                 // Didn't find it. Forward to aliasthis
768                 if (ad.aliasthis)
769                 {
770                     /* Rewrite op(e1) as:
771                      *      op(e1.aliasthis)
772                      */
773                     Expression e1 = new DotIdExp(e.loc, e.e1, ad.aliasthis.ident);
774                     result = e.copy();
775                     (cast(UnaExp)result).e1 = e1;
776                     result = result.trySemantic(sc);
777                     return;
778                 }
779             }
780         }
781 
782         override void visit(BinExp e)
783         {
784             //printf("BinExp::op_overload() (%s)\n", e->toChars());
785             Identifier id = opId(e);
786             Identifier id_r = opId_r(e);
787             Expressions args1;
788             Expressions args2;
789             int argsset = 0;
790             AggregateDeclaration ad1 = isAggregate(e.e1.type);
791             AggregateDeclaration ad2 = isAggregate(e.e2.type);
792             if (e.op == TOKassign && ad1 == ad2)
793             {
794                 StructDeclaration sd = ad1.isStructDeclaration();
795                 if (sd && !sd.hasIdentityAssign)
796                 {
797                     /* This is bitwise struct assignment. */
798                     return;
799                 }
800             }
801             Dsymbol s = null;
802             Dsymbol s_r = null;
803             version (all)
804             {
805                 // the old D1 scheme
806                 if (ad1 && id)
807                 {
808                     s = search_function(ad1, id);
809                 }
810                 if (ad2 && id_r)
811                 {
812                     s_r = search_function(ad2, id_r);
813                     // Bugzilla 12778: If both x.opBinary(y) and y.opBinaryRight(x) found,
814                     // and they are exactly same symbol, x.opBinary(y) should be preferred.
815                     if (s_r && s_r == s)
816                         s_r = null;
817                 }
818             }
819             Objects* tiargs = null;
820             if (e.op == TOKplusplus || e.op == TOKminusminus)
821             {
822                 // Bug4099 fix
823                 if (ad1 && search_function(ad1, Id.opUnary))
824                     return;
825             }
826             if (!s && !s_r && e.op != TOKequal && e.op != TOKnotequal && e.op != TOKassign && e.op != TOKplusplus && e.op != TOKminusminus)
827             {
828                 /* Try the new D2 scheme, opBinary and opBinaryRight
829                  */
830                 if (ad1)
831                 {
832                     s = search_function(ad1, Id.opBinary);
833                     if (s && !s.isTemplateDeclaration())
834                     {
835                         e.e1.error("%s.opBinary isn't a template", e.e1.toChars());
836                         result = new ErrorExp();
837                         return;
838                     }
839                 }
840                 if (ad2)
841                 {
842                     s_r = search_function(ad2, Id.opBinaryRight);
843                     if (s_r && !s_r.isTemplateDeclaration())
844                     {
845                         e.e2.error("%s.opBinaryRight isn't a template", e.e2.toChars());
846                         result = new ErrorExp();
847                         return;
848                     }
849                     if (s_r && s_r == s) // Bugzilla 12778
850                         s_r = null;
851                 }
852                 // Set tiargs, the template argument list, which will be the operator string
853                 if (s || s_r)
854                 {
855                     id = Id.opBinary;
856                     id_r = Id.opBinaryRight;
857                     tiargs = opToArg(sc, e.op);
858                 }
859             }
860             if (s || s_r)
861             {
862                 /* Try:
863                  *      a.opfunc(b)
864                  *      b.opfunc_r(a)
865                  * and see which is better.
866                  */
867                 args1.setDim(1);
868                 args1[0] = e.e1;
869                 expandTuples(&args1);
870                 args2.setDim(1);
871                 args2[0] = e.e2;
872                 expandTuples(&args2);
873                 argsset = 1;
874                 Match m;
875                 m.last = MATCHnomatch;
876                 if (s)
877                 {
878                     functionResolve(&m, s, e.loc, sc, tiargs, e.e1.type, &args2);
879                     if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
880                     {
881                         result = new ErrorExp();
882                         return;
883                     }
884                 }
885                 FuncDeclaration lastf = m.lastf;
886                 if (s_r)
887                 {
888                     functionResolve(&m, s_r, e.loc, sc, tiargs, e.e2.type, &args1);
889                     if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
890                     {
891                         result = new ErrorExp();
892                         return;
893                     }
894                 }
895                 if (m.count > 1)
896                 {
897                     // Error, ambiguous
898                     e.error("overloads %s and %s both match argument list for %s", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
899                 }
900                 else if (m.last <= MATCHnomatch)
901                 {
902                     m.lastf = m.anyf;
903                     if (tiargs)
904                         goto L1;
905                 }
906                 if (e.op == TOKplusplus || e.op == TOKminusminus)
907                 {
908                     // Kludge because operator overloading regards e++ and e--
909                     // as unary, but it's implemented as a binary.
910                     // Rewrite (e1 ++ e2) as e1.postinc()
911                     // Rewrite (e1 -- e2) as e1.postdec()
912                     result = build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s);
913                 }
914                 else if (lastf && m.lastf == lastf || !s_r && m.last <= MATCHnomatch)
915                 {
916                     // Rewrite (e1 op e2) as e1.opfunc(e2)
917                     result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
918                 }
919                 else
920                 {
921                     // Rewrite (e1 op e2) as e2.opfunc_r(e1)
922                     result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
923                 }
924                 return;
925             }
926         L1:
927             version (all)
928             {
929                 // Retained for D1 compatibility
930                 if (isCommutative(e.op) && !tiargs)
931                 {
932                     s = null;
933                     s_r = null;
934                     if (ad1 && id_r)
935                     {
936                         s_r = search_function(ad1, id_r);
937                     }
938                     if (ad2 && id)
939                     {
940                         s = search_function(ad2, id);
941                         if (s && s == s_r) // Bugzilla 12778
942                             s = null;
943                     }
944                     if (s || s_r)
945                     {
946                         /* Try:
947                          *  a.opfunc_r(b)
948                          *  b.opfunc(a)
949                          * and see which is better.
950                          */
951                         if (!argsset)
952                         {
953                             args1.setDim(1);
954                             args1[0] = e.e1;
955                             expandTuples(&args1);
956                             args2.setDim(1);
957                             args2[0] = e.e2;
958                             expandTuples(&args2);
959                         }
960                         Match m;
961                         m.last = MATCHnomatch;
962                         if (s_r)
963                         {
964                             functionResolve(&m, s_r, e.loc, sc, tiargs, e.e1.type, &args2);
965                             if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
966                             {
967                                 result = new ErrorExp();
968                                 return;
969                             }
970                         }
971                         FuncDeclaration lastf = m.lastf;
972                         if (s)
973                         {
974                             functionResolve(&m, s, e.loc, sc, tiargs, e.e2.type, &args1);
975                             if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
976                             {
977                                 result = new ErrorExp();
978                                 return;
979                             }
980                         }
981                         if (m.count > 1)
982                         {
983                             // Error, ambiguous
984                             e.error("overloads %s and %s both match argument list for %s", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
985                         }
986                         else if (m.last <= MATCHnomatch)
987                         {
988                             m.lastf = m.anyf;
989                         }
990                         if (lastf && m.lastf == lastf || !s && m.last <= MATCHnomatch)
991                         {
992                             // Rewrite (e1 op e2) as e1.opfunc_r(e2)
993                             result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r);
994                         }
995                         else
996                         {
997                             // Rewrite (e1 op e2) as e2.opfunc(e1)
998                             result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s);
999                         }
1000                         // When reversing operands of comparison operators,
1001                         // need to reverse the sense of the op
1002                         switch (e.op)
1003                         {
1004                         case TOKlt:
1005                             e.op = TOKgt;
1006                             break;
1007                         case TOKgt:
1008                             e.op = TOKlt;
1009                             break;
1010                         case TOKle:
1011                             e.op = TOKge;
1012                             break;
1013                         case TOKge:
1014                             e.op = TOKle;
1015                             break;
1016                         default:
1017                             break;
1018                         }
1019                         return;
1020                     }
1021                 }
1022             }
1023             // Try alias this on first operand
1024             if (ad1 && ad1.aliasthis && !(e.op == TOKassign && ad2 && ad1 == ad2)) // See Bugzilla 2943
1025             {
1026                 /* Rewrite (e1 op e2) as:
1027                  *      (e1.aliasthis op e2)
1028                  */
1029                 if (e.att1 && e.e1.type == e.att1)
1030                     return;
1031                 //printf("att bin e1 = %s\n", this->e1->type->toChars());
1032                 Expression e1 = new DotIdExp(e.loc, e.e1, ad1.aliasthis.ident);
1033                 BinExp be = cast(BinExp)e.copy();
1034                 if (!be.att1 && e.e1.type.checkAliasThisRec())
1035                     be.att1 = e.e1.type;
1036                 be.e1 = e1;
1037                 result = be.trySemantic(sc);
1038                 return;
1039             }
1040             // Try alias this on second operand
1041             /* Bugzilla 2943: make sure that when we're copying the struct, we don't
1042              * just copy the alias this member
1043              */
1044             if (ad2 && ad2.aliasthis && !(e.op == TOKassign && ad1 && ad1 == ad2))
1045             {
1046                 /* Rewrite (e1 op e2) as:
1047                  *      (e1 op e2.aliasthis)
1048                  */
1049                 if (e.att2 && e.e2.type == e.att2)
1050                     return;
1051                 //printf("att bin e2 = %s\n", e->e2->type->toChars());
1052                 Expression e2 = new DotIdExp(e.loc, e.e2, ad2.aliasthis.ident);
1053                 BinExp be = cast(BinExp)e.copy();
1054                 if (!be.att2 && e.e2.type.checkAliasThisRec())
1055                     be.att2 = e.e2.type;
1056                 be.e2 = e2;
1057                 result = be.trySemantic(sc);
1058                 return;
1059             }
1060             return;
1061         }
1062 
1063         override void visit(EqualExp e)
1064         {
1065             //printf("EqualExp::op_overload() (%s)\n", e->toChars());
1066             Type t1 = e.e1.type.toBasetype();
1067             Type t2 = e.e2.type.toBasetype();
1068 
1069             /* Check for array equality.
1070              */
1071             if ((t1.ty == Tarray || t1.ty == Tsarray) &&
1072                 (t2.ty == Tarray || t2.ty == Tsarray))
1073             {
1074                 bool needsDirectEq()
1075                 {
1076                     Type t1n = t1.nextOf().toBasetype();
1077                     Type t2n = t2.nextOf().toBasetype();
1078                     if (((t1n.ty == Tchar || t1n.ty == Twchar || t1n.ty == Tdchar) &&
1079                          (t2n.ty == Tchar || t2n.ty == Twchar || t2n.ty == Tdchar)) ||
1080                         (t1n.ty == Tvoid || t2n.ty == Tvoid))
1081                     {
1082                         return false;
1083                     }
1084                     if (t1n.constOf() != t2n.constOf())
1085                         return true;
1086 
1087                     Type t = t1n;
1088                     while (t.toBasetype().nextOf())
1089                         t = t.nextOf().toBasetype();
1090                     if (t.ty != Tstruct)
1091                         return false;
1092 
1093                     semanticTypeInfo(sc, t);
1094                     return (cast(TypeStruct)t).sym.hasIdentityEquals;
1095                 }
1096 
1097                 if (needsDirectEq())
1098                 {
1099                     /* Rewrite as:
1100                      *      _ArrayEq(e1, e2)
1101                      */
1102                     Expression eeq = new IdentifierExp(e.loc, Id._ArrayEq);
1103                     result = new CallExp(e.loc, eeq, e.e1, e.e2);
1104                     if (e.op == TOKnotequal)
1105                         result = new NotExp(e.loc, result);
1106                     result = result.trySemantic(sc); // for better error message
1107                     if (!result)
1108                     {
1109                         e.error("cannot compare %s and %s", t1.toChars(), t2.toChars());
1110                         result = new ErrorExp();
1111                     }
1112                     return;
1113                 }
1114             }
1115 
1116             /* Check for class equality with null literal or typeof(null).
1117              */
1118             if (t1.ty == Tclass && e.e2.op == TOKnull ||
1119                 t2.ty == Tclass && e.e1.op == TOKnull)
1120             {
1121                 e.error("use '%s' instead of '%s' when comparing with null",
1122                     Token.toChars(e.op == TOKequal ? TOKidentity : TOKnotidentity),
1123                     Token.toChars(e.op));
1124                 result = new ErrorExp();
1125                 return;
1126             }
1127             if (t1.ty == Tclass && t2.ty == Tnull ||
1128                 t1.ty == Tnull && t2.ty == Tclass)
1129             {
1130                 // Comparing a class with typeof(null) should not call opEquals
1131                 return;
1132             }
1133 
1134             /* Check for class equality.
1135              */
1136             if (t1.ty == Tclass && t2.ty == Tclass)
1137             {
1138                 ClassDeclaration cd1 = t1.isClassHandle();
1139                 ClassDeclaration cd2 = t2.isClassHandle();
1140                 if (!(cd1.cpp || cd2.cpp))
1141                 {
1142                     /* Rewrite as:
1143                      *      .object.opEquals(e1, e2)
1144                      */
1145                     Expression e1x = e.e1;
1146                     Expression e2x = e.e2;
1147 
1148                     /* The explicit cast is necessary for interfaces,
1149                      * see Bugzilla 4088.
1150                      */
1151                     Type to = ClassDeclaration.object.getType();
1152                     if (cd1.isInterfaceDeclaration())
1153                         e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf());
1154                     if (cd2.isInterfaceDeclaration())
1155                         e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf());
1156 
1157                     result = new IdentifierExp(e.loc, Id.empty);
1158                     result = new DotIdExp(e.loc, result, Id.object);
1159                     result = new DotIdExp(e.loc, result, Id.eq);
1160                     result = new CallExp(e.loc, result, e1x, e2x);
1161                     if (e.op == TOKnotequal)
1162                         result = new NotExp(e.loc, result);
1163                     result = result.semantic(sc);
1164                     return;
1165                 }
1166             }
1167 
1168             result = compare_overload(e, sc, Id.eq);
1169             if (result)
1170             {
1171                 if (result.op == TOKcall && e.op == TOKnotequal)
1172                 {
1173                     result = new NotExp(result.loc, result);
1174                     result = result.semantic(sc);
1175                 }
1176                 return;
1177             }
1178 
1179             /* Check for pointer equality.
1180              */
1181             if (t1.ty == Tpointer || t2.ty == Tpointer)
1182             {
1183                 /* Rewrite:
1184                  *      ptr1 == ptr2
1185                  * as:
1186                  *      ptr1 is ptr2
1187                  *
1188                  * This is just a rewriting for deterministic AST representation
1189                  * as the backend input.
1190                  */
1191                 auto op2 = e.op == TOKequal ? TOKidentity : TOKnotidentity;
1192                 result = new IdentityExp(op2, e.loc, e.e1, e.e2);
1193                 result = result.semantic(sc);
1194                 return;
1195             }
1196 
1197             /* Check for struct equality without opEquals.
1198              */
1199             if (t1.ty == Tstruct && t2.ty == Tstruct)
1200             {
1201                 auto sd = (cast(TypeStruct)t1).sym;
1202                 if (sd != (cast(TypeStruct)t2).sym)
1203                     return;
1204 
1205                 import ddmd.clone : needOpEquals;
1206                 if (!needOpEquals(sd))
1207                 {
1208                     // Use bitwise equality.
1209                     auto op2 = e.op == TOKequal ? TOKidentity : TOKnotidentity;
1210                     result = new IdentityExp(op2, e.loc, e.e1, e.e2);
1211                     result = result.semantic(sc);
1212                     return;
1213                 }
1214 
1215                 /* Do memberwise equality.
1216                  * Rewrite:
1217                  *      e1 == e2
1218                  * as:
1219                  *      e1.tupleof == e2.tupleof
1220                  *
1221                  * If sd is a nested struct, and if it's nested in a class, it will
1222                  * also compare the parent class's equality. Otherwise, compares
1223                  * the identity of parent context through void*.
1224                  */
1225                 if (e.att1 && t1 == e.att1) return;
1226                 if (e.att2 && t2 == e.att2) return;
1227 
1228                 e = cast(EqualExp)e.copy();
1229                 if (!e.att1) e.att1 = t1;
1230                 if (!e.att2) e.att2 = t2;
1231                 e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof);
1232                 e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof);
1233                 result = e.semantic(sc);
1234 
1235                 /* Bugzilla 15292, if the rewrite result is same with the original,
1236                  * the equality is unresolvable because it has recursive definition.
1237                  */
1238                 if (result.op == e.op &&
1239                     (cast(EqualExp)result).e1.type.toBasetype() == t1)
1240                 {
1241                     e.error("cannot compare %s because its auto generated member-wise equality has recursive definition",
1242                         t1.toChars());
1243                     result = new ErrorExp();
1244                 }
1245                 return;
1246             }
1247 
1248             /* Check for tuple equality.
1249              */
1250             if (e.e1.op == TOKtuple && e.e2.op == TOKtuple)
1251             {
1252                 auto tup1 = cast(TupleExp)e.e1;
1253                 auto tup2 = cast(TupleExp)e.e2;
1254                 size_t dim = tup1.exps.dim;
1255                 if (dim != tup2.exps.dim)
1256                 {
1257                     e.error("mismatched tuple lengths, %d and %d",
1258                         cast(int)dim, cast(int)tup2.exps.dim);
1259                     result = new ErrorExp();
1260                     return;
1261                 }
1262 
1263                 if (dim == 0)
1264                 {
1265                     // zero-length tuple comparison should always return true or false.
1266                     result = new IntegerExp(e.loc, (e.op == TOKequal), Type.tbool);
1267                 }
1268                 else
1269                 {
1270                     for (size_t i = 0; i < dim; i++)
1271                     {
1272                         auto ex1 = (*tup1.exps)[i];
1273                         auto ex2 = (*tup2.exps)[i];
1274                         auto eeq = new EqualExp(e.op, e.loc, ex1, ex2);
1275                         eeq.att1 = e.att1;
1276                         eeq.att2 = e.att2;
1277 
1278                         if (!result)
1279                             result = eeq;
1280                         else if (e.op == TOKequal)
1281                             result = new AndAndExp(e.loc, result, eeq);
1282                         else
1283                             result = new OrOrExp(e.loc, result, eeq);
1284                     }
1285                     assert(result);
1286                 }
1287                 result = Expression.combine(Expression.combine(tup1.e0, tup2.e0), result);
1288                 result = result.semantic(sc);
1289                 return;
1290             }
1291         }
1292 
1293         override void visit(CmpExp e)
1294         {
1295             //printf("CmpExp::op_overload() (%s)\n", e->toChars());
1296             result = compare_overload(e, sc, Id.cmp);
1297         }
1298 
1299         /*********************************
1300          * Operator overloading for op=
1301          */
1302         override void visit(BinAssignExp e)
1303         {
1304             //printf("BinAssignExp::op_overload() (%s)\n", e->toChars());
1305             if (e.e1.op == TOKarray)
1306             {
1307                 ArrayExp ae = cast(ArrayExp)e.e1;
1308                 ae.e1 = ae.e1.semantic(sc);
1309                 ae.e1 = resolveProperties(sc, ae.e1);
1310                 Expression ae1old = ae.e1;
1311                 const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOKinterval);
1312                 IntervalExp ie = null;
1313                 if (maybeSlice && ae.arguments.dim)
1314                 {
1315                     assert((*ae.arguments)[0].op == TOKinterval);
1316                     ie = cast(IntervalExp)(*ae.arguments)[0];
1317                 }
1318                 while (true)
1319                 {
1320                     if (ae.e1.op == TOKerror)
1321                     {
1322                         result = ae.e1;
1323                         return;
1324                     }
1325                     Expression e0 = null;
1326                     Expression ae1save = ae.e1;
1327                     ae.lengthVar = null;
1328                     Type t1b = ae.e1.type.toBasetype();
1329                     AggregateDeclaration ad = isAggregate(t1b);
1330                     if (!ad)
1331                         break;
1332                     if (search_function(ad, Id.opIndexOpAssign))
1333                     {
1334                         // Deal with $
1335                         result = resolveOpDollar(sc, ae, &e0);
1336                         if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j)
1337                             goto Lfallback;
1338                         if (result.op == TOKerror)
1339                             return;
1340                         result = e.e2.semantic(sc);
1341                         if (result.op == TOKerror)
1342                             return;
1343                         e.e2 = result;
1344                         /* Rewrite a[arguments] op= e2 as:
1345                          *      a.opIndexOpAssign!(op)(e2, arguments)
1346                          */
1347                         Expressions* a = ae.arguments.copy();
1348                         a.insert(0, e.e2);
1349                         Objects* tiargs = opToArg(sc, e.op);
1350                         result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs);
1351                         result = new CallExp(e.loc, result, a);
1352                         if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2)
1353                             result = result.trySemantic(sc);
1354                         else
1355                             result = result.semantic(sc);
1356                         if (result)
1357                         {
1358                             result = Expression.combine(e0, result);
1359                             return;
1360                         }
1361                     }
1362                 Lfallback:
1363                     if (maybeSlice && search_function(ad, Id.opSliceOpAssign))
1364                     {
1365                         // Deal with $
1366                         result = resolveOpDollar(sc, ae, ie, &e0);
1367                         if (result.op == TOKerror)
1368                             return;
1369                         result = e.e2.semantic(sc);
1370                         if (result.op == TOKerror)
1371                             return;
1372                         e.e2 = result;
1373                         /* Rewrite (a[i..j] op= e2) as:
1374                          *      a.opSliceOpAssign!(op)(e2, i, j)
1375                          */
1376                         auto a = new Expressions();
1377                         a.push(e.e2);
1378                         if (ie)
1379                         {
1380                             a.push(ie.lwr);
1381                             a.push(ie.upr);
1382                         }
1383                         Objects* tiargs = opToArg(sc, e.op);
1384                         result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs);
1385                         result = new CallExp(e.loc, result, a);
1386                         result = result.semantic(sc);
1387                         result = Expression.combine(e0, result);
1388                         return;
1389                     }
1390                     // Didn't find it. Forward to aliasthis
1391                     if (ad.aliasthis && t1b != ae.att1)
1392                     {
1393                         if (!ae.att1 && t1b.checkAliasThisRec())
1394                             ae.att1 = t1b;
1395                         /* Rewrite (a[arguments] op= e2) as:
1396                          *      a.aliasthis[arguments] op= e2
1397                          */
1398                         ae.e1 = resolveAliasThis(sc, ae1save, true);
1399                         if (ae.e1)
1400                             continue;
1401                     }
1402                     break;
1403                 }
1404                 ae.e1 = ae1old; // recovery
1405                 ae.lengthVar = null;
1406             }
1407             result = e.binSemanticProp(sc);
1408             if (result)
1409                 return;
1410             // Don't attempt 'alias this' if an error occured
1411             if (e.e1.type.ty == Terror || e.e2.type.ty == Terror)
1412             {
1413                 result = new ErrorExp();
1414                 return;
1415             }
1416             Identifier id = opId(e);
1417             Expressions args2;
1418             AggregateDeclaration ad1 = isAggregate(e.e1.type);
1419             Dsymbol s = null;
1420             version (all)
1421             {
1422                 // the old D1 scheme
1423                 if (ad1 && id)
1424                 {
1425                     s = search_function(ad1, id);
1426                 }
1427             }
1428             Objects* tiargs = null;
1429             if (!s)
1430             {
1431                 /* Try the new D2 scheme, opOpAssign
1432                  */
1433                 if (ad1)
1434                 {
1435                     s = search_function(ad1, Id.opOpAssign);
1436                     if (s && !s.isTemplateDeclaration())
1437                     {
1438                         e.error("%s.opOpAssign isn't a template", e.e1.toChars());
1439                         result = new ErrorExp();
1440                         return;
1441                     }
1442                 }
1443                 // Set tiargs, the template argument list, which will be the operator string
1444                 if (s)
1445                 {
1446                     id = Id.opOpAssign;
1447                     tiargs = opToArg(sc, e.op);
1448                 }
1449             }
1450             if (s)
1451             {
1452                 /* Try:
1453                  *      a.opOpAssign(b)
1454                  */
1455                 args2.setDim(1);
1456                 args2[0] = e.e2;
1457                 expandTuples(&args2);
1458                 Match m;
1459                 m.last = MATCHnomatch;
1460                 if (s)
1461                 {
1462                     functionResolve(&m, s, e.loc, sc, tiargs, e.e1.type, &args2);
1463                     if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
1464                     {
1465                         result = new ErrorExp();
1466                         return;
1467                     }
1468                 }
1469                 if (m.count > 1)
1470                 {
1471                     // Error, ambiguous
1472                     e.error("overloads %s and %s both match argument list for %s", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
1473                 }
1474                 else if (m.last <= MATCHnomatch)
1475                 {
1476                     m.lastf = m.anyf;
1477                     if (tiargs)
1478                         goto L1;
1479                 }
1480                 // Rewrite (e1 op e2) as e1.opOpAssign(e2)
1481                 result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
1482                 return;
1483             }
1484         L1:
1485             // Try alias this on first operand
1486             if (ad1 && ad1.aliasthis)
1487             {
1488                 /* Rewrite (e1 op e2) as:
1489                  *      (e1.aliasthis op e2)
1490                  */
1491                 if (e.att1 && e.e1.type == e.att1)
1492                     return;
1493                 //printf("att %s e1 = %s\n", Token::toChars(e->op), e->e1->type->toChars());
1494                 Expression e1 = new DotIdExp(e.loc, e.e1, ad1.aliasthis.ident);
1495                 BinExp be = cast(BinExp)e.copy();
1496                 if (!be.att1 && e.e1.type.checkAliasThisRec())
1497                     be.att1 = e.e1.type;
1498                 be.e1 = e1;
1499                 result = be.trySemantic(sc);
1500                 return;
1501             }
1502             // Try alias this on second operand
1503             AggregateDeclaration ad2 = isAggregate(e.e2.type);
1504             if (ad2 && ad2.aliasthis)
1505             {
1506                 /* Rewrite (e1 op e2) as:
1507                  *      (e1 op e2.aliasthis)
1508                  */
1509                 if (e.att2 && e.e2.type == e.att2)
1510                     return;
1511                 //printf("att %s e2 = %s\n", Token::toChars(e->op), e->e2->type->toChars());
1512                 Expression e2 = new DotIdExp(e.loc, e.e2, ad2.aliasthis.ident);
1513                 BinExp be = cast(BinExp)e.copy();
1514                 if (!be.att2 && e.e2.type.checkAliasThisRec())
1515                     be.att2 = e.e2.type;
1516                 be.e2 = e2;
1517                 result = be.trySemantic(sc);
1518                 return;
1519             }
1520         }
1521     }
1522 
1523     scope OpOverload v = new OpOverload(sc);
1524     e.accept(v);
1525     return v.result;
1526 }
1527 
1528 /******************************************
1529  * Common code for overloading of EqualExp and CmpExp
1530  */
1531 extern (C++) Expression compare_overload(BinExp e, Scope* sc, Identifier id)
1532 {
1533     //printf("BinExp::compare_overload(id = %s) %s\n", id->toChars(), e->toChars());
1534     AggregateDeclaration ad1 = isAggregate(e.e1.type);
1535     AggregateDeclaration ad2 = isAggregate(e.e2.type);
1536     Dsymbol s = null;
1537     Dsymbol s_r = null;
1538     if (ad1)
1539     {
1540         s = search_function(ad1, id);
1541     }
1542     if (ad2)
1543     {
1544         s_r = search_function(ad2, id);
1545         if (s == s_r)
1546             s_r = null;
1547     }
1548     Objects* tiargs = null;
1549     if (s || s_r)
1550     {
1551         /* Try:
1552          *      a.opEquals(b)
1553          *      b.opEquals(a)
1554          * and see which is better.
1555          */
1556         Expressions args1;
1557         Expressions args2;
1558         args1.setDim(1);
1559         args1[0] = e.e1;
1560         expandTuples(&args1);
1561         args2.setDim(1);
1562         args2[0] = e.e2;
1563         expandTuples(&args2);
1564         Match m;
1565         m.last = MATCHnomatch;
1566         if (0 && s && s_r)
1567         {
1568             printf("s  : %s\n", s.toPrettyChars());
1569             printf("s_r: %s\n", s_r.toPrettyChars());
1570         }
1571         if (s)
1572         {
1573             functionResolve(&m, s, e.loc, sc, tiargs, e.e1.type, &args2);
1574             if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
1575                 return new ErrorExp();
1576         }
1577         FuncDeclaration lastf = m.lastf;
1578         int count = m.count;
1579         if (s_r)
1580         {
1581             functionResolve(&m, s_r, e.loc, sc, tiargs, e.e2.type, &args1);
1582             if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
1583                 return new ErrorExp();
1584         }
1585         if (m.count > 1)
1586         {
1587             /* The following if says "not ambiguous" if there's one match
1588              * from s and one from s_r, in which case we pick s.
1589              * This doesn't follow the spec, but is a workaround for the case
1590              * where opEquals was generated from templates and we cannot figure
1591              * out if both s and s_r came from the same declaration or not.
1592              * The test case is:
1593              *   import std.typecons;
1594              *   void main() {
1595              *    assert(tuple("has a", 2u) == tuple("has a", 1));
1596              *   }
1597              */
1598             if (!(m.lastf == lastf && m.count == 2 && count == 1))
1599             {
1600                 // Error, ambiguous
1601                 e.error("overloads %s and %s both match argument list for %s", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
1602             }
1603         }
1604         else if (m.last <= MATCHnomatch)
1605         {
1606             m.lastf = m.anyf;
1607         }
1608         Expression result;
1609         if (lastf && m.lastf == lastf || !s_r && m.last <= MATCHnomatch)
1610         {
1611             // Rewrite (e1 op e2) as e1.opfunc(e2)
1612             result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
1613         }
1614         else
1615         {
1616             // Rewrite (e1 op e2) as e2.opfunc_r(e1)
1617             result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
1618             // When reversing operands of comparison operators,
1619             // need to reverse the sense of the op
1620             switch (e.op)
1621             {
1622             case TOKlt:
1623                 e.op = TOKgt;
1624                 break;
1625             case TOKgt:
1626                 e.op = TOKlt;
1627                 break;
1628             case TOKle:
1629                 e.op = TOKge;
1630                 break;
1631             case TOKge:
1632                 e.op = TOKle;
1633                 break;
1634             default:
1635                 break;
1636             }
1637         }
1638         return result;
1639     }
1640     // Try alias this on first operand
1641     if (ad1 && ad1.aliasthis)
1642     {
1643         /* Rewrite (e1 op e2) as:
1644          *      (e1.aliasthis op e2)
1645          */
1646         if (e.att1 && e.e1.type == e.att1)
1647             return null;
1648         //printf("att cmp_bin e1 = %s\n", e->e1->type->toChars());
1649         Expression e1 = new DotIdExp(e.loc, e.e1, ad1.aliasthis.ident);
1650         BinExp be = cast(BinExp)e.copy();
1651         if (!be.att1 && e.e1.type.checkAliasThisRec())
1652             be.att1 = e.e1.type;
1653         be.e1 = e1;
1654         return be.trySemantic(sc);
1655     }
1656     // Try alias this on second operand
1657     if (ad2 && ad2.aliasthis)
1658     {
1659         /* Rewrite (e1 op e2) as:
1660          *      (e1 op e2.aliasthis)
1661          */
1662         if (e.att2 && e.e2.type == e.att2)
1663             return null;
1664         //printf("att cmp_bin e2 = %s\n", e->e2->type->toChars());
1665         Expression e2 = new DotIdExp(e.loc, e.e2, ad2.aliasthis.ident);
1666         BinExp be = cast(BinExp)e.copy();
1667         if (!be.att2 && e.e2.type.checkAliasThisRec())
1668             be.att2 = e.e2.type;
1669         be.e2 = e2;
1670         return be.trySemantic(sc);
1671     }
1672     return null;
1673 }
1674 
1675 /***********************************
1676  * Utility to build a function call out of this reference and argument.
1677  */
1678 extern (C++) Expression build_overload(Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d)
1679 {
1680     assert(d);
1681     Expression e;
1682     //printf("build_overload(id = '%s')\n", id->toChars());
1683     //earg->print();
1684     //earg->type->print();
1685     Declaration decl = d.isDeclaration();
1686     if (decl)
1687         e = new DotVarExp(loc, ethis, decl, false);
1688     else
1689         e = new DotIdExp(loc, ethis, d.ident);
1690     e = new CallExp(loc, e, earg);
1691     e = e.semantic(sc);
1692     return e;
1693 }
1694 
1695 /***************************************
1696  * Search for function funcid in aggregate ad.
1697  */
1698 extern (C++) Dsymbol search_function(ScopeDsymbol ad, Identifier funcid)
1699 {
1700     Dsymbol s = ad.search(Loc(), funcid);
1701     if (s)
1702     {
1703         //printf("search_function: s = '%s'\n", s->kind());
1704         Dsymbol s2 = s.toAlias();
1705         //printf("search_function: s2 = '%s'\n", s2->kind());
1706         FuncDeclaration fd = s2.isFuncDeclaration();
1707         if (fd && fd.type.ty == Tfunction)
1708             return fd;
1709         TemplateDeclaration td = s2.isTemplateDeclaration();
1710         if (td)
1711             return td;
1712     }
1713     return null;
1714 }
1715 
1716 extern (C++) bool inferAggregate(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
1717 {
1718     Identifier idapply = (fes.op == TOKforeach) ? Id.apply : Id.applyReverse;
1719     Identifier idfront = (fes.op == TOKforeach) ? Id.Ffront : Id.Fback;
1720     int sliced = 0;
1721     Type tab;
1722     Type att = null;
1723     Expression aggr = fes.aggr;
1724     AggregateDeclaration ad;
1725     while (1)
1726     {
1727         if (!aggr.type)
1728             goto Lerr;
1729         tab = aggr.type.toBasetype();
1730         switch (tab.ty)
1731         {
1732         case Tarray:
1733         case Tsarray:
1734         case Ttuple:
1735         case Taarray:
1736             break;
1737         case Tclass:
1738             ad = (cast(TypeClass)tab).sym;
1739             goto Laggr;
1740         case Tstruct:
1741             ad = (cast(TypeStruct)tab).sym;
1742             goto Laggr;
1743         Laggr:
1744             if (!sliced)
1745             {
1746                 sapply = search_function(ad, idapply);
1747                 if (sapply)
1748                 {
1749                     // opApply aggregate
1750                     break;
1751                 }
1752                 if (fes.aggr.op != TOKtype)
1753                 {
1754                     Expression rinit = new ArrayExp(aggr.loc, fes.aggr);
1755                     rinit = rinit.trySemantic(sc);
1756                     if (rinit) // if application of [] succeeded
1757                     {
1758                         aggr = rinit;
1759                         sliced = 1;
1760                         continue;
1761                     }
1762                 }
1763             }
1764             if (ad.search(Loc(), idfront))
1765             {
1766                 // range aggregate
1767                 break;
1768             }
1769             if (ad.aliasthis)
1770             {
1771                 if (att == tab)
1772                     goto Lerr;
1773                 if (!att && tab.checkAliasThisRec())
1774                     att = tab;
1775                 aggr = resolveAliasThis(sc, aggr);
1776                 continue;
1777             }
1778             goto Lerr;
1779         case Tdelegate:
1780             if (aggr.op == TOKdelegate)
1781             {
1782                 sapply = (cast(DelegateExp)aggr).func;
1783             }
1784             break;
1785         case Terror:
1786             break;
1787         default:
1788             goto Lerr;
1789         }
1790         break;
1791     }
1792     fes.aggr = aggr;
1793     return true;
1794 Lerr:
1795     return false;
1796 }
1797 
1798 /*****************************************
1799  * Given array of parameters and an aggregate type,
1800  * if any of the parameter types are missing, attempt to infer
1801  * them from the aggregate type.
1802  */
1803 extern (C++) bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
1804 {
1805     if (!fes.parameters || !fes.parameters.dim)
1806         return false;
1807     if (sapply) // prefer opApply
1808     {
1809         for (size_t u = 0; u < fes.parameters.dim; u++)
1810         {
1811             Parameter p = (*fes.parameters)[u];
1812             if (p.type)
1813             {
1814                 p.type = p.type.semantic(fes.loc, sc);
1815                 p.type = p.type.addStorageClass(p.storageClass);
1816             }
1817         }
1818         Expression ethis;
1819         Type tab = fes.aggr.type.toBasetype();
1820         if (tab.ty == Tclass || tab.ty == Tstruct)
1821             ethis = fes.aggr;
1822         else
1823         {
1824             assert(tab.ty == Tdelegate && fes.aggr.op == TOKdelegate);
1825             ethis = (cast(DelegateExp)fes.aggr).e1;
1826         }
1827         /* Look for like an
1828          *  int opApply(int delegate(ref Type [, ...]) dg);
1829          * overload
1830          */
1831         FuncDeclaration fd = sapply.isFuncDeclaration();
1832         if (fd)
1833         {
1834             sapply = inferApplyArgTypesX(ethis, fd, fes.parameters);
1835         }
1836         return sapply !is null;
1837     }
1838     /* Return if no parameters need types.
1839      */
1840     for (size_t u = 0; u < fes.parameters.dim; u++)
1841     {
1842         Parameter p = (*fes.parameters)[u];
1843         if (!p.type)
1844             break;
1845     }
1846     AggregateDeclaration ad;
1847     Parameter p = (*fes.parameters)[0];
1848     Type taggr = fes.aggr.type;
1849     assert(taggr);
1850     Type tab = taggr.toBasetype();
1851     switch (tab.ty)
1852     {
1853     case Tarray:
1854     case Tsarray:
1855     case Ttuple:
1856         if (fes.parameters.dim == 2)
1857         {
1858             if (!p.type)
1859             {
1860                 p.type = Type.tsize_t; // key type
1861                 p.type = p.type.addStorageClass(p.storageClass);
1862             }
1863             p = (*fes.parameters)[1];
1864         }
1865         if (!p.type && tab.ty != Ttuple)
1866         {
1867             p.type = tab.nextOf(); // value type
1868             p.type = p.type.addStorageClass(p.storageClass);
1869         }
1870         break;
1871     case Taarray:
1872         {
1873             TypeAArray taa = cast(TypeAArray)tab;
1874             if (fes.parameters.dim == 2)
1875             {
1876                 if (!p.type)
1877                 {
1878                     p.type = taa.index; // key type
1879                     p.type = p.type.addStorageClass(p.storageClass);
1880                     if (p.storageClass & STCref) // key must not be mutated via ref
1881                         p.type = p.type.addMod(MODconst);
1882                 }
1883                 p = (*fes.parameters)[1];
1884             }
1885             if (!p.type)
1886             {
1887                 p.type = taa.next; // value type
1888                 p.type = p.type.addStorageClass(p.storageClass);
1889             }
1890             break;
1891         }
1892     case Tclass:
1893         ad = (cast(TypeClass)tab).sym;
1894         goto Laggr;
1895     case Tstruct:
1896         ad = (cast(TypeStruct)tab).sym;
1897         goto Laggr;
1898     Laggr:
1899         if (fes.parameters.dim == 1)
1900         {
1901             if (!p.type)
1902             {
1903                 /* Look for a front() or back() overload
1904                  */
1905                 Identifier id = (fes.op == TOKforeach) ? Id.Ffront : Id.Fback;
1906                 Dsymbol s = ad.search(Loc(), id);
1907                 FuncDeclaration fd = s ? s.isFuncDeclaration() : null;
1908                 if (fd)
1909                 {
1910                     // Resolve inout qualifier of front type
1911                     p.type = fd.type.nextOf();
1912                     if (p.type)
1913                     {
1914                         p.type = p.type.substWildTo(tab.mod);
1915                         p.type = p.type.addStorageClass(p.storageClass);
1916                     }
1917                 }
1918                 else if (s && s.isTemplateDeclaration())
1919                 {
1920                 }
1921                 else if (s && s.isDeclaration())
1922                     p.type = (cast(Declaration)s).type;
1923                 else
1924                     break;
1925             }
1926             break;
1927         }
1928         break;
1929     case Tdelegate:
1930         {
1931             if (!inferApplyArgTypesY(cast(TypeFunction)tab.nextOf(), fes.parameters))
1932                 return false;
1933             break;
1934         }
1935     default:
1936         break; // ignore error, caught later
1937     }
1938     return true;
1939 }
1940 
1941 extern (C++) static Dsymbol inferApplyArgTypesX(Expression ethis, FuncDeclaration fstart, Parameters* parameters)
1942 {
1943     MOD mod = ethis.type.mod;
1944     MATCH match = MATCHnomatch;
1945     FuncDeclaration fd_best;
1946     FuncDeclaration fd_ambig;
1947     overloadApply(fstart, (Dsymbol s)
1948     {
1949         auto f = s.isFuncDeclaration();
1950         if (!f)
1951             return 0;
1952         auto tf = cast(TypeFunction)f.type;
1953         MATCH m = MATCHexact;
1954         if (f.isThis())
1955         {
1956             if (!MODimplicitConv(mod, tf.mod))
1957                 m = MATCHnomatch;
1958             else if (mod != tf.mod)
1959                 m = MATCHconst;
1960         }
1961         if (!inferApplyArgTypesY(tf, parameters, 1))
1962             m = MATCHnomatch;
1963         if (m > match)
1964         {
1965             fd_best = f;
1966             fd_ambig = null;
1967             match = m;
1968         }
1969         else if (m == match)
1970             fd_ambig = f;
1971         return 0;
1972     });
1973 
1974     if (fd_best)
1975     {
1976         inferApplyArgTypesY(cast(TypeFunction)fd_best.type, parameters);
1977         if (fd_ambig)
1978         {
1979             .error(ethis.loc, "%s.%s matches more than one declaration:\n%s:     %s\nand:\n%s:     %s",
1980                 ethis.toChars(), fstart.ident.toChars(),
1981                 fd_best.loc.toChars(), fd_best.type.toChars(),
1982                 fd_ambig.loc.toChars(), fd_ambig.type.toChars());
1983             fd_best = null;
1984         }
1985     }
1986     return fd_best;
1987 }
1988 
1989 /******************************
1990  * Infer parameters from type of function.
1991  * Returns:
1992  *      1 match for this function
1993  *      0 no match for this function
1994  */
1995 extern (C++) static int inferApplyArgTypesY(TypeFunction tf, Parameters* parameters, int flags = 0)
1996 {
1997     size_t nparams;
1998     Parameter p;
1999     if (Parameter.dim(tf.parameters) != 1)
2000         goto Lnomatch;
2001     p = Parameter.getNth(tf.parameters, 0);
2002     if (p.type.ty != Tdelegate)
2003         goto Lnomatch;
2004     tf = cast(TypeFunction)p.type.nextOf();
2005     assert(tf.ty == Tfunction);
2006     /* We now have tf, the type of the delegate. Match it against
2007      * the parameters, filling in missing parameter types.
2008      */
2009     nparams = Parameter.dim(tf.parameters);
2010     if (nparams == 0 || tf.varargs)
2011         goto Lnomatch; // not enough parameters
2012     if (parameters.dim != nparams)
2013         goto Lnomatch; // not enough parameters
2014     for (size_t u = 0; u < nparams; u++)
2015     {
2016         p = (*parameters)[u];
2017         Parameter param = Parameter.getNth(tf.parameters, u);
2018         if (p.type)
2019         {
2020             if (!p.type.equals(param.type))
2021                 goto Lnomatch;
2022         }
2023         else if (!flags)
2024         {
2025             p.type = param.type;
2026             p.type = p.type.addStorageClass(p.storageClass);
2027         }
2028     }
2029     return 1;
2030 Lnomatch:
2031     return 0;
2032 }