1
/**
2
 * Handles operator overloading.
3
 *
4
 * Specification: $(LINK2 https://dlang.org/spec/operatoroverloading.html, Operator Overloading)
5
 *
6
 * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
7
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/opover.d, _opover.d)
10
 * Documentation:  https://dlang.org/phobos/dmd_opover.html
11
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/opover.d
12
 */
13

14
module dmd.opover;
15

16
import core.stdc.stdio;
17
import dmd.aggregate;
18
import dmd.aliasthis;
19
import dmd.arraytypes;
20
import dmd.dclass;
21
import dmd.declaration;
22
import dmd.dscope;
23
import dmd.dstruct;
24
import dmd.dsymbol;
25
import dmd.dtemplate;
26
import dmd.errors;
27
import dmd.expression;
28
import dmd.expressionsem;
29
import dmd.func;
30
import dmd.globals;
31
import dmd.id;
32
import dmd.identifier;
33
import dmd.mtype;
34
import dmd.statement;
35
import dmd.tokens;
36
import dmd.typesem;
37
import dmd.visitor;
38

39
/***********************************
40
 * Determine if operands of binary op can be reversed
41
 * to fit operator overload.
42
 */
43
bool isCommutative(TOK op)
44
{
45 1
    switch (op)
46
    {
47 1
    case TOK.add:
48 1
    case TOK.mul:
49 1
    case TOK.and:
50 1
    case TOK.or:
51 1
    case TOK.xor:
52
    // EqualExp
53 1
    case TOK.equal:
54 1
    case TOK.notEqual:
55
    // CmpExp
56 1
    case TOK.lessThan:
57 1
    case TOK.lessOrEqual:
58 1
    case TOK.greaterThan:
59 1
    case TOK.greaterOrEqual:
60 1
        return true;
61 1
    default:
62 1
        break;
63
    }
64 1
    return false;
65
}
66

67
/***********************************
68
 * Get Identifier for operator overload.
69
 */
70
private Identifier opId(Expression e)
71
{
72 1
    switch (e.op)
73
    {
74 1
    case TOK.uadd:                      return Id.uadd;
75 1
    case TOK.negate:                    return Id.neg;
76 1
    case TOK.tilde:                     return Id.com;
77 0
    case TOK.cast_:                     return Id._cast;
78 1
    case TOK.in_:                       return Id.opIn;
79 1
    case TOK.plusPlus:                  return Id.postinc;
80 1
    case TOK.minusMinus:                return Id.postdec;
81 1
    case TOK.add:                       return Id.add;
82 1
    case TOK.min:                       return Id.sub;
83 1
    case TOK.mul:                       return Id.mul;
84 1
    case TOK.div:                       return Id.div;
85 1
    case TOK.mod:                       return Id.mod;
86 1
    case TOK.pow:                       return Id.pow;
87 1
    case TOK.leftShift:                 return Id.shl;
88 1
    case TOK.rightShift:                return Id.shr;
89 1
    case TOK.unsignedRightShift:        return Id.ushr;
90 1
    case TOK.and:                       return Id.iand;
91 1
    case TOK.or:                        return Id.ior;
92 1
    case TOK.xor:                       return Id.ixor;
93 1
    case TOK.concatenate:               return Id.cat;
94 1
    case TOK.assign:                    return Id.assign;
95 1
    case TOK.addAssign:                 return Id.addass;
96 1
    case TOK.minAssign:                 return Id.subass;
97 1
    case TOK.mulAssign:                 return Id.mulass;
98 1
    case TOK.divAssign:                 return Id.divass;
99 1
    case TOK.modAssign:                 return Id.modass;
100 1
    case TOK.powAssign:                 return Id.powass;
101 1
    case TOK.leftShiftAssign:           return Id.shlass;
102 1
    case TOK.rightShiftAssign:          return Id.shrass;
103 1
    case TOK.unsignedRightShiftAssign:  return Id.ushrass;
104 1
    case TOK.andAssign:                 return Id.andass;
105 1
    case TOK.orAssign:                  return Id.orass;
106 1
    case TOK.xorAssign:                 return Id.xorass;
107 1
    case TOK.concatenateAssign:         return Id.catass;
108 0
    case TOK.equal:                     return Id.eq;
109 0
    case TOK.lessThan:
110 0
    case TOK.lessOrEqual:
111 0
    case TOK.greaterThan:
112 0
    case TOK.greaterOrEqual:            return Id.cmp;
113 0
    case TOK.array:                     return Id.index;
114 1
    case TOK.star:                      return Id.opStar;
115 0
    default:                            assert(0);
116
    }
117
}
118

119
/***********************************
120
 * Get Identifier for reverse operator overload,
121
 * `null` if not supported for this operator.
122
 */
123
private Identifier opId_r(Expression e)
124
{
125 1
    switch (e.op)
126
    {
127 1
    case TOK.in_:               return Id.opIn_r;
128 1
    case TOK.add:               return Id.add_r;
129 1
    case TOK.min:               return Id.sub_r;
130 1
    case TOK.mul:               return Id.mul_r;
131 1
    case TOK.div:               return Id.div_r;
132 1
    case TOK.mod:               return Id.mod_r;
133 1
    case TOK.pow:               return Id.pow_r;
134 1
    case TOK.leftShift:         return Id.shl_r;
135 1
    case TOK.rightShift:        return Id.shr_r;
136 1
    case TOK.unsignedRightShift:return Id.ushr_r;
137 1
    case TOK.and:               return Id.iand_r;
138 1
    case TOK.or:                return Id.ior_r;
139 1
    case TOK.xor:               return Id.ixor_r;
140 1
    case TOK.concatenate:       return Id.cat_r;
141 1
    default:                    return null;
142
    }
143
}
144

145
/*******************************************
146
 * Helper function to turn operator into template argument list
147
 */
148
Objects* opToArg(Scope* sc, TOK op)
149
{
150
    /* Remove the = from op=
151
     */
152 1
    switch (op)
153
    {
154 1
    case TOK.addAssign:
155 1
        op = TOK.add;
156 1
        break;
157 1
    case TOK.minAssign:
158 1
        op = TOK.min;
159 1
        break;
160 1
    case TOK.mulAssign:
161 1
        op = TOK.mul;
162 1
        break;
163 1
    case TOK.divAssign:
164 1
        op = TOK.div;
165 1
        break;
166 1
    case TOK.modAssign:
167 1
        op = TOK.mod;
168 1
        break;
169 1
    case TOK.andAssign:
170 1
        op = TOK.and;
171 1
        break;
172 1
    case TOK.orAssign:
173 1
        op = TOK.or;
174 1
        break;
175 1
    case TOK.xorAssign:
176 1
        op = TOK.xor;
177 1
        break;
178 1
    case TOK.leftShiftAssign:
179 1
        op = TOK.leftShift;
180 1
        break;
181 1
    case TOK.rightShiftAssign:
182 1
        op = TOK.rightShift;
183 1
        break;
184 1
    case TOK.unsignedRightShiftAssign:
185 1
        op = TOK.unsignedRightShift;
186 1
        break;
187 1
    case TOK.concatenateAssign:
188 1
        op = TOK.concatenate;
189 1
        break;
190 1
    case TOK.powAssign:
191 1
        op = TOK.pow;
192 1
        break;
193 1
    default:
194 1
        break;
195
    }
196 1
    Expression e = new StringExp(Loc.initial, Token.toString(op));
197 1
    e = e.expressionSemantic(sc);
198 1
    auto tiargs = new Objects();
199 1
    tiargs.push(e);
200 1
    return tiargs;
201
}
202

203
// Try alias this on first operand
204
private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinExp e)
205
{
206 1
    if (!ad || !ad.aliasthis)
207 1
        return null;
208

209
    /* Rewrite (e1 op e2) as:
210
     *      (e1.aliasthis op e2)
211
     */
212 1
    if (e.att1 && e.e1.type == e.att1)
213 1
        return null;
214
    //printf("att %s e1 = %s\n", Token::toChars(e.op), e.e1.type.toChars());
215 1
    Expression e1 = new DotIdExp(e.loc, e.e1, ad.aliasthis.ident);
216 1
    BinExp be = cast(BinExp)e.copy();
217 1
    if (!be.att1 && e.e1.type.checkAliasThisRec())
218 1
        be.att1 = e.e1.type;
219 1
    be.e1 = e1;
220

221 1
    Expression result;
222 1
    if (be.op == TOK.concatenateAssign)
223 0
        result = be.op_overload(sc);
224
    else
225 1
        result = be.trySemantic(sc);
226

227 1
    return result;
228
}
229

