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
16 import core
.stdc
.stdio
;
19 import dmd
.arraytypes
;
22 import dmd
.declaration
;
26 import dmd
.dsymbolsem
;
29 import dmd
.expression
;
30 import dmd
.expressionsem
;
35 import dmd
.identifier
;
44 /***********************************
45 * Determine if operands of binary op can be reversed
46 * to fit operator overload.
48 bool isCommutative(EXP op
) @safe
64 case EXP
.greaterOrEqual
:
72 /***********************************
73 * Get Identifier for operator overload.
75 private Identifier
opId(Expression e
)
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
;
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
;
124 /***********************************
125 * Get Identifier for reverse operator overload,
126 * `null` if not supported for this operator.
128 private Identifier
opId_r(Expression e
)
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=
183 case EXP
.leftShiftAssign
:
186 case EXP
.rightShiftAssign
:
189 case EXP
.unsignedRightShiftAssign
:
190 op
= EXP
.unsignedRightShift
;
192 case EXP
.concatenateAssign
:
193 op
= EXP
.concatenate
;
201 Expression e
= new StringExp(Loc
.initial
, EXPtoString(op
));
202 e
= e
.expressionSemantic(sc
);
203 auto tiargs
= new Objects();
208 // Try alias this on first operand
209 private Expression
checkAliasThisForLhs(AggregateDeclaration ad
, Scope
* sc
, BinExp e
)
211 if (!ad ||
!ad
.aliasthis
)
214 /* Rewrite (e1 op e2) as:
215 * (e1.aliasthis op e2)
217 if (isRecursiveAliasThis(e
.att1
, e
.e1
.type
))
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
);
229 if (be
.op
== EXP
.concatenateAssign
)
230 result
= be
.op_overload(sc
);
232 result
= be
.trySemantic(sc
);
237 // Try alias this on second operand
238 private Expression
checkAliasThisForRhs(AggregateDeclaration ad
, Scope
* sc
, BinExp e
)
240 if (!ad ||
!ad
.aliasthis
)
242 /* Rewrite (e1 op e2) as:
243 * (e1 op e2.aliasthis)
245 if (isRecursiveAliasThis(e
.att2
, e
.e2
.type
))
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);
254 if (be
.op
== EXP
.concatenateAssign
)
255 result
= be
.op_overload(sc
);
257 result
= be
.trySemantic(sc
);
262 /************************************
264 * Check for operator overload, if so, replace
265 * with function call.
267 * e = expression with operator
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
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
)
283 Expression
visitUna(UnaExp e
)
285 //printf("UnaExp::op_overload() (%s)\n", e.toChars());
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
301 if (ae
.e1
.op
== EXP
.error
)
305 Expression e0
= null;
306 Expression ae1save
= ae
.e1
;
308 Type t1b
= ae
.e1
.type
.toBasetype();
309 AggregateDeclaration ad
= isAggregate(t1b
);
312 if (search_function(ad
, Id
.opIndexUnary
))
315 result
= resolveOpDollar(sc
, ae
, &e0
);
316 if (!result
) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j)
318 if (result
.op
== EXP
.error
)
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
);
330 result
= result
.expressionSemantic(sc
);
333 return Expression
.combine(e0
, result
);
337 if (maybeSlice
&& search_function(ad
, Id
.opSliceUnary
))
340 result
= resolveOpDollar(sc
, ae
, ie
, &e0
);
341 if (result
.op
== EXP
.error
)
343 /* Rewrite op(a[i..j]) as:
344 * a.opSliceUnary!(op)(i, j)
346 auto a
= new Expressions();
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
);
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);
371 ae
.e1
= ae1old
; // recovery
374 e
.e1
= e
.e1
.expressionSemantic(sc
);
375 e
.e1
= resolveProperties(sc
, e
.e1
);
376 Type att
= null; // first cyclic `alias this` type
379 if (e
.e1
.op
== EXP
.error
)
384 AggregateDeclaration ad
= isAggregate(e
.e1
.type
);
392 fd
= search_function(ad
, Id
.opUnary
);
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
);
401 // D1-style operator overloads, deprecated
402 if (e
.op
!= EXP
.prePlusPlus
&& e
.op
!= EXP
.preMinusMinus
)
405 fd
= search_function(ad
, id
);
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:
420 //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars());
421 if (auto e1
= resolveAliasThis(sc
, e
.e1
, true))
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();
446 Type att
= null; // first cyclic `alias this` type
449 if (ae
.e1
.op
== EXP
.error
)
453 Expression e0
= null;
454 Expression ae1save
= ae
.e1
;
456 Type t1b
= ae
.e1
.type
.toBasetype();
457 AggregateDeclaration ad
= isAggregate(t1b
);
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
467 result
= new SliceExp(ae
.loc
, ae
.e1
, ie
);
468 result
= result
.expressionSemantic(sc
);
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
);
481 if (search_function(ad
, Id
.index
))
484 result
= resolveOpDollar(sc
, ae
, &e0
);
485 if (!result
) // a[i..j] might be: a.opSlice(i, j)
487 if (result
.op
== EXP
.error
)
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
);
498 result
= result
.expressionSemantic(sc
);
501 return Expression
.combine(e0
, result
);
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
);
512 if (maybeSlice
&& search_function(ad
, Id
.slice
))
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());
524 /* Rewrite a[i..j] as:
527 auto a
= new Expressions();
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
);
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);
552 ae
.e1
= ae1old
; // recovery
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());
565 AggregateDeclaration ad
= isAggregate(e
.e1
.type
);
572 fd
= search_function(ad
, Id
._cast
);
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();
586 result
= new DotTemplateInstanceExp(e
.loc
, e
.e1
, fd
.ident
, tiargs
);
587 result
= new CallExp(e
.loc
, result
);
588 result
= result
.expressionSemantic(sc
);
591 // Didn't find it. Forward to aliasthis
592 if (ad
.aliasthis
&& !isRecursiveAliasThis(att
, e
.e1
.type
))
594 /* Rewrite op(e1) as:
597 if (auto e1
= resolveAliasThis(sc
, e
.e1
, true))
600 (cast(UnaExp
)result
).e1
= e1
;
601 result
= visitCast(result
.isCastExp(), att
);
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
);
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();
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. */
633 Objects
* tiargs
= null;
634 if (e
.op
== EXP
.plusPlus || e
.op
== EXP
.minusMinus
)
637 if (ad1
&& search_function(ad1
, Id
.opUnary
))
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
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();
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
664 // Set tiargs, the template argument list, which will be the operator string
668 id_r
= Id
.opBinaryRight
;
669 tiargs
= opToArg(sc
, e
.op
);
674 // Try the D1-style operators, deprecated
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
);
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();
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.
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();
713 * and see which is better.
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
;
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();
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
)
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
);
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
);
773 // Retained for D1 compatibility
774 if (isCommutative(e
.op
) && !tiargs
)
780 s_r
= search_function(ad1
, id_r
);
784 s
= search_function(ad2
, id
);
785 if (s
&& s
== s_r
) // https://issues.dlang.org/show_bug.cgi?id=12778
793 * and see which is better.
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
;
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();
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
)
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
);
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
844 *pop = reverseRelation(e
.op
);
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`
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
)
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
))
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();
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
))
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
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
))
944 * .object.opEquals(e1, e2)
946 if (!ClassDeclaration
.object
)
948 error(e
.loc
, "cannot compare classes for equality because `object.Object` was not declared");
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
);
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
);
985 /* Check for pointer equality.
987 if (t1
.ty
== Tpointer || t2
.ty
== Tpointer
)
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
)
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
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
);
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();
1058 // zero-length tuple comparison should always return true or false.
1059 result
= IntegerExp
.createBool(e
.op
== EXP
.equal
);
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
);
1071 else if (e
.op
== EXP
.equal
)
1072 result
= new LogicalExp(e
.loc
, EXP
.andAnd
, result
, eeq
);
1074 result
= new LogicalExp(e
.loc
, EXP
.orOr
, result
, eeq
);
1078 result
= Expression
.combine(tup1
.e0
, tup2
.e0
, result
);
1079 result
= result
.expressionSemantic(sc
);
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
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(att
, 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
);
1204 Expressions
* args2
= new Expressions();
1205 AggregateDeclaration ad1
= isAggregate(e
.e1
.type
);
1207 Objects
* tiargs
= null;
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
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 error(e
.loc
, "`%s` is obsolete. Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id
.toChars(), op
.ptr
);
1237 return ErrorExp
.get();
1248 expandTuples(args2
);
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();
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
)
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
1274 return checkAliasThisForRhs(isAggregate(e
.e2
.type
), sc
, e
);
1282 case EXP
.cast_
: return visitCast(e
.isCastExp());
1283 case EXP
.array
: return visitArray(e
.isArrayExp());
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
);
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
);
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
);
1313 s
= search_function(ad1
, id
);
1317 s_r
= search_function(ad2
, id
);
1321 Objects
* tiargs
= null;
1327 * and see which is better.
1329 Expressions
* args1
= new Expressions(1);
1331 expandTuples(args1
);
1332 Expressions
* args2
= new Expressions(1);
1334 expandTuples(args2
);
1338 printf("s : %s\n", s
.toPrettyChars());
1339 printf("s_r: %s\n", s_r
.toPrettyChars());
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
;
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();
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.
1363 * import std.typecons;
1365 * assert(tuple("has a", 2u) == tuple("has a", 1));
1368 if (!(m
.lastf
== lastf
&& m
.count
== 2 && count
== 1))
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
)
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
);
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
1391 *pop = reverseRelation(e
.op
);
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
)
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
)
1413 Declaration decl
= d
.isDeclaration();
1415 e
= new DotVarExp(loc
, ethis
, decl
, false);
1417 e
= new DotIdExp(loc
, ethis
, d
.ident
);
1418 e
= new CallExp(loc
, e
, earg
);
1419 e
= e
.expressionSemantic(sc
);
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
);
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
)
1437 TemplateDeclaration td
= s2
.isTemplateDeclaration();
1444 /**************************************
1445 * Figure out what is being foreach'd over by looking at the ForeachAggregate.
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.
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());
1464 aggr
= aggr
.expressionSemantic(sc
);
1465 aggr
= resolveProperties(sc
, aggr
);
1466 aggr
= aggr
.optimize(WANTvalue
);
1467 if (!aggr
.type || aggr
.op
== EXP
.error
)
1469 Type tab
= aggr
.type
.toBasetype();
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
1481 AggregateDeclaration ad
= (tab
.ty
== Tclass
) ? tab
.isTypeClass().sym
1482 : tab
.isTypeStruct().sym
;
1485 sapply
= search_function(ad
, isForeach ? Id
.apply
: Id
.applyReverse
);
1488 // https://dlang.org/spec/statement.html#foreach_over_struct_and_classes
1489 // opApply aggregate
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
1501 sliced
= true; // only try it once
1506 if (ad
.search(Loc
.initial
, isForeach ? Id
.Ffront
: Id
.Fback
))
1508 // https://dlang.org/spec/statement.html#foreach-with-ranges
1514 if (isRecursiveAliasThis(att
, tab
)) // error, circular alias this
1516 aggr
= resolveAliasThis(sc
, aggr
);
1522 case Tdelegate
: // https://dlang.org/spec/statement.html#foreach_over_delegates
1523 if (auto de = aggr
.isDelegateExp())
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.
1547 * fes = the foreach statement
1549 * sapply = null or opApply or delegate, overload resolution has not been done.
1550 * Do overload resolution on sapply.
1554 bool inferApplyArgTypes(ForeachStatement fes
, Scope
* sc
, ref Dsymbol sapply
)
1556 if (!fes
.parameters ||
!fes
.parameters
.length
)
1558 if (sapply
) // prefer opApply
1560 foreach (Parameter p
; *fes
.parameters
)
1564 p
.type
= p
.type
.typeSemantic(fes
.loc
, sc
);
1565 p
.type
= p
.type
.addStorageClass(p
.storageClass
);
1569 // Determine ethis for sapply
1571 Type tab
= fes
.aggr
.type
.toBasetype();
1572 if (tab
.ty
== Tclass || tab
.ty
== Tstruct
)
1576 assert(tab
.ty
== Tdelegate
&& fes
.aggr
.op
== EXP
.delegate_
);
1577 ethis
= fes
.aggr
.isDelegateExp().e1
;
1581 * int opApply(int delegate(ref Type [, ...]) dg);
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);
1595 return true; // shouldn't this be false?
1598 Parameter p
= (*fes
.parameters
)[0];
1599 Type taggr
= fes
.aggr
.type
;
1601 Type tab
= taggr
.toBasetype();
1607 if (fes
.parameters
.length
== 2)
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
);
1625 TypeAArray taa
= tab
.isTypeAArray();
1626 if (fes
.parameters
.length
== 2)
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];
1639 p
.type
= taa
.next
; // value type
1640 p
.type
= p
.type
.addStorageClass(p
.storageClass
);
1648 AggregateDeclaration ad
= (tab
.ty
== Tclass
) ? tab
.isTypeClass().sym
1649 : tab
.isTypeStruct().sym
;
1650 if (fes
.parameters
.length
== 1)
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;
1661 // Resolve inout qualifier of front type
1662 p
.type
= fd
.type
.nextOf();
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
;
1684 auto td
= tab
.isTypeDelegate();
1685 if (!matchParamsToOpApply(td
.next
.isTypeFunction(), fes
.parameters
, true))
1691 break; // ignore error, caught later
1696 /*********************************************
1697 * Find best overload match on fstart given ethis and parameters[].
1699 * ethis = expression to use for `this`
1700 * fstart = opApply or foreach delegate
1701 * parameters = ForeachTypeList (i.e. foreach parameters)
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();
1716 return 0; // continue
1717 auto tf
= f
.type
.isTypeFunction();
1718 MATCH m
= MATCH
.exact
;
1721 if (!MODimplicitConv(mod
, tf
.mod
))
1723 else if (mod
!= tf
.mod
)
1726 if (!matchParamsToOpApply(tf
, parameters
, false))
1734 else if (m
== match
&& m
> MATCH
.nomatch
)
1737 auto bestTf
= fd_best
.type
.isTypeFunction();
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
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:",
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());
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
.length || 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 @safe
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;