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)
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                         e.error("`%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                     e.e1 = resolveAliasThis(sc, e.e1, true);
420                     if (e.e1)
421                         continue;
422                     break;
423                 }
424                 break;
425             }
426             return result;
427         }
428 
429         Expression visitArray(ArrayExp ae)
430         {
431             //printf("ArrayExp::op_overload() (%s)\n", ae.toChars());
432             ae.e1 = ae.e1.expressionSemantic(sc);
433             ae.e1 = resolveProperties(sc, ae.e1);
434             Expression ae1old = ae.e1;
435             const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval);
436             IntervalExp ie = null;
437             if (maybeSlice && ae.arguments.length)
438             {
439                 ie = (*ae.arguments)[0].isIntervalExp();
440             }
441             Expression result;
442             Type att = null; // first cyclic `alias this` type
443             while (true)
444             {
445                 if (ae.e1.op == EXP.error)
446                 {
447                     return ae.e1;
448                 }
449                 Expression e0 = null;
450                 Expression ae1save = ae.e1;
451                 ae.lengthVar = null;
452                 Type t1b = ae.e1.type.toBasetype();
453                 AggregateDeclaration ad = isAggregate(t1b);
454                 if (!ad)
455                 {
456                     // If the non-aggregate expression ae.e1 is indexable or sliceable,
457                     // convert it to the corresponding concrete expression.
458                     if (isIndexableNonAggregate(t1b) || ae.e1.op == EXP.type)
459                     {
460                         // Convert to SliceExp
461                         if (maybeSlice)
462                         {
463                             result = new SliceExp(ae.loc, ae.e1, ie);
464                             result = result.expressionSemantic(sc);
465                             return result;
466                         }
467                         // Convert to IndexExp
468                         if (ae.arguments.length == 1)
469                         {
470                             result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]);
471                             result = result.expressionSemantic(sc);
472                             return result;
473                         }
474                     }
475                     break;
476                 }
477                 if (search_function(ad, Id.index))
478                 {
479                     // Deal with $
480                     result = resolveOpDollar(sc, ae, &e0);
481                     if (!result) // a[i..j] might be: a.opSlice(i, j)
482                         goto Lfallback;
483                     if (result.op == EXP.error)
484                         return result;
485                     /* Rewrite e1[arguments] as:
486                      *      e1.opIndex(arguments)
487                      */
488                     Expressions* a = ae.arguments.copy();
489                     result = new DotIdExp(ae.loc, ae.e1, Id.index);
490                     result = new CallExp(ae.loc, result, a);
491                     if (maybeSlice) // a[] might be: a.opSlice()
492                         result = result.trySemantic(sc);
493                     else
494                         result = result.expressionSemantic(sc);
495                     if (result)
496                     {
497                         return Expression.combine(e0, result);
498                     }
499                 }
500             Lfallback:
501                 if (maybeSlice && ae.e1.op == EXP.type)
502                 {
503                     result = new SliceExp(ae.loc, ae.e1, ie);
504                     result = result.expressionSemantic(sc);
505                     result = Expression.combine(e0, result);
506                     return result;
507                 }
508                 if (maybeSlice && search_function(ad, Id.slice))
509                 {
510                     // Deal with $
511                     result = resolveOpDollar(sc, ae, ie, &e0);
512 
513                     if (result.op == EXP.error)
514                     {
515                         if (!e0 && !search_function(ad, Id.dollar)) {
516                             ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars());
517                         }
518                         return result;
519                     }
520                     /* Rewrite a[i..j] as:
521                      *      a.opSlice(i, j)
522                      */
523                     auto a = new Expressions();
524                     if (ie)
525                     {
526                         a.push(ie.lwr);
527                         a.push(ie.upr);
528                     }
529                     result = new DotIdExp(ae.loc, ae.e1, Id.slice);
530                     result = new CallExp(ae.loc, result, a);
531                     result = result.expressionSemantic(sc);
532                     result = Expression.combine(e0, result);
533                     return result;
534                 }
535                 // Didn't find it. Forward to aliasthis
536                 if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
537                 {
538                     //printf("att arr e1 = %s\n", this.e1.type.toChars());
539                     /* Rewrite op(a[arguments]) as:
540                      *      op(a.aliasthis[arguments])
541                      */
542                     ae.e1 = resolveAliasThis(sc, ae1save, true);
543                     if (ae.e1)
544                         continue;
545                 }
546                 break;
547             }
548             ae.e1 = ae1old; // recovery
549             ae.lengthVar = null;
550             return result;
551         }
552 
553         /***********************************************
554          * This is mostly the same as UnaryExp::op_overload(), but has
555          * a different rewrite.
556          */
557         Expression visitCast(CastExp e, Type att = null)
558         {
559             //printf("CastExp::op_overload() (%s)\n", e.toChars());
560             Expression result;
561             AggregateDeclaration ad = isAggregate(e.e1.type);
562             if (ad)
563             {
564                 Dsymbol fd = null;
565                 /* Rewrite as:
566                  *      e1.opCast!(T)()
567                  */
568                 fd = search_function(ad, Id._cast);
569                 if (fd)
570                 {
571                     version (all)
572                     {
573                         // Backwards compatibility with D1 if opCast is a function, not a template
574                         if (fd.isFuncDeclaration())
575                         {
576                             // Rewrite as:  e1.opCast()
577                             return build_overload(e.loc, sc, e.e1, null, fd);
578                         }
579                     }
580                     auto tiargs = new Objects();
581                     tiargs.push(e.to);
582                     result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
583                     result = new CallExp(e.loc, result);
584                     result = result.expressionSemantic(sc);
585                     return result;
586                 }
587                 // Didn't find it. Forward to aliasthis
588                 if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type))
589                 {
590                     /* Rewrite op(e1) as:
591                      *      op(e1.aliasthis)
592                      */
593                     if (auto e1 = resolveAliasThis(sc, e.e1, true))
594                     {
595                         result = e.copy();
596                         (cast(UnaExp)result).e1 = e1;
597                         result = visitCast(result.isCastExp(), att);
598                         return result;
599                     }
600                 }
601             }
602             return result;
603         }
604 
605         Expression visitBin(BinExp e)
606         {
607             //printf("BinExp::op_overload() (%s)\n", e.toChars());
608             Identifier id = opId(e);
609             Identifier id_r = opId_r(e);
610             Expressions args1;
611             Expressions args2;
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                         e.e1.error("`%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                         e.e2.error("`%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                             e.error("`%s` is obsolete.  Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
682                         else
683                             e.error("`%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                         e.error("`%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             if (s || s_r)
705             {
706                 /* Try:
707                  *      a.opfunc(b)
708                  *      b.opfunc_r(a)
709                  * and see which is better.
710                  */
711                 args1.setDim(1);
712                 args1[0] = e.e1;
713                 expandTuples(&args1);
714                 args2.setDim(1);
715                 args2[0] = e.e2;
716                 expandTuples(&args2);
717                 argsset = 1;
718                 MatchAccumulator m;
719                 if (s)
720                 {
721                     functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(&args2));
722                     if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
723                     {
724                         return ErrorExp.get();
725                     }
726                 }
727                 FuncDeclaration lastf = m.lastf;
728                 if (s_r)
729                 {
730                     functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(&args1));
731                     if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
732                     {
733                         return ErrorExp.get();
734                     }
735                 }
736                 if (m.count > 1)
737                 {
738                     // Error, ambiguous
739                     e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
740                 }
741                 else if (m.last == MATCH.nomatch)
742                 {
743                     if (tiargs)
744                         goto L1;
745                     m.lastf = null;
746                 }
747                 if (e.op == EXP.plusPlus || e.op == EXP.minusMinus)
748                 {
749                     // Kludge because operator overloading regards e++ and e--
750                     // as unary, but it's implemented as a binary.
751                     // Rewrite (e1 ++ e2) as e1.postinc()
752                     // Rewrite (e1 -- e2) as e1.postdec()
753                     return build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s);
754                 }
755                 else if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch)
756                 {
757                     // Rewrite (e1 op e2) as e1.opfunc(e2)
758                     return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
759                 }
760                 else
761                 {
762                     // Rewrite (e1 op e2) as e2.opfunc_r(e1)
763                     return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
764                 }
765             }
766         L1:
767             version (all)
768             {
769                 // Retained for D1 compatibility
770                 if (isCommutative(e.op) && !tiargs)
771                 {
772                     s = null;
773                     s_r = null;
774                     if (ad1 && id_r)
775                     {
776                         s_r = search_function(ad1, id_r);
777                     }
778                     if (ad2 && id)
779                     {
780                         s = search_function(ad2, id);
781                         if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778
782                             s = null;
783                     }
784                     if (s || s_r)
785                     {
786                         /* Try:
787                          *  a.opfunc_r(b)
788                          *  b.opfunc(a)
789                          * and see which is better.
790                          */
791                         if (!argsset)
792                         {
793                             args1.setDim(1);
794                             args1[0] = e.e1;
795                             expandTuples(&args1);
796                             args2.setDim(1);
797                             args2[0] = e.e2;
798                             expandTuples(&args2);
799                         }
800                         MatchAccumulator m;
801                         if (s_r)
802                         {
803                             functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, ArgumentList(&args2));
804                             if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
805                             {
806                                 return ErrorExp.get();
807                             }
808                         }
809                         FuncDeclaration lastf = m.lastf;
810                         if (s)
811                         {
812                             functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, ArgumentList(&args1));
813                             if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
814                             {
815                                 return ErrorExp.get();
816                             }
817                         }
818                         if (m.count > 1)
819                         {
820                             // Error, ambiguous
821                             e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
822                         }
823                         else if (m.last == MATCH.nomatch)
824                         {
825                             m.lastf = null;
826                         }
827 
828                         if (lastf && m.lastf == lastf || !s && m.last == MATCH.nomatch)
829                         {
830                             // Rewrite (e1 op e2) as e1.opfunc_r(e2)
831                             return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r);
832                         }
833                         else
834                         {
835                             // Rewrite (e1 op e2) as e2.opfunc(e1)
836                             Expression result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s);
837                             // When reversing operands of comparison operators,
838                             // need to reverse the sense of the op
839                             if (pop)
840                                 *pop = reverseRelation(e.op);
841                             return result;
842                         }
843                     }
844                 }
845             }
846 
847             Expression rewrittenLhs;
848             if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
849             {
850                 if (Expression result = checkAliasThisForLhs(ad1, sc, e))
851                 {
852                     /* https://issues.dlang.org/show_bug.cgi?id=19441
853                      *
854                      * alias this may not be used for partial assignment.
855                      * If a struct has a single member which is aliased this
856                      * directly or aliased to a ref getter function that returns
857                      * the mentioned member, then alias this may be
858                      * used since the object will be fully initialised.
859                      * If the struct is nested, the context pointer is considered
860                      * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis`
861                      * condition.
862                      */
863                     if (result.op != EXP.assign)
864                         return result;     // i.e: Rewrote `e1 = e2` -> `e1(e2)`
865 
866                     auto ae = result.isAssignExp();
867                     if (ae.e1.op != EXP.dotVariable)
868                         return result;     // i.e: Rewrote `e1 = e2` -> `e1() = e2`
869 
870                     auto dve = ae.e1.isDotVarExp();
871                     if (auto ad = dve.var.isMember2())
872                     {
873                         // i.e: Rewrote `e1 = e2` -> `e1.some.var = e2`
874                         // Ensure that `var` is the only field member in `ad`
875                         if (ad.fields.length == 1 || (ad.fields.length == 2 && ad.vthis))
876                         {
877                             if (dve.var == ad.aliasthis.sym)
878                                 return result;
879                         }
880                     }
881                     rewrittenLhs = ae.e1;
882                 }
883             }
884             if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
885             {
886                 if (Expression result = checkAliasThisForRhs(ad2, sc, e))
887                     return result;
888             }
889             if (rewrittenLhs)
890             {
891                 e.error("cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`",
892                         e.e1.toChars(), ad1.toChars(), rewrittenLhs.toChars());
893                 return ErrorExp.get();
894             }
895             return null;
896         }
897 
898         Expression visitEqual(EqualExp e)
899         {
900             //printf("EqualExp::op_overload() (%s)\n", e.toChars());
901             Type t1 = e.e1.type.toBasetype();
902             Type t2 = e.e2.type.toBasetype();
903 
904             /* Array equality is handled by expressionSemantic() potentially
905              * lowering to object.__equals(), which takes care of overloaded
906              * operators for the element types.
907              */
908             if ((t1.ty == Tarray || t1.ty == Tsarray) &&
909                 (t2.ty == Tarray || t2.ty == Tsarray))
910             {
911                 return null;
912             }
913 
914             /* Check for class equality with null literal or typeof(null).
915              */
916             if (t1.ty == Tclass && e.e2.op == EXP.null_ ||
917                 t2.ty == Tclass && e.e1.op == EXP.null_)
918             {
919                 e.error("use `%s` instead of `%s` when comparing with `null`",
920                     EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr,
921                     EXPtoString(e.op).ptr);
922                 return ErrorExp.get();
923             }
924             if (t1.ty == Tclass && t2.ty == Tnull ||
925                 t1.ty == Tnull && t2.ty == Tclass)
926             {
927                 // Comparing a class with typeof(null) should not call opEquals
928                 return null;
929             }
930 
931             /* Check for class equality.
932              */
933             if (t1.ty == Tclass && t2.ty == Tclass)
934             {
935                 ClassDeclaration cd1 = t1.isClassHandle();
936                 ClassDeclaration cd2 = t2.isClassHandle();
937                 if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp))
938                 {
939                     /* Rewrite as:
940                      *      .object.opEquals(e1, e2)
941                      */
942                     if (!ClassDeclaration.object)
943                     {
944                         e.error("cannot compare classes for equality because `object.Object` was not declared");
945                         return null;
946                     }
947 
948                     Expression e1x = e.e1;
949                     Expression e2x = e.e2;
950 
951                     /* The explicit cast is necessary for interfaces
952                      * https://issues.dlang.org/show_bug.cgi?id=4088
953                      */
954                     Type to = ClassDeclaration.object.getType();
955                     if (cd1.isInterfaceDeclaration())
956                         e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf());
957                     if (cd2.isInterfaceDeclaration())
958                         e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf());
959 
960                     Expression result = new IdentifierExp(e.loc, Id.empty);
961                     result = new DotIdExp(e.loc, result, Id.object);
962                     result = new DotIdExp(e.loc, result, Id.eq);
963                     result = new CallExp(e.loc, result, e1x, e2x);
964                     if (e.op == EXP.notEqual)
965                         result = new NotExp(e.loc, result);
966                     result = result.expressionSemantic(sc);
967                     return result;
968                 }
969             }
970 
971             if (Expression result = compare_overload(e, sc, Id.eq, null))
972             {
973                 if (lastComma(result).op == EXP.call && e.op == EXP.notEqual)
974                 {
975                     result = new NotExp(result.loc, result);
976                     result = result.expressionSemantic(sc);
977                 }
978                 return result;
979             }
980 
981             /* Check for pointer equality.
982              */
983             if (t1.ty == Tpointer || t2.ty == Tpointer)
984             {
985                 /* Rewrite:
986                  *      ptr1 == ptr2
987                  * as:
988                  *      ptr1 is ptr2
989                  *
990                  * This is just a rewriting for deterministic AST representation
991                  * as the backend input.
992                  */
993                 auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity;
994                 Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2);
995                 return r.expressionSemantic(sc);
996             }
997 
998             /* Check for struct equality without opEquals.
999              */
1000             if (t1.ty == Tstruct && t2.ty == Tstruct)
1001             {
1002                 auto sd = t1.isTypeStruct().sym;
1003                 if (sd != t2.isTypeStruct().sym)
1004                     return null;
1005 
1006                 import dmd.clone : needOpEquals;
1007                 if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd))
1008                 {
1009                     // Use bitwise equality.
1010                     auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity;
1011                     Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2);
1012                     return r.expressionSemantic(sc);
1013                 }
1014 
1015                 /* Do memberwise equality.
1016                  * https://dlang.org/spec/expression.html#equality_expressions
1017                  * Rewrite:
1018                  *      e1 == e2
1019                  * as:
1020                  *      e1.tupleof == e2.tupleof
1021                  *
1022                  * If sd is a nested struct, and if it's nested in a class, it will
1023                  * also compare the parent class's equality. Otherwise, compares
1024                  * the identity of parent context through void*.
1025                  */
1026                 e = e.copy().isEqualExp();
1027                 e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof);
1028                 e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof);
1029 
1030                 auto sc2 = sc.push();
1031                 sc2.flags |= SCOPE.noaccesscheck;
1032                 Expression r = e.expressionSemantic(sc2);
1033                 sc2.pop();
1034                 return r;
1035             }
1036 
1037             /* Check for tuple equality.
1038              */
1039             if (e.e1.op == EXP.tuple && e.e2.op == EXP.tuple)
1040             {
1041                 auto tup1 = e.e1.isTupleExp();
1042                 auto tup2 = e.e2.isTupleExp();
1043                 size_t dim = tup1.exps.length;
1044                 if (dim != tup2.exps.length)
1045                 {
1046                     e.error("mismatched tuple lengths, `%d` and `%d`",
1047                         cast(int)dim, cast(int)tup2.exps.length);
1048                     return ErrorExp.get();
1049                 }
1050 
1051                 Expression result;
1052                 if (dim == 0)
1053                 {
1054                     // zero-length tuple comparison should always return true or false.
1055                     result = IntegerExp.createBool(e.op == EXP.equal);
1056                 }
1057                 else
1058                 {
1059                     for (size_t i = 0; i < dim; i++)
1060                     {
1061                         auto ex1 = (*tup1.exps)[i];
1062                         auto ex2 = (*tup2.exps)[i];
1063                         auto eeq = new EqualExp(e.op, e.loc, ex1, ex2);
1064 
1065                         if (!result)
1066                             result = eeq;
1067                         else if (e.op == EXP.equal)
1068                             result = new LogicalExp(e.loc, EXP.andAnd, result, eeq);
1069                         else
1070                             result = new LogicalExp(e.loc, EXP.orOr, result, eeq);
1071                     }
1072                     assert(result);
1073                 }
1074                 result = Expression.combine(tup1.e0, tup2.e0, result);
1075                 result = result.expressionSemantic(sc);
1076 
1077                 return result;
1078             }
1079             return null;
1080         }
1081 
1082         Expression visitCmp(CmpExp e)
1083         {
1084             //printf("CmpExp:: () (%s)\n", e.toChars());
1085             return compare_overload(e, sc, Id.cmp, pop);
1086         }
1087 
1088         /*********************************
1089          * Operator overloading for op=
1090          */
1091         Expression visitBinAssign(BinAssignExp e)
1092         {
1093             //printf("BinAssignExp::op_overload() (%s)\n", e.toChars());
1094             if (auto ae = e.e1.isArrayExp())
1095             {
1096                 ae.e1 = ae.e1.expressionSemantic(sc);
1097                 ae.e1 = resolveProperties(sc, ae.e1);
1098                 Expression ae1old = ae.e1;
1099                 const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval);
1100                 IntervalExp ie = null;
1101                 if (maybeSlice && ae.arguments.length)
1102                 {
1103                     ie = (*ae.arguments)[0].isIntervalExp();
1104                 }
1105                 Type att = null; // first cyclic `alias this` type
1106                 while (true)
1107                 {
1108                     if (ae.e1.op == EXP.error)
1109                     {
1110                         return ae.e1;
1111                     }
1112                     Expression e0 = null;
1113                     Expression ae1save = ae.e1;
1114                     ae.lengthVar = null;
1115                     Type t1b = ae.e1.type.toBasetype();
1116                     AggregateDeclaration ad = isAggregate(t1b);
1117                     if (!ad)
1118                         break;
1119                     if (search_function(ad, Id.opIndexOpAssign))
1120                     {
1121                         // Deal with $
1122                         Expression result = resolveOpDollar(sc, ae, &e0);
1123                         if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j)
1124                             goto Lfallback;
1125                         if (result.op == EXP.error)
1126                             return result;
1127                         result = e.e2.expressionSemantic(sc);
1128                         if (result.op == EXP.error)
1129                             return result;
1130                         e.e2 = result;
1131                         /* Rewrite a[arguments] op= e2 as:
1132                          *      a.opIndexOpAssign!(op)(e2, arguments)
1133                          */
1134                         Expressions* a = ae.arguments.copy();
1135                         a.insert(0, e.e2);
1136                         Objects* tiargs = opToArg(sc, e.op);
1137                         result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs);
1138                         result = new CallExp(e.loc, result, a);
1139                         if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2)
1140                             result = result.trySemantic(sc);
1141                         else
1142                             result = result.expressionSemantic(sc);
1143                         if (result)
1144                         {
1145                             return Expression.combine(e0, result);
1146                         }
1147                     }
1148                 Lfallback:
1149                     if (maybeSlice && search_function(ad, Id.opSliceOpAssign))
1150                     {
1151                         // Deal with $
1152                         Expression result = resolveOpDollar(sc, ae, ie, &e0);
1153                         if (result.op == EXP.error)
1154                             return result;
1155                         result = e.e2.expressionSemantic(sc);
1156                         if (result.op == EXP.error)
1157                             return result;
1158                         e.e2 = result;
1159                         /* Rewrite (a[i..j] op= e2) as:
1160                          *      a.opSliceOpAssign!(op)(e2, i, j)
1161                          */
1162                         auto a = new Expressions();
1163                         a.push(e.e2);
1164                         if (ie)
1165                         {
1166                             a.push(ie.lwr);
1167                             a.push(ie.upr);
1168                         }
1169                         Objects* tiargs = opToArg(sc, e.op);
1170                         result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs);
1171                         result = new CallExp(e.loc, result, a);
1172                         result = result.expressionSemantic(sc);
1173                         result = Expression.combine(e0, result);
1174                         return result;
1175                     }
1176                     // Didn't find it. Forward to aliasthis
1177                     if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
1178                     {
1179                         /* Rewrite (a[arguments] op= e2) as:
1180                          *      a.aliasthis[arguments] op= e2
1181                          */
1182                         ae.e1 = resolveAliasThis(sc, ae1save, true);
1183                         if (ae.e1)
1184                             continue;
1185                     }
1186                     break;
1187                 }
1188                 ae.e1 = ae1old; // recovery
1189                 ae.lengthVar = null;
1190             }
1191             Expression result = e.binSemanticProp(sc);
1192             if (result)
1193                 return result;
1194             // Don't attempt 'alias this' if an error occurred
1195             if (e.e1.type.ty == Terror || e.e2.type.ty == Terror)
1196             {
1197                 return ErrorExp.get();
1198             }
1199             Identifier id = opId(e);
1200             Expressions args2;
1201             AggregateDeclaration ad1 = isAggregate(e.e1.type);
1202             Dsymbol s = null;
1203             Objects* tiargs = null;
1204             /* Try opOpAssign
1205              */
1206             if (ad1)
1207             {
1208                 s = search_function(ad1, Id.opOpAssign);
1209                 if (s && !s.isTemplateDeclaration())
1210                 {
1211                     e.error("`%s.opOpAssign` isn't a template", e.e1.toChars());
1212                     return ErrorExp.get();
1213                 }
1214             }
1215             // Set tiargs, the template argument list, which will be the operator string
1216             if (s)
1217             {
1218                 id = Id.opOpAssign;
1219                 tiargs = opToArg(sc, e.op);
1220             }
1221 
1222             // Try D1-style operator overload, deprecated
1223             if (!s && ad1 && id)
1224             {
1225                 s = search_function(ad1, id);
1226                 if (s)
1227                 {
1228                     // @@@DEPRECATED_2.110@@@.
1229                     // Deprecated in 2.088, made an error in 2.100
1230                     scope char[] op = EXPtoString(e.op).dup;
1231                     op[$-1] = '\0'; // remove trailing `=`
1232                     e.error("`%s` is obsolete.  Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr);
1233                     return ErrorExp.get();
1234                 }
1235             }
1236 
1237             if (s)
1238             {
1239                 /* Try:
1240                  *      a.opOpAssign(b)
1241                  */
1242                 args2.setDim(1);
1243                 args2[0] = e.e2;
1244                 expandTuples(&args2);
1245                 MatchAccumulator m;
1246                 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(&args2));
1247                 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
1248                 {
1249                     return ErrorExp.get();
1250                 }
1251                 if (m.count > 1)
1252                 {
1253                     // Error, ambiguous
1254                     e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
1255                 }
1256                 else if (m.last == MATCH.nomatch)
1257                 {
1258                     if (tiargs)
1259                         goto L1;
1260                     m.lastf = null;
1261                 }
1262                 // Rewrite (e1 op e2) as e1.opOpAssign(e2)
1263                 return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
1264             }
1265         L1:
1266             result = checkAliasThisForLhs(ad1, sc, e);
1267             if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs
1268                 return result;
1269 
1270             return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
1271         }
1272 
1273     if (pop)
1274         *pop = e.op;
1275 
1276     switch (e.op)
1277     {
1278         case EXP.cast_         : return visitCast(e.isCastExp());
1279         case EXP.array         : return visitArray(e.isArrayExp());
1280 
1281         case EXP.notEqual      :
1282         case EXP.equal         : return visitEqual(e.isEqualExp());
1283 
1284         case EXP.lessOrEqual   :
1285         case EXP.greaterThan   :
1286         case EXP.greaterOrEqual:
1287         case EXP.lessThan      : return visitCmp(cast(CmpExp)e);
1288 
1289         default:
1290             if (auto ex = e.isBinAssignExp()) return visitBinAssign(ex);
1291             if (auto ex = e.isBinExp())       return visitBin(ex);
1292             if (auto ex = e.isUnaExp())       return visitUna(ex);
1293             return visit(e);
1294     }
1295 }
1296 
1297 /******************************************
1298  * Common code for overloading of EqualExp and CmpExp
1299  */
1300 private Expression compare_overload(BinExp e, Scope* sc, Identifier id, EXP* pop)
1301 {
1302     //printf("BinExp::compare_overload(id = %s) %s\n", id.toChars(), e.toChars());
1303     AggregateDeclaration ad1 = isAggregate(e.e1.type);
1304     AggregateDeclaration ad2 = isAggregate(e.e2.type);
1305     Dsymbol s = null;
1306     Dsymbol s_r = null;
1307     if (ad1)
1308     {
1309         s = search_function(ad1, id);
1310     }
1311     if (ad2)
1312     {
1313         s_r = search_function(ad2, id);
1314         if (s == s_r)
1315             s_r = null;
1316     }
1317     Objects* tiargs = null;
1318     if (s || s_r)
1319     {
1320         /* Try:
1321          *      a.opEquals(b)
1322          *      b.opEquals(a)
1323          * and see which is better.
1324          */
1325         Expressions args1 = Expressions(1);
1326         args1[0] = e.e1;
1327         expandTuples(&args1);
1328         Expressions args2 = Expressions(1);
1329         args2[0] = e.e2;
1330         expandTuples(&args2);
1331         MatchAccumulator m;
1332         if (0 && s && s_r)
1333         {
1334             printf("s  : %s\n", s.toPrettyChars());
1335             printf("s_r: %s\n", s_r.toPrettyChars());
1336         }
1337         if (s)
1338         {
1339             functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(&args2));
1340             if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
1341                 return ErrorExp.get();
1342         }
1343         FuncDeclaration lastf = m.lastf;
1344         int count = m.count;
1345         if (s_r)
1346         {
1347             functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(&args1));
1348             if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
1349                 return ErrorExp.get();
1350         }
1351         if (m.count > 1)
1352         {
1353             /* The following if says "not ambiguous" if there's one match
1354              * from s and one from s_r, in which case we pick s.
1355              * This doesn't follow the spec, but is a workaround for the case
1356              * where opEquals was generated from templates and we cannot figure
1357              * out if both s and s_r came from the same declaration or not.
1358              * The test case is:
1359              *   import std.typecons;
1360              *   void main() {
1361              *    assert(tuple("has a", 2u) == tuple("has a", 1));
1362              *   }
1363              */
1364             if (!(m.lastf == lastf && m.count == 2 && count == 1))
1365             {
1366                 // Error, ambiguous
1367                 e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
1368             }
1369         }
1370         else if (m.last == MATCH.nomatch)
1371         {
1372             m.lastf = null;
1373         }
1374         Expression result;
1375         if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch)
1376         {
1377             // Rewrite (e1 op e2) as e1.opfunc(e2)
1378             result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
1379         }
1380         else
1381         {
1382             // Rewrite (e1 op e2) as e2.opfunc_r(e1)
1383             result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
1384             // When reversing operands of comparison operators,
1385             // need to reverse the sense of the op
1386             if (pop)
1387                 *pop = reverseRelation(e.op);
1388         }
1389         return result;
1390     }
1391     /*
1392      * https://issues.dlang.org/show_bug.cgi?id=16657
1393      * at this point, no matching opEquals was found for structs,
1394      * so we should not follow the alias this comparison code.
1395      */
1396     if ((e.op == EXP.equal || e.op == EXP.notEqual) && ad1 == ad2)
1397         return null;
1398     Expression result = checkAliasThisForLhs(ad1, sc, e);
1399     return result ? result : checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
1400 }
1401 
1402 /***********************************
1403  * Utility to build a function call out of this reference and argument.
1404  */
1405 Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d)
1406 {
1407     assert(d);
1408     Expression e;
1409     Declaration decl = d.isDeclaration();
1410     if (decl)
1411         e = new DotVarExp(loc, ethis, decl, false);
1412     else
1413         e = new DotIdExp(loc, ethis, d.ident);
1414     e = new CallExp(loc, e, earg);
1415     e = e.expressionSemantic(sc);
1416     return e;
1417 }
1418 
1419 /***************************************
1420  * Search for function funcid in aggregate ad.
1421  */
1422 Dsymbol search_function(ScopeDsymbol ad, Identifier funcid)
1423 {
1424     Dsymbol s = ad.search(Loc.initial, funcid);
1425     if (s)
1426     {
1427         //printf("search_function: s = '%s'\n", s.kind());
1428         Dsymbol s2 = s.toAlias();
1429         //printf("search_function: s2 = '%s'\n", s2.kind());
1430         FuncDeclaration fd = s2.isFuncDeclaration();
1431         if (fd && fd.type.ty == Tfunction)
1432             return fd;
1433         TemplateDeclaration td = s2.isTemplateDeclaration();
1434         if (td)
1435             return td;
1436     }
1437     return null;
1438 }
1439 
1440 /**************************************
1441  * Figure out what is being foreach'd over by looking at the ForeachAggregate.
1442  * Params:
1443  *      sc = context
1444  *      isForeach = true for foreach, false for foreach_reverse
1445  *      feaggr = ForeachAggregate
1446  *      sapply = set to function opApply/opApplyReverse, or delegate, or null.
1447  *               Overload resolution is not done.
1448  * Returns:
1449  *      true if successfully figured it out; feaggr updated with semantic analysis.
1450  *      false for failed, which is an error.
1451  */
1452 bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out Dsymbol sapply)
1453 {
1454     //printf("inferForeachAggregate(%s)\n", feaggr.toChars());
1455     bool sliced;
1456     Type att = null;
1457     auto aggr = feaggr;
1458     while (1)
1459     {
1460         aggr = aggr.expressionSemantic(sc);
1461         aggr = resolveProperties(sc, aggr);
1462         aggr = aggr.optimize(WANTvalue);
1463         if (!aggr.type || aggr.op == EXP.error)
1464             return false;
1465         Type tab = aggr.type.toBasetype();
1466         switch (tab.ty)
1467         {
1468         case Tarray:            // https://dlang.org/spec/statement.html#foreach_over_arrays
1469         case Tsarray:           // https://dlang.org/spec/statement.html#foreach_over_arrays
1470         case Ttuple:            // https://dlang.org/spec/statement.html#foreach_over_tuples
1471         case Taarray:           // https://dlang.org/spec/statement.html#foreach_over_associative_arrays
1472             break;
1473 
1474         case Tclass:
1475         case Tstruct:
1476         {
1477             AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym
1478                                                          : tab.isTypeStruct().sym;
1479             if (!sliced)
1480             {
1481                 sapply = search_function(ad, isForeach ? Id.apply : Id.applyReverse);
1482                 if (sapply)
1483                 {
1484                     // https://dlang.org/spec/statement.html#foreach_over_struct_and_classes
1485                     // opApply aggregate
1486                     break;
1487                 }
1488                 if (feaggr.op != EXP.type)
1489                 {
1490                     /* See if rewriting `aggr` to `aggr[]` will work
1491                      */
1492                     Expression rinit = new ArrayExp(aggr.loc, feaggr);
1493                     rinit = rinit.trySemantic(sc);
1494                     if (rinit) // if it worked
1495                     {
1496                         aggr = rinit;
1497                         sliced = true;  // only try it once
1498                         continue;
1499                     }
1500                 }
1501             }
1502             if (ad.search(Loc.initial, isForeach ? Id.Ffront : Id.Fback))
1503             {
1504                 // https://dlang.org/spec/statement.html#foreach-with-ranges
1505                 // range aggregate
1506                 break;
1507             }
1508             if (ad.aliasthis)
1509             {
1510                 if (isRecursiveAliasThis(att, tab))     // error, circular alias this
1511                     return false;
1512                 aggr = resolveAliasThis(sc, aggr);
1513                 continue;
1514             }
1515             return false;
1516         }
1517 
1518         case Tdelegate:        // https://dlang.org/spec/statement.html#foreach_over_delegates
1519             if (auto de = aggr.isDelegateExp())
1520             {
1521                 sapply = de.func;
1522             }
1523             break;
1524 
1525         case Terror:
1526             break;
1527 
1528         default:
1529             return false;
1530         }
1531         feaggr = aggr;
1532         return true;
1533     }
1534     assert(0);
1535 }
1536 
1537 /*****************************************
1538  * Given array of foreach parameters and an aggregate type,
1539  * find best opApply overload,
1540  * if any of the parameter types are missing, attempt to infer
1541  * them from the aggregate type.
1542  * Params:
1543  *      fes = the foreach statement
1544  *      sc = context
1545  *      sapply = null or opApply or delegate, overload resolution has not been done.
1546  *               Do overload resolution on sapply.
1547  * Returns:
1548  *      false for errors
1549  */
1550 bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
1551 {
1552     if (!fes.parameters || !fes.parameters.length)
1553         return false;
1554     if (sapply) // prefer opApply
1555     {
1556         foreach (Parameter p; *fes.parameters)
1557         {
1558             if (p.type)
1559             {
1560                 p.type = p.type.typeSemantic(fes.loc, sc);
1561                 p.type = p.type.addStorageClass(p.storageClass);
1562             }
1563         }
1564 
1565         // Determine ethis for sapply
1566         Expression ethis;
1567         Type tab = fes.aggr.type.toBasetype();
1568         if (tab.ty == Tclass || tab.ty == Tstruct)
1569             ethis = fes.aggr;
1570         else
1571         {
1572             assert(tab.ty == Tdelegate && fes.aggr.op == EXP.delegate_);
1573             ethis = fes.aggr.isDelegateExp().e1;
1574         }
1575 
1576         /* Look for like an
1577          *  int opApply(int delegate(ref Type [, ...]) dg);
1578          * overload
1579          */
1580         if (FuncDeclaration fd = sapply.isFuncDeclaration())
1581         {
1582             if (auto fdapply = findBestOpApplyMatch(ethis, fd, fes.parameters))
1583             {
1584                 // Fill in any missing types on foreach parameters[]
1585                 matchParamsToOpApply(fdapply.type.isTypeFunction(), fes.parameters, true);
1586                 sapply = fdapply;
1587                 return true;
1588             }
1589             return false;
1590         }
1591         return true;   // shouldn't this be false?
1592     }
1593 
1594     Parameter p = (*fes.parameters)[0];
1595     Type taggr = fes.aggr.type;
1596     assert(taggr);
1597     Type tab = taggr.toBasetype();
1598     switch (tab.ty)
1599     {
1600     case Tarray:
1601     case Tsarray:
1602     case Ttuple:
1603         if (fes.parameters.length == 2)
1604         {
1605             if (!p.type)
1606             {
1607                 p.type = Type.tsize_t; // key type
1608                 p.type = p.type.addStorageClass(p.storageClass);
1609             }
1610             p = (*fes.parameters)[1];
1611         }
1612         if (!p.type && tab.ty != Ttuple)
1613         {
1614             p.type = tab.nextOf(); // value type
1615             p.type = p.type.addStorageClass(p.storageClass);
1616         }
1617         break;
1618 
1619     case Taarray:
1620         {
1621             TypeAArray taa = tab.isTypeAArray();
1622             if (fes.parameters.length == 2)
1623             {
1624                 if (!p.type)
1625                 {
1626                     p.type = taa.index; // key type
1627                     p.type = p.type.addStorageClass(p.storageClass);
1628                     if (p.storageClass & STC.ref_) // key must not be mutated via ref
1629                         p.type = p.type.addMod(MODFlags.const_);
1630                 }
1631                 p = (*fes.parameters)[1];
1632             }
1633             if (!p.type)
1634             {
1635                 p.type = taa.next; // value type
1636                 p.type = p.type.addStorageClass(p.storageClass);
1637             }
1638             break;
1639         }
1640 
1641     case Tclass:
1642     case Tstruct:
1643     {
1644         AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym
1645                                                      : tab.isTypeStruct().sym;
1646         if (fes.parameters.length == 1)
1647         {
1648             if (!p.type)
1649             {
1650                 /* Look for a front() or back() overload
1651                  */
1652                 Identifier id = (fes.op == TOK.foreach_) ? Id.Ffront : Id.Fback;
1653                 Dsymbol s = ad.search(Loc.initial, id);
1654                 FuncDeclaration fd = s ? s.isFuncDeclaration() : null;
1655                 if (fd)
1656                 {
1657                     // Resolve inout qualifier of front type
1658                     p.type = fd.type.nextOf();
1659                     if (p.type)
1660                     {
1661                         p.type = p.type.substWildTo(tab.mod);
1662                         p.type = p.type.addStorageClass(p.storageClass);
1663                     }
1664                 }
1665                 else if (s && s.isTemplateDeclaration())
1666                 {
1667                 }
1668                 else if (s && s.isDeclaration())
1669                     p.type = s.isDeclaration().type;
1670                 else
1671                     break;
1672             }
1673             break;
1674         }
1675         break;
1676     }
1677 
1678     case Tdelegate:
1679     {
1680         auto td = tab.isTypeDelegate();
1681         if (!matchParamsToOpApply(td.next.isTypeFunction(), fes.parameters, true))
1682             return false;
1683         break;
1684     }
1685 
1686     default:
1687         break; // ignore error, caught later
1688     }
1689     return true;
1690 }
1691 
1692 /*********************************************
1693  * Find best overload match on fstart given ethis and parameters[].
1694  * Params:
1695  *      ethis = expression to use for `this`
1696  *      fstart = opApply or foreach delegate
1697  *      parameters = ForeachTypeList (i.e. foreach parameters)
1698  * Returns:
1699  *      best match if there is one, null if error
1700  */
1701 private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration fstart, Parameters* parameters)
1702 {
1703     MOD mod = ethis.type.mod;
1704     MATCH match = MATCH.nomatch;
1705     FuncDeclaration fd_best;
1706     FuncDeclaration fd_ambig;
1707 
1708     overloadApply(fstart, (Dsymbol s)
1709     {
1710         auto f = s.isFuncDeclaration();
1711         if (!f)
1712             return 0;           // continue
1713         auto tf = f.type.isTypeFunction();
1714         MATCH m = MATCH.exact;
1715         if (f.isThis())
1716         {
1717             if (!MODimplicitConv(mod, tf.mod))
1718                 m = MATCH.nomatch;
1719             else if (mod != tf.mod)
1720                 m = MATCH.constant;
1721         }
1722         if (!matchParamsToOpApply(tf, parameters, false))
1723             m = MATCH.nomatch;
1724         if (m > match)
1725         {
1726             fd_best = f;
1727             fd_ambig = null;
1728             match = m;
1729         }
1730         else if (m == match && m > MATCH.nomatch)
1731         {
1732             assert(fd_best);
1733             auto bestTf = fd_best.type.isTypeFunction();
1734             assert(bestTf);
1735 
1736             // Found another overload with different attributes?
1737             // e.g. @system vs. @safe opApply
1738             // @@@DEPRECATED_2.112@@@
1739             // See semantic2.d Semantic2Visitor.visit(FuncDeclaration):
1740             // Remove `false` after deprecation period is over.
1741             bool ambig = tf.attributesEqual(bestTf, false);
1742 
1743             // opApplies with identical attributes could still accept
1744             // different function bodies as delegate
1745             // => different parameters or attributes
1746             if (ambig)
1747             {
1748                 // Fetch the delegates that receive the function body
1749                 auto tfBody = tf.parameterList[0].type.isTypeDelegate().next;
1750                 assert(tfBody);
1751 
1752                 auto bestBody = bestTf.parameterList[0].type.isTypeDelegate().next;
1753                 assert(bestBody);
1754 
1755                 // Ignore covariant matches, as later on it can be redone
1756                 // after the opApply delegate has its attributes inferred.
1757                 ambig = !(tfBody.covariant(bestBody) == Covariant.yes || bestBody.covariant(tfBody) == Covariant.yes);
1758             }
1759 
1760             if (ambig)
1761                 fd_ambig = f;                           // not covariant, so ambiguous
1762         }
1763         return 0;               // continue
1764     });
1765 
1766     if (fd_ambig)
1767     {
1768         .error(ethis.loc, "`%s.%s` matches more than one declaration:\n`%s`:     `%s`\nand:\n`%s`:     `%s`",
1769             ethis.toChars(), fstart.ident.toChars(),
1770             fd_best.loc.toChars(), fd_best.type.toChars(),
1771             fd_ambig.loc.toChars(), fd_ambig.type.toChars());
1772         return null;
1773     }
1774 
1775     return fd_best;
1776 }
1777 
1778 /******************************
1779  * Determine if foreach parameters match opApply parameters.
1780  * Infer missing foreach parameter types from type of opApply delegate.
1781  * Params:
1782  *      tf = type of opApply or delegate
1783  *      parameters = foreach parameters
1784  *      infer = infer missing parameter types
1785  * Returns:
1786  *      true for match for this function
1787  *      false for no match for this function
1788  */
1789 private bool matchParamsToOpApply(TypeFunction tf, Parameters* parameters, bool infer)
1790 {
1791     enum nomatch = false;
1792 
1793     /* opApply/delegate has exactly one parameter, and that parameter
1794      * is a delegate that looks like:
1795      *     int opApply(int delegate(ref Type [, ...]) dg);
1796      */
1797     if (tf.parameterList.length != 1)
1798         return nomatch;
1799 
1800     /* Get the type of opApply's dg parameter
1801      */
1802     Parameter p0 = tf.parameterList[0];
1803     auto de = p0.type.isTypeDelegate();
1804     if (!de)
1805         return nomatch;
1806     TypeFunction tdg = de.next.isTypeFunction();
1807 
1808     /* We now have tdg, the type of the delegate.
1809      * tdg's parameters must match that of the foreach arglist (i.e. parameters).
1810      * Fill in missing types in parameters.
1811      */
1812     const nparams = tdg.parameterList.length;
1813     if (nparams == 0 || nparams != parameters.length || tdg.parameterList.varargs != VarArg.none)
1814         return nomatch; // parameter mismatch
1815 
1816     foreach (u, p; *parameters)
1817     {
1818         Parameter param = tdg.parameterList[u];
1819         if (p.type)
1820         {
1821             if (!p.type.equals(param.type))
1822                 return nomatch;
1823         }
1824         else if (infer)
1825         {
1826             p.type = param.type;
1827             p.type = p.type.addStorageClass(p.storageClass);
1828         }
1829     }
1830     return true;
1831 }
1832 
1833 /**
1834  * Reverse relational operator, eg >= becomes <=
1835  * Note this is not negation.
1836  * Params:
1837  *      op = comparison operator to reverse
1838  * Returns:
1839  *      reverse of op
1840  */
1841 private EXP reverseRelation(EXP op) pure
1842 {
1843     switch (op)
1844     {
1845         case EXP.greaterOrEqual:  op = EXP.lessOrEqual;    break;
1846         case EXP.greaterThan:     op = EXP.lessThan;       break;
1847         case EXP.lessOrEqual:     op = EXP.greaterOrEqual; break;
1848         case EXP.lessThan:        op = EXP.greaterThan;    break;
1849         default:                  break;
1850     }
1851     return op;
1852 }