230
// Try alias this on second operand
231
private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinExp e)
232
{
233 1
    if (!ad || !ad.aliasthis)
234 1
        return null;
235
    /* Rewrite (e1 op e2) as:
236
     *      (e1 op e2.aliasthis)
237
     */
238 1
    if (e.att2 && e.e2.type == e.att2)
239 1
        return null;
240
    //printf("att %s e2 = %s\n", Token::toChars(e.op), e.e2.type.toChars());
241 1
    Expression e2 = new DotIdExp(e.loc, e.e2, ad.aliasthis.ident);
242 1
    BinExp be = cast(BinExp)e.copy();
243 1
    if (!be.att2 && e.e2.type.checkAliasThisRec())
244 1
        be.att2 = e.e2.type;
245 1
    be.e2 = e2;
246

247 1
    Expression result;
248 1
    if (be.op == TOK.concatenateAssign)
249 0
        result = be.op_overload(sc);
250
    else
251 1
        result = be.trySemantic(sc);
252

253 1
    return result;
254
}
255

256
/************************************
257
 * Operator overload.
258
 * Check for operator overload, if so, replace
259
 * with function call.
260
 * Params:
261
 *      e = expression with operator
262
 *      sc = context
263
 *      pop = if not null, is set to the operator that was actually overloaded,
264
 *            which may not be `e.op`. Happens when operands are reversed to
265
 *            match an overload
266
 * Returns:
267
 *      `null` if not an operator overload,
268
 *      otherwise the lowered expression
269
 */
270
Expression op_overload(Expression e, Scope* sc, TOK* pop = null)
271
{
272
    extern (C++) final class OpOverload : Visitor
273
    {
274
        alias visit = Visitor.visit;
275
    public:
276
        Scope* sc;
277
        TOK* pop;
278
        Expression result;
279

280 1
        extern (D) this(Scope* sc, TOK* pop)
281
        {
282 1
            this.sc = sc;
283 1
            this.pop = pop;
284
        }
285

286
        override void visit(Expression e)
287
        {
288 0
            assert(0);
289
        }
290

291
        override void visit(UnaExp e)
292
        {
293
            //printf("UnaExp::op_overload() (%s)\n", e.toChars());
294 1
            if (e.e1.op == TOK.array)
295
            {
296 1
                ArrayExp ae = cast(ArrayExp)e.e1;
297 1
                ae.e1 = ae.e1.expressionSemantic(sc);
298 1
                ae.e1 = resolveProperties(sc, ae.e1);
299 1
                Expression ae1old = ae.e1;
300 1
                const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOK.interval);
301 1
                IntervalExp ie = null;
302 1
                if (maybeSlice && ae.arguments.dim)
303
                {
304 1
                    assert((*ae.arguments)[0].op == TOK.interval);
305 1
                    ie = cast(IntervalExp)(*ae.arguments)[0];
306
                }
307 1
                while (true)
308
                {
309 1
                    if (ae.e1.op == TOK.error)
310
                    {
311 0
                        result = ae.e1;
312 0
                        return;
313
                    }
314 1
                    Expression e0 = null;
315 1
                    Expression ae1save = ae.e1;
316 1
                    ae.lengthVar = null;
317 1
                    Type t1b = ae.e1.type.toBasetype();
318 1
                    AggregateDeclaration ad = isAggregate(t1b);
319 1
                    if (!ad)
320 1
                        break;
321 1
                    if (search_function(ad, Id.opIndexUnary))
322
                    {
323
                        // Deal with $
324 1
                        result = resolveOpDollar(sc, ae, &e0);
325 1
                        if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j)
326 1
                            goto Lfallback;
327 1
                        if (result.op == TOK.error)
328 1
                            return;
329
                        /* Rewrite op(a[arguments]) as:
330
                         *      a.opIndexUnary!(op)(arguments)
331
                         */
332 1
                        Expressions* a = ae.arguments.copy();
333 1
                        Objects* tiargs = opToArg(sc, e.op);
334 1
                        result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexUnary, tiargs);
335 1
                        result = new CallExp(e.loc, result, a);
336 1
                        if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)()
337 1
                            result = result.trySemantic(sc);
338
                        else
339 1
                            result = result.expressionSemantic(sc);
340 1
                        if (result)
341
                        {
342 1
                            result = Expression.combine(e0, result);
343 1
                            return;
344
                        }
345
                    }
346
                Lfallback:
347 1
                    if (maybeSlice && search_function(ad, Id.opSliceUnary))
348
                    {
349
                        // Deal with $
350 1
                        result = resolveOpDollar(sc, ae, ie, &e0);
351 1
                        if (result.op == TOK.error)
352 0
                            return;
353
                        /* Rewrite op(a[i..j]) as:
354
                         *      a.opSliceUnary!(op)(i, j)
355
                         */
356 1
                        auto a = new Expressions();
357 1
                        if (ie)
358
                        {
359 1
                            a.push(ie.lwr);
360 1
                            a.push(ie.upr);
361
                        }
362 1
                        Objects* tiargs = opToArg(sc, e.op);
363 1
                        result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceUnary, tiargs);
364 1
                        result = new CallExp(e.loc, result, a);
365 1
                        result = result.expressionSemantic(sc);
366 1
                        result = Expression.combine(e0, result);
367 1
                        return;
368
                    }
369
                    // Didn't find it. Forward to aliasthis
370 1
                    if (ad.aliasthis && t1b != ae.att1)
371
                    {
372 1
                        if (!ae.att1 && t1b.checkAliasThisRec())
373 1
                            ae.att1 = t1b;
374
                        /* Rewrite op(a[arguments]) as:
375
                         *      op(a.aliasthis[arguments])
376
                         */
377 1
                        ae.e1 = resolveAliasThis(sc, ae1save, true);
378 1
                        if (ae.e1)
379 1
                            continue;
380
                    }
381 1
                    break;
382
                }
383 1
                ae.e1 = ae1old; // recovery
384 1
                ae.lengthVar = null;
385
            }
386 1
            e.e1 = e.e1.expressionSemantic(sc);
387 1
            e.e1 = resolveProperties(sc, e.e1);
388 1
            if (e.e1.op == TOK.error)
389
            {
390 1
                result = e.e1;
391 1
                return;
392
            }
393 1
            AggregateDeclaration ad = isAggregate(e.e1.type);
394 1
            if (ad)
395
            {
396 1
                Dsymbol fd = null;
397
                /* Rewrite as:
398
                 *      e1.opUnary!(op)()
399
                 */
400 1
                fd = search_function(ad, Id.opUnary);
401 1
                if (fd)
402
                {
403 1
                    Objects* tiargs = opToArg(sc, e.op);
404 1
                    result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
405 1
                    result = new CallExp(e.loc, result);
406 1
                    result = result.expressionSemantic(sc);
407 1
                    return;
408
                }
409
                // D1-style operator overloads, deprecated
410 1
                if (e.op != TOK.prePlusPlus && e.op != TOK.preMinusMinus)
411
                {
412 1
                    auto id = opId(e);
413 1
                    fd = search_function(ad, id);
414 1
                    if (fd)
415
                    {
416
                        // @@@DEPRECATED_2.094@@@.
417
                        // Deprecated in 2.088
418
                        // Make an error in 2.094
419 1
                        e.deprecation("`%s` is deprecated.  Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), Token.toChars(e.op));
420
                        // Rewrite +e1 as e1.add()
421 1
                        result = build_overload(e.loc, sc, e.e1, null, fd);
422 1
                        return;
423
                    }
424
                }
425
                // Didn't find it. Forward to aliasthis
426 1
                if (ad.aliasthis && e.e1.type != e.att1)
427
                {
428
                    /* Rewrite op(e1) as:
429
                     *      op(e1.aliasthis)
430
                     */
431
                    //printf("att una %s e1 = %s\n", Token::toChars(op), this.e1.type.toChars());
432 1
                    Expression e1 = new DotIdExp(e.loc, e.e1, ad.aliasthis.ident);
433 1
                    UnaExp ue = cast(UnaExp)e.copy();
434 1
                    if (!ue.att1 && e.e1.type.checkAliasThisRec())
435 1
                        ue.att1 = e.e1.type;
436 1
                    ue.e1 = e1;
437 1
                    result = ue.trySemantic(sc);
438 1
                    return;
439
                }
440
            }
441
        }
442

443
        override void visit(ArrayExp ae)
444
        {
445
            //printf("ArrayExp::op_overload() (%s)\n", ae.toChars());
446 1
            ae.e1 = ae.e1.expressionSemantic(sc);
447 1
            ae.e1 = resolveProperties(sc, ae.e1);
448 1
            Expression ae1old = ae.e1;
449 1
            const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOK.interval);
450 1
            IntervalExp ie = null;
451 1
            if (maybeSlice && ae.arguments.dim)
452
            {
453 1
                assert((*ae.arguments)[0].op == TOK.interval);
454 1
                ie = cast(IntervalExp)(*ae.arguments)[0];
455
            }
456 1
            while (true)
