d: Merge upstream dmd 4d1bfcf14, druntime 9ba9a6ae, phobos c0cc5e917.
[official-gcc.git] / gcc / d / dmd / opover.d
blob4f6903cbab721284a49b34cb0fa781a223477743
1 /**
2 * Handles operator overloading.
4 * Specification: $(LINK2 https://dlang.org/spec/operatoroverloading.html, Operator Overloading)
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
14 module dmd.opover;
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;
41 /***********************************
42 * Determine if operands of binary op can be reversed
43 * to fit operator overload.
45 bool isCommutative(EXP op)
47 switch (op)
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;
66 return false;
69 /***********************************
70 * Get Identifier for operator overload.
72 private Identifier opId(Expression e)
74 switch (e.op)
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);
121 /***********************************
122 * Get Identifier for reverse operator overload,
123 * `null` if not supported for this operator.
125 private Identifier opId_r(Expression e)
127 switch (e.op)
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;
147 /*******************************************
148 * Helper function to turn operator into template argument list
150 Objects* opToArg(Scope* sc, EXP op)
152 /* Remove the = from op=
154 switch (op)
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;
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;
205 // Try alias this on first operand
206 private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinExp e)
208 if (!ad || !ad.aliasthis)
209 return null;
211 /* Rewrite (e1 op e2) as:
212 * (e1.aliasthis op e2)
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;
225 Expression result;
226 if (be.op == EXP.concatenateAssign)
227 result = be.op_overload(sc);
228 else
229 result = be.trySemantic(sc);
231 return result;
234 // Try alias this on second operand
235 private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinExp e)
237 if (!ad || !ad.aliasthis)
238 return null;
239 /* Rewrite (e1 op e2) as:
240 * (e1 op e2.aliasthis)
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;
250 Expression result;
251 if (be.op == EXP.concatenateAssign)
252 result = be.op_overload(sc);
253 else
254 result = be.trySemantic(sc);
256 return result;
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
273 Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
275 Expression visit(Expression e)
277 assert(0);
280 Expression visitUna(UnaExp e)
282 //printf("UnaExp::op_overload() (%s)\n", e.toChars());
283 Expression result;
284 if (auto ae = e.e1.isArrayExp())
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)
293 ie = (*ae.arguments)[0].isIntervalExp();
295 while (true)
297 if (ae.e1.op == EXP.error)
299 return ae.e1;
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))
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)
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)
329 return Expression.combine(e0, result);
332 Lfallback:
333 if (maybeSlice && search_function(ad, Id.opSliceUnary))
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)
342 auto a = new Expressions();
343 if (ie)
345 a.push(ie.lwr);
346 a.push(ie.upr);
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;
355 // Didn't find it. Forward to aliasthis
356 if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
358 /* Rewrite op(a[arguments]) as:
359 * op(a.aliasthis[arguments])
361 ae.e1 = resolveAliasThis(sc, ae1save, true);
362 if (ae.e1)
363 continue;
365 break;
367 ae.e1 = ae1old; // recovery
368 ae.lengthVar = null;
370 e.e1 = e.e1.expressionSemantic(sc);
371 e.e1 = resolveProperties(sc, e.e1);
372 if (e.e1.op == EXP.error)
374 return e.e1;
376 AggregateDeclaration ad = isAggregate(e.e1.type);
377 if (ad)
379 Dsymbol fd = null;
380 /* Rewrite as:
381 * e1.opUnary!(op)()
383 fd = search_function(ad, Id.opUnary);
384 if (fd)
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;
392 // D1-style operator overloads, deprecated
393 if (e.op != EXP.prePlusPlus && e.op != EXP.preMinusMinus)
395 auto id = opId(e);
396 fd = search_function(ad, id);
397 if (fd)
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();
405 // Didn't find it. Forward to aliasthis
406 if (ad.aliasthis && !isRecursiveAliasThis(e.att1, e.e1.type))
408 /* Rewrite op(e1) as:
409 * op(e1.aliasthis)
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;
419 return result;
422 Expression visitArray(ArrayExp ae)
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)
432 ie = (*ae.arguments)[0].isIntervalExp();
434 Expression result;
435 while (true)
437 if (ae.e1.op == EXP.error)
439 return ae.e1;
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)
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)
452 // Convert to SliceExp
453 if (maybeSlice)
455 result = new SliceExp(ae.loc, ae.e1, ie);
456 result = result.expressionSemantic(sc);
457 return result;
459 // Convert to IndexExp
460 if (ae.arguments.dim == 1)
462 result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]);
463 result = result.expressionSemantic(sc);
464 return result;
467 break;
469 if (search_function(ad, Id.index))
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)
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)
489 return Expression.combine(e0, result);
492 Lfallback:
493 if (maybeSlice && ae.e1.op == EXP.type)
495 result = new SliceExp(ae.loc, ae.e1, ie);
496 result = result.expressionSemantic(sc);
497 result = Expression.combine(e0, result);
498 return result;
500 if (maybeSlice && search_function(ad, Id.slice))
502 // Deal with $
503 result = resolveOpDollar(sc, ae, ie, &e0);
505 if (result.op == EXP.error)
507 if (!e0 && !search_function(ad, Id.dollar)) {
508 ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars());
510 return result;
512 /* Rewrite a[i..j] as:
513 * a.opSlice(i, j)
515 auto a = new Expressions();
516 if (ie)
518 a.push(ie.lwr);
519 a.push(ie.upr);
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;
527 // Didn't find it. Forward to aliasthis
528 if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
530 //printf("att arr e1 = %s\n", this.e1.type.toChars());
531 /* Rewrite op(a[arguments]) as:
532 * op(a.aliasthis[arguments])
534 ae.e1 = resolveAliasThis(sc, ae1save, true);
535 if (ae.e1)
536 continue;
538 break;
540 ae.e1 = ae1old; // recovery
541 ae.lengthVar = null;
542 return result;
545 /***********************************************
546 * This is mostly the same as UnaryExp::op_overload(), but has
547 * a different rewrite.
549 Expression visitCast(CastExp e)
551 //printf("CastExp::op_overload() (%s)\n", e.toChars());
552 Expression result;
553 AggregateDeclaration ad = isAggregate(e.e1.type);
554 if (ad)
556 Dsymbol fd = null;
557 /* Rewrite as:
558 * e1.opCast!(T)()
560 fd = search_function(ad, Id._cast);
561 if (fd)
563 version (all)
565 // Backwards compatibility with D1 if opCast is a function, not a template
566 if (fd.isFuncDeclaration())
568 // Rewrite as: e1.opCast()
569 return build_overload(e.loc, sc, e.e1, null, fd);
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;
579 // Didn't find it. Forward to aliasthis
580 if (ad.aliasthis && !isRecursiveAliasThis(e.att1, e.e1.type))
582 /* Rewrite op(e1) as:
583 * op(e1.aliasthis)
585 if (auto e1 = resolveAliasThis(sc, e.e1, true))
587 result = e.copy();
588 (cast(UnaExp)result).e1 = e1;
589 result = result.op_overload(sc);
590 return result;
594 return result;
597 Expression visitBin(BinExp e)
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)
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.
615 (sd.hasBlitAssign && !e.e2.isLvalue())))
617 /* This is bitwise struct assignment. */
618 return null;
621 Dsymbol s = null;
622 Dsymbol s_r = null;
623 Objects* tiargs = null;
624 if (e.op == EXP.plusPlus || e.op == EXP.minusMinus)
626 // Bug4099 fix
627 if (ad1 && search_function(ad1, Id.opUnary))
628 return null;
630 if (e.op != EXP.equal && e.op != EXP.notEqual && e.op != EXP.assign && e.op != EXP.plusPlus && e.op != EXP.minusMinus)
632 /* Try opBinary and opBinaryRight
634 if (ad1)
636 s = search_function(ad1, Id.opBinary);
637 if (s && !s.isTemplateDeclaration())
639 e.e1.error("`%s.opBinary` isn't a template", e.e1.toChars());
640 return ErrorExp.get();
643 if (ad2)
645 s_r = search_function(ad2, Id.opBinaryRight);
646 if (s_r && !s_r.isTemplateDeclaration())
648 e.e2.error("`%s.opBinaryRight` isn't a template", e.e2.toChars());
649 return ErrorExp.get();
651 if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778
652 s_r = null;
654 // Set tiargs, the template argument list, which will be the operator string
655 if (s || s_r)
657 id = Id.opBinary;
658 id_r = Id.opBinaryRight;
659 tiargs = opToArg(sc, e.op);
662 if (!s && !s_r)
664 // Try the D1-style operators, deprecated
665 if (ad1 && id)
667 s = search_function(ad1, id);
668 if (s && id != Id.assign)
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();
679 if (ad2 && id_r)
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)
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();
696 if (s || s_r)
698 /* Try:
699 * a.opfunc(b)
700 * b.opfunc_r(a)
701 * and see which is better.
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)
713 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
714 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
716 return ErrorExp.get();
719 FuncDeclaration lastf = m.lastf;
720 if (s_r)
722 functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, &args1);
723 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
725 return ErrorExp.get();
728 if (m.count > 1)
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());
733 else if (m.last == MATCH.nomatch)
735 if (tiargs)
736 goto L1;
737 m.lastf = null;
739 if (e.op == EXP.plusPlus || e.op == EXP.minusMinus)
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);
747 else if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch)
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);
752 else
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);
759 version (all)
761 // Retained for D1 compatibility
762 if (isCommutative(e.op) && !tiargs)
764 s = null;
765 s_r = null;
766 if (ad1 && id_r)
768 s_r = search_function(ad1, id_r);
770 if (ad2 && id)
772 s = search_function(ad2, id);
773 if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778
774 s = null;
776 if (s || s_r)
778 /* Try:
779 * a.opfunc_r(b)
780 * b.opfunc(a)
781 * and see which is better.
783 if (!argsset)
785 args1.setDim(1);
786 args1[0] = e.e1;
787 expandTuples(&args1);
788 args2.setDim(1);
789 args2[0] = e.e2;
790 expandTuples(&args2);
792 MatchAccumulator m;
793 if (s_r)
795 functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, &args2);
796 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
798 return ErrorExp.get();
801 FuncDeclaration lastf = m.lastf;
802 if (s)
804 functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, &args1);
805 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
807 return ErrorExp.get();
810 if (m.count > 1)
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());
815 else if (m.last == MATCH.nomatch)
817 m.lastf = null;
820 if (lastf && m.lastf == lastf || !s && m.last == MATCH.nomatch)
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);
825 else
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;
839 Expression rewrittenLhs;
840 if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
842 if (Expression result = checkAliasThisForLhs(ad1, sc, e))
844 /* https://issues.dlang.org/show_bug.cgi?id=19441
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.
855 if (result.op != EXP.assign)
856 return result; // i.e: Rewrote `e1 = e2` -> `e1(e2)`
858 auto ae = result.isAssignExp();
859 if (ae.e1.op != EXP.dotVariable)
860 return result; // i.e: Rewrote `e1 = e2` -> `e1() = e2`
862 auto dve = ae.e1.isDotVarExp();
863 if (auto ad = dve.var.isMember2())
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))
869 if (dve.var == ad.aliasthis.sym)
870 return result;
873 rewrittenLhs = ae.e1;
876 if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
878 if (Expression result = checkAliasThisForRhs(ad2, sc, e))
879 return result;
881 if (rewrittenLhs)
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();
887 return null;
890 Expression visitEqual(EqualExp e)
892 //printf("EqualExp::op_overload() (%s)\n", e.toChars());
893 Type t1 = e.e1.type.toBasetype();
894 Type t2 = e.e2.type.toBasetype();
896 /* Array equality is handled by expressionSemantic() potentially
897 * lowering to object.__equals(), which takes care of overloaded
898 * operators for the element types.
900 if ((t1.ty == Tarray || t1.ty == Tsarray) &&
901 (t2.ty == Tarray || t2.ty == Tsarray))
903 return null;
906 /* Check for class equality with null literal or typeof(null).
908 if (t1.ty == Tclass && e.e2.op == EXP.null_ ||
909 t2.ty == Tclass && e.e1.op == EXP.null_)
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();
916 if (t1.ty == Tclass && t2.ty == Tnull ||
917 t1.ty == Tnull && t2.ty == Tclass)
919 // Comparing a class with typeof(null) should not call opEquals
920 return null;
923 /* Check for class equality.
925 if (t1.ty == Tclass && t2.ty == Tclass)
927 ClassDeclaration cd1 = t1.isClassHandle();
928 ClassDeclaration cd2 = t2.isClassHandle();
929 if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp))
931 /* Rewrite as:
932 * .object.opEquals(e1, e2)
934 Expression e1x = e.e1;
935 Expression e2x = e.e2;
937 /* The explicit cast is necessary for interfaces
938 * https://issues.dlang.org/show_bug.cgi?id=4088
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());
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;
957 if (Expression result = compare_overload(e, sc, Id.eq, null))
959 if (lastComma(result).op == EXP.call && e.op == EXP.notEqual)
961 result = new NotExp(result.loc, result);
962 result = result.expressionSemantic(sc);
964 return result;
967 /* Check for pointer equality.
969 if (t1.ty == Tpointer || t2.ty == Tpointer)
971 /* Rewrite:
972 * ptr1 == ptr2
973 * as:
974 * ptr1 is ptr2
976 * This is just a rewriting for deterministic AST representation
977 * as the backend input.
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);
984 /* Check for struct equality without opEquals.
986 if (t1.ty == Tstruct && t2.ty == Tstruct)
988 auto sd = t1.isTypeStruct().sym;
989 if (sd != t2.isTypeStruct().sym)
990 return null;
992 import dmd.clone : needOpEquals;
993 if (!global.params.fieldwise && !needOpEquals(sd))
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);
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
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*.
1012 if (e.att1 && t1.equivalent(e.att1)) return null;
1013 if (e.att2 && t2.equivalent(e.att2)) return null;
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);
1021 auto sc2 = sc.push();
1022 sc2.flags |= SCOPE.noaccesscheck;
1023 Expression r = e.expressionSemantic(sc2);
1024 sc2.pop();
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.
1030 if (r.op == e.op &&
1031 r.isEqualExp().e1.type.toBasetype() == t1)
1033 e.error("cannot compare `%s` because its auto generated member-wise equality has recursive definition",
1034 t1.toChars());
1035 return ErrorExp.get();
1037 return r;
1040 /* Check for tuple equality.
1042 if (e.e1.op == EXP.tuple && e.e2.op == EXP.tuple)
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)
1049 e.error("mismatched tuple lengths, `%d` and `%d`",
1050 cast(int)dim, cast(int)tup2.exps.dim);
1051 return ErrorExp.get();
1054 Expression result;
1055 if (dim == 0)
1057 // zero-length tuple comparison should always return true or false.
1058 result = IntegerExp.createBool(e.op == EXP.equal);
1060 else
1062 for (size_t i = 0; i < dim; i++)
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;
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);
1077 assert(result);
1079 result = Expression.combine(tup1.e0, tup2.e0, result);
1080 result = result.expressionSemantic(sc);
1082 return result;
1084 return null;
1087 Expression visitCmp(CmpExp e)
1089 //printf("CmpExp:: () (%s)\n", e.toChars());
1090 return compare_overload(e, sc, Id.cmp, pop);
1093 /*********************************
1094 * Operator overloading for op=
1096 Expression visitBinAssign(BinAssignExp e)
1098 //printf("BinAssignExp::op_overload() (%s)\n", e.toChars());
1099 if (auto ae = e.e1.isArrayExp())
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)
1108 ie = (*ae.arguments)[0].isIntervalExp();
1110 while (true)
1112 if (ae.e1.op == EXP.error)
1114 return ae.e1;
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))
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)
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)
1149 return Expression.combine(e0, result);
1152 Lfallback:
1153 if (maybeSlice && search_function(ad, Id.opSliceOpAssign))
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)
1166 auto a = new Expressions();
1167 a.push(e.e2);
1168 if (ie)
1170 a.push(ie.lwr);
1171 a.push(ie.upr);
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;
1180 // Didn't find it. Forward to aliasthis
1181 if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
1183 /* Rewrite (a[arguments] op= e2) as:
1184 * a.aliasthis[arguments] op= e2
1186 ae.e1 = resolveAliasThis(sc, ae1save, true);
1187 if (ae.e1)
1188 continue;
1190 break;
1192 ae.e1 = ae1old; // recovery
1193 ae.lengthVar = null;
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)
1201 return ErrorExp.get();
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
1210 if (ad1)
1212 s = search_function(ad1, Id.opOpAssign);
1213 if (s && !s.isTemplateDeclaration())
1215 e.error("`%s.opOpAssign` isn't a template", e.e1.toChars());
1216 return ErrorExp.get();
1219 // Set tiargs, the template argument list, which will be the operator string
1220 if (s)
1222 id = Id.opOpAssign;
1223 tiargs = opToArg(sc, e.op);
1226 // Try D1-style operator overload, deprecated
1227 if (!s && ad1 && id)
1229 s = search_function(ad1, id);
1230 if (s)
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();
1241 if (s)
1243 /* Try:
1244 * a.opOpAssign(b)
1246 args2.setDim(1);
1247 args2[0] = e.e2;
1248 expandTuples(&args2);
1249 MatchAccumulator m;
1250 if (s)
1252 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
1253 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
1255 return ErrorExp.get();
1258 if (m.count > 1)
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());
1263 else if (m.last == MATCH.nomatch)
1265 if (tiargs)
1266 goto L1;
1267 m.lastf = null;
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);
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;
1277 return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
1280 if (pop)
1281 *pop = e.op;
1283 switch (e.op)
1285 case EXP.cast_ : return visitCast(e.isCastExp());
1286 case EXP.array : return visitArray(e.isArrayExp());
1288 case EXP.notEqual :
1289 case EXP.equal : return visitEqual(e.isEqualExp());
1291 case EXP.lessOrEqual :
1292 case EXP.greaterThan :
1293 case EXP.greaterOrEqual:
1294 case EXP.lessThan : return visitCmp(cast(CmpExp)e);
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);
1304 /******************************************
1305 * Common code for overloading of EqualExp and CmpExp
1307 private Expression compare_overload(BinExp e, Scope* sc, Identifier id, EXP* pop)
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)
1316 s = search_function(ad1, id);
1318 if (ad2)
1320 s_r = search_function(ad2, id);
1321 if (s == s_r)
1322 s_r = null;
1324 Objects* tiargs = null;
1325 if (s || s_r)
1327 /* Try:
1328 * a.opEquals(b)
1329 * b.opEquals(a)
1330 * and see which is better.
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)
1341 printf("s : %s\n", s.toPrettyChars());
1342 printf("s_r: %s\n", s_r.toPrettyChars());
1344 if (s)
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();
1350 FuncDeclaration lastf = m.lastf;
1351 int count = m.count;
1352 if (s_r)
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();
1358 if (m.count > 1)
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));
1371 if (!(m.lastf == lastf && m.count == 2 && count == 1))
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());
1377 else if (m.last == MATCH.nomatch)
1379 m.lastf = null;
1381 Expression result;
1382 if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch)
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);
1387 else
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);
1396 return result;
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.
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);
1409 /***********************************
1410 * Utility to build a function call out of this reference and argument.
1412 Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d)
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;
1426 /***************************************
1427 * Search for function funcid in aggregate ad.
1429 Dsymbol search_function(ScopeDsymbol ad, Identifier funcid)
1431 Dsymbol s = ad.search(Loc.initial, funcid);
1432 if (s)
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;
1444 return null;
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.
1459 bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out Dsymbol sapply)
1461 //printf("inferForeachAggregate(%s)\n", feaggr.toChars());
1462 bool sliced;
1463 Type att = null;
1464 auto aggr = feaggr;
1465 while (1)
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)
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;
1481 case Tclass:
1482 case Tstruct:
1484 AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym
1485 : tab.isTypeStruct().sym;
1486 if (!sliced)
1488 sapply = search_function(ad, isForeach ? Id.apply : Id.applyReverse);
1489 if (sapply)
1491 // https://dlang.org/spec/statement.html#foreach_over_struct_and_classes
1492 // opApply aggregate
1493 break;
1495 if (feaggr.op != EXP.type)
1497 /* See if rewriting `aggr` to `aggr[]` will work
1499 Expression rinit = new ArrayExp(aggr.loc, feaggr);
1500 rinit = rinit.trySemantic(sc);
1501 if (rinit) // if it worked
1503 aggr = rinit;
1504 sliced = true; // only try it once
1505 continue;
1509 if (ad.search(Loc.initial, isForeach ? Id.Ffront : Id.Fback))
1511 // https://dlang.org/spec/statement.html#foreach-with-ranges
1512 // range aggregate
1513 break;
1515 if (ad.aliasthis)
1517 if (isRecursiveAliasThis(att, tab)) // error, circular alias this
1518 return false;
1519 aggr = resolveAliasThis(sc, aggr);
1520 continue;
1522 return false;
1525 case Tdelegate: // https://dlang.org/spec/statement.html#foreach_over_delegates
1526 if (auto de = aggr.isDelegateExp())
1528 sapply = de.func;
1530 break;
1532 case Terror:
1533 break;
1535 default:
1536 return false;
1538 feaggr = aggr;
1539 return true;
1541 assert(0);
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
1557 bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
1559 if (!fes.parameters || !fes.parameters.dim)
1560 return false;
1561 if (sapply) // prefer opApply
1563 foreach (Parameter p; *fes.parameters)
1565 if (p.type)
1567 p.type = p.type.typeSemantic(fes.loc, sc);
1568 p.type = p.type.addStorageClass(p.storageClass);
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
1579 assert(tab.ty == Tdelegate && fes.aggr.op == EXP.delegate_);
1580 ethis = fes.aggr.isDelegateExp().e1;
1583 /* Look for like an
1584 * int opApply(int delegate(ref Type [, ...]) dg);
1585 * overload
1587 if (FuncDeclaration fd = sapply.isFuncDeclaration())
1589 if (auto fdapply = findBestOpApplyMatch(ethis, fd, fes.parameters))
1591 // Fill in any missing types on foreach parameters[]
1592 matchParamsToOpApply(fdapply.type.isTypeFunction(), fes.parameters, true);
1593 sapply = fdapply;
1594 return true;
1596 return false;
1598 return true; // shouldn't this be false?
1601 Parameter p = (*fes.parameters)[0];
1602 Type taggr = fes.aggr.type;
1603 assert(taggr);
1604 Type tab = taggr.toBasetype();
1605 switch (tab.ty)
1607 case Tarray:
1608 case Tsarray:
1609 case Ttuple:
1610 if (fes.parameters.dim == 2)
1612 if (!p.type)
1614 p.type = Type.tsize_t; // key type
1615 p.type = p.type.addStorageClass(p.storageClass);
1617 p = (*fes.parameters)[1];
1619 if (!p.type && tab.ty != Ttuple)
1621 p.type = tab.nextOf(); // value type
1622 p.type = p.type.addStorageClass(p.storageClass);
1624 break;
1626 case Taarray:
1628 TypeAArray taa = tab.isTypeAArray();
1629 if (fes.parameters.dim == 2)
1631 if (!p.type)
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_);
1638 p = (*fes.parameters)[1];
1640 if (!p.type)
1642 p.type = taa.next; // value type
1643 p.type = p.type.addStorageClass(p.storageClass);
1645 break;
1648 case Tclass:
1649 case Tstruct:
1651 AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym
1652 : tab.isTypeStruct().sym;
1653 if (fes.parameters.dim == 1)
1655 if (!p.type)
1657 /* Look for a front() or back() overload
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)
1664 // Resolve inout qualifier of front type
1665 p.type = fd.type.nextOf();
1666 if (p.type)
1668 p.type = p.type.substWildTo(tab.mod);
1669 p.type = p.type.addStorageClass(p.storageClass);
1672 else if (s && s.isTemplateDeclaration())
1675 else if (s && s.isDeclaration())
1676 p.type = s.isDeclaration().type;
1677 else
1678 break;
1680 break;
1682 break;
1685 case Tdelegate:
1687 auto td = tab.isTypeDelegate();
1688 if (!matchParamsToOpApply(td.next.isTypeFunction(), fes.parameters, true))
1689 return false;
1690 break;
1693 default:
1694 break; // ignore error, caught later
1696 return true;
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
1708 private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration fstart, Parameters* parameters)
1710 MOD mod = ethis.type.mod;
1711 MATCH match = MATCH.nomatch;
1712 FuncDeclaration fd_best;
1713 FuncDeclaration fd_ambig;
1715 overloadApply(fstart, (Dsymbol s)
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())
1724 if (!MODimplicitConv(mod, tf.mod))
1725 m = MATCH.nomatch;
1726 else if (mod != tf.mod)
1727 m = MATCH.constant;
1729 if (!matchParamsToOpApply(tf, parameters, false))
1730 m = MATCH.nomatch;
1731 if (m > match)
1733 fd_best = f;
1734 fd_ambig = null;
1735 match = m;
1737 else if (m == match && m > MATCH.nomatch)
1739 assert(fd_best);
1740 auto bestTf = fd_best.type.isTypeFunction();
1741 assert(bestTf);
1743 // Found another overload with different attributes?
1744 // e.g. @system vs. @safe opApply
1745 bool ambig = tf.attributesEqual(bestTf);
1747 // opApplies with identical attributes could still accept
1748 // different function bodies as delegate
1749 // => different parameters or attributes
1750 if (ambig)
1752 // Fetch the delegates that receive the function body
1753 auto tfBody = tf.parameterList[0].type.isTypeDelegate().next;
1754 assert(tfBody);
1756 auto bestBody = bestTf.parameterList[0].type.isTypeDelegate().next;
1757 assert(bestBody);
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);
1764 if (ambig)
1765 fd_ambig = f; // not covariant, so ambiguous
1767 return 0; // continue
1770 if (fd_ambig)
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;
1779 return fd_best;
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
1793 private bool matchParamsToOpApply(TypeFunction tf, Parameters* parameters, bool infer)
1795 enum nomatch = false;
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);
1801 if (tf.parameterList.length != 1)
1802 return nomatch;
1804 /* Get the type of opApply's dg parameter
1806 Parameter p0 = tf.parameterList[0];
1807 auto de = p0.type.isTypeDelegate();
1808 if (!de)
1809 return nomatch;
1810 TypeFunction tdg = de.next.isTypeFunction();
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.
1816 const nparams = tdg.parameterList.length;
1817 if (nparams == 0 || nparams != parameters.dim || tdg.parameterList.varargs != VarArg.none)
1818 return nomatch; // parameter mismatch
1820 foreach (u, p; *parameters)
1822 Parameter param = tdg.parameterList[u];
1823 if (p.type)
1825 if (!p.type.equals(param.type))
1826 return nomatch;
1828 else if (infer)
1830 p.type = param.type;
1831 p.type = p.type.addStorageClass(p.storageClass);
1834 return true;
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
1845 private EXP reverseRelation(EXP op) pure
1847 switch (op)
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;
1855 return op;