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  *            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.098@@@.
417                         // Deprecated in 2.088
418                         // Make an error in 2.098
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 = ErrorExp.get();
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 = ErrorExp.get();
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.098@@@.
688                         // Deprecated in 2.088
689                         // Make an error in 2.098
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.098@@@.
707                         // Deprecated in 2.088
708                         // Make an error in 2.098
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 = ErrorExp.get();
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 = ErrorExp.get();
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 = ErrorExp.get();
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 = ErrorExp.get();
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             /* Array equality is handled by expressionSemantic() potentially
921              * lowering to object.__equals(), which takes care of overloaded
922              * operators for the element types.
923              */
924             if ((t1.ty == Tarray || t1.ty == Tsarray) &&
925                 (t2.ty == Tarray || t2.ty == Tsarray))
926             {
927                 return;
928             }
929 
930             /* Check for class equality with null literal or typeof(null).
931              */
932             if (t1.ty == Tclass && e.e2.op == TOK.null_ ||
933                 t2.ty == Tclass && e.e1.op == TOK.null_)
934             {
935                 e.error("use `%s` instead of `%s` when comparing with `null`",
936                     Token.toChars(e.op == TOK.equal ? TOK.identity : TOK.notIdentity),
937                     Token.toChars(e.op));
938                 result = ErrorExp.get();
939                 return;
940             }
941             if (t1.ty == Tclass && t2.ty == Tnull ||
942                 t1.ty == Tnull && t2.ty == Tclass)
943             {
944                 // Comparing a class with typeof(null) should not call opEquals
945                 return;
946             }
947 
948             /* Check for class equality.
949              */
950             if (t1.ty == Tclass && t2.ty == Tclass)
951             {
952                 ClassDeclaration cd1 = t1.isClassHandle();
953                 ClassDeclaration cd2 = t2.isClassHandle();
954                 if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp))
955                 {
956                     /* Rewrite as:
957                      *      .object.opEquals(e1, e2)
958                      */
959                     Expression e1x = e.e1;
960                     Expression e2x = e.e2;
961 
962                     /* The explicit cast is necessary for interfaces
963                      * https://issues.dlang.org/show_bug.cgi?id=4088
964                      */
965                     Type to = ClassDeclaration.object.getType();
966                     if (cd1.isInterfaceDeclaration())
967                         e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf());
968                     if (cd2.isInterfaceDeclaration())
969                         e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf());
970 
971                     result = new IdentifierExp(e.loc, Id.empty);
972                     result = new DotIdExp(e.loc, result, Id.object);
973                     result = new DotIdExp(e.loc, result, Id.eq);
974                     result = new CallExp(e.loc, result, e1x, e2x);
975                     if (e.op == TOK.notEqual)
976                         result = new NotExp(e.loc, result);
977                     result = result.expressionSemantic(sc);
978                     return;
979                 }
980             }
981 
982             result = compare_overload(e, sc, Id.eq, null);
983             if (result)
984             {
985                 if (result.op == TOK.call && e.op == TOK.notEqual)
986                 {
987                     result = new NotExp(result.loc, result);
988                     result = result.expressionSemantic(sc);
989                 }
990                 return;
991             }
992 
993             /* Check for pointer equality.
994              */
995             if (t1.ty == Tpointer || t2.ty == Tpointer)
996             {
997                 /* Rewrite:
998                  *      ptr1 == ptr2
999                  * as:
1000                  *      ptr1 is ptr2
1001                  *
1002                  * This is just a rewriting for deterministic AST representation
1003                  * as the backend input.
1004                  */
1005                 auto op2 = e.op == TOK.equal ? TOK.identity : TOK.notIdentity;
1006                 result = new IdentityExp(op2, e.loc, e.e1, e.e2);
1007                 result = result.expressionSemantic(sc);
1008                 return;
1009             }
1010 
1011             /* Check for struct equality without opEquals.
1012              */
1013             if (t1.ty == Tstruct && t2.ty == Tstruct)
1014             {
1015                 auto sd = (cast(TypeStruct)t1).sym;
1016                 if (sd != (cast(TypeStruct)t2).sym)
1017                     return;
1018 
1019                 import dmd.clone : needOpEquals;
1020                 if (!global.params.fieldwise && !needOpEquals(sd))
1021                 {
1022                     // Use bitwise equality.
1023                     auto op2 = e.op == TOK.equal ? TOK.identity : TOK.notIdentity;
1024                     result = new IdentityExp(op2, e.loc, e.e1, e.e2);
1025                     result = result.expressionSemantic(sc);
1026                     return;
1027                 }
1028 
1029                 /* Do memberwise equality.
1030                  * https://dlang.org/spec/expression.html#equality_expressions
1031                  * Rewrite:
1032                  *      e1 == e2
1033                  * as:
1034                  *      e1.tupleof == e2.tupleof
1035                  *
1036                  * If sd is a nested struct, and if it's nested in a class, it will
1037                  * also compare the parent class's equality. Otherwise, compares
1038                  * the identity of parent context through void*.
1039                  */
1040                 if (e.att1 && t1 == e.att1) return;
1041                 if (e.att2 && t2 == e.att2) return;
1042 
1043                 e = cast(EqualExp)e.copy();
1044                 if (!e.att1) e.att1 = t1;
1045                 if (!e.att2) e.att2 = t2;
1046                 e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof);
1047                 e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof);
1048 
1049                 auto sc2 = sc.push();
1050                 sc2.flags = (sc2.flags & ~SCOPE.onlysafeaccess) | SCOPE.noaccesscheck;
1051                 result = e.expressionSemantic(sc2);
1052                 sc2.pop();
1053 
1054                 /* https://issues.dlang.org/show_bug.cgi?id=15292
1055                  * if the rewrite result is same with the original,
1056                  * the equality is unresolvable because it has recursive definition.
1057                  */
1058                 if (result.op == e.op &&
1059                     (cast(EqualExp)result).e1.type.toBasetype() == t1)
1060                 {
1061                     e.error("cannot compare `%s` because its auto generated member-wise equality has recursive definition",
1062                         t1.toChars());
1063                     result = ErrorExp.get();
1064                 }
1065                 return;
1066             }
1067 
1068             /* Check for tuple equality.
1069              */
1070             if (e.e1.op == TOK.tuple && e.e2.op == TOK.tuple)
1071             {
1072                 auto tup1 = cast(TupleExp)e.e1;
1073                 auto tup2 = cast(TupleExp)e.e2;
1074                 size_t dim = tup1.exps.dim;
1075                 if (dim != tup2.exps.dim)
1076                 {
1077                     e.error("mismatched tuple lengths, `%d` and `%d`",
1078                         cast(int)dim, cast(int)tup2.exps.dim);
1079                     result = ErrorExp.get();
1080                     return;
1081                 }
1082 
1083                 if (dim == 0)
1084                 {
1085                     // zero-length tuple comparison should always return true or false.
1086                     result = IntegerExp.createBool(e.op == TOK.equal);
1087                 }
1088                 else
1089                 {
1090                     for (size_t i = 0; i < dim; i++)
1091                     {
1092                         auto ex1 = (*tup1.exps)[i];
1093                         auto ex2 = (*tup2.exps)[i];
1094                         auto eeq = new EqualExp(e.op, e.loc, ex1, ex2);
1095                         eeq.att1 = e.att1;
1096                         eeq.att2 = e.att2;
1097 
1098                         if (!result)
1099                             result = eeq;
1100                         else if (e.op == TOK.equal)
1101                             result = new LogicalExp(e.loc, TOK.andAnd, result, eeq);
1102                         else
1103                             result = new LogicalExp(e.loc, TOK.orOr, result, eeq);
1104                     }
1105                     assert(result);
1106                 }
1107                 result = Expression.combine(tup1.e0, tup2.e0, result);
1108                 result = result.expressionSemantic(sc);
1109 
1110                 return;
1111             }
1112         }
1113 
1114         override void visit(CmpExp e)
1115         {
1116             //printf("CmpExp:: () (%s)\n", e.toChars());
1117             result = compare_overload(e, sc, Id.cmp, pop);
1118         }
1119 
1120         /*********************************
1121          * Operator overloading for op=
1122          */
1123         override void visit(BinAssignExp e)
1124         {
1125             //printf("BinAssignExp::op_overload() (%s)\n", e.toChars());
1126             if (e.e1.op == TOK.array)
1127             {
1128                 ArrayExp ae = cast(ArrayExp)e.e1;
1129                 ae.e1 = ae.e1.expressionSemantic(sc);
1130                 ae.e1 = resolveProperties(sc, ae.e1);
1131                 Expression ae1old = ae.e1;
1132                 const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOK.interval);
1133                 IntervalExp ie = null;
1134                 if (maybeSlice && ae.arguments.dim)
1135                 {
1136                     assert((*ae.arguments)[0].op == TOK.interval);
1137                     ie = cast(IntervalExp)(*ae.arguments)[0];
1138                 }
1139                 while (true)
1140                 {
1141                     if (ae.e1.op == TOK.error)
1142                     {
1143                         result = ae.e1;
1144                         return;
1145                     }
1146                     Expression e0 = null;
1147                     Expression ae1save = ae.e1;
1148                     ae.lengthVar = null;
1149                     Type t1b = ae.e1.type.toBasetype();
1150                     AggregateDeclaration ad = isAggregate(t1b);
1151                     if (!ad)
1152                         break;
1153                     if (search_function(ad, Id.opIndexOpAssign))
1154                     {
1155                         // Deal with $
1156                         result = resolveOpDollar(sc, ae, &e0);
1157                         if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j)
1158                             goto Lfallback;
1159                         if (result.op == TOK.error)
1160                             return;
1161                         result = e.e2.expressionSemantic(sc);
1162                         if (result.op == TOK.error)
1163                             return;
1164                         e.e2 = result;
1165                         /* Rewrite a[arguments] op= e2 as:
1166                          *      a.opIndexOpAssign!(op)(e2, arguments)
1167                          */
1168                         Expressions* a = ae.arguments.copy();
1169                         a.insert(0, e.e2);
1170                         Objects* tiargs = opToArg(sc, e.op);
1171                         result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs);
1172                         result = new CallExp(e.loc, result, a);
1173                         if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2)
1174                             result = result.trySemantic(sc);
1175                         else
1176                             result = result.expressionSemantic(sc);
1177                         if (result)
1178                         {
1179                             result = Expression.combine(e0, result);
1180                             return;
1181                         }
1182                     }
1183                 Lfallback:
1184                     if (maybeSlice && search_function(ad, Id.opSliceOpAssign))
1185                     {
1186                         // Deal with $
1187                         result = resolveOpDollar(sc, ae, ie, &e0);
1188                         if (result.op == TOK.error)
1189                             return;
1190                         result = e.e2.expressionSemantic(sc);
1191                         if (result.op == TOK.error)
1192                             return;
1193                         e.e2 = result;
1194                         /* Rewrite (a[i..j] op= e2) as:
1195                          *      a.opSliceOpAssign!(op)(e2, i, j)
1196                          */
1197                         auto a = new Expressions();
1198                         a.push(e.e2);
1199                         if (ie)
1200                         {
1201                             a.push(ie.lwr);
1202                             a.push(ie.upr);
1203                         }
1204                         Objects* tiargs = opToArg(sc, e.op);
1205                         result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs);
1206                         result = new CallExp(e.loc, result, a);
1207                         result = result.expressionSemantic(sc);
1208                         result = Expression.combine(e0, result);
1209                         return;
1210                     }
1211                     // Didn't find it. Forward to aliasthis
1212                     if (ad.aliasthis && t1b != ae.att1)
1213                     {
1214                         if (!ae.att1 && t1b.checkAliasThisRec())
1215                             ae.att1 = t1b;
1216                         /* Rewrite (a[arguments] op= e2) as:
1217                          *      a.aliasthis[arguments] op= e2
1218                          */
1219                         ae.e1 = resolveAliasThis(sc, ae1save, true);
1220                         if (ae.e1)
1221                             continue;
1222                     }
1223                     break;
1224                 }
1225                 ae.e1 = ae1old; // recovery
1226                 ae.lengthVar = null;
1227             }
1228             result = e.binSemanticProp(sc);
1229             if (result)
1230                 return;
1231             // Don't attempt 'alias this' if an error occurred
1232             if (e.e1.type.ty == Terror || e.e2.type.ty == Terror)
1233             {
1234                 result = ErrorExp.get();
1235                 return;
1236             }
1237             Identifier id = opId(e);
1238             Expressions args2;
1239             AggregateDeclaration ad1 = isAggregate(e.e1.type);
1240             Dsymbol s = null;
1241             Objects* tiargs = null;
1242             /* Try opOpAssign
1243              */
1244             if (ad1)
1245             {
1246                 s = search_function(ad1, Id.opOpAssign);
1247                 if (s && !s.isTemplateDeclaration())
1248                 {
1249                     e.error("`%s.opOpAssign` isn't a template", e.e1.toChars());
1250                     result = ErrorExp.get();
1251                     return;
1252                 }
1253             }
1254             // Set tiargs, the template argument list, which will be the operator string
1255             if (s)
1256             {
1257                 id = Id.opOpAssign;
1258                 tiargs = opToArg(sc, e.op);
1259             }
1260 
1261             // Try D1-style operator overload, deprecated
1262             if (!s && ad1 && id)
1263             {
1264                 s = search_function(ad1, id);
1265                 if (s)
1266                 {
1267                     // @@@DEPRECATED_2.098@@@.
1268                     // Deprecated in 2.088
1269                     // Make an error in 2.098
1270                     scope char[] op = Token.toString(e.op).dup;
1271                     op[$-1] = '\0'; // remove trailing `=`
1272                     e.deprecation("`%s` is deprecated.  Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr);
1273                 }
1274             }
1275 
1276             if (s)
1277             {
1278                 /* Try:
1279                  *      a.opOpAssign(b)
1280                  */
1281                 args2.setDim(1);
1282                 args2[0] = e.e2;
1283                 expandTuples(&args2);
1284                 MatchAccumulator m;
1285                 if (s)
1286                 {
1287                     functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
1288                     if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
1289                     {
1290                         result = ErrorExp.get();
1291                         return;
1292                     }
1293                 }
1294                 if (m.count > 1)
1295                 {
1296                     // Error, ambiguous
1297                     e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
1298                 }
1299                 else if (m.last <= MATCH.nomatch)
1300                 {
1301                     if (tiargs)
1302                         goto L1;
1303                     m.lastf = null;
1304                 }
1305                 // Rewrite (e1 op e2) as e1.opOpAssign(e2)
1306                 result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
1307                 return;
1308             }
1309         L1:
1310             result = checkAliasThisForLhs(ad1, sc, e);
1311             if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs
1312                 return;
1313 
1314             result = checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
1315         }
1316     }
1317 
1318     if (pop)
1319         *pop = e.op;
1320     scope OpOverload v = new OpOverload(sc, pop);
1321     e.accept(v);
1322     return v.result;
1323 }
1324 
1325 /******************************************
1326  * Common code for overloading of EqualExp and CmpExp
1327  */
1328 private Expression compare_overload(BinExp e, Scope* sc, Identifier id, TOK* pop)
1329 {
1330     //printf("BinExp::compare_overload(id = %s) %s\n", id.toChars(), e.toChars());
1331     AggregateDeclaration ad1 = isAggregate(e.e1.type);
1332     AggregateDeclaration ad2 = isAggregate(e.e2.type);
1333     Dsymbol s = null;
1334     Dsymbol s_r = null;
1335     if (ad1)
1336     {
1337         s = search_function(ad1, id);
1338     }
1339     if (ad2)
1340     {
1341         s_r = search_function(ad2, id);
1342         if (s == s_r)
1343             s_r = null;
1344     }
1345     Objects* tiargs = null;
1346     if (s || s_r)
1347     {
1348         /* Try:
1349          *      a.opEquals(b)
1350          *      b.opEquals(a)
1351          * and see which is better.
1352          */
1353         Expressions args1 = Expressions(1);
1354         args1[0] = e.e1;
1355         expandTuples(&args1);
1356         Expressions args2 = Expressions(1);
1357         args2[0] = e.e2;
1358         expandTuples(&args2);
1359         MatchAccumulator m;
1360         if (0 && s && s_r)
1361         {
1362             printf("s  : %s\n", s.toPrettyChars());
1363             printf("s_r: %s\n", s_r.toPrettyChars());
1364         }
1365         if (s)
1366         {
1367             functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
1368             if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
1369                 return ErrorExp.get();
1370         }
1371         FuncDeclaration lastf = m.lastf;
1372         int count = m.count;
1373         if (s_r)
1374         {
1375             functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, &args1);
1376             if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
1377                 return ErrorExp.get();
1378         }
1379         if (m.count > 1)
1380         {
1381             /* The following if says "not ambiguous" if there's one match
1382              * from s and one from s_r, in which case we pick s.
1383              * This doesn't follow the spec, but is a workaround for the case
1384              * where opEquals was generated from templates and we cannot figure
1385              * out if both s and s_r came from the same declaration or not.
1386              * The test case is:
1387              *   import std.typecons;
1388              *   void main() {
1389              *    assert(tuple("has a", 2u) == tuple("has a", 1));
1390              *   }
1391              */
1392             if (!(m.lastf == lastf && m.count == 2 && count == 1))
1393             {
1394                 // Error, ambiguous
1395                 e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
1396             }
1397         }
1398         else if (m.last <= MATCH.nomatch)
1399         {
1400             m.lastf = null;
1401         }
1402         Expression result;
1403         if (lastf && m.lastf == lastf || !s_r && m.last <= MATCH.nomatch)
1404         {
1405             // Rewrite (e1 op e2) as e1.opfunc(e2)
1406             result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
1407         }
1408         else
1409         {
1410             // Rewrite (e1 op e2) as e2.opfunc_r(e1)
1411             result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
1412             // When reversing operands of comparison operators,
1413             // need to reverse the sense of the op
1414             if (pop)
1415                 *pop = reverseRelation(e.op);
1416         }
1417         return result;
1418     }
1419     /*
1420      * https://issues.dlang.org/show_bug.cgi?id=16657
1421      * at this point, no matching opEquals was found for structs,
1422      * so we should not follow the alias this comparison code.
1423      */
1424     if ((e.op == TOK.equal || e.op == TOK.notEqual) && ad1 == ad2)
1425         return null;
1426     Expression result = checkAliasThisForLhs(ad1, sc, e);
1427     return result ? result : checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
1428 }
1429 
1430 /***********************************
1431  * Utility to build a function call out of this reference and argument.
1432  */
1433 Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d)
1434 {
1435     assert(d);
1436     Expression e;
1437     Declaration decl = d.isDeclaration();
1438     if (decl)
1439         e = new DotVarExp(loc, ethis, decl, false);
1440     else
1441         e = new DotIdExp(loc, ethis, d.ident);
1442     e = new CallExp(loc, e, earg);
1443     e = e.expressionSemantic(sc);
1444     return e;
1445 }
1446 
1447 /***************************************
1448  * Search for function funcid in aggregate ad.
1449  */
1450 Dsymbol search_function(ScopeDsymbol ad, Identifier funcid)
1451 {
1452     Dsymbol s = ad.search(Loc.initial, funcid);
1453     if (s)
1454     {
1455         //printf("search_function: s = '%s'\n", s.kind());
1456         Dsymbol s2 = s.toAlias();
1457         //printf("search_function: s2 = '%s'\n", s2.kind());
1458         FuncDeclaration fd = s2.isFuncDeclaration();
1459         if (fd && fd.type.ty == Tfunction)
1460             return fd;
1461         TemplateDeclaration td = s2.isTemplateDeclaration();
1462         if (td)
1463             return td;
1464     }
1465     return null;
1466 }
1467 
1468 /**************************************
1469  * Figure out what is being foreach'd over by looking at the ForeachAggregate.
1470  * Params:
1471  *      sc = context
1472  *      isForeach = true for foreach, false for foreach_reverse
1473  *      feaggr = ForeachAggregate
1474  *      sapply = set to function opApply/opApplyReverse, or delegate, or null.
1475  *               Overload resolution is not done.
1476  * Returns:
1477  *      true if successfully figured it out; feaggr updated with semantic analysis.
1478  *      false for failed, which is an error.
1479  */
1480 bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out Dsymbol sapply)
1481 {
1482     //printf("inferForeachAggregate(%s)\n", feaggr.toChars());
1483     bool sliced;
1484     Type att = null;
1485     auto aggr = feaggr;
1486     while (1)
1487     {
1488         aggr = aggr.expressionSemantic(sc);
1489         aggr = resolveProperties(sc, aggr);
1490         aggr = aggr.optimize(WANTvalue);
1491         if (!aggr.type || aggr.op == TOK.error)
1492             return false;
1493         Type tab = aggr.type.toBasetype();
1494         switch (tab.ty)
1495         {
1496         case Tarray:            // https://dlang.org/spec/statement.html#foreach_over_arrays
1497         case Tsarray:           // https://dlang.org/spec/statement.html#foreach_over_arrays
1498         case Ttuple:            // https://dlang.org/spec/statement.html#foreach_over_tuples
1499         case Taarray:           // https://dlang.org/spec/statement.html#foreach_over_associative_arrays
1500             break;
1501 
1502         case Tclass:
1503         case Tstruct:
1504         {
1505             AggregateDeclaration ad = (tab.ty == Tclass) ? (cast(TypeClass)tab).sym
1506                                                          : (cast(TypeStruct)tab).sym;
1507             if (!sliced)
1508             {
1509                 sapply = search_function(ad, isForeach ? Id.apply : Id.applyReverse);
1510                 if (sapply)
1511                 {
1512                     // https://dlang.org/spec/statement.html#foreach_over_struct_and_classes
1513                     // opApply aggregate
1514                     break;
1515                 }
1516                 if (feaggr.op != TOK.type)
1517                 {
1518                     /* See if rewriting `aggr` to `aggr[]` will work
1519                      */
1520                     Expression rinit = new ArrayExp(aggr.loc, feaggr);
1521                     rinit = rinit.trySemantic(sc);
1522                     if (rinit) // if it worked
1523                     {
1524                         aggr = rinit;
1525                         sliced = true;  // only try it once
1526                         continue;
1527                     }
1528                 }
1529             }
1530             if (ad.search(Loc.initial, isForeach ? Id.Ffront : Id.Fback))
1531             {
1532                 // https://dlang.org/spec/statement.html#foreach-with-ranges
1533                 // range aggregate
1534                 break;
1535             }
1536             if (ad.aliasthis)
1537             {
1538                 if (att == tab)         // error, circular alias this
1539                     return false;
1540                 if (!att && tab.checkAliasThisRec())
1541                     att = tab;
1542                 aggr = resolveAliasThis(sc, aggr);
1543                 continue;
1544             }
1545             return false;
1546         }
1547 
1548         case Tdelegate:        // https://dlang.org/spec/statement.html#foreach_over_delegates
1549             if (aggr.op == TOK.delegate_)
1550             {
1551                 sapply = (cast(DelegateExp)aggr).func;
1552             }
1553             break;
1554 
1555         case Terror:
1556             break;
1557 
1558         default:
1559             return false;
1560         }
1561         feaggr = aggr;
1562         return true;
1563     }
1564     assert(0);
1565 }
1566 
1567 /*****************************************
1568  * Given array of foreach parameters and an aggregate type,
1569  * find best opApply overload,
1570  * if any of the parameter types are missing, attempt to infer
1571  * them from the aggregate type.
1572  * Params:
1573  *      fes = the foreach statement
1574  *      sc = context
1575  *      sapply = null or opApply or delegate
1576  * Returns:
1577  *      false for errors
1578  */
1579 bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
1580 {
1581     if (!fes.parameters || !fes.parameters.dim)
1582         return false;
1583     if (sapply) // prefer opApply
1584     {
1585         foreach (Parameter p; *fes.parameters)
1586         {
1587             if (p.type)
1588             {
1589                 p.type = p.type.typeSemantic(fes.loc, sc);
1590                 p.type = p.type.addStorageClass(p.storageClass);
1591             }
1592         }
1593 
1594         // Determine ethis for sapply
1595         Expression ethis;
1596         Type tab = fes.aggr.type.toBasetype();
1597         if (tab.ty == Tclass || tab.ty == Tstruct)
1598             ethis = fes.aggr;
1599         else
1600         {
1601             assert(tab.ty == Tdelegate && fes.aggr.op == TOK.delegate_);
1602             ethis = (cast(DelegateExp)fes.aggr).e1;
1603         }
1604 
1605         /* Look for like an
1606          *  int opApply(int delegate(ref Type [, ...]) dg);
1607          * overload
1608          */
1609         if (FuncDeclaration fd = sapply.isFuncDeclaration())
1610         {
1611             auto fdapply = findBestOpApplyMatch(ethis, fd, fes.parameters);
1612             if (fdapply)
1613             {
1614                 // Fill in any missing types on foreach parameters[]
1615                 matchParamsToOpApply(cast(TypeFunction)fdapply.type, fes.parameters, true);
1616                 sapply = fdapply;
1617                 return true;
1618             }
1619             return false;
1620         }
1621         return sapply !is null;
1622     }
1623 
1624     Parameter p = (*fes.parameters)[0];
1625     Type taggr = fes.aggr.type;
1626     assert(taggr);
1627     Type tab = taggr.toBasetype();
1628     switch (tab.ty)
1629     {
1630     case Tarray:
1631     case Tsarray:
1632     case Ttuple:
1633         if (fes.parameters.dim == 2)
1634         {
1635             if (!p.type)
1636             {
1637                 p.type = Type.tsize_t; // key type
1638                 p.type = p.type.addStorageClass(p.storageClass);
1639             }
1640             p = (*fes.parameters)[1];
1641         }
1642         if (!p.type && tab.ty != Ttuple)
1643         {
1644             p.type = tab.nextOf(); // value type
1645             p.type = p.type.addStorageClass(p.storageClass);
1646         }
1647         break;
1648 
1649     case Taarray:
1650         {
1651             TypeAArray taa = cast(TypeAArray)tab;
1652             if (fes.parameters.dim == 2)
1653             {
1654                 if (!p.type)
1655                 {
1656                     p.type = taa.index; // key type
1657                     p.type = p.type.addStorageClass(p.storageClass);
1658                     if (p.storageClass & STC.ref_) // key must not be mutated via ref
1659                         p.type = p.type.addMod(MODFlags.const_);
1660                 }
1661                 p = (*fes.parameters)[1];
1662             }
1663             if (!p.type)
1664             {
1665                 p.type = taa.next; // value type
1666                 p.type = p.type.addStorageClass(p.storageClass);
1667             }
1668             break;
1669         }
1670 
1671     case Tclass:
1672     case Tstruct:
1673     {
1674         AggregateDeclaration ad = (tab.ty == Tclass) ? (cast(TypeClass)tab).sym
1675                                                      : (cast(TypeStruct)tab).sym;
1676         if (fes.parameters.dim == 1)
1677         {
1678             if (!p.type)
1679             {
1680                 /* Look for a front() or back() overload
1681                  */
1682                 Identifier id = (fes.op == TOK.foreach_) ? Id.Ffront : Id.Fback;
1683                 Dsymbol s = ad.search(Loc.initial, id);
1684                 FuncDeclaration fd = s ? s.isFuncDeclaration() : null;
1685                 if (fd)
1686                 {
1687                     // Resolve inout qualifier of front type
1688                     p.type = fd.type.nextOf();
1689                     if (p.type)
1690                     {
1691                         p.type = p.type.substWildTo(tab.mod);
1692                         p.type = p.type.addStorageClass(p.storageClass);
1693                     }
1694                 }
1695                 else if (s && s.isTemplateDeclaration())
1696                 {
1697                 }
1698                 else if (s && s.isDeclaration())
1699                     p.type = (cast(Declaration)s).type;
1700                 else
1701                     break;
1702             }
1703             break;
1704         }
1705         break;
1706     }
1707 
1708     case Tdelegate:
1709         if (!matchParamsToOpApply(cast(TypeFunction)tab.nextOf(), fes.parameters, true))
1710             return false;
1711         break;
1712 
1713     default:
1714         break; // ignore error, caught later
1715     }
1716     return true;
1717 }
1718 
1719 /*********************************************
1720  * Find best overload match on fstart given ethis and parameters[].
1721  * Params:
1722  *      ethis = expression to use for `this`
1723  *      fstart = opApply or foreach delegate
1724  *      parameters = ForeachTypeList (i.e. foreach parameters)
1725  * Returns:
1726  *      best match if there is one, null if error
1727  */
1728 private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration fstart, Parameters* parameters)
1729 {
1730     MOD mod = ethis.type.mod;
1731     MATCH match = MATCH.nomatch;
1732     FuncDeclaration fd_best;
1733     FuncDeclaration fd_ambig;
1734 
1735     overloadApply(fstart, (Dsymbol s)
1736     {
1737         auto f = s.isFuncDeclaration();
1738         if (!f)
1739             return 0;           // continue
1740         auto tf = cast(TypeFunction)f.type;
1741         MATCH m = MATCH.exact;
1742         if (f.isThis())
1743         {
1744             if (!MODimplicitConv(mod, tf.mod))
1745                 m = MATCH.nomatch;
1746             else if (mod != tf.mod)
1747                 m = MATCH.constant;
1748         }
1749         if (!matchParamsToOpApply(tf, parameters, false))
1750             m = MATCH.nomatch;
1751         if (m > match)
1752         {
1753             fd_best = f;
1754             fd_ambig = null;
1755             match = m;
1756         }
1757         else if (m == match && m > MATCH.nomatch)
1758         {
1759             assert(fd_best);
1760             /* Ignore covariant matches, as later on it can be redone
1761              * after the opApply delegate has its attributes inferred.
1762              */
1763             if (tf.covariant(fd_best.type) != 1 &&
1764                 fd_best.type.covariant(tf) != 1)
1765                 fd_ambig = f;                           // not covariant, so ambiguous
1766         }
1767         return 0;               // continue
1768     });
1769 
1770     if (fd_ambig)
1771     {
1772         .error(ethis.loc, "`%s.%s` matches more than one declaration:\n`%s`:     `%s`\nand:\n`%s`:     `%s`",
1773             ethis.toChars(), fstart.ident.toChars(),
1774             fd_best.loc.toChars(), fd_best.type.toChars(),
1775             fd_ambig.loc.toChars(), fd_ambig.type.toChars());
1776         return null;
1777     }
1778 
1779     return fd_best;
1780 }
1781 
1782 /******************************
1783  * Determine if foreach parameters match opApply parameters.
1784  * Infer missing foreach parameter types from type of opApply delegate.
1785  * Params:
1786  *      tf = type of opApply or delegate
1787  *      parameters = foreach parameters
1788  *      infer = infer missing parameter types
1789  * Returns:
1790  *      true for match for this function
1791  *      false for no match for this function
1792  */
1793 private bool matchParamsToOpApply(TypeFunction tf, Parameters* parameters, bool infer)
1794 {
1795     enum nomatch = false;
1796 
1797     /* opApply/delegate has exactly one parameter, and that parameter
1798      * is a delegate that looks like:
1799      *     int opApply(int delegate(ref Type [, ...]) dg);
1800      */
1801     if (tf.parameterList.length != 1)
1802         return nomatch;
1803 
1804     /* Get the type of opApply's dg parameter
1805      */
1806     Parameter p0 = tf.parameterList[0];
1807     if (p0.type.ty != Tdelegate)
1808         return nomatch;
1809     TypeFunction tdg = cast(TypeFunction)p0.type.nextOf();
1810     assert(tdg.ty == Tfunction);
1811 
1812     /* We now have tdg, the type of the delegate.
1813      * tdg's parameters must match that of the foreach arglist (i.e. parameters).
1814      * Fill in missing types in parameters.
1815      */
1816     const nparams = tdg.parameterList.length;
1817     if (nparams == 0 || nparams != parameters.dim || tdg.parameterList.varargs != VarArg.none)
1818         return nomatch; // parameter mismatch
1819 
1820     foreach (u, p; *parameters)
1821     {
1822         Parameter param = tdg.parameterList[u];
1823         if (p.type)
1824         {
1825             if (!p.type.equals(param.type))
1826                 return nomatch;
1827         }
1828         else if (infer)
1829         {
1830             p.type = param.type;
1831             p.type = p.type.addStorageClass(p.storageClass);
1832         }
1833     }
1834     return true;
1835 }
1836 
1837 /**
1838  * Reverse relational operator, eg >= becomes <=
1839  * Note this is not negation.
1840  * Params:
1841  *      op = comparison operator to reverse
1842  * Returns:
1843  *      reverse of op
1844  */
1845 private TOK reverseRelation(TOK op) pure
1846 {
1847     switch (op)
1848     {
1849         case TOK.greaterOrEqual:  op = TOK.lessOrEqual;    break;
1850         case TOK.greaterThan:     op = TOK.lessThan;       break;
1851         case TOK.lessOrEqual:     op = TOK.greaterOrEqual; break;
1852         case TOK.lessThan:        op = TOK.greaterThan;    break;
1853         default:                  break;
1854     }
1855     return op;
1856 }