d: Merge upstream dmd ff57fec515, druntime ff57fec515, phobos 17bafda79.
[official-gcc.git] / gcc / d / dmd / opover.d
blobb7bc9257b3ed67243ab8f99b4f45b5e91d4de37d
1 /**
2 * Handles operator overloading.
4 * Specification: $(LINK2 https://dlang.org/spec/operatoroverloading.html, Operator Overloading)
6 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/opover.d, _opover.d)
10 * Documentation: https://dlang.org/phobos/dmd_opover.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/opover.d
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.dsymbolsem;
27 import dmd.dtemplate;
28 import dmd.errors;
29 import dmd.expression;
30 import dmd.expressionsem;
31 import dmd.func;
32 import dmd.globals;
33 import dmd.hdrgen;
34 import dmd.id;
35 import dmd.identifier;
36 import dmd.location;
37 import dmd.mtype;
38 import dmd.optimize;
39 import dmd.statement;
40 import dmd.tokens;
41 import dmd.typesem;
42 import dmd.visitor;
44 /***********************************
45 * Determine if operands of binary op can be reversed
46 * to fit operator overload.
48 bool isCommutative(EXP op) @safe
50 switch (op)
52 case EXP.add:
53 case EXP.mul:
54 case EXP.and:
55 case EXP.or:
56 case EXP.xor:
57 // EqualExp
58 case EXP.equal:
59 case EXP.notEqual:
60 // CmpExp
61 case EXP.lessThan:
62 case EXP.lessOrEqual:
63 case EXP.greaterThan:
64 case EXP.greaterOrEqual:
65 return true;
66 default:
67 break;
69 return false;
72 /***********************************
73 * Get Identifier for operator overload.
75 private Identifier opId(Expression e)
77 switch (e.op)
79 case EXP.uadd: return Id.uadd;
80 case EXP.negate: return Id.neg;
81 case EXP.tilde: return Id.com;
82 case EXP.cast_: return Id._cast;
83 case EXP.in_: return Id.opIn;
84 case EXP.plusPlus: return Id.postinc;
85 case EXP.minusMinus: return Id.postdec;
86 case EXP.add: return Id.add;
87 case EXP.min: return Id.sub;
88 case EXP.mul: return Id.mul;
89 case EXP.div: return Id.div;
90 case EXP.mod: return Id.mod;
91 case EXP.pow: return Id.pow;
92 case EXP.leftShift: return Id.shl;
93 case EXP.rightShift: return Id.shr;
94 case EXP.unsignedRightShift: return Id.ushr;
95 case EXP.and: return Id.iand;
96 case EXP.or: return Id.ior;
97 case EXP.xor: return Id.ixor;
98 case EXP.concatenate: return Id.cat;
99 case EXP.assign: return Id.assign;
100 case EXP.addAssign: return Id.addass;
101 case EXP.minAssign: return Id.subass;
102 case EXP.mulAssign: return Id.mulass;
103 case EXP.divAssign: return Id.divass;
104 case EXP.modAssign: return Id.modass;
105 case EXP.powAssign: return Id.powass;
106 case EXP.leftShiftAssign: return Id.shlass;
107 case EXP.rightShiftAssign: return Id.shrass;
108 case EXP.unsignedRightShiftAssign: return Id.ushrass;
109 case EXP.andAssign: return Id.andass;
110 case EXP.orAssign: return Id.orass;
111 case EXP.xorAssign: return Id.xorass;
112 case EXP.concatenateAssign: return Id.catass;
113 case EXP.equal: return Id.eq;
114 case EXP.lessThan:
115 case EXP.lessOrEqual:
116 case EXP.greaterThan:
117 case EXP.greaterOrEqual: return Id.cmp;
118 case EXP.array: return Id.index;
119 case EXP.star: return Id.opStar;
120 default: assert(0);
124 /***********************************
125 * Get Identifier for reverse operator overload,
126 * `null` if not supported for this operator.
128 private Identifier opId_r(Expression e)
130 switch (e.op)
132 case EXP.in_: return Id.opIn_r;
133 case EXP.add: return Id.add_r;
134 case EXP.min: return Id.sub_r;
135 case EXP.mul: return Id.mul_r;
136 case EXP.div: return Id.div_r;
137 case EXP.mod: return Id.mod_r;
138 case EXP.pow: return Id.pow_r;
139 case EXP.leftShift: return Id.shl_r;
140 case EXP.rightShift: return Id.shr_r;
141 case EXP.unsignedRightShift:return Id.ushr_r;
142 case EXP.and: return Id.iand_r;
143 case EXP.or: return Id.ior_r;
144 case EXP.xor: return Id.ixor_r;
145 case EXP.concatenate: return Id.cat_r;
146 default: return null;
150 /*******************************************
151 * Helper function to turn operator into template argument list
153 Objects* opToArg(Scope* sc, EXP op)
155 /* Remove the = from op=
157 switch (op)
159 case EXP.addAssign:
160 op = EXP.add;
161 break;
162 case EXP.minAssign:
163 op = EXP.min;
164 break;
165 case EXP.mulAssign:
166 op = EXP.mul;
167 break;
168 case EXP.divAssign:
169 op = EXP.div;
170 break;
171 case EXP.modAssign:
172 op = EXP.mod;
173 break;
174 case EXP.andAssign:
175 op = EXP.and;
176 break;
177 case EXP.orAssign:
178 op = EXP.or;
179 break;
180 case EXP.xorAssign:
181 op = EXP.xor;
182 break;
183 case EXP.leftShiftAssign:
184 op = EXP.leftShift;
185 break;
186 case EXP.rightShiftAssign:
187 op = EXP.rightShift;
188 break;
189 case EXP.unsignedRightShiftAssign:
190 op = EXP.unsignedRightShift;
191 break;
192 case EXP.concatenateAssign:
193 op = EXP.concatenate;
194 break;
195 case EXP.powAssign:
196 op = EXP.pow;
197 break;
198 default:
199 break;
201 Expression e = new StringExp(Loc.initial, EXPtoString(op));
202 e = e.expressionSemantic(sc);
203 auto tiargs = new Objects();
204 tiargs.push(e);
205 return tiargs;
208 // Try alias this on first operand
209 private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinExp e)
211 if (!ad || !ad.aliasthis)
212 return null;
214 /* Rewrite (e1 op e2) as:
215 * (e1.aliasthis op e2)
217 if (isRecursiveAliasThis(e.att1, e.e1.type))
218 return null;
219 //printf("att %s e1 = %s\n", Token.toChars(e.op), e.e1.type.toChars());
220 BinExp be = cast(BinExp)e.copy();
221 // Resolve 'alias this' but in case of assigment don't resolve properties yet
222 // because 'e1 = e2' could mean 'e1(e2)' or 'e1() = e2'
223 bool findOnly = (e.op == EXP.assign);
224 be.e1 = resolveAliasThis(sc, e.e1, true, findOnly);
225 if (!be.e1)
226 return null;
228 Expression result;
229 if (be.op == EXP.concatenateAssign)
230 result = be.op_overload(sc);
231 else
232 result = be.trySemantic(sc);
234 return result;
237 // Try alias this on second operand
238 private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinExp e)
240 if (!ad || !ad.aliasthis)
241 return null;
242 /* Rewrite (e1 op e2) as:
243 * (e1 op e2.aliasthis)
245 if (isRecursiveAliasThis(e.att2, e.e2.type))
246 return null;
247 //printf("att %s e2 = %s\n", Token.toChars(e.op), e.e2.type.toChars());
248 BinExp be = cast(BinExp)e.copy();
249 be.e2 = resolveAliasThis(sc, e.e2, true);
250 if (!be.e2)
251 return null;
253 Expression result;
254 if (be.op == EXP.concatenateAssign)
255 result = be.op_overload(sc);
256 else
257 result = be.trySemantic(sc);
259 return result;
262 /************************************
263 * Operator overload.
264 * Check for operator overload, if so, replace
265 * with function call.
266 * Params:
267 * e = expression with operator
268 * sc = context
269 * pop = if not null, is set to the operator that was actually overloaded,
270 * which may not be `e.op`. Happens when operands are reversed to
271 * match an overload
272 * Returns:
273 * `null` if not an operator overload,
274 * otherwise the lowered expression
276 Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
278 Expression visit(Expression e)
280 assert(0);
283 Expression visitUna(UnaExp e)
285 //printf("UnaExp::op_overload() (%s)\n", e.toChars());
286 Expression result;
287 if (auto ae = e.e1.isArrayExp())
289 ae.e1 = ae.e1.expressionSemantic(sc);
290 ae.e1 = resolveProperties(sc, ae.e1);
291 Expression ae1old = ae.e1;
292 const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval);
293 IntervalExp ie = null;
294 if (maybeSlice && ae.arguments.length)
296 ie = (*ae.arguments)[0].isIntervalExp();
298 Type att = null; // first cyclic `alias this` type
299 while (true)
301 if (ae.e1.op == EXP.error)
303 return ae.e1;
305 Expression e0 = null;
306 Expression ae1save = ae.e1;
307 ae.lengthVar = null;
308 Type t1b = ae.e1.type.toBasetype();
309 AggregateDeclaration ad = isAggregate(t1b);
310 if (!ad)
311 break;
312 if (search_function(ad, Id.opIndexUnary))
314 // Deal with $
315 result = resolveOpDollar(sc, ae, &e0);
316 if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j)
317 goto Lfallback;
318 if (result.op == EXP.error)
319 return result;
320 /* Rewrite op(a[arguments]) as:
321 * a.opIndexUnary!(op)(arguments)
323 Expressions* a = ae.arguments.copy();
324 Objects* tiargs = opToArg(sc, e.op);
325 result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexUnary, tiargs);
326 result = new CallExp(e.loc, result, a);
327 if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)()
328 result = result.trySemantic(sc);
329 else
330 result = result.expressionSemantic(sc);
331 if (result)
333 return Expression.combine(e0, result);
336 Lfallback:
337 if (maybeSlice && search_function(ad, Id.opSliceUnary))
339 // Deal with $
340 result = resolveOpDollar(sc, ae, ie, &e0);
341 if (result.op == EXP.error)
342 return result;
343 /* Rewrite op(a[i..j]) as:
344 * a.opSliceUnary!(op)(i, j)
346 auto a = new Expressions();
347 if (ie)
349 a.push(ie.lwr);
350 a.push(ie.upr);
352 Objects* tiargs = opToArg(sc, e.op);
353 result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceUnary, tiargs);
354 result = new CallExp(e.loc, result, a);
355 result = result.expressionSemantic(sc);
356 result = Expression.combine(e0, result);
357 return result;
359 // Didn't find it. Forward to aliasthis
360 if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
362 /* Rewrite op(a[arguments]) as:
363 * op(a.aliasthis[arguments])
365 ae.e1 = resolveAliasThis(sc, ae1save, true);
366 if (ae.e1)
367 continue;
369 break;
371 ae.e1 = ae1old; // recovery
372 ae.lengthVar = null;
374 e.e1 = e.e1.expressionSemantic(sc);
375 e.e1 = resolveProperties(sc, e.e1);
376 Type att = null; // first cyclic `alias this` type
377 while (1)
379 if (e.e1.op == EXP.error)
381 return e.e1;
384 AggregateDeclaration ad = isAggregate(e.e1.type);
385 if (!ad)
386 break;
388 Dsymbol fd = null;
389 /* Rewrite as:
390 * e1.opUnary!(op)()
392 fd = search_function(ad, Id.opUnary);
393 if (fd)
395 Objects* tiargs = opToArg(sc, e.op);
396 result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
397 result = new CallExp(e.loc, result);
398 result = result.expressionSemantic(sc);
399 return result;
401 // D1-style operator overloads, deprecated
402 if (e.op != EXP.prePlusPlus && e.op != EXP.preMinusMinus)
404 auto id = opId(e);
405 fd = search_function(ad, id);
406 if (fd)
408 // @@@DEPRECATED_2.110@@@.
409 // Deprecated in 2.088, made an error in 2.100
410 error(e.loc, "`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
411 return ErrorExp.get();
414 // Didn't find it. Forward to aliasthis
415 if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type))
417 /* Rewrite op(e1) as:
418 * op(e1.aliasthis)
420 //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars());
421 if (auto e1 = resolveAliasThis(sc, e.e1, true))
423 e.e1 = e1;
424 continue;
426 break;
428 break;
430 return result;
433 Expression visitArray(ArrayExp ae)
435 //printf("ArrayExp::op_overload() (%s)\n", ae.toChars());
436 ae.e1 = ae.e1.expressionSemantic(sc);
437 ae.e1 = resolveProperties(sc, ae.e1);
438 Expression ae1old = ae.e1;
439 const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval);
440 IntervalExp ie = null;
441 if (maybeSlice && ae.arguments.length)
443 ie = (*ae.arguments)[0].isIntervalExp();
445 Expression result;
446 Type att = null; // first cyclic `alias this` type
447 while (true)
449 if (ae.e1.op == EXP.error)
451 return ae.e1;
453 Expression e0 = null;
454 Expression ae1save = ae.e1;
455 ae.lengthVar = null;
456 Type t1b = ae.e1.type.toBasetype();
457 AggregateDeclaration ad = isAggregate(t1b);
458 if (!ad)
460 // If the non-aggregate expression ae.e1 is indexable or sliceable,
461 // convert it to the corresponding concrete expression.
462 if (isIndexableNonAggregate(t1b) || ae.e1.op == EXP.type)
464 // Convert to SliceExp
465 if (maybeSlice)
467 result = new SliceExp(ae.loc, ae.e1, ie);
468 result = result.expressionSemantic(sc);
469 return result;
471 // Convert to IndexExp
472 if (ae.arguments.length == 1)
474 result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]);
475 result = result.expressionSemantic(sc);
476 return result;
479 break;
481 if (search_function(ad, Id.index))
483 // Deal with $
484 result = resolveOpDollar(sc, ae, &e0);
485 if (!result) // a[i..j] might be: a.opSlice(i, j)
486 goto Lfallback;
487 if (result.op == EXP.error)
488 return result;
489 /* Rewrite e1[arguments] as:
490 * e1.opIndex(arguments)
492 Expressions* a = ae.arguments.copy();
493 result = new DotIdExp(ae.loc, ae.e1, Id.index);
494 result = new CallExp(ae.loc, result, a);
495 if (maybeSlice) // a[] might be: a.opSlice()
496 result = result.trySemantic(sc);
497 else
498 result = result.expressionSemantic(sc);
499 if (result)
501 return Expression.combine(e0, result);
504 Lfallback:
505 if (maybeSlice && ae.e1.op == EXP.type)
507 result = new SliceExp(ae.loc, ae.e1, ie);
508 result = result.expressionSemantic(sc);
509 result = Expression.combine(e0, result);
510 return result;
512 if (maybeSlice && search_function(ad, Id.slice))
514 // Deal with $
515 result = resolveOpDollar(sc, ae, ie, &e0);
517 if (result.op == EXP.error)
519 if (!e0 && !search_function(ad, Id.dollar)) {
520 ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars());
522 return result;
524 /* Rewrite a[i..j] as:
525 * a.opSlice(i, j)
527 auto a = new Expressions();
528 if (ie)
530 a.push(ie.lwr);
531 a.push(ie.upr);
533 result = new DotIdExp(ae.loc, ae.e1, Id.slice);
534 result = new CallExp(ae.loc, result, a);
535 result = result.expressionSemantic(sc);
536 result = Expression.combine(e0, result);
537 return result;
539 // Didn't find it. Forward to aliasthis
540 if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
542 //printf("att arr e1 = %s\n", this.e1.type.toChars());
543 /* Rewrite op(a[arguments]) as:
544 * op(a.aliasthis[arguments])
546 ae.e1 = resolveAliasThis(sc, ae1save, true);
547 if (ae.e1)
548 continue;
550 break;
552 ae.e1 = ae1old; // recovery
553 ae.lengthVar = null;
554 return result;
557 /***********************************************
558 * This is mostly the same as UnaryExp::op_overload(), but has
559 * a different rewrite.
561 Expression visitCast(CastExp e, Type att = null)
563 //printf("CastExp::op_overload() (%s)\n", e.toChars());
564 Expression result;
565 AggregateDeclaration ad = isAggregate(e.e1.type);
566 if (ad)
568 Dsymbol fd = null;
569 /* Rewrite as:
570 * e1.opCast!(T)()
572 fd = search_function(ad, Id._cast);
573 if (fd)
575 version (all)
577 // Backwards compatibility with D1 if opCast is a function, not a template
578 if (fd.isFuncDeclaration())
580 // Rewrite as: e1.opCast()
581 return build_overload(e.loc, sc, e.e1, null, fd);
584 auto tiargs = new Objects();
585 tiargs.push(e.to);
586 result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
587 result = new CallExp(e.loc, result);
588 result = result.expressionSemantic(sc);
589 return result;
591 // Didn't find it. Forward to aliasthis
592 if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type))
594 /* Rewrite op(e1) as:
595 * op(e1.aliasthis)
597 if (auto e1 = resolveAliasThis(sc, e.e1, true))
599 result = e.copy();
600 (cast(UnaExp)result).e1 = e1;
601 result = visitCast(result.isCastExp(), att);
602 return result;
606 return result;
609 Expression visitBin(BinExp e)
611 //printf("BinExp::op_overload() (%s)\n", e.toChars());
612 Identifier id = opId(e);
613 Identifier id_r = opId_r(e);
614 int argsset = 0;
615 AggregateDeclaration ad1 = isAggregate(e.e1.type);
616 AggregateDeclaration ad2 = isAggregate(e.e2.type);
617 if (e.op == EXP.assign && ad1 == ad2)
619 StructDeclaration sd = ad1.isStructDeclaration();
620 if (sd &&
621 (!sd.hasIdentityAssign ||
622 /* Do a blit if we can and the rvalue is something like .init,
623 * where a postblit is not necessary.
625 (sd.hasBlitAssign && !e.e2.isLvalue())))
627 /* This is bitwise struct assignment. */
628 return null;
631 Dsymbol s = null;
632 Dsymbol s_r = null;
633 Objects* tiargs = null;
634 if (e.op == EXP.plusPlus || e.op == EXP.minusMinus)
636 // Bug4099 fix
637 if (ad1 && search_function(ad1, Id.opUnary))
638 return null;
640 if (e.op != EXP.equal && e.op != EXP.notEqual && e.op != EXP.assign && e.op != EXP.plusPlus && e.op != EXP.minusMinus)
642 /* Try opBinary and opBinaryRight
644 if (ad1)
646 s = search_function(ad1, Id.opBinary);
647 if (s && !s.isTemplateDeclaration())
649 error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars());
650 return ErrorExp.get();
653 if (ad2)
655 s_r = search_function(ad2, Id.opBinaryRight);
656 if (s_r && !s_r.isTemplateDeclaration())
658 error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars());
659 return ErrorExp.get();
661 if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778
662 s_r = null;
664 // Set tiargs, the template argument list, which will be the operator string
665 if (s || s_r)
667 id = Id.opBinary;
668 id_r = Id.opBinaryRight;
669 tiargs = opToArg(sc, e.op);
672 if (!s && !s_r)
674 // Try the D1-style operators, deprecated
675 if (ad1 && id)
677 s = search_function(ad1, id);
678 if (s && id != Id.assign)
680 // @@@DEPRECATED_2.110@@@.
681 // Deprecated in 2.088, made an error in 2.100
682 if (id == Id.postinc || id == Id.postdec)
683 error(e.loc, "`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
684 else
685 error(e.loc, "`%s` is obsolete. Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
686 return ErrorExp.get();
689 if (ad2 && id_r)
691 s_r = search_function(ad2, id_r);
692 // https://issues.dlang.org/show_bug.cgi?id=12778
693 // If both x.opBinary(y) and y.opBinaryRight(x) found,
694 // and they are exactly same symbol, x.opBinary(y) should be preferred.
695 if (s_r && s_r == s)
696 s_r = null;
697 if (s_r)
699 // @@@DEPRECATED_2.110@@@.
700 // Deprecated in 2.088, made an error in 2.100
701 error(e.loc, "`%s` is obsolete. Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), EXPtoString(e.op).ptr);
702 return ErrorExp.get();
706 Expressions* args1 = new Expressions();
707 Expressions* args2 = new Expressions();
708 if (s || s_r)
710 /* Try:
711 * a.opfunc(b)
712 * b.opfunc_r(a)
713 * and see which is better.
715 args1.setDim(1);
716 (*args1)[0] = e.e1;
717 expandTuples(args1);
718 args2.setDim(1);
719 (*args2)[0] = e.e2;
720 expandTuples(args2);
721 argsset = 1;
722 MatchAccumulator m;
723 if (s)
725 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2));
726 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
728 return ErrorExp.get();
731 FuncDeclaration lastf = m.lastf;
732 if (s_r)
734 functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1));
735 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
737 return ErrorExp.get();
740 if (m.count > 1)
742 // Error, ambiguous
743 error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
745 else if (m.last == MATCH.nomatch)
747 if (tiargs)
748 goto L1;
749 m.lastf = null;
751 if (e.op == EXP.plusPlus || e.op == EXP.minusMinus)
753 // Kludge because operator overloading regards e++ and e--
754 // as unary, but it's implemented as a binary.
755 // Rewrite (e1 ++ e2) as e1.postinc()
756 // Rewrite (e1 -- e2) as e1.postdec()
757 return build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s);
759 else if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch)
761 // Rewrite (e1 op e2) as e1.opfunc(e2)
762 return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
764 else
766 // Rewrite (e1 op e2) as e2.opfunc_r(e1)
767 return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
771 version (all)
773 // Retained for D1 compatibility
774 if (isCommutative(e.op) && !tiargs)
776 s = null;
777 s_r = null;
778 if (ad1 && id_r)
780 s_r = search_function(ad1, id_r);
782 if (ad2 && id)
784 s = search_function(ad2, id);
785 if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778
786 s = null;
788 if (s || s_r)
790 /* Try:
791 * a.opfunc_r(b)
792 * b.opfunc(a)
793 * and see which is better.
795 if (!argsset)
797 args1.setDim(1);
798 (*args1)[0] = e.e1;
799 expandTuples(args1);
800 args2.setDim(1);
801 (*args2)[0] = e.e2;
802 expandTuples(args2);
804 MatchAccumulator m;
805 if (s_r)
807 functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2));
808 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
810 return ErrorExp.get();
813 FuncDeclaration lastf = m.lastf;
814 if (s)
816 functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1));
817 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
819 return ErrorExp.get();
822 if (m.count > 1)
824 // Error, ambiguous
825 error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
827 else if (m.last == MATCH.nomatch)
829 m.lastf = null;
832 if (lastf && m.lastf == lastf || !s && m.last == MATCH.nomatch)
834 // Rewrite (e1 op e2) as e1.opfunc_r(e2)
835 return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r);
837 else
839 // Rewrite (e1 op e2) as e2.opfunc(e1)
840 Expression result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s);
841 // When reversing operands of comparison operators,
842 // need to reverse the sense of the op
843 if (pop)
844 *pop = reverseRelation(e.op);
845 return result;
851 Expression rewrittenLhs;
852 if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
854 if (Expression result = checkAliasThisForLhs(ad1, sc, e))
856 /* https://issues.dlang.org/show_bug.cgi?id=19441
858 * alias this may not be used for partial assignment.
859 * If a struct has a single member which is aliased this
860 * directly or aliased to a ref getter function that returns
861 * the mentioned member, then alias this may be
862 * used since the object will be fully initialised.
863 * If the struct is nested, the context pointer is considered
864 * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis`
865 * condition.
867 if (result.op != EXP.assign)
868 return result; // i.e: Rewrote `e1 = e2` -> `e1(e2)`
870 auto ae = result.isAssignExp();
871 if (ae.e1.op != EXP.dotVariable)
872 return result; // i.e: Rewrote `e1 = e2` -> `e1() = e2`
874 auto dve = ae.e1.isDotVarExp();
875 if (auto ad = dve.var.isMember2())
877 // i.e: Rewrote `e1 = e2` -> `e1.some.var = e2`
878 // Ensure that `var` is the only field member in `ad`
879 if (ad.fields.length == 1 || (ad.fields.length == 2 && ad.vthis))
881 if (dve.var == ad.aliasthis.sym)
882 return result;
885 rewrittenLhs = ae.e1;
888 if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
890 if (Expression result = checkAliasThisForRhs(ad2, sc, e))
891 return result;
893 if (rewrittenLhs)
895 error(e.loc, "cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`",
896 e.e1.toChars(), ad1.toChars(), rewrittenLhs.toChars());
897 return ErrorExp.get();
899 return null;
902 Expression visitEqual(EqualExp e)
904 //printf("EqualExp::op_overload() (%s)\n", e.toChars());
905 Type t1 = e.e1.type.toBasetype();
906 Type t2 = e.e2.type.toBasetype();
908 /* Array equality is handled by expressionSemantic() potentially
909 * lowering to object.__equals(), which takes care of overloaded
910 * operators for the element types.
912 if ((t1.ty == Tarray || t1.ty == Tsarray) &&
913 (t2.ty == Tarray || t2.ty == Tsarray))
915 return null;
918 /* Check for class equality with null literal or typeof(null).
920 if (t1.ty == Tclass && e.e2.op == EXP.null_ ||
921 t2.ty == Tclass && e.e1.op == EXP.null_)
923 error(e.loc, "use `%s` instead of `%s` when comparing with `null`",
924 EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr,
925 EXPtoString(e.op).ptr);
926 return ErrorExp.get();
928 if (t1.ty == Tclass && t2.ty == Tnull ||
929 t1.ty == Tnull && t2.ty == Tclass)
931 // Comparing a class with typeof(null) should not call opEquals
932 return null;
935 /* Check for class equality.
937 if (t1.ty == Tclass && t2.ty == Tclass)
939 ClassDeclaration cd1 = t1.isClassHandle();
940 ClassDeclaration cd2 = t2.isClassHandle();
941 if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp))
943 /* Rewrite as:
944 * .object.opEquals(e1, e2)
946 if (!ClassDeclaration.object)
948 error(e.loc, "cannot compare classes for equality because `object.Object` was not declared");
949 return null;
952 Expression e1x = e.e1;
953 Expression e2x = e.e2;
955 /* The explicit cast is necessary for interfaces
956 * https://issues.dlang.org/show_bug.cgi?id=4088
958 Type to = ClassDeclaration.object.getType();
959 if (cd1.isInterfaceDeclaration())
960 e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf());
961 if (cd2.isInterfaceDeclaration())
962 e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf());
964 Expression result = new IdentifierExp(e.loc, Id.empty);
965 result = new DotIdExp(e.loc, result, Id.object);
966 result = new DotIdExp(e.loc, result, Id.eq);
967 result = new CallExp(e.loc, result, e1x, e2x);
968 if (e.op == EXP.notEqual)
969 result = new NotExp(e.loc, result);
970 result = result.expressionSemantic(sc);
971 return result;
975 if (Expression result = compare_overload(e, sc, Id.eq, null))
977 if (lastComma(result).op == EXP.call && e.op == EXP.notEqual)
979 result = new NotExp(result.loc, result);
980 result = result.expressionSemantic(sc);
982 return result;
985 /* Check for pointer equality.
987 if (t1.ty == Tpointer || t2.ty == Tpointer)
989 /* Rewrite:
990 * ptr1 == ptr2
991 * as:
992 * ptr1 is ptr2
994 * This is just a rewriting for deterministic AST representation
995 * as the backend input.
997 auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity;
998 Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2);
999 return r.expressionSemantic(sc);
1002 /* Check for struct equality without opEquals.
1004 if (t1.ty == Tstruct && t2.ty == Tstruct)
1006 auto sd = t1.isTypeStruct().sym;
1007 if (sd != t2.isTypeStruct().sym)
1008 return null;
1010 import dmd.clone : needOpEquals;
1011 if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd))
1013 // Use bitwise equality.
1014 auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity;
1015 Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2);
1016 return r.expressionSemantic(sc);
1019 /* Do memberwise equality.
1020 * https://dlang.org/spec/expression.html#equality_expressions
1021 * Rewrite:
1022 * e1 == e2
1023 * as:
1024 * e1.tupleof == e2.tupleof
1026 * If sd is a nested struct, and if it's nested in a class, it will
1027 * also compare the parent class's equality. Otherwise, compares
1028 * the identity of parent context through void*.
1030 e = e.copy().isEqualExp();
1031 e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof);
1032 e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof);
1034 auto sc2 = sc.push();
1035 sc2.flags |= SCOPE.noaccesscheck;
1036 Expression r = e.expressionSemantic(sc2);
1037 sc2.pop();
1038 return r;
1041 /* Check for tuple equality.
1043 if (e.e1.op == EXP.tuple && e.e2.op == EXP.tuple)
1045 auto tup1 = e.e1.isTupleExp();
1046 auto tup2 = e.e2.isTupleExp();
1047 size_t dim = tup1.exps.length;
1048 if (dim != tup2.exps.length)
1050 error(e.loc, "mismatched sequence lengths, `%d` and `%d`",
1051 cast(int)dim, cast(int)tup2.exps.length);
1052 return ErrorExp.get();
1055 Expression result;
1056 if (dim == 0)
1058 // zero-length tuple comparison should always return true or false.
1059 result = IntegerExp.createBool(e.op == EXP.equal);
1061 else
1063 for (size_t i = 0; i < dim; i++)
1065 auto ex1 = (*tup1.exps)[i];
1066 auto ex2 = (*tup2.exps)[i];
1067 auto eeq = new EqualExp(e.op, e.loc, ex1, ex2);
1069 if (!result)
1070 result = eeq;
1071 else if (e.op == EXP.equal)
1072 result = new LogicalExp(e.loc, EXP.andAnd, result, eeq);
1073 else
1074 result = new LogicalExp(e.loc, EXP.orOr, result, eeq);
1076 assert(result);
1078 result = Expression.combine(tup1.e0, tup2.e0, result);
1079 result = result.expressionSemantic(sc);
1081 return result;
1083 return null;
1086 Expression visitCmp(CmpExp e)
1088 //printf("CmpExp:: () (%s)\n", e.toChars());
1089 return compare_overload(e, sc, Id.cmp, pop);
1092 /*********************************
1093 * Operator overloading for op=
1095 Expression visitBinAssign(BinAssignExp e)
1097 //printf("BinAssignExp::op_overload() (%s)\n", e.toChars());
1098 if (auto ae = e.e1.isArrayExp())
1100 ae.e1 = ae.e1.expressionSemantic(sc);
1101 ae.e1 = resolveProperties(sc, ae.e1);
1102 Expression ae1old = ae.e1;
1103 const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval);
1104 IntervalExp ie = null;
1105 if (maybeSlice && ae.arguments.length)
1107 ie = (*ae.arguments)[0].isIntervalExp();
1109 Type att = null; // first cyclic `alias this` type
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(att, 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 = new Expressions();
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 error(e.loc, "`%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 error(e.loc, "`%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 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2));
1251 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
1253 return ErrorExp.get();
1255 if (m.count > 1)
1257 // Error, ambiguous
1258 error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
1260 else if (m.last == MATCH.nomatch)
1262 if (tiargs)
1263 goto L1;
1264 m.lastf = null;
1266 // Rewrite (e1 op e2) as e1.opOpAssign(e2)
1267 return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
1270 result = checkAliasThisForLhs(ad1, sc, e);
1271 if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs
1272 return result;
1274 return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
1277 if (pop)
1278 *pop = e.op;
1280 switch (e.op)
1282 case EXP.cast_ : return visitCast(e.isCastExp());
1283 case EXP.array : return visitArray(e.isArrayExp());
1285 case EXP.notEqual :
1286 case EXP.equal : return visitEqual(e.isEqualExp());
1288 case EXP.lessOrEqual :
1289 case EXP.greaterThan :
1290 case EXP.greaterOrEqual:
1291 case EXP.lessThan : return visitCmp(cast(CmpExp)e);
1293 default:
1294 if (auto ex = e.isBinAssignExp()) return visitBinAssign(ex);
1295 if (auto ex = e.isBinExp()) return visitBin(ex);
1296 if (auto ex = e.isUnaExp()) return visitUna(ex);
1297 return visit(e);
1301 /******************************************
1302 * Common code for overloading of EqualExp and CmpExp
1304 private Expression compare_overload(BinExp e, Scope* sc, Identifier id, EXP* pop)
1306 //printf("BinExp::compare_overload(id = %s) %s\n", id.toChars(), e.toChars());
1307 AggregateDeclaration ad1 = isAggregate(e.e1.type);
1308 AggregateDeclaration ad2 = isAggregate(e.e2.type);
1309 Dsymbol s = null;
1310 Dsymbol s_r = null;
1311 if (ad1)
1313 s = search_function(ad1, id);
1315 if (ad2)
1317 s_r = search_function(ad2, id);
1318 if (s == s_r)
1319 s_r = null;
1321 Objects* tiargs = null;
1322 if (s || s_r)
1324 /* Try:
1325 * a.opEquals(b)
1326 * b.opEquals(a)
1327 * and see which is better.
1329 Expressions* args1 = new Expressions(1);
1330 (*args1)[0] = e.e1;
1331 expandTuples(args1);
1332 Expressions* args2 = new Expressions(1);
1333 (*args2)[0] = e.e2;
1334 expandTuples(args2);
1335 MatchAccumulator m;
1336 if (0 && s && s_r)
1338 printf("s : %s\n", s.toPrettyChars());
1339 printf("s_r: %s\n", s_r.toPrettyChars());
1341 if (s)
1343 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2));
1344 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
1345 return ErrorExp.get();
1347 FuncDeclaration lastf = m.lastf;
1348 int count = m.count;
1349 if (s_r)
1351 functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1));
1352 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
1353 return ErrorExp.get();
1355 if (m.count > 1)
1357 /* The following if says "not ambiguous" if there's one match
1358 * from s and one from s_r, in which case we pick s.
1359 * This doesn't follow the spec, but is a workaround for the case
1360 * where opEquals was generated from templates and we cannot figure
1361 * out if both s and s_r came from the same declaration or not.
1362 * The test case is:
1363 * import std.typecons;
1364 * void main() {
1365 * assert(tuple("has a", 2u) == tuple("has a", 1));
1368 if (!(m.lastf == lastf && m.count == 2 && count == 1))
1370 // Error, ambiguous
1371 error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
1374 else if (m.last == MATCH.nomatch)
1376 m.lastf = null;
1378 Expression result;
1379 if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch)
1381 // Rewrite (e1 op e2) as e1.opfunc(e2)
1382 result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
1384 else
1386 // Rewrite (e1 op e2) as e2.opfunc_r(e1)
1387 result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
1388 // When reversing operands of comparison operators,
1389 // need to reverse the sense of the op
1390 if (pop)
1391 *pop = reverseRelation(e.op);
1393 return result;
1396 * https://issues.dlang.org/show_bug.cgi?id=16657
1397 * at this point, no matching opEquals was found for structs,
1398 * so we should not follow the alias this comparison code.
1400 if ((e.op == EXP.equal || e.op == EXP.notEqual) && ad1 == ad2)
1401 return null;
1402 Expression result = checkAliasThisForLhs(ad1, sc, e);
1403 return result ? result : checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
1406 /***********************************
1407 * Utility to build a function call out of this reference and argument.
1409 Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d)
1411 assert(d);
1412 Expression e;
1413 Declaration decl = d.isDeclaration();
1414 if (decl)
1415 e = new DotVarExp(loc, ethis, decl, false);
1416 else
1417 e = new DotIdExp(loc, ethis, d.ident);
1418 e = new CallExp(loc, e, earg);
1419 e = e.expressionSemantic(sc);
1420 return e;
1423 /***************************************
1424 * Search for function funcid in aggregate ad.
1426 Dsymbol search_function(ScopeDsymbol ad, Identifier funcid)
1428 Dsymbol s = ad.search(Loc.initial, funcid);
1429 if (s)
1431 //printf("search_function: s = '%s'\n", s.kind());
1432 Dsymbol s2 = s.toAlias();
1433 //printf("search_function: s2 = '%s'\n", s2.kind());
1434 FuncDeclaration fd = s2.isFuncDeclaration();
1435 if (fd && fd.type.ty == Tfunction)
1436 return fd;
1437 TemplateDeclaration td = s2.isTemplateDeclaration();
1438 if (td)
1439 return td;
1441 return null;
1444 /**************************************
1445 * Figure out what is being foreach'd over by looking at the ForeachAggregate.
1446 * Params:
1447 * sc = context
1448 * isForeach = true for foreach, false for foreach_reverse
1449 * feaggr = ForeachAggregate
1450 * sapply = set to function opApply/opApplyReverse, or delegate, or null.
1451 * Overload resolution is not done.
1452 * Returns:
1453 * true if successfully figured it out; feaggr updated with semantic analysis.
1454 * false for failed, which is an error.
1456 bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out Dsymbol sapply)
1458 //printf("inferForeachAggregate(%s)\n", feaggr.toChars());
1459 bool sliced;
1460 Type att = null;
1461 auto aggr = feaggr;
1462 while (1)
1464 aggr = aggr.expressionSemantic(sc);
1465 aggr = resolveProperties(sc, aggr);
1466 aggr = aggr.optimize(WANTvalue);
1467 if (!aggr.type || aggr.op == EXP.error)
1468 return false;
1469 Type tab = aggr.type.toBasetype();
1470 switch (tab.ty)
1472 case Tarray: // https://dlang.org/spec/statement.html#foreach_over_arrays
1473 case Tsarray: // https://dlang.org/spec/statement.html#foreach_over_arrays
1474 case Ttuple: // https://dlang.org/spec/statement.html#foreach_over_tuples
1475 case Taarray: // https://dlang.org/spec/statement.html#foreach_over_associative_arrays
1476 break;
1478 case Tclass:
1479 case Tstruct:
1481 AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym
1482 : tab.isTypeStruct().sym;
1483 if (!sliced)
1485 sapply = search_function(ad, isForeach ? Id.apply : Id.applyReverse);
1486 if (sapply)
1488 // https://dlang.org/spec/statement.html#foreach_over_struct_and_classes
1489 // opApply aggregate
1490 break;
1492 if (feaggr.op != EXP.type)
1494 /* See if rewriting `aggr` to `aggr[]` will work
1496 Expression rinit = new ArrayExp(aggr.loc, feaggr);
1497 rinit = rinit.trySemantic(sc);
1498 if (rinit) // if it worked
1500 aggr = rinit;
1501 sliced = true; // only try it once
1502 continue;
1506 if (ad.search(Loc.initial, isForeach ? Id.Ffront : Id.Fback))
1508 // https://dlang.org/spec/statement.html#foreach-with-ranges
1509 // range aggregate
1510 break;
1512 if (ad.aliasthis)
1514 if (isRecursiveAliasThis(att, tab)) // error, circular alias this
1515 return false;
1516 aggr = resolveAliasThis(sc, aggr);
1517 continue;
1519 return false;
1522 case Tdelegate: // https://dlang.org/spec/statement.html#foreach_over_delegates
1523 if (auto de = aggr.isDelegateExp())
1525 sapply = de.func;
1527 break;
1529 case Terror:
1530 break;
1532 default:
1533 return false;
1535 feaggr = aggr;
1536 return true;
1538 assert(0);
1541 /*****************************************
1542 * Given array of foreach parameters and an aggregate type,
1543 * find best opApply overload,
1544 * if any of the parameter types are missing, attempt to infer
1545 * them from the aggregate type.
1546 * Params:
1547 * fes = the foreach statement
1548 * sc = context
1549 * sapply = null or opApply or delegate, overload resolution has not been done.
1550 * Do overload resolution on sapply.
1551 * Returns:
1552 * false for errors
1554 bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
1556 if (!fes.parameters || !fes.parameters.length)
1557 return false;
1558 if (sapply) // prefer opApply
1560 foreach (Parameter p; *fes.parameters)
1562 if (p.type)
1564 p.type = p.type.typeSemantic(fes.loc, sc);
1565 p.type = p.type.addStorageClass(p.storageClass);
1569 // Determine ethis for sapply
1570 Expression ethis;
1571 Type tab = fes.aggr.type.toBasetype();
1572 if (tab.ty == Tclass || tab.ty == Tstruct)
1573 ethis = fes.aggr;
1574 else
1576 assert(tab.ty == Tdelegate && fes.aggr.op == EXP.delegate_);
1577 ethis = fes.aggr.isDelegateExp().e1;
1580 /* Look for like an
1581 * int opApply(int delegate(ref Type [, ...]) dg);
1582 * overload
1584 if (FuncDeclaration fd = sapply.isFuncDeclaration())
1586 if (auto fdapply = findBestOpApplyMatch(ethis, fd, fes.parameters))
1588 // Fill in any missing types on foreach parameters[]
1589 matchParamsToOpApply(fdapply.type.isTypeFunction(), fes.parameters, true);
1590 sapply = fdapply;
1591 return true;
1593 return false;
1595 return true; // shouldn't this be false?
1598 Parameter p = (*fes.parameters)[0];
1599 Type taggr = fes.aggr.type;
1600 assert(taggr);
1601 Type tab = taggr.toBasetype();
1602 switch (tab.ty)
1604 case Tarray:
1605 case Tsarray:
1606 case Ttuple:
1607 if (fes.parameters.length == 2)
1609 if (!p.type)
1611 p.type = Type.tsize_t; // key type
1612 p.type = p.type.addStorageClass(p.storageClass);
1614 p = (*fes.parameters)[1];
1616 if (!p.type && tab.ty != Ttuple)
1618 p.type = tab.nextOf(); // value type
1619 p.type = p.type.addStorageClass(p.storageClass);
1621 break;
1623 case Taarray:
1625 TypeAArray taa = tab.isTypeAArray();
1626 if (fes.parameters.length == 2)
1628 if (!p.type)
1630 p.type = taa.index; // key type
1631 p.type = p.type.addStorageClass(p.storageClass);
1632 if (p.storageClass & STC.ref_) // key must not be mutated via ref
1633 p.type = p.type.addMod(MODFlags.const_);
1635 p = (*fes.parameters)[1];
1637 if (!p.type)
1639 p.type = taa.next; // value type
1640 p.type = p.type.addStorageClass(p.storageClass);
1642 break;
1645 case Tclass:
1646 case Tstruct:
1648 AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym
1649 : tab.isTypeStruct().sym;
1650 if (fes.parameters.length == 1)
1652 if (!p.type)
1654 /* Look for a front() or back() overload
1656 Identifier id = (fes.op == TOK.foreach_) ? Id.Ffront : Id.Fback;
1657 Dsymbol s = ad.search(Loc.initial, id);
1658 FuncDeclaration fd = s ? s.isFuncDeclaration() : null;
1659 if (fd)
1661 // Resolve inout qualifier of front type
1662 p.type = fd.type.nextOf();
1663 if (p.type)
1665 p.type = p.type.substWildTo(tab.mod);
1666 p.type = p.type.addStorageClass(p.storageClass);
1669 else if (s && s.isTemplateDeclaration())
1672 else if (s && s.isDeclaration())
1673 p.type = s.isDeclaration().type;
1674 else
1675 break;
1677 break;
1679 break;
1682 case Tdelegate:
1684 auto td = tab.isTypeDelegate();
1685 if (!matchParamsToOpApply(td.next.isTypeFunction(), fes.parameters, true))
1686 return false;
1687 break;
1690 default:
1691 break; // ignore error, caught later
1693 return true;
1696 /*********************************************
1697 * Find best overload match on fstart given ethis and parameters[].
1698 * Params:
1699 * ethis = expression to use for `this`
1700 * fstart = opApply or foreach delegate
1701 * parameters = ForeachTypeList (i.e. foreach parameters)
1702 * Returns:
1703 * best match if there is one, null if error
1705 private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration fstart, Parameters* parameters)
1707 MOD mod = ethis.type.mod;
1708 MATCH match = MATCH.nomatch;
1709 FuncDeclaration fd_best;
1710 FuncDeclaration fd_ambig;
1712 overloadApply(fstart, (Dsymbol s)
1714 auto f = s.isFuncDeclaration();
1715 if (!f)
1716 return 0; // continue
1717 auto tf = f.type.isTypeFunction();
1718 MATCH m = MATCH.exact;
1719 if (f.isThis())
1721 if (!MODimplicitConv(mod, tf.mod))
1722 m = MATCH.nomatch;
1723 else if (mod != tf.mod)
1724 m = MATCH.constant;
1726 if (!matchParamsToOpApply(tf, parameters, false))
1727 m = MATCH.nomatch;
1728 if (m > match)
1730 fd_best = f;
1731 fd_ambig = null;
1732 match = m;
1734 else if (m == match && m > MATCH.nomatch)
1736 assert(fd_best);
1737 auto bestTf = fd_best.type.isTypeFunction();
1738 assert(bestTf);
1740 // Found another overload with different attributes?
1741 // e.g. @system vs. @safe opApply
1742 // @@@DEPRECATED_2.112@@@
1743 // See semantic2.d Semantic2Visitor.visit(FuncDeclaration):
1744 // Remove `false` after deprecation period is over.
1745 bool ambig = tf.attributesEqual(bestTf, false);
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:",
1773 ethis.toChars(), fstart.ident.toChars());
1774 .errorSupplemental(fd_best.loc, "`%s`\nand:", fd_best.type.toChars());
1775 .errorSupplemental(fd_ambig.loc, "`%s`", 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.length || 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 @safe
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;