457
            {
458 1
                if (ae.e1.op == TOK.error)
459
                {
460 1
                    result = ae.e1;
461 1
                    return;
462
                }
463 1
                Expression e0 = null;
464 1
                Expression ae1save = ae.e1;
465 1
                ae.lengthVar = null;
466 1
                Type t1b = ae.e1.type.toBasetype();
467 1
                AggregateDeclaration ad = isAggregate(t1b);
468 1
                if (!ad)
469
                {
470
                    // If the non-aggregate expression ae.e1 is indexable or sliceable,
471
                    // convert it to the corresponding concrete expression.
472 1
                    if (isIndexableNonAggregate(t1b) || ae.e1.op == TOK.type)
473
                    {
474
                        // Convert to SliceExp
475 1
                        if (maybeSlice)
476
                        {
477 1
                            result = new SliceExp(ae.loc, ae.e1, ie);
478 1
                            result = result.expressionSemantic(sc);
479 1
                            return;
480
                        }
481
                        // Convert to IndexExp
482 1
                        if (ae.arguments.dim == 1)
483
                        {
484 1
                            result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]);
485 1
                            result = result.expressionSemantic(sc);
486 1
                            return;
487
                        }
488
                    }
489 1
                    break;
490
                }
491 1
                if (search_function(ad, Id.index))
492
                {
493
                    // Deal with $
494 1
                    result = resolveOpDollar(sc, ae, &e0);
495 1
                    if (!result) // a[i..j] might be: a.opSlice(i, j)
496 1
                        goto Lfallback;
497 1
                    if (result.op == TOK.error)
498 1
                        return;
499
                    /* Rewrite e1[arguments] as:
500
                     *      e1.opIndex(arguments)
501
                     */
502 1
                    Expressions* a = ae.arguments.copy();
503 1
                    result = new DotIdExp(ae.loc, ae.e1, Id.index);
504 1
                    result = new CallExp(ae.loc, result, a);
505 1
                    if (maybeSlice) // a[] might be: a.opSlice()
506 1
                        result = result.trySemantic(sc);
507
                    else
508 1
                        result = result.expressionSemantic(sc);
509 1
                    if (result)
510
                    {
511 1
                        result = Expression.combine(e0, result);
512 1
                        return;
513
                    }
514
                }
515
            Lfallback:
516 1
                if (maybeSlice && ae.e1.op == TOK.type)
517
                {
518 1
                    result = new SliceExp(ae.loc, ae.e1, ie);
519 1
                    result = result.expressionSemantic(sc);
520 1
                    result = Expression.combine(e0, result);
521 1
                    return;
522
                }
523 1
                if (maybeSlice && search_function(ad, Id.slice))
524
                {
525
                    // Deal with $
526 1
                    result = resolveOpDollar(sc, ae, ie, &e0);
527 1
                    if (result.op == TOK.error)
528 0
                        return;
529
                    /* Rewrite a[i..j] as:
530
                     *      a.opSlice(i, j)
531
                     */
532 1
                    auto a = new Expressions();
533 1
                    if (ie)
534
                    {
535 1
                        a.push(ie.lwr);
536 1
                        a.push(ie.upr);
537
                    }
538 1
                    result = new DotIdExp(ae.loc, ae.e1, Id.slice);
539 1
                    result = new CallExp(ae.loc, result, a);
540 1
                    result = result.expressionSemantic(sc);
541 1
                    result = Expression.combine(e0, result);
542 1
                    return;
543
                }
544
                // Didn't find it. Forward to aliasthis
545 1
                if (ad.aliasthis && t1b != ae.att1)
546
                {
547 1
                    if (!ae.att1 && t1b.checkAliasThisRec())
548 1
                        ae.att1 = t1b;
549
                    //printf("att arr e1 = %s\n", this.e1.type.toChars());
550
                    /* Rewrite op(a[arguments]) as:
551
                     *      op(a.aliasthis[arguments])
552
                     */
553 1
                    ae.e1 = resolveAliasThis(sc, ae1save, true);
554 1
                    if (ae.e1)
555 1
                        continue;
556
                }
557 1
                break;
558
            }
559 1
            ae.e1 = ae1old; // recovery
560 1
            ae.lengthVar = null;
561
        }
562

563
        /***********************************************
564
         * This is mostly the same as UnaryExp::op_overload(), but has
565
         * a different rewrite.
566
         */
567
        override void visit(CastExp e)
568
        {
569
            //printf("CastExp::op_overload() (%s)\n", e.toChars());
570 1
            AggregateDeclaration ad = isAggregate(e.e1.type);
571 1
            if (ad)
572
            {
573 1
                Dsymbol fd = null;
574
                /* Rewrite as:
575
                 *      e1.opCast!(T)()
576
                 */
577 1
                fd = search_function(ad, Id._cast);
578 1
                if (fd)
579
                {
580
                    version (all)
581
                    {
582
                        // Backwards compatibility with D1 if opCast is a function, not a template
583 1
                        if (fd.isFuncDeclaration())
584
                        {
585
                            // Rewrite as:  e1.opCast()
586 1
                            result = build_overload(e.loc, sc, e.e1, null, fd);
587 1
                            return;
588
                        }
589
                    }
590 1
                    auto tiargs = new Objects();
591 1
                    tiargs.push(e.to);
592 1
                    result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
593 1
                    result = new CallExp(e.loc, result);
594 1
                    result = result.expressionSemantic(sc);
595 1
                    return;
596
                }
597
                // Didn't find it. Forward to aliasthis
598 1
                if (ad.aliasthis)
599
                {
600
                    /* Rewrite op(e1) as:
601
                     *      op(e1.aliasthis)
602
                     */
603 1
                    Expression e1 = resolveAliasThis(sc, e.e1);
604 1
                    result = e.copy();
605 1
                    (cast(UnaExp)result).e1 = e1;
606 1
                    result = result.op_overload(sc);
607 1
                    return;
608
                }
609
            }
610
        }
611

612
        override void visit(BinExp e)
