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
16 import core
.stdc
.stdio
;
19 import dmd
.arraytypes
;
22 import dmd
.declaration
;
28 import dmd
.expression
;
29 import dmd
.expressionsem
;
34 import dmd
.identifier
;
41 /***********************************
42 * Determine if operands of binary op can be reversed
43 * to fit operator overload.
45 bool isCommutative(EXP op
)
61 case EXP
.greaterOrEqual
:
69 /***********************************
70 * Get Identifier for operator overload.
72 private Identifier
opId(Expression e
)
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
;
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
;
121 /***********************************
122 * Get Identifier for reverse operator overload,
123 * `null` if not supported for this operator.
125 private Identifier
opId_r(Expression e
)
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=
180 case EXP
.leftShiftAssign
:
183 case EXP
.rightShiftAssign
:
186 case EXP
.unsignedRightShiftAssign
:
187 op
= EXP
.unsignedRightShift
;
189 case EXP
.concatenateAssign
:
190 op
= EXP
.concatenate
;
198 Expression e
= new StringExp(Loc
.initial
, EXPtoString(op
));
199 e
= e
.expressionSemantic(sc
);
200 auto tiargs
= new Objects();
205 // Try alias this on first operand
206 private Expression
checkAliasThisForLhs(AggregateDeclaration ad
, Scope
* sc
, BinExp e
)
208 if (!ad ||
!ad
.aliasthis
)
211 /* Rewrite (e1 op e2) as:
212 * (e1.aliasthis op e2)
214 if (isRecursiveAliasThis(e
.att1
, e
.e1
.type
))
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
);
226 if (be
.op
== EXP
.concatenateAssign
)
227 result
= be
.op_overload(sc
);
229 result
= be
.trySemantic(sc
);
234 // Try alias this on second operand
235 private Expression
checkAliasThisForRhs(AggregateDeclaration ad
, Scope
* sc
, BinExp e
)
237 if (!ad ||
!ad
.aliasthis
)
239 /* Rewrite (e1 op e2) as:
240 * (e1 op e2.aliasthis)
242 if (isRecursiveAliasThis(e
.att2
, e
.e2
.type
))
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);
251 if (be
.op
== EXP
.concatenateAssign
)
252 result
= be
.op_overload(sc
);
254 result
= be
.trySemantic(sc
);
259 /************************************
261 * Check for operator overload, if so, replace
262 * with function call.
264 * e = expression with operator
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
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
)
280 Expression
visitUna(UnaExp e
)
282 //printf("UnaExp::op_overload() (%s)\n", e.toChars());
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();
297 if (ae
.e1
.op
== EXP
.error
)
301 Expression e0
= null;
302 Expression ae1save
= ae
.e1
;
304 Type t1b
= ae
.e1
.type
.toBasetype();
305 AggregateDeclaration ad
= isAggregate(t1b
);
308 if (search_function(ad
, Id
.opIndexUnary
))
311 result
= resolveOpDollar(sc
, ae
, &e0
);
312 if (!result
) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j)
314 if (result
.op
== EXP
.error
)
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
);
326 result
= result
.expressionSemantic(sc
);
329 return Expression
.combine(e0
, result
);
333 if (maybeSlice
&& search_function(ad
, Id
.opSliceUnary
))
336 result
= resolveOpDollar(sc
, ae
, ie
, &e0
);
337 if (result
.op
== EXP
.error
)
339 /* Rewrite op(a[i..j]) as:
340 * a.opSliceUnary!(op)(i, j)
342 auto a
= new Expressions();
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
);
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);
367 ae
.e1
= ae1old
; // recovery
370 e
.e1
= e
.e1
.expressionSemantic(sc
);
371 e
.e1
= resolveProperties(sc
, e
.e1
);
372 if (e
.e1
.op
== EXP
.error
)
376 AggregateDeclaration ad
= isAggregate(e
.e1
.type
);
383 fd
= search_function(ad
, Id
.opUnary
);
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
);
392 // D1-style operator overloads, deprecated
393 if (e
.op
!= EXP
.prePlusPlus
&& e
.op
!= EXP
.preMinusMinus
)
396 fd
= search_function(ad
, id
);
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:
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();
415 result
= ue
.trySemantic(sc
);
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();
437 if (ae
.e1
.op
== EXP
.error
)
441 Expression e0
= null;
442 Expression ae1save
= ae
.e1
;
444 Type t1b
= ae
.e1
.type
.toBasetype();
445 AggregateDeclaration ad
= isAggregate(t1b
);
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
455 result
= new SliceExp(ae
.loc
, ae
.e1
, ie
);
456 result
= result
.expressionSemantic(sc
);
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
);
469 if (search_function(ad
, Id
.index
))
472 result
= resolveOpDollar(sc
, ae
, &e0
);
473 if (!result
) // a[i..j] might be: a.opSlice(i, j)
475 if (result
.op
== EXP
.error
)
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
);
486 result
= result
.expressionSemantic(sc
);
489 return Expression
.combine(e0
, result
);
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
);
500 if (maybeSlice
&& search_function(ad
, Id
.slice
))
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());
512 /* Rewrite a[i..j] as:
515 auto a
= new Expressions();
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
);
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);
540 ae
.e1
= ae1old
; // recovery
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());
553 AggregateDeclaration ad
= isAggregate(e
.e1
.type
);
560 fd
= search_function(ad
, Id
._cast
);
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();
574 result
= new DotTemplateInstanceExp(e
.loc
, e
.e1
, fd
.ident
, tiargs
);
575 result
= new CallExp(e
.loc
, result
);
576 result
= result
.expressionSemantic(sc
);
579 // Didn't find it. Forward to aliasthis
580 if (ad
.aliasthis
&& !isRecursiveAliasThis(e
.att1
, e
.e1
.type
))
582 /* Rewrite op(e1) as:
585 if (auto e1
= resolveAliasThis(sc
, e
.e1
, true))
588 (cast(UnaExp
)result
).e1
= e1
;
589 result
= result
.op_overload(sc
);
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
);
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();
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. */
623 Objects
* tiargs
= null;
624 if (e
.op
== EXP
.plusPlus || e
.op
== EXP
.minusMinus
)
627 if (ad1
&& search_function(ad1
, Id
.opUnary
))
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
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();
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
654 // Set tiargs, the template argument list, which will be the operator string
658 id_r
= Id
.opBinaryRight
;
659 tiargs
= opToArg(sc
, e
.op
);
664 // Try the D1-style operators, deprecated
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
);
675 e
.error("`%s` is obsolete. Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id
.toChars(), EXPtoString(e
.op
).ptr
);
676 return ErrorExp
.get();
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.
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();
701 * and see which is better.
705 expandTuples(&args1
);
708 expandTuples(&args2
);
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
;
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();
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
)
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
);
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
);
761 // Retained for D1 compatibility
762 if (isCommutative(e
.op
) && !tiargs
)
768 s_r
= search_function(ad1
, id_r
);
772 s
= search_function(ad2
, id
);
773 if (s
&& s
== s_r
) // https://issues.dlang.org/show_bug.cgi?id=12778
781 * and see which is better.
787 expandTuples(&args1
);
790 expandTuples(&args2
);
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
;
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();
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
)
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
);
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
832 *pop = reverseRelation(e
.op
);
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`
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
)
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
))
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();
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
))
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
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
))
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
);
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
);
967 /* Check for pointer equality.
969 if (t1
.ty
== Tpointer || t2
.ty
== Tpointer
)
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
)
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
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
);
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.
1031 r
.isEqualExp().e1
.type
.toBasetype() == t1
)
1033 e
.error("cannot compare `%s` because its auto generated member-wise equality has recursive definition",
1035 return ErrorExp
.get();
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();
1057 // zero-length tuple comparison should always return true or false.
1058 result
= IntegerExp
.createBool(e
.op
== EXP
.equal
);
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
);
1072 else if (e
.op
== EXP
.equal
)
1073 result
= new LogicalExp(e
.loc
, EXP
.andAnd
, result
, eeq
);
1075 result
= new LogicalExp(e
.loc
, EXP
.orOr
, result
, eeq
);
1079 result
= Expression
.combine(tup1
.e0
, tup2
.e0
, result
);
1080 result
= result
.expressionSemantic(sc
);
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();
1112 if (ae
.e1
.op
== EXP
.error
)
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
);
1123 if (search_function(ad
, Id
.opIndexOpAssign
))
1126 Expression result
= resolveOpDollar(sc
, ae
, &e0
);
1127 if (!result
) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j)
1129 if (result
.op
== EXP
.error
)
1131 result
= e
.e2
.expressionSemantic(sc
);
1132 if (result
.op
== EXP
.error
)
1135 /* Rewrite a[arguments] op= e2 as:
1136 * a.opIndexOpAssign!(op)(e2, arguments)
1138 Expressions
* a
= ae
.arguments
.copy();
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
);
1146 result
= result
.expressionSemantic(sc
);
1149 return Expression
.combine(e0
, result
);
1153 if (maybeSlice
&& search_function(ad
, Id
.opSliceOpAssign
))
1156 Expression result
= resolveOpDollar(sc
, ae
, ie
, &e0
);
1157 if (result
.op
== EXP
.error
)
1159 result
= e
.e2
.expressionSemantic(sc
);
1160 if (result
.op
== EXP
.error
)
1163 /* Rewrite (a[i..j] op= e2) as:
1164 * a.opSliceOpAssign!(op)(e2, i, j)
1166 auto a
= new Expressions();
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
);
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);
1192 ae
.e1
= ae1old
; // recovery
1193 ae
.lengthVar
= null;
1195 Expression result
= e
.binSemanticProp(sc
);
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
);
1205 AggregateDeclaration ad1
= isAggregate(e
.e1
.type
);
1207 Objects
* tiargs
= null;
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
1223 tiargs
= opToArg(sc
, e
.op
);
1226 // Try D1-style operator overload, deprecated
1227 if (!s
&& ad1
&& id
)
1229 s
= search_function(ad1
, id
);
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();
1248 expandTuples(&args2
);
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();
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
)
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
1277 return checkAliasThisForRhs(isAggregate(e
.e2
.type
), sc
, e
);
1285 case EXP
.cast_
: return visitCast(e
.isCastExp());
1286 case EXP
.array
: return visitArray(e
.isArrayExp());
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
);
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
);
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
);
1316 s
= search_function(ad1
, id
);
1320 s_r
= search_function(ad2
, id
);
1324 Objects
* tiargs
= null;
1330 * and see which is better.
1332 Expressions args1
= Expressions(1);
1334 expandTuples(&args1
);
1335 Expressions args2
= Expressions(1);
1337 expandTuples(&args2
);
1341 printf("s : %s\n", s
.toPrettyChars());
1342 printf("s_r: %s\n", s_r
.toPrettyChars());
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
;
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();
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.
1366 * import std.typecons;
1368 * assert(tuple("has a", 2u) == tuple("has a", 1));
1371 if (!(m
.lastf
== lastf
&& m
.count
== 2 && count
== 1))
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
)
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
);
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
1394 *pop = reverseRelation(e
.op
);
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
)
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
)
1416 Declaration decl
= d
.isDeclaration();
1418 e
= new DotVarExp(loc
, ethis
, decl
, false);
1420 e
= new DotIdExp(loc
, ethis
, d
.ident
);
1421 e
= new CallExp(loc
, e
, earg
);
1422 e
= e
.expressionSemantic(sc
);
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
);
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
)
1440 TemplateDeclaration td
= s2
.isTemplateDeclaration();
1447 /**************************************
1448 * Figure out what is being foreach'd over by looking at the ForeachAggregate.
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.
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());
1467 aggr
= aggr
.expressionSemantic(sc
);
1468 aggr
= resolveProperties(sc
, aggr
);
1469 aggr
= aggr
.optimize(WANTvalue
);
1470 if (!aggr
.type || aggr
.op
== EXP
.error
)
1472 Type tab
= aggr
.type
.toBasetype();
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
1484 AggregateDeclaration ad
= (tab
.ty
== Tclass
) ? tab
.isTypeClass().sym
1485 : tab
.isTypeStruct().sym
;
1488 sapply
= search_function(ad
, isForeach ? Id
.apply
: Id
.applyReverse
);
1491 // https://dlang.org/spec/statement.html#foreach_over_struct_and_classes
1492 // opApply aggregate
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
1504 sliced
= true; // only try it once
1509 if (ad
.search(Loc
.initial
, isForeach ? Id
.Ffront
: Id
.Fback
))
1511 // https://dlang.org/spec/statement.html#foreach-with-ranges
1517 if (isRecursiveAliasThis(att
, tab
)) // error, circular alias this
1519 aggr
= resolveAliasThis(sc
, aggr
);
1525 case Tdelegate
: // https://dlang.org/spec/statement.html#foreach_over_delegates
1526 if (auto de = aggr
.isDelegateExp())
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.
1550 * fes = the foreach statement
1552 * sapply = null or opApply or delegate, overload resolution has not been done.
1553 * Do overload resolution on sapply.
1557 bool inferApplyArgTypes(ForeachStatement fes
, Scope
* sc
, ref Dsymbol sapply
)
1559 if (!fes
.parameters ||
!fes
.parameters
.dim
)
1561 if (sapply
) // prefer opApply
1563 foreach (Parameter p
; *fes
.parameters
)
1567 p
.type
= p
.type
.typeSemantic(fes
.loc
, sc
);
1568 p
.type
= p
.type
.addStorageClass(p
.storageClass
);
1572 // Determine ethis for sapply
1574 Type tab
= fes
.aggr
.type
.toBasetype();
1575 if (tab
.ty
== Tclass || tab
.ty
== Tstruct
)
1579 assert(tab
.ty
== Tdelegate
&& fes
.aggr
.op
== EXP
.delegate_
);
1580 ethis
= fes
.aggr
.isDelegateExp().e1
;
1584 * int opApply(int delegate(ref Type [, ...]) dg);
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);
1598 return true; // shouldn't this be false?
1601 Parameter p
= (*fes
.parameters
)[0];
1602 Type taggr
= fes
.aggr
.type
;
1604 Type tab
= taggr
.toBasetype();
1610 if (fes
.parameters
.dim
== 2)
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
);
1628 TypeAArray taa
= tab
.isTypeAArray();
1629 if (fes
.parameters
.dim
== 2)
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];
1642 p
.type
= taa
.next
; // value type
1643 p
.type
= p
.type
.addStorageClass(p
.storageClass
);
1651 AggregateDeclaration ad
= (tab
.ty
== Tclass
) ? tab
.isTypeClass().sym
1652 : tab
.isTypeStruct().sym
;
1653 if (fes
.parameters
.dim
== 1)
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;
1664 // Resolve inout qualifier of front type
1665 p
.type
= fd
.type
.nextOf();
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
;
1687 auto td
= tab
.isTypeDelegate();
1688 if (!matchParamsToOpApply(td
.next
.isTypeFunction(), fes
.parameters
, true))
1694 break; // ignore error, caught later
1699 /*********************************************
1700 * Find best overload match on fstart given ethis and parameters[].
1702 * ethis = expression to use for `this`
1703 * fstart = opApply or foreach delegate
1704 * parameters = ForeachTypeList (i.e. foreach parameters)
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();
1719 return 0; // continue
1720 auto tf
= f
.type
.isTypeFunction();
1721 MATCH m
= MATCH
.exact
;
1724 if (!MODimplicitConv(mod
, tf
.mod
))
1726 else if (mod
!= tf
.mod
)
1729 if (!matchParamsToOpApply(tf
, parameters
, false))
1737 else if (m
== match
&& m
> MATCH
.nomatch
)
1740 auto bestTf
= fd_best
.type
.isTypeFunction();
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
1752 // Fetch the delegates that receive the function body
1753 auto tfBody
= tf
.parameterList
[0].type
.isTypeDelegate().next
;
1756 auto bestBody
= bestTf
.parameterList
[0].type
.isTypeDelegate().next
;
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
);
1765 fd_ambig
= f
; // not covariant, so ambiguous
1767 return 0; // continue
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());
1782 /******************************
1783 * Determine if foreach parameters match opApply parameters.
1784 * Infer missing foreach parameter types from type of opApply delegate.
1786 * tf = type of opApply or delegate
1787 * parameters = foreach parameters
1788 * infer = infer missing parameter types
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)
1804 /* Get the type of opApply's dg parameter
1806 Parameter p0
= tf
.parameterList
[0];
1807 auto de = p0
.type
.isTypeDelegate();
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
];
1825 if (!p
.type
.equals(param
.type
))
1830 p
.type
= param
.type
;
1831 p
.type
= p
.type
.addStorageClass(p
.storageClass
);
1838 * Reverse relational operator, eg >= becomes <=
1839 * Note this is not negation.
1841 * op = comparison operator to reverse
1845 private EXP
reverseRelation(EXP op
) pure
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;