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