613
        {
614
            //printf("BinExp::op_overload() (%s)\n", e.toChars());
615 1
            Identifier id = opId(e);
616 1
            Identifier id_r = opId_r(e);
617 1
            Expressions args1;
618 1
            Expressions args2;
619 1
            int argsset = 0;
620 1
            AggregateDeclaration ad1 = isAggregate(e.e1.type);
621 1
            AggregateDeclaration ad2 = isAggregate(e.e2.type);
622 1
            if (e.op == TOK.assign && ad1 == ad2)
623
            {
624 1
                StructDeclaration sd = ad1.isStructDeclaration();
625 1
                if (sd &&
626 1
                    (!sd.hasIdentityAssign ||
627
                     /* Do a blit if we can and the rvalue is something like .init,
628
                      * where a postblit is not necessary.
629
                      */
630 1
                     (sd.hasBlitAssign && !e.e2.isLvalue())))
631
                {
632
                    /* This is bitwise struct assignment. */
633 1
                    return;
634
                }
635
            }
636 1
            Dsymbol s = null;
637 1
            Dsymbol s_r = null;
638 1
            Objects* tiargs = null;
639 1
            if (e.op == TOK.plusPlus || e.op == TOK.minusMinus)
640
            {
641
                // Bug4099 fix
642 1
                if (ad1 && search_function(ad1, Id.opUnary))
643 1
                    return;
644
            }
645 1
            if (e.op != TOK.equal && e.op != TOK.notEqual && e.op != TOK.assign && e.op != TOK.plusPlus && e.op != TOK.minusMinus)
646
            {
647
                /* Try opBinary and opBinaryRight
648
                 */
649 1
                if (ad1)
650
                {
651 1
                    s = search_function(ad1, Id.opBinary);
652 1
                    if (s && !s.isTemplateDeclaration())
653
                    {
654 1
                        e.e1.error("`%s.opBinary` isn't a template", e.e1.toChars());
655 1
                        result = ErrorExp.get();
656 1
                        return;
657
                    }
658
                }
659 1
                if (ad2)
660
                {
661 1
                    s_r = search_function(ad2, Id.opBinaryRight);
662 1
                    if (s_r && !s_r.isTemplateDeclaration())
663
                    {
664 1
                        e.e2.error("`%s.opBinaryRight` isn't a template", e.e2.toChars());
665 1
                        result = ErrorExp.get();
666 1
                        return;
667
                    }
668 1
                    if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778
669 1
                        s_r = null;
670
                }
671
                // Set tiargs, the template argument list, which will be the operator string
672 1
                if (s || s_r)
673
                {
674 1
                    id = Id.opBinary;
675 1
                    id_r = Id.opBinaryRight;
676 1
                    tiargs = opToArg(sc, e.op);
677
                }
678
            }
679 1
            if (!s && !s_r)
680
            {
681
                // Try the D1-style operators, deprecated
682 1
                if (ad1 && id)
683
                {
684 1
                    s = search_function(ad1, id);
685 1
                    if (s && id != Id.assign)
686
                    {
687
                        // @@@DEPRECATED_2.094@@@.
688
                        // Deprecated in 2.088
689
                        // Make an error in 2.094
690 1
                        if (id == Id.postinc || id == Id.postdec)
691 1
                            e.deprecation("`%s` is deprecated.  Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), Token.toChars(e.op));
692
                        else
693 1
                            e.deprecation("`%s` is deprecated.  Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), Token.toChars(e.op));
694
                    }
695
                }
696 1
                if (ad2 && id_r)
697
                {
698 1
                    s_r = search_function(ad2, id_r);
699
                    // https://issues.dlang.org/show_bug.cgi?id=12778
700
                    // If both x.opBinary(y) and y.opBinaryRight(x) found,
701
                    // and they are exactly same symbol, x.opBinary(y) should be preferred.
702 1
                    if (s_r && s_r == s)
703 1
                        s_r = null;
704 1
                    if (s_r)
705
                    {
706
                        // @@@DEPRECATED_2.094@@@.
707
                        // Deprecated in 2.088
708
                        // Make an error in 2.094
709 1
                        e.deprecation("`%s` is deprecated.  Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), Token.toChars(e.op));
710
                    }
711
                }
712
            }
713 1
            if (s || s_r)
714
            {
715
                /* Try:
716
                 *      a.opfunc(b)
717
                 *      b.opfunc_r(a)
718
                 * and see which is better.
719
                 */
720 1
                args1.setDim(1);
721 1
                args1[0] = e.e1;
722 1
                expandTuples(&args1);
723 1
                args2.setDim(1);
724 1
                args2[0] = e.e2;
725 1
                expandTuples(&args2);
726 1
                argsset = 1;
727 1
                MatchAccumulator m;
728 1
                if (s)
729
                {
730 1
                    functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
731 1
                    if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
732
                    {
733 1
                        result = ErrorExp.get();
734 1
                        return;
735
                    }
736
                }
737 1
                FuncDeclaration lastf = m.lastf;
738 1
                if (s_r)
739
                {
740 1
                    functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, &args1);
741 1
                    if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
742
                    {
743 1
                        result = ErrorExp.get();
744 1
                        return;
745
                    }
746
                }
747 1
                if (m.count > 1)
748
                {
749
                    // Error, ambiguous
750 1
                    e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
751
                }
752 1
                else if (m.last <= MATCH.nomatch)
753
                {
754 1
                    if (tiargs)
755 1
                        goto L1;
756 1
                    m.lastf = null;
757
                }
758 1
                if (e.op == TOK.plusPlus || e.op == TOK.minusMinus)
759
                {
760
                    // Kludge because operator overloading regards e++ and e--
761
                    // as unary, but it's implemented as a binary.
762
                    // Rewrite (e1 ++ e2) as e1.postinc()
763
                    // Rewrite (e1 -- e2) as e1.postdec()
764 1
                    result = build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s);
765
                }
766 1
                else if (lastf && m.lastf == lastf || !s_r && m.last <= MATCH.nomatch)
767
                {
768
                    // Rewrite (e1 op e2) as e1.opfunc(e2)
769 1
                    result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
770
                }
771
                else
772
                {
773
                    // Rewrite (e1 op e2) as e2.opfunc_r(e1)
774 1
                    result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
775
                }
776 1
                return;
777
            }
778
        L1:
779
            version (all)
780
            {
781
                // Retained for D1 compatibility
782 1
                if (isCommutative(e.op) && !tiargs)
783
                {
784 1
                    s = null;
785 1
                    s_r = null;
786 1
                    if (ad1 && id_r)
787
                    {
788 1
                        s_r = search_function(ad1, id_r);
789
                    }
790 1
                    if (ad2 && id)
791
                    {
792 1
                        s = search_function(ad2, id);
793 1
                        if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778
794 0
                            s = null;
795
                    }
796 1
                    if (s || s_r)
797
                    {
798
                        /* Try:
799
                         *  a.opfunc_r(b)
800
                         *  b.opfunc(a)
801
                         * and see which is better.
802
                         */
803 1
                        if (!argsset)
804
                        {
805 1
                            args1.setDim(1);
806 1
                            args1[0] = e.e1;
807 1
                            expandTuples(&args1);
808 1
                            args2.setDim(1);
809 1
                            args2[0] = e.e2;
810 1
                            expandTuples(&args2);
811
                        }
812 1
                        MatchAccumulator m;
813 1
                        if (s_r)
814
                        {
815 1
                            functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, &args2);
816 1
                            if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
817
                            {
818 0
                                result = ErrorExp.get();
819 0
                                return;
820
                            }
821
                        }
822 1
                        FuncDeclaration lastf = m.lastf;
823 1
                        if (s)
824
                        {
825 1
                            functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, &args1);
826 1
                            if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
827
                            {
828 0
                                result = ErrorExp.get();
829 0
                                return;
830
                            }
831
                        }
832 1
                        if (m.count > 1)
833
                        {
834
                            // Error, ambiguous
835 0
                            e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
836
                        }
837 1
                        else if (m.last <= MATCH.nomatch)
838
                        {
839 1
                            m.lastf = null;
840
                        }
841

842 1
                        if (lastf && m.lastf == lastf || !s && m.last <= MATCH.nomatch)
843
                        {
844
                            // Rewrite (e1 op e2) as e1.opfunc_r(e2)
845 1
                            result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r);
846
                        }
847
                        else
848
                        {
849
                            // Rewrite (e1 op e2) as e2.opfunc(e1)
850 1
                            result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s);
851
                        }
852
                        // When reversing operands of comparison operators,
853
                        // need to reverse the sense of the op
854 1
                        if (pop)
855 0
                            *pop = reverseRelation(e.op);
856 1
                        return;
857
                    }
858
                }
859
            }
860

861 1
            Expression tempResult;
862 1
            if (!(e.op == TOK.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
863
            {
864 1
                result = checkAliasThisForLhs(ad1, sc, e);
865 1
                if (result)
866
                {
867
                    /* https://issues.dlang.org/show_bug.cgi?id=19441
868
                     *
869
                     * alias this may not be used for partial assignment.
870
                     * If a struct has a single member which is aliased this
871
                     * directly or aliased to a ref getter function that returns
872
                     * the mentioned member, then alias this may be
873
                     * used since the object will be fully initialised.
874
                     * If the struct is nested, the context pointer is considered
875
                     * one of the members, hence the `ad1.fields.dim == 2 && ad1.vthis`
876
                     * condition.
877
                     */
878 1
                    if (e.op != TOK.assign || e.e1.op == TOK.type)
879 1
                        return;
880

881 1
                    if (ad1.fields.dim == 1 || (ad1.fields.dim == 2 && ad1.vthis))
882
                    {
883 1
                        auto var = ad1.aliasthis.sym.isVarDeclaration();
884 1
                        if (var && var.type == ad1.fields[0].type)
885 1
                            return;
886

887 0
                        auto func = ad1.aliasthis.sym.isFuncDeclaration();
888 0
                        auto tf = cast(TypeFunction)(func.type);
889 0
                        if (tf.isref && ad1.fields[0].type == tf.next)
890 0
                            return;
891
                    }
892 1
                    tempResult = result;
893
                }
894
            }
895 1
            if (!(e.op == TOK.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
896
            {
897 1
                result = checkAliasThisForRhs(ad2, sc, e);
898 1
                if (result)
899 1
                    return;
900
            }
901

902
            // @@@DEPRECATED_2019-02@@@
903
            // 1. Deprecation for 1 year
904
            // 2. Turn to error after
905 1
            if (tempResult)
906
            {
907
                // move this line where tempResult is assigned to result and turn to error when derecation period is over
908 1
                e.deprecation("Cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`", e.e1.toChars(), ad1.toChars(), (cast(BinExp)tempResult).e1.toChars());
909
                // delete this line when deprecation period is over
910 1
                result = tempResult;
911
            }
912
        }
913

914
        override void visit(EqualExp e)
915
        {
916
            //printf("EqualExp::op_overload() (%s)\n", e.toChars());
917 1
            Type t1 = e.e1.type.toBasetype();
918 1
            Type t2 = e.e2.type.toBasetype();
919

920
            /* Array equality is handled by expressionSemantic() potentially
921
             * lowering to object.__equals(), which takes care of overloaded
922
             * operators for the element types.
923
             */
924 1
            if ((t1.ty == Tarray || t1.ty == Tsarray) &&
925 1
                (t2.ty == Tarray || t2.ty == Tsarray))
926
            {
927 1
                return;
928
            }
929

930
            /* Check for class equality with null literal or typeof(null).
931
             */
932 1
            if (t1.ty == Tclass && e.e2.op == TOK.null_ ||
933 1
                t2.ty == Tclass && e.e1.op == TOK.null_)
934
            {
935 0
                e.error("use `%s` instead of `%s` when comparing with `null`",
936 0
                    Token.toChars(e.op == TOK.equal ? TOK.identity : TOK.notIdentity),
937
                    Token.toChars(e.op));
938 0
                result = ErrorExp.get();
939 0
                return;
940
            }
941 1
            if (t1.ty == Tclass && t2.ty == Tnull ||
942 1
                t1.ty == Tnull && t2.ty == Tclass)
943
            {
944
                // Comparing a class with typeof(null) should not call opEquals
945 1
                return;
946
            }
947

948
            /* Check for class equality.
949
             */
950 1
            if (t1.ty == Tclass && t2.ty == Tclass)
951
            {
952 1
                ClassDeclaration cd1 = t1.isClassHandle();
953 1
                ClassDeclaration cd2 = t2.isClassHandle();
954 1
                if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp))
955
                {
956
                    /* Rewrite as:
957
                     *      .object.opEquals(e1, e2)
958
                     */
959 1
                    Expression e1x = e.e1;
960 1
                    Expression e2x = e.e2;
961

962
                    /* The explicit cast is necessary for interfaces
963
                     * https://issues.dlang.org/show_bug.cgi?id=4088
964
                     */
965 1
                    Type to = ClassDeclaration.object.getType();
966 1
                    if (cd1.isInterfaceDeclaration())
967 1
                        e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf());
968 1
                    if (cd2.isInterfaceDeclaration())
969 1
                        e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf());
970

971 1
                    result = new IdentifierExp(e.loc, Id.empty);
972 1
                    result = new DotIdExp(e.loc, result, Id.object);
973 1
                    result = new DotIdExp(e.loc, result, Id.eq);
974 1
                    result = new CallExp(e.loc, result, e1x, e2x);
975 1
                    if (e.op == TOK.notEqual)
976 1
                        result = new NotExp(e.loc, result);
977 1
                    result = result.expressionSemantic(sc);
978 1
                    return;
979
                }
980
            }
