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