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