981

982 1
            result = compare_overload(e, sc, Id.eq, null);
983 1
            if (result)
984
            {
985 1
                if (result.op == TOK.call && e.op == TOK.notEqual)
986
                {
987 1
                    result = new NotExp(result.loc, result);
988 1
                    result = result.expressionSemantic(sc);
989
                }
990 1
                return;
991
            }
992

993
            /* Check for pointer equality.
994
             */
995 1
            if (t1.ty == Tpointer || t2.ty == Tpointer)
996
            {
997
                /* Rewrite:
998
                 *      ptr1 == ptr2
999
                 * as:
1000
                 *      ptr1 is ptr2
1001
                 *
1002
                 * This is just a rewriting for deterministic AST representation
1003
                 * as the backend input.
1004
                 */
1005 1
                auto op2 = e.op == TOK.equal ? TOK.identity : TOK.notIdentity;
1006 1
                result = new IdentityExp(op2, e.loc, e.e1, e.e2);
1007 1
                result = result.expressionSemantic(sc);
1008 1
                return;
1009
            }
1010

1011
            /* Check for struct equality without opEquals.
1012
             */
1013 1
            if (t1.ty == Tstruct && t2.ty == Tstruct)
1014
            {
1015 1
                auto sd = (cast(TypeStruct)t1).sym;
1016 1
                if (sd != (cast(TypeStruct)t2).sym)
1017 1
                    return;
1018

1019
                import dmd.clone : needOpEquals;
1020 1
                if (!global.params.fieldwise && !needOpEquals(sd))
1021
                {
1022
                    // Use bitwise equality.
1023 1
                    auto op2 = e.op == TOK.equal ? TOK.identity : TOK.notIdentity;
1024 1
                    result = new IdentityExp(op2, e.loc, e.e1, e.e2);
1025 1
                    result = result.expressionSemantic(sc);
1026 1
                    return;
1027
                }
1028

1029
                /* Do memberwise equality.
1030
                 * https://dlang.org/spec/expression.html#equality_expressions
1031
                 * Rewrite:
1032
                 *      e1 == e2
1033
                 * as:
1034
                 *      e1.tupleof == e2.tupleof
1035
                 *
1036
                 * If sd is a nested struct, and if it's nested in a class, it will
1037
                 * also compare the parent class's equality. Otherwise, compares
1038
                 * the identity of parent context through void*.
1039
                 */
1040 1
                if (e.att1 && t1 == e.att1) return;
1041 1
                if (e.att2 && t2 == e.att2) return;
1042

1043 1
                e = cast(EqualExp)e.copy();
1044 1
                if (!e.att1) e.att1 = t1;
1045 1
                if (!e.att2) e.att2 = t2;
1046 1
                e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof);
1047 1
                e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof);
1048

1049 1
                auto sc2 = sc.push();
1050 1
                sc2.flags = (sc2.flags & ~SCOPE.onlysafeaccess) | SCOPE.noaccesscheck;
1051 1
                result = e.expressionSemantic(sc2);
1052 1
                sc2.pop();
1053

1054
                /* https://issues.dlang.org/show_bug.cgi?id=15292
1055
                 * if the rewrite result is same with the original,
1056
                 * the equality is unresolvable because it has recursive definition.
1057
                 */
1058 1
                if (result.op == e.op &&
1059 1
                    (cast(EqualExp)result).e1.type.toBasetype() == t1)
1060
                {
1061 0
                    e.error("cannot compare `%s` because its auto generated member-wise equality has recursive definition",
1062
                        t1.toChars());
1063 0
                    result = ErrorExp.get();
1064
                }
1065 1
                return;
1066
            }
1067

1068
            /* Check for tuple equality.
1069
             */
1070 1
            if (e.e1.op == TOK.tuple && e.e2.op == TOK.tuple)
1071
            {
1072 1
                auto tup1 = cast(TupleExp)e.e1;
1073 1
                auto tup2 = cast(TupleExp)e.e2;
1074 1
                size_t dim = tup1.exps.dim;
1075 1
                if (dim != tup2.exps.dim)
1076
                {
1077 1
                    e.error("mismatched tuple lengths, `%d` and `%d`",
1078
                        cast(int)dim, cast(int)tup2.exps.dim);
1079 1
                    result = ErrorExp.get();
1080 1
                    return;
1081
                }
1082

1083 1
                if (dim == 0)
1084
                {
1085
                    // zero-length tuple comparison should always return true or false.
1086 1
                    result = IntegerExp.createBool(e.op == TOK.equal);
1087
                }
1088
                else
1089
                {
1090 1
                    for (size_t i = 0; i < dim; i++)
1091
                    {
1092 1
                        auto ex1 = (*tup1.exps)[i];
1093 1
                        auto ex2 = (*tup2.exps)[i];
1094 1
                        auto eeq = new EqualExp(e.op, e.loc, ex1, ex2);
1095 1
                        eeq.att1 = e.att1;
1096 1
                        eeq.att2 = e.att2;
1097

1098 1
                        if (!result)
1099 1
                            result = eeq;
1100 1
                        else if (e.op == TOK.equal)
1101 1
                            result = new LogicalExp(e.loc, TOK.andAnd, result, eeq);
1102
                        else
1103 1
                            result = new LogicalExp(e.loc, TOK.orOr, result, eeq);
1104
                    }
1105 1
                    assert(result);
1106
                }
1107 1
                result = Expression.combine(tup1.e0, tup2.e0, result);
1108 1
                result = result.expressionSemantic(sc);
1109

1110 1
                return;
1111
            }
1112
        }
1113

1114
        override void visit(CmpExp e)
1115
        {
1116
            //printf("CmpExp:: () (%s)\n", e.toChars());
1117 1
            result = compare_overload(e, sc, Id.cmp, pop);
1118
        }
1119

1120
        /*********************************
1121
         * Operator overloading for op=
1122
         */
1123
        override void visit(BinAssignExp e)
1124
        {
1125
            //printf("BinAssignExp::op_overload() (%s)\n", e.toChars());
1126 1
            if (e.e1.op == TOK.array)
1127
            {
1128 1
                ArrayExp ae = cast(ArrayExp)e.e1;
1129 1
                ae.e1 = ae.e1.expressionSemantic(sc);
1130 1
                ae.e1 = resolveProperties(sc, ae.e1);
1131 1
                Expression ae1old = ae.e1;
1132 1
                const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOK.interval);
1133 1
                IntervalExp ie = null;
1134 1
                if (maybeSlice && ae.arguments.dim)
1135
                {
1136 1
                    assert((*ae.arguments)[0].op == TOK.interval);
1137 1
                    ie = cast(IntervalExp)(*ae.arguments)[0];
1138
                }
1139 1
                while (true)
1140
                {
1141 1
                    if (ae.e1.op == TOK.error)
1142
                    {
1143 1
                        result = ae.e1;
1144 1
                        return;
1145
                    }
1146 1
                    Expression e0 = null;
1147 1
                    Expression ae1save = ae.e1;
1148 1
                    ae.lengthVar = null;
1149 1
                    Type t1b = ae.e1.type.toBasetype();
1150 1
                    AggregateDeclaration ad = isAggregate(t1b);
1151 1
                    if (!ad)
1152 1
                        break;
1153 1
                    if (search_function(ad, Id.opIndexOpAssign))
1154
                    {
1155
                        // Deal with $
1156 1
                        result = resolveOpDollar(sc, ae, &e0);
1157 1
                        if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j)
1158 1
                            goto Lfallback;
1159 1
                        if (result.op == TOK.error)
1160 1
                            return;
1161 1
                        result = e.e2.expressionSemantic(sc);
1162 1
                        if (result.op == TOK.error)
1163 0
                            return;
1164 1
                        e.e2 = result;
1165
                        /* Rewrite a[arguments] op= e2 as:
1166
                         *      a.opIndexOpAssign!(op)(e2, arguments)
1167
                         */
1168 1
                        Expressions* a = ae.arguments.copy();
1169 1
                        a.insert(0, e.e2);
1170 1
                        Objects* tiargs = opToArg(sc, e.op);
1171 1
                        result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs);
1172 1
                        result = new CallExp(e.loc, result, a);
1173 1
                        if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2)
