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