1174 1
                            result = result.trySemantic(sc);
1175
                        else
1176 1
                            result = result.expressionSemantic(sc);
1177 1
                        if (result)
1178
                        {
1179 1
                            result = Expression.combine(e0, result);
1180 1
                            return;
1181
                        }
1182
                    }
1183
                Lfallback:
1184 1
                    if (maybeSlice && search_function(ad, Id.opSliceOpAssign))
1185
                    {
1186
                        // Deal with $
1187 1
                        result = resolveOpDollar(sc, ae, ie, &e0);
1188 1
                        if (result.op == TOK.error)
1189 0
                            return;
1190 1
                        result = e.e2.expressionSemantic(sc);
1191 1
                        if (result.op == TOK.error)
1192 0
                            return;
1193 1
                        e.e2 = result;
1194
                        /* Rewrite (a[i..j] op= e2) as:
1195
                         *      a.opSliceOpAssign!(op)(e2, i, j)
1196
                         */
1197 1
                        auto a = new Expressions();
1198 1
                        a.push(e.e2);
1199 1
                        if (ie)
1200
                        {
1201 1
                            a.push(ie.lwr);
1202 1
                            a.push(ie.upr);
1203
                        }
1204 1
                        Objects* tiargs = opToArg(sc, e.op);
1205 1
                        result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs);
1206 1
                        result = new CallExp(e.loc, result, a);
1207 1
                        result = result.expressionSemantic(sc);
1208 1
                        result = Expression.combine(e0, result);
1209 1
                        return;
1210
                    }
1211
                    // Didn't find it. Forward to aliasthis
1212 1
                    if (ad.aliasthis && t1b != ae.att1)
1213
                    {
1214 1
                        if (!ae.att1 && t1b.checkAliasThisRec())
1215 1
                            ae.att1 = t1b;
1216
                        /* Rewrite (a[arguments] op= e2) as:
1217
                         *      a.aliasthis[arguments] op= e2
1218
                         */
1219 1
                        ae.e1 = resolveAliasThis(sc, ae1save, true);
1220 1
                        if (ae.e1)
1221 1
                            continue;
1222
                    }
1223 1
                    break;
1224
                }
1225 1
                ae.e1 = ae1old; // recovery
1226 1
                ae.lengthVar = null;
1227
            }
1228 1
            result = e.binSemanticProp(sc);
1229 1
            if (result)
1230 1
                return;
1231
            // Don't attempt 'alias this' if an error occurred
1232 1
            if (e.e1.type.ty == Terror || e.e2.type.ty == Terror)
1233
            {
1234 0
                result = ErrorExp.get();
1235 0
                return;
1236
            }
1237 1
            Identifier id = opId(e);
1238 1
            Expressions args2;
1239 1
            AggregateDeclaration ad1 = isAggregate(e.e1.type);
1240 1
            Dsymbol s = null;
1241 1
            Objects* tiargs = null;
1242
            /* Try opOpAssign
1243
             */
1244 1
            if (ad1)
1245
            {
1246 1
                s = search_function(ad1, Id.opOpAssign);
1247 1
                if (s && !s.isTemplateDeclaration())
1248
                {
1249 1
                    e.error("`%s.opOpAssign` isn't a template", e.e1.toChars());
1250 1
                    result = ErrorExp.get();
1251 1
                    return;
1252
                }
1253
            }
1254
            // Set tiargs, the template argument list, which will be the operator string
1255 1
            if (s)
1256
            {
1257 1
                id = Id.opOpAssign;
1258 1
                tiargs = opToArg(sc, e.op);
1259
            }
1260

1261
            // Try D1-style operator overload, deprecated
1262 1
            if (!s && ad1 && id)
1263
            {
1264 1
                s = search_function(ad1, id);
1265 1
                if (s)
1266
                {
1267
                    // @@@DEPRECATED_2.094@@@.
1268
                    // Deprecated in 2.088
1269
                    // Make an error in 2.094
1270 1
                    scope char[] op = Token.toString(e.op).dup;
1271 1
                    op[$-1] = '\0'; // remove trailing `=`
1272 1
                    e.deprecation("`%s` is deprecated.  Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr);
1273
                }
1274
            }
1275

1276 1
            if (s)
1277
            {
1278
                /* Try:
1279
                 *      a.opOpAssign(b)
1280
                 */
1281 1
                args2.setDim(1);
1282 1
                args2[0] = e.e2;
1283 1
                expandTuples(&args2);
1284 1
                MatchAccumulator m;
1285 1
                if (s)
1286
                {
1287 1
                    functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
1288 1
                    if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
1289
                    {
1290 0
                        result = ErrorExp.get();
1291 0
                        return;
1292
                    }
1293
                }
1294 1
                if (m.count > 1)
1295
                {
1296
                    // Error, ambiguous
1297 0
                    e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
1298
                }
1299 1
                else if (m.last <= MATCH.nomatch)
1300
                {
1301 1
                    if (tiargs)
1302 1
                        goto L1;
1303 1
                    m.lastf = null;
1304
                }
1305
                // Rewrite (e1 op e2) as e1.opOpAssign(e2)
1306 1
                result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
1307 1
                return;
1308
            }
1309
        L1:
1310 1
            result = checkAliasThisForLhs(ad1, sc, e);
1311 1
            if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs
1312 1
                return;
1313

1314 1
            result = checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
1315
        }
1316
    }
1317

1318 1
    if (pop)
1319 1
        *pop = e.op;
1320 1
    scope OpOverload v = new OpOverload(sc, pop);
1321 1
    e.accept(v);
1322 1
    return v.result;
1323
}
1324

1325
/******************************************
1326
 * Common code for overloading of EqualExp and CmpExp
1327
 */
1328
private Expression compare_overload(BinExp e, Scope* sc, Identifier id, TOK* pop)
1329
{
1330
    //printf("BinExp::compare_overload(id = %s) %s\n", id.toChars(), e.toChars());
1331 1
    AggregateDeclaration ad1 = isAggregate(e.e1.type);
1332 1
    AggregateDeclaration ad2 = isAggregate(e.e2.type);
1333 1
    Dsymbol s = null;
1334 1
    Dsymbol s_r = null;
1335 1
    if (ad1)
1336
    {
1337 1
        s = search_function(ad1, id);
1338
    }
1339 1
    if (ad2)
1340
    {
1341 1
        s_r = search_function(ad2, id);
1342 1
        if (s == s_r)
1343 1
            s_r = null;
1344
    }
1345 1
    Objects* tiargs = null;
1346 1
    if (s || s_r)
1347
    {
1348
        /* Try:
1349
         *      a.opEquals(b)
1350
         *      b.opEquals(a)
1351
         * and see which is better.
1352
         */
1353 1
        Expressions args1 = Expressions(1);
1354 1
        args1[0] = e.e1;
1355 1
        expandTuples(&args1);
1356 1
        Expressions args2 = Expressions(1);
1357 1
        args2[0] = e.e2;
1358 1
        expandTuples(&args2);
1359 1
        MatchAccumulator m;
1360 1
        if (0 && s && s_r)
1361
        {
1362 0
            printf("s  : %s\n", s.toPrettyChars());
1363 0
            printf("s_r: %s\n", s_r.toPrettyChars());
1364
        }
1365 1
        if (s)
1366
        {
1367 1
            functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
1368 1
            if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
1369 1
                return ErrorExp.get();
1370
        }
1371 1
        FuncDeclaration lastf = m.lastf;
1372 1
        int count = m.count;
1373 1
        if (s_r)
1374
        {
1375 1
            functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, &args1);
1376 1
            if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
1377 0
                return ErrorExp.get();
1378
        }
1379 1
        if (m.count > 1)
1380
        {
1381
            /* The following if says "not ambiguous" if there's one match
1382
             * from s and one from s_r, in which case we pick s.
1383
             * This doesn't follow the spec, but is a workaround for the case
1384
             * where opEquals was generated from templates and we cannot figure
1385
             * out if both s and s_r came from the same declaration or not.
1386
             * The test case is:
1387
             *   import std.typecons;
1388
             *   void main() {
1389
             *    assert(tuple("has a", 2u) == tuple("has a", 1));
1390
             *   }
1391
             */
1392 0
            if (!(m.lastf == lastf && m.count == 2 && count == 1))
1393
            {
1394
                // Error, ambiguous
1395 0
                e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
1396
            }
1397
        }
1398 1
        else if (m.last <= MATCH.nomatch)
1399
        {
1400 1
            m.lastf = null;
1401
        }
1402 1
        Expression result;
1403 1
        if (lastf && m.lastf == lastf || !s_r && m.last <= MATCH.nomatch)
1404
        {
1405
            // Rewrite (e1 op e2) as e1.opfunc(e2)
1406 1
            result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
1407
        }
1408
        else
1409
        {
1410
            // Rewrite (e1 op e2) as e2.opfunc_r(e1)
1411 1
            result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
1412
            // When reversing operands of comparison operators,
1413
            // need to reverse the sense of the op
1414 1
            if (pop)
1415 1
                *pop = reverseRelation(e.op);
1416
        }
1417 1
        return result;
1418
    }
1419
    /*
1420
     * https://issues.dlang.org/show_bug.cgi?id=16657
1421
     * at this point, no matching opEquals was found for structs,
1422
     * so we should not follow the alias this comparison code.
1423
     */
1424 1
    if ((e.op == TOK.equal || e.op == TOK.notEqual) && ad1 == ad2)
1425 1
        return null;
1426 1
    Expression result = checkAliasThisForLhs(ad1, sc, e);
1427 1
    return result ? result : checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
1428
}
1429

1430
/***********************************
1431
 * Utility to build a function call out of this reference and argument.
1432
 */
1433
Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d)
1434
{
1435 1
    assert(d);
1436 1
    Expression e;
1437 1
    Declaration decl = d.isDeclaration();
1438 1
    if (decl)
1439 1
        e = new DotVarExp(loc, ethis, decl, false);
1440
    else
1441 1
        e = new DotIdExp(loc, ethis, d.ident);
1442 1
    e = new CallExp(loc, e, earg);
1443 1
    e = e.expressionSemantic(sc);
1444 1
    return e;
1445
}
1446

1447
/***************************************
1448
 * Search for function funcid in aggregate ad.
1449
 */
1450
Dsymbol search_function(ScopeDsymbol ad, Identifier funcid)
1451
{
1452 1
    Dsymbol s = ad.search(Loc.initial, funcid);
1453 1
    if (s)
1454
    {
1455
        //printf("search_function: s = '%s'\n", s.kind());
1456 1
        Dsymbol s2 = s.toAlias();
1457
        //printf("search_function: s2 = '%s'\n", s2.kind());
1458 1
        FuncDeclaration fd = s2.isFuncDeclaration();
1459 1
        if (fd && fd.type.ty == Tfunction)
1460 1
            return fd;
1461 1
        TemplateDeclaration td = s2.isTemplateDeclaration();
1462 1
        if (td)
1463 1
            return td;
1464
    }
1465 1
    return null;
1466
}
1467

1468
/**************************************
1469
 * Figure out what is being foreach'd over by looking at the ForeachAggregate.
1470
 * Params:
1471
 *      sc = context
1472
 *      isForeach = true for foreach, false for foreach_reverse
1473
 *      feaggr = ForeachAggregate
1474
 *      sapply = set to function opApply/opApplyReverse, or delegate, or null.
1475
 *               Overload resolution is not done.
1476
 * Returns:
1477
 *      true if successfully figured it out; feaggr updated with semantic analysis.
1478
 *      false for failed, which is an error.
1479
 */
1480
bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out Dsymbol sapply)
1481
{
1482
    //printf("inferForeachAggregate(%s)\n", feaggr.toChars());
1483 1
    bool sliced;
1484 1
    Type att = null;
1485 1
    auto aggr = feaggr;
1486 1
    while (1)
1487
    {
1488 1
        aggr = aggr.expressionSemantic(sc);
1489 1
        aggr = resolveProperties(sc, aggr);
1490 1
        aggr = aggr.optimize(WANTvalue);
1491 1
        if (!aggr.type || aggr.op == TOK.error)
1492 0
            return false;
1493 1
        Type tab = aggr.type.toBasetype();
1494 1
        switch (tab.ty)
1495
        {
1496 1
        case Tarray:            // https://dlang.org/spec/statement.html#foreach_over_arrays
1497 1
        case Tsarray:           // https://dlang.org/spec/statement.html#foreach_over_arrays
1498 1
        case Ttuple:            // https://dlang.org/spec/statement.html#foreach_over_tuples
1499 1
        case Taarray:           // https://dlang.org/spec/statement.html#foreach_over_associative_arrays
1500 1
            break;
1501

1502 1
        case Tclass:
1503 1
        case Tstruct:
1504
        {
1505 1
            AggregateDeclaration ad = (tab.ty == Tclass) ? (cast(TypeClass)tab).sym
1506 1
                                                         : (cast(TypeStruct)tab).sym;
1507 1
            if (!sliced)
1508
            {
1509 1
                sapply = search_function(ad, isForeach ? Id.apply : Id.applyReverse);
1510 1
                if (sapply)
1511
                {
1512
                    // https://dlang.org/spec/statement.html#foreach_over_struct_and_classes
1513
                    // opApply aggregate
1514 1
                    break;
1515
                }
1516 1
                if (feaggr.op != TOK.type)
1517
                {
1518
                    /* See if rewriting `aggr` to `aggr[]` will work
1519
                     */
1520 1
                    Expression rinit = new ArrayExp(aggr.loc, feaggr);
1521 1
                    rinit = rinit.trySemantic(sc);
1522 1
                    if (rinit) // if it worked
1523
                    {
1524 1
                        aggr = rinit;
1525 1
                        sliced = true;  // only try it once
1526 1
                        continue;
1527
                    }
1528
                }
1529
            }
1530 1
            if (ad.search(Loc.initial, isForeach ? Id.Ffront : Id.Fback))
1531
            {
1532
                // https://dlang.org/spec/statement.html#foreach-with-ranges
1533
                // range aggregate
1534 1
                break;
1535
            }
1536 1
            if (ad.aliasthis)
1537
            {
1538 1
                if (att == tab)         // error, circular alias this
1539 1
                    return false;
1540 1
                if (!att && tab.checkAliasThisRec())
1541 1
                    att = tab;
1542 1
                aggr = resolveAliasThis(sc, aggr);
1543 1
                continue;
1544
            }
1545 1
            return false;
1546
        }
1547

1548 1
        case Tdelegate:        // https://dlang.org/spec/statement.html#foreach_over_delegates
1549 1
            if (aggr.op == TOK.delegate_)
1550
            {
1551 1
                sapply = (cast(DelegateExp)aggr).func;
1552
            }
1553 1
            break;
1554

1555 0
        case Terror:
1556 0
            break;
1557

1558 1
        default:
1559 1
            return false;
1560
        }
1561 1
        feaggr = aggr;
1562 1
        return true;
1563
    }
1564 0
    assert(0);
1565
}
1566

1567
/*****************************************
1568
 * Given array of foreach parameters and an aggregate type,
1569
 * find best opApply overload,
1570
 * if any of the parameter types are missing, attempt to infer
1571
 * them from the aggregate type.
1572
 * Params:
1573
 *      fes = the foreach statement
1574
 *      sc = context
1575
 *      sapply = null or opApply or delegate
1576
 * Returns:
1577
 *      false for errors
1578
 */
1579
bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
1580
{
1581 1
    if (!fes.parameters || !fes.parameters.dim)
1582 0
        return false;
1583 1
    if (sapply) // prefer opApply
1584
    {
1585 1
        foreach (Parameter p; *fes.parameters)
1586
        {
1587 1
            if (p.type)
1588
            {
1589 1
                p.type = p.type.typeSemantic(fes.loc, sc);
1590 1
                p.type = p.type.addStorageClass(p.storageClass);
1591
            }
1592
        }
1593

1594
        // Determine ethis for sapply
1595 1
        Expression ethis;
1596 1
        Type tab = fes.aggr.type.toBasetype();
1597 1
        if (tab.ty == Tclass || tab.ty == Tstruct)
1598 1
            ethis = fes.aggr;
1599
        else
1600
        {
1601 1
            assert(tab.ty == Tdelegate && fes.aggr.op == TOK.delegate_);
1602 1
            ethis = (cast(DelegateExp)fes.aggr).e1;
1603
        }
1604

1605
        /* Look for like an
1606
         *  int opApply(int delegate(ref Type [, ...]) dg);
1607
         * overload
1608
         */
1609 1
        if (FuncDeclaration fd = sapply.isFuncDeclaration())
1610
        {
1611 1
            auto fdapply = findBestOpApplyMatch(ethis, fd, fes.parameters);
1612 1
            if (fdapply)
1613
            {
1614
                // Fill in any missing types on foreach parameters[]
1615 1
                matchParamsToOpApply(cast(TypeFunction)fdapply.type, fes.parameters, true);
1616 1
                sapply = fdapply;
1617 1
                return true;
1618
            }
1619 1
            return false;
1620
        }
1621 1
        return sapply !is null;
1622
    }
1623

1624 1
    Parameter p = (*fes.parameters)[0];
1625 1
    Type taggr = fes.aggr.type;
1626 1
    assert(taggr);
1627 1
    Type tab = taggr.toBasetype();
1628 1
    switch (tab.ty)
1629
    {
1630 1
    case Tarray:
1631 1
    case Tsarray:
1632 1
    case Ttuple:
1633 1
        if (fes.parameters.dim == 2)
1634
        {
1635 1
            if (!p.type)
1636
            {
1637 1
                p.type = Type.tsize_t; // key type
1638 1
                p.type = p.type.addStorageClass(p.storageClass);
1639
            }
1640 1
            p = (*fes.parameters)[1];
1641
        }
1642 1
        if (!p.type && tab.ty != Ttuple)
1643
        {
1644 1
            p.type = tab.nextOf(); // value type
1645 1
            p.type = p.type.addStorageClass(p.storageClass);
1646
        }
1647 1
        break;
1648

1649 1
    case Taarray:
1650
        {
1651 1
            TypeAArray taa = cast(TypeAArray)tab;
1652 1
            if (fes.parameters.dim == 2)
1653
            {
1654 1
                if (!p.type)
1655
                {
1656 1
                    p.type = taa.index; // key type
1657 1
                    p.type = p.type.addStorageClass(p.storageClass);
1658 1
                    if (p.storageClass & STC.ref_) // key must not be mutated via ref
1659 1
                        p.type = p.type.addMod(MODFlags.const_);
1660
                }
1661 1
                p = (*fes.parameters)[1];
1662
            }
1663 1
            if (!p.type)
1664
            {
1665 1
                p.type = taa.next; // value type
1666 1
                p.type = p.type.addStorageClass(p.storageClass);
1667
            }
1668 1
            break;
1669
        }
1670

1671 1
    case Tclass:
1672 1
    case Tstruct:
1673
    {
1674 1
        AggregateDeclaration ad = (tab.ty == Tclass) ? (cast(TypeClass)tab).sym
1675 1
                                                     : (cast(TypeStruct)tab).sym;
1676 1
        if (fes.parameters.dim == 1)
1677
        {
1678 1
            if (!p.type)
1679
            {
1680
                /* Look for a front() or back() overload
1681
                 */
1682 1
                Identifier id = (fes.op == TOK.foreach_) ? Id.Ffront : Id.Fback;
1683 1
                Dsymbol s = ad.search(Loc.initial, id);
1684 1
                FuncDeclaration fd = s ? s.isFuncDeclaration() : null;
1685 1
                if (fd)
1686
                {
1687
                    // Resolve inout qualifier of front type
1688 1
                    p.type = fd.type.nextOf();
1689 1
                    if (p.type)
1690
                    {
1691 1
                        p.type = p.type.substWildTo(tab.mod);
1692 1
                        p.type = p.type.addStorageClass(p.storageClass);
1693
                    }
1694
                }
1695 1
                else if (s && s.isTemplateDeclaration())
1696
                {
1697
                }
1698 1
                else if (s && s.isDeclaration())
1699 1
                    p.type = (cast(Declaration)s).type;
1700
                else
1701 0
                    break;
1702
            }
1703 1
            break;
1704
        }
1705 1
        break;
1706
    }
1707

1708 0
    case Tdelegate:
1709 0
        if (!matchParamsToOpApply(cast(TypeFunction)tab.nextOf(), fes.parameters, true))
1710 0
            return false;
1711 0
        break;
1712

1713 0
    default:
1714 0
        break; // ignore error, caught later
1715
    }
1716 1
    return true;
1717
}
1718

1719
/*********************************************
1720
 * Find best overload match on fstart given ethis and parameters[].
1721
 * Params:
1722
 *      ethis = expression to use for `this`
1723
 *      fstart = opApply or foreach delegate
1724
 *      parameters = ForeachTypeList (i.e. foreach parameters)
1725
 * Returns:
1726
 *      best match if there is one, null if error
1727
 */
1728
private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration fstart, Parameters* parameters)
1729
{
1730 1
    MOD mod = ethis.type.mod;
1731 1
    MATCH match = MATCH.nomatch;
1732 1
    FuncDeclaration fd_best;
1733 1
    FuncDeclaration fd_ambig;
1734

1735 1
    overloadApply(fstart, (Dsymbol s)
1736
    {
1737 1
        auto f = s.isFuncDeclaration();
1738 1
        if (!f)
1739 0
            return 0;           // continue
1740 1
        auto tf = cast(TypeFunction)f.type;
1741 1
        MATCH m = MATCH.exact;
1742 1
        if (f.isThis())
1743
        {
1744 1
            if (!MODimplicitConv(mod, tf.mod))
1745 1
                m = MATCH.nomatch;
1746 1
            else if (mod != tf.mod)
1747 1
                m = MATCH.constant;
1748
        }
1749 1
        if (!matchParamsToOpApply(tf, parameters, false))
1750 1
            m = MATCH.nomatch;
1751 1
        if (m > match)
1752
        {
1753 1
            fd_best = f;
1754 1
            fd_ambig = null;
1755 1
            match = m;
1756
        }
1757 1
        else if (m == match && m > MATCH.nomatch)
1758
        {
1759 1
            assert(fd_best);
1760
            /* Ignore covariant matches, as later on it can be redone
1761
             * after the opApply delegate has its attributes inferred.
1762
             */
1763 1
            if (tf.covariant(fd_best.type) != 1 &&
1764 1
                fd_best.type.covariant(tf) != 1)
1765 1
                fd_ambig = f;                           // not covariant, so ambiguous
1766
        }
1767 1
        return 0;               // continue
1768
    });
1769

1770 1
    if (fd_ambig)
1771
    {
1772 1
        .error(ethis.loc, "`%s.%s` matches more than one declaration:\n`%s`:     `%s`\nand:\n`%s`:     `%s`",
1773
            ethis.toChars(), fstart.ident.toChars(),
1774
            fd_best.loc.toChars(), fd_best.type.toChars(),
1775
            fd_ambig.loc.toChars(), fd_ambig.type.toChars());
1776 1
        return null;
1777
    }
1778

1779 1
    return fd_best;
1780
}
1781

1782
/******************************
1783
 * Determine if foreach parameters match opApply parameters.
1784
 * Infer missing foreach parameter types from type of opApply delegate.
1785
 * Params:
1786
 *      tf = type of opApply or delegate
1787
 *      parameters = foreach parameters
1788
 *      infer = infer missing parameter types
1789
 * Returns:
1790
 *      true for match for this function
1791
 *      false for no match for this function
1792
 */
1793
private bool matchParamsToOpApply(TypeFunction tf, Parameters* parameters, bool infer)
1794
{
1795
    enum nomatch = false;
1796

1797
    /* opApply/delegate has exactly one parameter, and that parameter
1798
     * is a delegate that looks like:
1799
     *     int opApply(int delegate(ref Type [, ...]) dg);
1800
     */
1801 1
    if (tf.parameterList.length != 1)
1802 1
        return nomatch;
1803

1804
    /* Get the type of opApply's dg parameter
1805
     */
1806 1
    Parameter p0 = tf.parameterList[0];
1807 1
    if (p0.type.ty != Tdelegate)
1808 1
        return nomatch;
1809 1
    TypeFunction tdg = cast(TypeFunction)p0.type.nextOf();
1810 1
    assert(tdg.ty == Tfunction);
1811

1812
    /* We now have tdg, the type of the delegate.
1813
     * tdg's parameters must match that of the foreach arglist (i.e. parameters).
1814
     * Fill in missing types in parameters.
1815
     */
1816 1
    const nparams = tdg.parameterList.length;
1817 1
    if (nparams == 0 || nparams != parameters.dim || tdg.parameterList.varargs != VarArg.none)
1818 1
        return nomatch; // parameter mismatch
1819

1820 1
    foreach (u, p; *parameters)
1821
    {
1822 1
        Parameter param = tdg.parameterList[u];
1823 1
        if (p.type)
1824
        {
1825 1
            if (!p.type.equals(param.type))
1826 1
                return nomatch;
1827
        }
1828 1
        else if (infer)
1829
        {
1830 1
            p.type = param.type;
1831 1
            p.type = p.type.addStorageClass(p.storageClass);
1832
        }
1833
    }
1834 1
    return true;
1835
}
1836

1837
/**
1838
 * Reverse relational operator, eg >= becomes <=
1839
 * Note this is not negation.
1840
 * Params:
1841
 *      op = comparison operator to reverse
1842
 * Returns:
1843
 *      reverse of op
1844
 */
1845
private TOK reverseRelation(TOK op) pure
1846
{
1847 1
    switch (op)
1848
    {
1849 1
        case TOK.greaterOrEqual:  op = TOK.lessOrEqual;    break;
1850 1
        case TOK.greaterThan:     op = TOK.lessThan;       break;
1851 1
        case TOK.lessOrEqual:     op = TOK.greaterOrEqual; break;
1852 1
        case TOK.lessThan:        op = TOK.greaterThan;    break;
1853 0
        default:                  break;
1854
    }
1855 1
    return op;
1856
}

Read our documentation on viewing source code .

Loading