1 /**
2  * Perform constant folding.
3  *
4  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/optimize.d, _optimize.d)
8  * Documentation:  https://dlang.org/phobos/dmd_optimize.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/optimize.d
10  */
11 
12 module dmd.optimize;
13 
14 import core.stdc.stdio;
15 
16 import dmd.astenums;
17 import dmd.constfold;
18 import dmd.ctfeexpr;
19 import dmd.dclass;
20 import dmd.declaration;
21 import dmd.dsymbol;
22 import dmd.dsymbolsem;
23 import dmd.errors;
24 import dmd.expression;
25 import dmd.expressionsem;
26 import dmd.globals;
27 import dmd.init;
28 import dmd.location;
29 import dmd.mtype;
30 import dmd.printast;
31 import dmd.root.ctfloat;
32 import dmd.sideeffect;
33 import dmd.tokens;
34 import dmd.visitor;
35 
36 /*************************************
37  * If variable has a const initializer,
38  * return that initializer.
39  * Returns:
40  *      initializer if there is one,
41  *      null if not,
42  *      ErrorExp if error
43  */
44 Expression expandVar(int result, VarDeclaration v)
45 {
46     //printf("expandVar(result = %d, v = %p, %s)\n", result, v, v ? v.toChars() : "null");
47 
48     /********
49      * Params:
50      *  e = initializer expression
51      */
52     Expression initializerReturn(Expression e)
53     {
54         if (e.type != v.type)
55         {
56             e = e.castTo(null, v.type);
57         }
58         v.inuse++;
59         e = e.optimize(result);
60         v.inuse--;
61         //if (e) printf("\te = %p, %s, e.type = %d, %s\n", e, e.toChars(), e.type.ty, e.type.toChars());
62         return e;
63     }
64 
65     static Expression nullReturn()
66     {
67         return null;
68     }
69 
70     static Expression errorReturn()
71     {
72         return ErrorExp.get();
73     }
74 
75     if (!v)
76         return nullReturn();
77     if (!v.originalType && v.semanticRun < PASS.semanticdone) // semantic() not yet run
78         v.dsymbolSemantic(null);
79     if (v.type &&
80         (v.isConst() || v.isImmutable() || v.storage_class & STC.manifest))
81     {
82         Type tb = v.type.toBasetype();
83         if (v.storage_class & STC.manifest ||
84             tb.isscalar() ||
85             ((result & WANTexpand) && (tb.ty != Tsarray && tb.ty != Tstruct)))
86         {
87             if (v._init)
88             {
89                 if (v.inuse)
90                 {
91                     if (v.storage_class & STC.manifest)
92                     {
93                         v.error("recursive initialization of constant");
94                         return errorReturn();
95                     }
96                     return nullReturn();
97                 }
98                 Expression ei = v.getConstInitializer();
99                 if (!ei)
100                 {
101                     if (v.storage_class & STC.manifest)
102                     {
103                         v.error("enum cannot be initialized with `%s`", v._init.toChars());
104                         return errorReturn();
105                     }
106                     return nullReturn();
107                 }
108                 if (ei.op == EXP.construct || ei.op == EXP.blit)
109                 {
110                     AssignExp ae = cast(AssignExp)ei;
111                     ei = ae.e2;
112                     if (ei.isConst() == 1)
113                     {
114                     }
115                     else if (ei.op == EXP.string_)
116                     {
117                         // https://issues.dlang.org/show_bug.cgi?id=14459
118                         // Do not constfold the string literal
119                         // if it's typed as a C string, because the value expansion
120                         // will drop the pointer identity.
121                         if (!(result & WANTexpand) && ei.type.toBasetype().ty == Tpointer)
122                             return nullReturn();
123                     }
124                     else
125                         return nullReturn();
126                     if (ei.type == v.type)
127                     {
128                         // const variable initialized with const expression
129                     }
130                     else if (ei.implicitConvTo(v.type) >= MATCH.constant)
131                     {
132                         // const var initialized with non-const expression
133                         ei = ei.implicitCastTo(null, v.type);
134                         ei = ei.expressionSemantic(null);
135                     }
136                     else
137                         return nullReturn();
138                 }
139                 else if (!(v.storage_class & STC.manifest) &&
140                          ei.isConst() != 1 &&
141                          ei.op != EXP.string_ &&
142                          ei.op != EXP.address)
143                 {
144                     return nullReturn();
145                 }
146 
147                 if (!ei.type)
148                 {
149                     return nullReturn();
150                 }
151                 else
152                 {
153                     // Should remove the copy() operation by
154                     // making all mods to expressions copy-on-write
155                     return initializerReturn(ei.copy());
156                 }
157             }
158             else
159             {
160                 // v does not have an initializer
161                 version (all)
162                 {
163                     return nullReturn();
164                 }
165                 else
166                 {
167                     // BUG: what if const is initialized in constructor?
168                     auto e = v.type.defaultInit();
169                     e.loc = e1.loc;
170                     return initializerReturn(e);
171                 }
172             }
173             assert(0);
174         }
175     }
176     return nullReturn();
177 }
178 
179 private Expression fromConstInitializer(int result, Expression e1)
180 {
181     //printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars());
182     //static int xx; if (xx++ == 10) assert(0);
183     Expression e = e1;
184     if (auto ve = e1.isVarExp())
185     {
186         VarDeclaration v = ve.var.isVarDeclaration();
187         e = expandVar(result, v);
188         if (e)
189         {
190             // If it is a comma expression involving a declaration, we mustn't
191             // perform a copy -- we'd get two declarations of the same variable.
192             // See bugzilla 4465.
193             if (e.op == EXP.comma && e.isCommaExp().e1.isDeclarationExp())
194                 e = e1;
195             else if (e.type != e1.type && e1.type && e1.type.ty != Tident)
196             {
197                 // Type 'paint' operation
198                 e = e.copy();
199                 e.type = e1.type;
200             }
201             e.loc = e1.loc;
202         }
203         else
204         {
205             e = e1;
206         }
207     }
208     return e;
209 }
210 
211 /***
212  * It is possible for constant folding to change an array expression of
213  * unknown length, into one where the length is known.
214  * If the expression 'arr' is a literal, set lengthVar to be its length.
215  * Params:
216  *    lengthVar = variable declaration for the `.length` property
217  *    arr = String, ArrayLiteral, or of TypeSArray
218  */
219 package void setLengthVarIfKnown(VarDeclaration lengthVar, Expression arr)
220 {
221     if (!lengthVar)
222         return;
223     if (lengthVar._init && !lengthVar._init.isVoidInitializer())
224         return; // we have previously calculated the length
225     dinteger_t len;
226     if (auto se = arr.isStringExp())
227         len = se.len;
228     else if (auto ale = arr.isArrayLiteralExp())
229         len = ale.elements.length;
230     else
231     {
232         auto tsa = arr.type.toBasetype().isTypeSArray();
233         if (!tsa)
234             return; // we don't know the length yet
235         len = tsa.dim.toInteger();
236     }
237     Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t);
238     lengthVar._init = new ExpInitializer(Loc.initial, dollar);
239     lengthVar.storage_class |= STC.static_ | STC.const_;
240 }
241 
242 /***
243  * Same as above, but determines the length from 'type'.
244  * Params:
245  *    lengthVar = variable declaration for the `.length` property
246  *    type = TypeSArray
247  */
248 package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type)
249 {
250     if (!lengthVar)
251         return;
252     if (lengthVar._init && !lengthVar._init.isVoidInitializer())
253         return; // we have previously calculated the length
254     auto tsa = type.toBasetype().isTypeSArray();
255     if (!tsa)
256         return; // we don't know the length yet
257     const len = tsa.dim.toInteger();
258     Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t);
259     lengthVar._init = new ExpInitializer(Loc.initial, dollar);
260     lengthVar.storage_class |= STC.static_ | STC.const_;
261 }
262 
263 /*********************************
264  * Constant fold an Expression.
265  * Params:
266  *      e = expression to const fold; this may get modified in-place
267  *      result = WANTvalue, WANTexpand, or both
268  *      keepLvalue = `e` is an lvalue, and keep it as an lvalue since it is
269  *                   an argument to a `ref` or `out` parameter, or the operand of `&` operator
270  * Returns:
271  *      Constant folded version of `e`
272  */
273 Expression Expression_optimize(Expression e, int result, bool keepLvalue)
274 {
275     //printf("Expression_optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue);
276     Expression ret = e;
277 
278     void error()
279     {
280         ret = ErrorExp.get();
281     }
282 
283     /* Returns: true if error
284      */
285     bool expOptimize(ref Expression e, int flags, bool keepLvalue = false)
286     {
287         if (!e)
288             return false;
289         Expression ex = Expression_optimize(e, flags, keepLvalue);
290         if (ex.op == EXP.error)
291         {
292             ret = ex; // store error result
293             return true;
294         }
295         else
296         {
297             e = ex; // modify original
298             return false;
299         }
300     }
301 
302     bool unaOptimize(UnaExp e, int flags)
303     {
304         return expOptimize(e.e1, flags);
305     }
306 
307     bool binOptimize(BinExp e, int flags, bool keepLhsLvalue = false)
308     {
309         return expOptimize(e.e1, flags, keepLhsLvalue) |
310                expOptimize(e.e2, flags);
311     }
312 
313     void visitExp(Expression e)
314     {
315         //printf("Expression::optimize(result = x%x) %s\n", result, e.toChars());
316     }
317 
318     void visitVar(VarExp e)
319     {
320         VarDeclaration v = e.var.isVarDeclaration();
321 
322         if (!(keepLvalue && v && !(v.storage_class & STC.manifest)))
323             ret = fromConstInitializer(result, e);
324 
325         // if unoptimized, try to optimize the dtor expression
326         // (e.g., might be a LogicalExp with constant lhs)
327         if (ret == e && v && v.edtor)
328         {
329             // prevent infinite recursion (`<var>.~this()`)
330             if (!v.inuse)
331             {
332                 v.inuse++;
333                 expOptimize(v.edtor, WANTvalue);
334                 v.inuse--;
335             }
336         }
337     }
338 
339     void visitTuple(TupleExp e)
340     {
341         expOptimize(e.e0, WANTvalue);
342         foreach (ref ex; (*e.exps)[])
343         {
344             expOptimize(ex, WANTvalue);
345         }
346     }
347 
348     void visitArrayLiteral(ArrayLiteralExp e)
349     {
350         if (e.elements)
351         {
352             expOptimize(e.basis, result & WANTexpand);
353             foreach (ref ex; (*e.elements)[])
354             {
355                 expOptimize(ex, result & WANTexpand);
356             }
357         }
358     }
359 
360     void visitAssocArrayLiteral(AssocArrayLiteralExp e)
361     {
362         assert(e.keys.length == e.values.length);
363         foreach (i, ref ekey; (*e.keys)[])
364         {
365             expOptimize(ekey, result & WANTexpand);
366             expOptimize((*e.values)[i], result & WANTexpand);
367         }
368     }
369 
370     void visitStructLiteral(StructLiteralExp e)
371     {
372         if (e.stageflags & stageOptimize)
373             return;
374         const old = e.stageflags;
375         e.stageflags |= stageOptimize;
376         if (e.elements)
377         {
378             foreach (ref ex; (*e.elements)[])
379             {
380                 expOptimize(ex, result & WANTexpand);
381             }
382         }
383         e.stageflags = old;
384     }
385 
386     void visitUna(UnaExp e)
387     {
388         //printf("UnaExp::optimize() %s\n", e.toChars());
389         if (unaOptimize(e, result))
390             return;
391     }
392 
393     void visitNeg(NegExp e)
394     {
395         if (unaOptimize(e, result))
396             return;
397         if (e.e1.isConst() == 1)
398         {
399             ret = Neg(e.type, e.e1).copy();
400         }
401     }
402 
403     void visitCom(ComExp e)
404     {
405         if (unaOptimize(e, result))
406             return;
407         if (e.e1.isConst() == 1)
408         {
409             ret = Com(e.type, e.e1).copy();
410         }
411     }
412 
413     void visitNop(NotExp e)
414     {
415         if (unaOptimize(e, result))
416             return;
417         if (e.e1.isConst() == 1)
418         {
419             ret = Not(e.type, e.e1).copy();
420         }
421     }
422 
423     void visitSymOff(SymOffExp e)
424     {
425         assert(e.var);
426     }
427 
428     void visitAddr(AddrExp e)
429     {
430         //printf("AddrExp::optimize(result = %d, keepLvalue = %d) %s\n", result, keepLvalue, e.toChars());
431         /* Rewrite &(a,b) as (a,&b)
432          */
433         if (auto ce = e.e1.isCommaExp())
434         {
435             auto ae = new AddrExp(e.loc, ce.e2, e.type);
436             ret = new CommaExp(ce.loc, ce.e1, ae);
437             ret.type = e.type;
438             return;
439         }
440         // Keep lvalue-ness
441         if (expOptimize(e.e1, result, true))
442             return;                     // error return
443 
444         // Convert &*ex to ex
445         if (auto pe = e.e1.isPtrExp())
446         {
447             Expression ex = pe.e1;
448             if (e.type.equals(ex.type))
449                 ret = ex;
450             else if (e.type.toBasetype().equivalent(ex.type.toBasetype()))
451             {
452                 ret = ex.copy();
453                 ret.type = e.type;
454             }
455             return;
456         }
457         if (auto ve = e.e1.isVarExp())
458         {
459             if (!ve.var.isReference() && !ve.var.isImportedSymbol())
460             {
461                 ret = new SymOffExp(e.loc, ve.var, 0, ve.hasOverloads);
462                 ret.type = e.type;
463                 return;
464             }
465         }
466         if (e.e1.isDotVarExp())
467         {
468             /******************************
469              * Run down the left side of the a.b.c expression to determine the
470              * leftmost variable being addressed (`a`), and accumulate the offsets of the `.b` and `.c`.
471              * Params:
472              *      e = the DotVarExp or VarExp
473              *      var = set to the VarExp at the end, or null if doesn't end in VarExp
474              *      eint = set to the IntegerExp at the end, or null if doesn't end in IntegerExp
475              *      offset = accumulation of all the .var offsets encountered
476              * Returns: true on error
477              */
478             static bool getVarAndOffset(Expression e, out VarDeclaration var, out IntegerExp eint, ref uint offset)
479             {
480                 if (e.type.size() == SIZE_INVALID)  // trigger computation of v.offset
481                     return true;
482 
483                 if (auto dve = e.isDotVarExp())
484                 {
485                     auto v = dve.var.isVarDeclaration();
486                     if (!v || !v.isField() || v.isBitFieldDeclaration())
487                         return false;
488 
489                     if (getVarAndOffset(dve.e1, var, eint, offset))
490                         return true;
491                     offset += v.offset;
492                 }
493                 else if (auto ve = e.isVarExp())
494                 {
495                     if (!ve.var.isReference() &&
496                         !ve.var.isImportedSymbol() &&
497                         ve.var.isDataseg() &&
498                         ve.var.isCsymbol())
499                     {
500                         var = ve.var.isVarDeclaration();
501                     }
502                 }
503                 else if (auto ep = e.isPtrExp())
504                 {
505                     if (auto ei = ep.e1.isIntegerExp())
506                     {
507                         eint = ei;
508                     }
509                     else if (auto se = ep.e1.isSymOffExp())
510                     {
511                         if (!se.var.isReference() &&
512                             !se.var.isImportedSymbol() &&
513                             se.var.isDataseg())
514                         {
515                             var = se.var.isVarDeclaration();
516                             offset += se.offset;
517                         }
518                     }
519                 }
520                 else if (auto ei = e.isIndexExp())
521                 {
522                     if (auto ve = ei.e1.isVarExp())
523                     {
524                         if (!ve.var.isReference() &&
525                             !ve.var.isImportedSymbol() &&
526                             ve.var.isDataseg() &&
527                             ve.var.isCsymbol())
528                         {
529                             if (auto ie = ei.e2.isIntegerExp())
530                             {
531                                 var = ve.var.isVarDeclaration();
532                                 offset += ie.toInteger() * ve.type.toBasetype().nextOf().size();
533                             }
534                         }
535                     }
536                 }
537                 return false;
538             }
539 
540             uint offset;
541             VarDeclaration var;
542             IntegerExp eint;
543             if (getVarAndOffset(e.e1, var, eint, offset))
544             {
545                 ret = ErrorExp.get();
546                 return;
547             }
548             if (var)
549             {
550                 ret = new SymOffExp(e.loc, var, offset, false);
551                 ret.type = e.type;
552                 return;
553             }
554             if (eint)
555             {
556                 ret = new IntegerExp(e.loc, eint.toInteger() + offset, e.type);
557                 return;
558             }
559         }
560         else if (auto ae = e.e1.isIndexExp())
561         {
562             if (ae.e2.isIntegerExp() && ae.e1.isIndexExp())
563             {
564                 /* Rewrite `(a[i])[index]` to `(&a[i]) + index*size`
565                  */
566                 sinteger_t index = ae.e2.toInteger();
567                 auto ae1 = ae.e1.isIndexExp();          // ae1 is a[i]
568                 if (auto ts = ae1.type.isTypeSArray())
569                 {
570                     sinteger_t dim = ts.dim.toInteger();
571 
572                     if (index < 0 || index > dim)
573                     {
574                         e.error("array index %lld is out of bounds `[0..%lld]`", index, dim);
575                         return error();
576                     }
577 
578                     import core.checkedint : mulu;
579                     bool overflow;
580                     const offset = mulu(index, ts.nextOf().size(e.loc), overflow); // offset = index*size
581                     if (overflow)
582                     {
583                         e.error("array offset overflow");
584                         return error();
585                     }
586 
587                     Expression ex = new AddrExp(ae1.loc, ae1);  // &a[i]
588                     ex.type = ae1.type.pointerTo();
589 
590                     Expression add = new AddExp(ae.loc, ex, new IntegerExp(ae.e2.loc, offset, ae.e2.type));
591                     add.type = e.type;
592                     ret = Expression_optimize(add, result, keepLvalue);
593                     return;
594                 }
595             }
596 
597             // Convert &array[n] to &array+n
598             if (ae.e2.isIntegerExp() && ae.e1.isVarExp())
599             {
600                 sinteger_t index = ae.e2.toInteger();
601                 VarExp ve = ae.e1.isVarExp();
602                 if (ve.type.isTypeSArray() && !ve.var.isImportedSymbol())
603                 {
604                     TypeSArray ts = ve.type.isTypeSArray();
605                     sinteger_t dim = ts.dim.toInteger();
606                     if (index < 0 || index >= dim)
607                     {
608                         /* 0 for C static arrays means size is unknown, no need to check,
609                          * and address one past the end is OK, too
610                          */
611                         if (!((dim == 0 || dim == index) && ve.var.isCsymbol()))
612                         {
613                             e.error("array index %lld is out of bounds `[0..%lld]`", index, dim);
614                             return error();
615                         }
616                     }
617 
618                     import core.checkedint : mulu;
619                     bool overflow;
620                     const offset = mulu(index, ts.nextOf().size(e.loc), overflow);
621                     if (overflow)
622                     {
623                         e.error("array offset overflow");
624                         return error();
625                     }
626 
627                     ret = new SymOffExp(e.loc, ve.var, offset);
628                     ret.type = e.type;
629                     return;
630                 }
631             }
632             // Convert &((a.b)[index]) to (&a.b)+index*elementsize
633             else if (ae.e2.isIntegerExp() && ae.e1.isDotVarExp())
634             {
635                 sinteger_t index = ae.e2.toInteger();
636                 DotVarExp ve = ae.e1.isDotVarExp();
637                 if (ve.type.isTypeSArray() && ve.var.isField() && ve.e1.isPtrExp())
638                 {
639                     TypeSArray ts = ve.type.isTypeSArray();
640                     sinteger_t dim = ts.dim.toInteger();
641                     if (index < 0 || index >= dim)
642                     {
643                         /* 0 for C static arrays means size is unknown, no need to check,
644                          * and address one past the end is OK, too
645                          */
646                         if (!((dim == 0 || dim == index) && ve.var.isCsymbol()))
647                         {
648                             e.error("array index %lld is out of bounds `[0..%lld]`", index, dim);
649                             return error();
650                         }
651                     }
652 
653                     import core.checkedint : mulu;
654                     bool overflow;
655                     const offset = mulu(index, ts.nextOf().size(e.loc), overflow); // index*elementsize
656                     if (overflow)
657                     {
658                         e.error("array offset overflow");
659                         return error();
660                     }
661 
662                     auto pe = new AddrExp(e.loc, ve);
663                     pe.type = e.type;
664                     ret = new AddExp(e.loc, pe, new IntegerExp(e.loc, offset, Type.tsize_t));
665                     ret.type = e.type;
666                     return;
667                 }
668             }
669         }
670     }
671 
672     void visitPtr(PtrExp e)
673     {
674         //printf("PtrExp::optimize(result = x%x) %s\n", result, e.toChars());
675         if (expOptimize(e.e1, result))
676             return;
677         // Convert *&ex to ex
678         // But only if there is no type punning involved
679         if (auto ey = e.e1.isAddrExp())
680         {
681             Expression ex = ey.e1;
682             if (e.type.equals(ex.type))
683                 ret = ex;
684             else if (e.type.toBasetype().equivalent(ex.type.toBasetype()))
685             {
686                 ret = ex.copy();
687                 ret.type = e.type;
688             }
689         }
690         if (keepLvalue)
691             return;
692         // Constant fold *(&structliteral + offset)
693         if (e.e1.op == EXP.add)
694         {
695             Expression ex = Ptr(e.type, e.e1).copy();
696             if (!CTFEExp.isCantExp(ex))
697             {
698                 ret = ex;
699                 return;
700             }
701         }
702         if (auto se = e.e1.isSymOffExp())
703         {
704             VarDeclaration v = se.var.isVarDeclaration();
705             Expression ex = expandVar(result, v);
706             if (ex && ex.isStructLiteralExp())
707             {
708                 StructLiteralExp sle = ex.isStructLiteralExp();
709                 ex = sle.getField(e.type, cast(uint)se.offset);
710                 if (ex && !CTFEExp.isCantExp(ex))
711                 {
712                     ret = ex;
713                     return;
714                 }
715             }
716         }
717     }
718 
719     void visitDotVar(DotVarExp e)
720     {
721         //printf("DotVarExp::optimize(result = x%x) %s\n", result, e.toChars());
722         if (expOptimize(e.e1, result))
723             return;
724         if (keepLvalue)
725             return;
726         Expression ex = e.e1;
727         if (auto ve = ex.isVarExp())
728         {
729             VarDeclaration v = ve.var.isVarDeclaration();
730             ex = expandVar(result, v);
731         }
732         if (ex && ex.isStructLiteralExp())
733         {
734             StructLiteralExp sle = ex.isStructLiteralExp();
735             VarDeclaration vf = e.var.isVarDeclaration();
736             if (vf && !vf.overlapped)
737             {
738                 /* https://issues.dlang.org/show_bug.cgi?id=13021
739                  * Prevent optimization if vf has overlapped fields.
740                  */
741                 ex = sle.getField(e.type, vf.offset);
742                 if (ex && !CTFEExp.isCantExp(ex))
743                 {
744                     ret = ex;
745                     return;
746                 }
747             }
748         }
749     }
750 
751     void visitNew(NewExp e)
752     {
753         expOptimize(e.thisexp, WANTvalue);
754         // Optimize parameters
755         if (e.arguments)
756         {
757             foreach (ref arg; (*e.arguments)[])
758             {
759                 expOptimize(arg, WANTvalue);
760             }
761         }
762     }
763 
764     void visitCall(CallExp e)
765     {
766         //printf("CallExp::optimize(result = %d) %s\n", result, e.toChars());
767         // Optimize parameters with keeping lvalue-ness
768         if (expOptimize(e.e1, result))
769             return;
770         if (e.arguments)
771         {
772             Type t1 = e.e1.type.toBasetype();
773             if (auto td = t1.isTypeDelegate())
774                 t1 = td.next;
775             // t1 can apparently be void for __ArrayDtor(T) calls
776             if (auto tf = t1.isTypeFunction())
777             {
778                 foreach (i, ref arg; (*e.arguments)[])
779                 {
780                     Parameter p = tf.parameterList[i];
781                     bool keep = p && p.isReference();
782                     expOptimize(arg, WANTvalue, keep);
783                 }
784             }
785         }
786     }
787 
788     void visitCast(CastExp e)
789     {
790         //printf("CastExp::optimize(result = %d) %s\n", result, e.toChars());
791         //printf("from %s to %s\n", e.type.toChars(), e.to.toChars());
792         //printf("from %s\n", e.type.toChars());
793         //printf("e1.type %s\n", e.e1.type.toChars());
794         //printf("type = %p\n", e.type);
795         assert(e.type);
796         const op1 = e.e1.op;
797         Expression e1old = e.e1;
798         if (expOptimize(e.e1, result, keepLvalue))
799             return;
800         if (!keepLvalue)
801             e.e1 = fromConstInitializer(result, e.e1);
802         if (e.e1 == e1old && e.e1.op == EXP.arrayLiteral && e.type.toBasetype().ty == Tpointer && e.e1.type.toBasetype().ty != Tsarray)
803         {
804             // Casting this will result in the same expression, and
805             // infinite loop because of Expression::implicitCastTo()
806             return; // no change
807         }
808         if ((e.e1.op == EXP.string_ || e.e1.op == EXP.arrayLiteral) &&
809             (e.type.ty == Tpointer || e.type.ty == Tarray))
810         {
811             const esz  = e.type.nextOf().size(e.loc);
812             const e1sz = e.e1.type.toBasetype().nextOf().size(e.e1.loc);
813             if (esz == SIZE_INVALID || e1sz == SIZE_INVALID)
814                 return error();
815 
816             if (e1sz == esz)
817             {
818                 // https://issues.dlang.org/show_bug.cgi?id=12937
819                 // If target type is void array, trying to paint
820                 // e.e1 with that type will cause infinite recursive optimization.
821                 if (e.type.nextOf().ty == Tvoid)
822                     return;
823                 ret = e.e1.castTo(null, e.type);
824                 //printf(" returning1 %s\n", ret.toChars());
825                 return;
826             }
827         }
828 
829         // Returning e.e1 with changing its type
830         void returnE_e1()
831         {
832             ret = (e1old == e.e1 ? e.e1.copy() : e.e1);
833             ret.type = e.type;
834         }
835 
836         if (e.e1.op == EXP.structLiteral && e.e1.type.implicitConvTo(e.type) >= MATCH.constant)
837         {
838             //printf(" returning2 %s\n", e.e1.toChars());
839             return returnE_e1();
840         }
841         /* The first test here is to prevent infinite loops
842          */
843         if (op1 != EXP.arrayLiteral && e.e1.op == EXP.arrayLiteral)
844         {
845             ret = e.e1.castTo(null, e.to);
846             return;
847         }
848         if (e.e1.op == EXP.null_ && (e.type.ty == Tpointer || e.type.ty == Tclass || e.type.ty == Tarray))
849         {
850             //printf(" returning3 %s\n", e.e1.toChars());
851             return returnE_e1();
852         }
853         if (e.type.ty == Tclass && e.e1.type.ty == Tclass)
854         {
855             import dmd.astenums : Sizeok;
856 
857             // See if we can remove an unnecessary cast
858             ClassDeclaration cdfrom = e.e1.type.isClassHandle();
859             ClassDeclaration cdto = e.type.isClassHandle();
860             if (cdfrom.errors || cdto.errors)
861                 return error();
862             if (cdto == ClassDeclaration.object && !cdfrom.isInterfaceDeclaration())
863                 return returnE_e1();    // can always convert a class to Object
864             // Need to determine correct offset before optimizing away the cast.
865             // https://issues.dlang.org/show_bug.cgi?id=16980
866             if (cdfrom.size(e.loc) == SIZE_INVALID)
867                 return error();
868             assert(cdfrom.sizeok == Sizeok.done);
869             assert(cdto.sizeok == Sizeok.done || !cdto.isBaseOf(cdfrom, null));
870             int offset;
871             if (cdto.isBaseOf(cdfrom, &offset) && offset == 0)
872             {
873                 //printf(" returning4 %s\n", e.e1.toChars());
874                 return returnE_e1();
875             }
876         }
877         if (e.e1.type.mutableOf().unSharedOf().equals(e.to.mutableOf().unSharedOf()))
878         {
879             //printf(" returning5 %s\n", e.e1.toChars());
880             return returnE_e1();
881         }
882         if (e.e1.isConst())
883         {
884             if (e.e1.op == EXP.symbolOffset)
885             {
886                 if (e.type.toBasetype().ty != Tsarray)
887                 {
888                     const esz = e.type.size(e.loc);
889                     const e1sz = e.e1.type.size(e.e1.loc);
890                     if (esz == SIZE_INVALID ||
891                         e1sz == SIZE_INVALID)
892                         return error();
893 
894                     if (esz == e1sz)
895                         return returnE_e1();
896                 }
897                 return;
898             }
899             if (e.to.toBasetype().ty != Tvoid)
900             {
901                 if (e.e1.type.equals(e.type) && e.type.equals(e.to))
902                     ret = e.e1;
903                 else
904                     ret = Cast(e.loc, e.type, e.to, e.e1).copy();
905             }
906         }
907         //printf(" returning6 %s\n", ret.toChars());
908     }
909 
910     void visitBinAssign(BinAssignExp e)
911     {
912         //printf("BinAssignExp::optimize(result = %d) %s\n", result, e.toChars());
913         if (binOptimize(e, result, /*keepLhsLvalue*/ true))
914             return;
915         if (e.op == EXP.leftShiftAssign || e.op == EXP.rightShiftAssign || e.op == EXP.unsignedRightShiftAssign)
916         {
917             if (e.e2.isConst() == 1)
918             {
919                 sinteger_t i2 = e.e2.toInteger();
920                 uinteger_t sz = e.e1.type.size(e.e1.loc);
921                 assert(sz != SIZE_INVALID);
922                 sz *= 8;
923                 if (i2 < 0 || i2 >= sz)
924                 {
925                     e.error("shift assign by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1);
926                     return error();
927                 }
928             }
929         }
930     }
931 
932     void visitBin(BinExp e)
933     {
934         //printf("BinExp::optimize(result = %d) %s\n", result, e.toChars());
935         const keepLhsLvalue = e.op == EXP.construct || e.op == EXP.blit || e.op == EXP.assign
936             || e.op == EXP.plusPlus || e.op == EXP.minusMinus
937             || e.op == EXP.prePlusPlus || e.op == EXP.preMinusMinus;
938         binOptimize(e, result, keepLhsLvalue);
939     }
940 
941     void visitAdd(AddExp e)
942     {
943         //printf("AddExp::optimize(%s)\n", e.toChars());
944         if (binOptimize(e, result))
945             return;
946         if (e.e1.isConst() && e.e2.isConst())
947         {
948             if (e.e1.op == EXP.symbolOffset && e.e2.op == EXP.symbolOffset)
949                 return;
950             ret = Add(e.loc, e.type, e.e1, e.e2).copy();
951         }
952     }
953 
954     void visitMin(MinExp e)
955     {
956         //printf("MinExp::optimize(%s)\n", e.toChars());
957         if (binOptimize(e, result))
958             return;
959         if (e.e1.isConst() && e.e2.isConst())
960         {
961             if (e.e2.op == EXP.symbolOffset)
962                 return;
963             ret = Min(e.loc, e.type, e.e1, e.e2).copy();
964         }
965     }
966 
967     void visitMul(MulExp e)
968     {
969         //printf("MulExp::optimize(result = %d) %s\n", result, e.toChars());
970         if (binOptimize(e, result))
971             return;
972         if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
973         {
974             ret = Mul(e.loc, e.type, e.e1, e.e2).copy();
975         }
976     }
977 
978     void visitDiv(DivExp e)
979     {
980         //printf("DivExp::optimize(%s)\n", e.toChars());
981         if (binOptimize(e, result))
982             return;
983         if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
984         {
985             ret = Div(e.loc, e.type, e.e1, e.e2).copy();
986         }
987     }
988 
989     void visitMod(ModExp e)
990     {
991         if (binOptimize(e, result))
992             return;
993         if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
994         {
995             ret = Mod(e.loc, e.type, e.e1, e.e2).copy();
996         }
997     }
998 
999     extern (D) void shift_optimize(BinExp e, UnionExp function(const ref Loc, Type, Expression, Expression) shift)
1000     {
1001         if (binOptimize(e, result))
1002             return;
1003         if (e.e2.isConst() == 1)
1004         {
1005             sinteger_t i2 = e.e2.toInteger();
1006             uinteger_t sz = e.e1.type.size(e.e1.loc);
1007             assert(sz != SIZE_INVALID);
1008             sz *= 8;
1009             if (i2 < 0 || i2 >= sz)
1010             {
1011                 e.error("shift by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1);
1012                 return error();
1013             }
1014             if (e.e1.isConst() == 1)
1015                 ret = (*shift)(e.loc, e.type, e.e1, e.e2).copy();
1016         }
1017     }
1018 
1019     void visitShl(ShlExp e)
1020     {
1021         //printf("ShlExp::optimize(result = %d) %s\n", result, e.toChars());
1022         shift_optimize(e, &Shl);
1023     }
1024 
1025     void visitShr(ShrExp e)
1026     {
1027         //printf("ShrExp::optimize(result = %d) %s\n", result, e.toChars());
1028         shift_optimize(e, &Shr);
1029     }
1030 
1031     void visitUshr(UshrExp e)
1032     {
1033         //printf("UshrExp::optimize(result = %d) %s\n", result, toChars());
1034         shift_optimize(e, &Ushr);
1035     }
1036 
1037     void visitAnd(AndExp e)
1038     {
1039         if (binOptimize(e, result))
1040             return;
1041         if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
1042             ret = And(e.loc, e.type, e.e1, e.e2).copy();
1043     }
1044 
1045     void visitOr(OrExp e)
1046     {
1047         if (binOptimize(e, result))
1048             return;
1049         if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
1050             ret = Or(e.loc, e.type, e.e1, e.e2).copy();
1051     }
1052 
1053     void visitXor(XorExp e)
1054     {
1055         if (binOptimize(e, result))
1056             return;
1057         if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
1058             ret = Xor(e.loc, e.type, e.e1, e.e2).copy();
1059     }
1060 
1061     void visitPow(PowExp e)
1062     {
1063         if (binOptimize(e, result))
1064             return;
1065         // All negative integral powers are illegal.
1066         if (e.e1.type.isintegral() && (e.e2.op == EXP.int64) && cast(sinteger_t)e.e2.toInteger() < 0)
1067         {
1068             e.error("cannot raise `%s` to a negative integer power. Did you mean `(cast(real)%s)^^%s` ?", e.e1.type.toBasetype().toChars(), e.e1.toChars(), e.e2.toChars());
1069             return error();
1070         }
1071         // If e2 *could* have been an integer, make it one.
1072         if (e.e2.op == EXP.float64 && e.e2.toReal() == real_t(cast(sinteger_t)e.e2.toReal()))
1073         {
1074             // This only applies to floating point, or positive integral powers.
1075             if (e.e1.type.isfloating() || cast(sinteger_t)e.e2.toInteger() >= 0)
1076                 e.e2 = new IntegerExp(e.loc, e.e2.toInteger(), Type.tint64);
1077         }
1078         if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
1079         {
1080             Expression ex = Pow(e.loc, e.type, e.e1, e.e2).copy();
1081             if (!CTFEExp.isCantExp(ex))
1082             {
1083                 ret = ex;
1084                 return;
1085             }
1086         }
1087     }
1088 
1089     void visitComma(CommaExp e)
1090     {
1091         //printf("CommaExp::optimize(result = %d) %s\n", result, e.toChars());
1092         // Comma needs special treatment, because it may
1093         // contain compiler-generated declarations. We can interpret them, but
1094         // otherwise we must NOT attempt to constant-fold them.
1095         // In particular, if the comma returns a temporary variable, it needs
1096         // to be an lvalue (this is particularly important for struct constructors)
1097         expOptimize(e.e1, WANTvalue);
1098         expOptimize(e.e2, result, keepLvalue);
1099         if (ret.op == EXP.error)
1100             return;
1101         if (!e.e1 || e.e1.op == EXP.int64 || e.e1.op == EXP.float64 || !hasSideEffect(e.e1))
1102         {
1103             ret = e.e2;
1104             if (ret)
1105                 ret.type = e.type;
1106         }
1107         //printf("-CommaExp::optimize(result = %d) %s\n", result, e.e.toChars());
1108     }
1109 
1110     void visitArrayLength(ArrayLengthExp e)
1111     {
1112         //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, e.toChars());
1113         if (unaOptimize(e, WANTexpand))
1114             return;
1115         // CTFE interpret static immutable arrays (to get better diagnostics)
1116         if (auto ve = e.e1.isVarExp())
1117         {
1118             VarDeclaration v = ve.var.isVarDeclaration();
1119             if (v && (v.storage_class & STC.static_) && (v.storage_class & STC.immutable_) && v._init)
1120             {
1121                 if (Expression ci = v.getConstInitializer())
1122                     e.e1 = ci;
1123             }
1124         }
1125         if (e.e1.op == EXP.string_ || e.e1.op == EXP.arrayLiteral || e.e1.op == EXP.assocArrayLiteral || e.e1.type.toBasetype().ty == Tsarray || e.e1.op == EXP.null_)
1126         {
1127             ret = ArrayLength(e.type, e.e1).copy();
1128         }
1129     }
1130 
1131     void visitEqual(EqualExp e)
1132     {
1133         //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars());
1134         if (binOptimize(e, WANTvalue))
1135             return;
1136         Expression e1 = fromConstInitializer(result, e.e1);
1137         Expression e2 = fromConstInitializer(result, e.e2);
1138         if (e1.op == EXP.error)
1139         {
1140             ret = e1;
1141             return;
1142         }
1143         if (e2.op == EXP.error)
1144         {
1145             ret = e2;
1146             return;
1147         }
1148         ret = Equal(e.op, e.loc, e.type, e1, e2).copy();
1149         if (CTFEExp.isCantExp(ret))
1150             ret = e;
1151     }
1152 
1153     void visitIdentity(IdentityExp e)
1154     {
1155         //printf("IdentityExp::optimize(result = %d) %s\n", result, e.toChars());
1156         if (binOptimize(e, WANTvalue))
1157             return;
1158         if ((e.e1.isConst() && e.e2.isConst()) || (e.e1.op == EXP.null_ && e.e2.op == EXP.null_))
1159         {
1160             ret = Identity(e.op, e.loc, e.type, e.e1, e.e2).copy();
1161             if (CTFEExp.isCantExp(ret))
1162                 ret = e;
1163         }
1164     }
1165 
1166     void visitIndex(IndexExp e)
1167     {
1168         //printf("IndexExp::optimize(result = %d) %s\n", result, e.toChars());
1169         if (expOptimize(e.e1, result & WANTexpand))
1170             return;
1171         Expression ex = fromConstInitializer(result, e.e1);
1172         // We might know $ now
1173         setLengthVarIfKnown(e.lengthVar, ex);
1174         if (expOptimize(e.e2, WANTvalue))
1175             return;
1176         // Don't optimize to an array literal element directly in case an lvalue is requested
1177         if (keepLvalue && ex.op == EXP.arrayLiteral)
1178             return;
1179         ret = Index(e.type, ex, e.e2, e.indexIsInBounds).copy();
1180         if (CTFEExp.isCantExp(ret) || (!ret.isErrorExp() && keepLvalue && !ret.isLvalue()))
1181             ret = e;
1182     }
1183 
1184     void visitSlice(SliceExp e)
1185     {
1186         //printf("SliceExp::optimize(result = %d) %s\n", result, e.toChars());
1187         if (expOptimize(e.e1, result & WANTexpand))
1188             return;
1189         if (!e.lwr)
1190         {
1191             if (e.e1.op == EXP.string_)
1192             {
1193                 // Convert slice of string literal into dynamic array
1194                 Type t = e.e1.type.toBasetype();
1195                 if (Type tn = t.nextOf())
1196                     ret = e.e1.castTo(null, tn.arrayOf());
1197             }
1198         }
1199         else
1200         {
1201             e.e1 = fromConstInitializer(result, e.e1);
1202             // We might know $ now
1203             setLengthVarIfKnown(e.lengthVar, e.e1);
1204             expOptimize(e.lwr, WANTvalue);
1205             expOptimize(e.upr, WANTvalue);
1206             if (ret.op == EXP.error)
1207                 return;
1208             ret = Slice(e.type, e.e1, e.lwr, e.upr).copy();
1209             if (CTFEExp.isCantExp(ret))
1210                 ret = e;
1211         }
1212         // https://issues.dlang.org/show_bug.cgi?id=14649
1213         // Leave the slice form so it might be
1214         // a part of array operation.
1215         // Assume that the backend codegen will handle the form `e[]`
1216         // as an equal to `e` itself.
1217         if (ret.op == EXP.string_)
1218         {
1219             e.e1 = ret;
1220             e.lwr = null;
1221             e.upr = null;
1222             ret = e;
1223         }
1224         //printf("-SliceExp::optimize() %s\n", ret.toChars());
1225     }
1226 
1227     void visitLogical(LogicalExp e)
1228     {
1229         //printf("LogicalExp::optimize(%d) %s\n", result, e.toChars());
1230         if (expOptimize(e.e1, WANTvalue))
1231             return;
1232         const oror = e.op == EXP.orOr;
1233         if (e.e1.toBool().hasValue(oror))
1234         {
1235             // Replace with (e1, oror)
1236             ret = IntegerExp.createBool(oror);
1237             ret = Expression.combine(e.e1, ret);
1238             if (e.type.toBasetype().ty == Tvoid)
1239             {
1240                 ret = new CastExp(e.loc, ret, Type.tvoid);
1241                 ret.type = e.type;
1242             }
1243             ret = Expression_optimize(ret, result, false);
1244             return;
1245         }
1246         expOptimize(e.e2, WANTvalue);
1247         if (e.e1.isConst())
1248         {
1249             const e1Opt = e.e1.toBool();
1250             if (e.e2.isConst())
1251             {
1252                 bool n1 = e1Opt.get();
1253                 bool n2 = e.e2.toBool().get();
1254                 ret = new IntegerExp(e.loc, oror ? (n1 || n2) : (n1 && n2), e.type);
1255             }
1256             else if (e1Opt.hasValue(!oror))
1257             {
1258                 if (e.type.toBasetype().ty == Tvoid)
1259                     ret = e.e2;
1260                 else
1261                 {
1262                     ret = new CastExp(e.loc, e.e2, e.type);
1263                     ret.type = e.type;
1264                 }
1265             }
1266         }
1267     }
1268 
1269     void visitCmp(CmpExp e)
1270     {
1271         //printf("CmpExp::optimize() %s\n", e.toChars());
1272         if (binOptimize(e, WANTvalue))
1273             return;
1274         Expression e1 = fromConstInitializer(result, e.e1);
1275         Expression e2 = fromConstInitializer(result, e.e2);
1276         ret = Cmp(e.op, e.loc, e.type, e1, e2).copy();
1277         if (CTFEExp.isCantExp(ret))
1278             ret = e;
1279     }
1280 
1281     void visitCat(CatExp e)
1282     {
1283         //printf("CatExp::optimize(%d) %s\n", result, e.toChars());
1284         if (binOptimize(e, result))
1285             return;
1286         if (auto ce1 = e.e1.isCatExp())
1287         {
1288             // https://issues.dlang.org/show_bug.cgi?id=12798
1289             // optimize ((expr ~ str1) ~ str2)
1290             scope CatExp cex = new CatExp(e.loc, ce1.e2, e.e2);
1291             cex.type = e.type;
1292             Expression ex = Expression_optimize(cex, result, false);
1293             if (ex != cex)
1294             {
1295                 e.e1 = ce1.e1;
1296                 e.e2 = ex;
1297             }
1298         }
1299         // optimize "str"[] -> "str"
1300         if (auto se1 = e.e1.isSliceExp())
1301         {
1302             if (se1.e1.op == EXP.string_ && !se1.lwr)
1303                 e.e1 = se1.e1;
1304         }
1305         if (auto se2 = e.e2.isSliceExp())
1306         {
1307             if (se2.e1.op == EXP.string_ && !se2.lwr)
1308                 e.e2 = se2.e1;
1309         }
1310         ret = Cat(e.loc, e.type, e.e1, e.e2).copy();
1311         if (CTFEExp.isCantExp(ret))
1312             ret = e;
1313     }
1314 
1315     void visitCond(CondExp e)
1316     {
1317         if (expOptimize(e.econd, WANTvalue))
1318             return;
1319         const opt = e.econd.toBool();
1320         if (opt.hasValue(true))
1321             ret = Expression_optimize(e.e1, result, keepLvalue);
1322         else if (opt.hasValue(false))
1323             ret = Expression_optimize(e.e2, result, keepLvalue);
1324         else
1325         {
1326             expOptimize(e.e1, result, keepLvalue);
1327             expOptimize(e.e2, result, keepLvalue);
1328         }
1329     }
1330 
1331     // Optimize the expression until it can no longer be simplified.
1332     size_t b;
1333     while (1)
1334     {
1335         if (b++ == global.recursionLimit)
1336         {
1337             e.error("infinite loop while optimizing expression");
1338             fatal();
1339         }
1340 
1341         auto ex = ret;
1342         switch (ex.op)
1343         {
1344             case EXP.variable:          visitVar(ex.isVarExp()); break;
1345             case EXP.tuple:             visitTuple(ex.isTupleExp()); break;
1346             case EXP.arrayLiteral:      visitArrayLiteral(ex.isArrayLiteralExp()); break;
1347             case EXP.assocArrayLiteral: visitAssocArrayLiteral(ex.isAssocArrayLiteralExp()); break;
1348             case EXP.structLiteral:     visitStructLiteral(ex.isStructLiteralExp()); break;
1349 
1350             case EXP.import_:
1351             case EXP.assert_:
1352             case EXP.dotIdentifier:
1353             case EXP.dotTemplateDeclaration:
1354             case EXP.dotTemplateInstance:
1355             case EXP.delegate_:
1356             case EXP.dotType:
1357             case EXP.uadd:
1358             case EXP.delete_:
1359             case EXP.vector:
1360             case EXP.vectorArray:
1361             case EXP.array:
1362             case EXP.delegatePointer:
1363             case EXP.delegateFunctionPointer:
1364             case EXP.preMinusMinus:
1365             case EXP.prePlusPlus:       visitUna(cast(UnaExp)ex); break;
1366 
1367             case EXP.negate:            visitNeg(ex.isNegExp()); break;
1368             case EXP.tilde:             visitCom(ex.isComExp()); break;
1369             case EXP.not:               visitNop(ex.isNotExp()); break;
1370             case EXP.symbolOffset:      visitSymOff(ex.isSymOffExp()); break;
1371             case EXP.address:           visitAddr(ex.isAddrExp()); break;
1372             case EXP.star:              visitPtr(ex.isPtrExp()); break;
1373             case EXP.dotVariable:       visitDotVar(ex.isDotVarExp()); break;
1374             case EXP.new_:              visitNew(ex.isNewExp()); break;
1375             case EXP.call:              visitCall(ex.isCallExp()); break;
1376             case EXP.cast_:             visitCast(ex.isCastExp()); break;
1377 
1378             case EXP.addAssign:
1379             case EXP.minAssign:
1380             case EXP.mulAssign:
1381             case EXP.divAssign:
1382             case EXP.modAssign:
1383             case EXP.andAssign:
1384             case EXP.orAssign:
1385             case EXP.xorAssign:
1386             case EXP.powAssign:
1387             case EXP.leftShiftAssign:
1388             case EXP.rightShiftAssign:
1389             case EXP.unsignedRightShiftAssign:
1390             case EXP.concatenateElemAssign:
1391             case EXP.concatenateDcharAssign:
1392             case EXP.concatenateAssign: visitBinAssign(ex.isBinAssignExp()); break;
1393 
1394             case EXP.minusMinus:
1395             case EXP.plusPlus:
1396             case EXP.assign:
1397             case EXP.construct:
1398             case EXP.blit:
1399             case EXP.in_:
1400             case EXP.remove:
1401             case EXP.dot:                       visitBin(cast(BinExp)ex); break;
1402 
1403             case EXP.add:                       visitAdd(ex.isAddExp()); break;
1404             case EXP.min:                       visitMin(ex.isMinExp()); break;
1405             case EXP.mul:                       visitMul(ex.isMulExp()); break;
1406             case EXP.div:                       visitDiv(ex.isDivExp()); break;
1407             case EXP.mod:                       visitMod(ex.isModExp()); break;
1408             case EXP.leftShift:                 visitShl(ex.isShlExp()); break;
1409             case EXP.rightShift:                visitShr(ex.isShrExp()); break;
1410             case EXP.unsignedRightShift:        visitUshr(ex.isUshrExp()); break;
1411             case EXP.and:                       visitAnd(ex.isAndExp()); break;
1412             case EXP.or:                        visitOr(ex.isOrExp()); break;
1413             case EXP.xor:                       visitXor(ex.isXorExp()); break;
1414             case EXP.pow:                       visitPow(ex.isPowExp()); break;
1415             case EXP.comma:                     visitComma(ex.isCommaExp()); break;
1416             case EXP.arrayLength:               visitArrayLength(ex.isArrayLengthExp()); break;
1417             case EXP.notEqual:
1418             case EXP.equal:                     visitEqual(ex.isEqualExp()); break;
1419             case EXP.notIdentity:
1420             case EXP.identity:                  visitIdentity(ex.isIdentityExp()); break;
1421             case EXP.index:                     visitIndex(ex.isIndexExp()); break;
1422             case EXP.slice:                     visitSlice(ex.isSliceExp()); break;
1423             case EXP.andAnd:
1424             case EXP.orOr:                      visitLogical(ex.isLogicalExp()); break;
1425             case EXP.lessThan:
1426             case EXP.lessOrEqual:
1427             case EXP.greaterThan:
1428             case EXP.greaterOrEqual:            visitCmp(cast(CmpExp)ex); break;
1429             case EXP.concatenate:               visitCat(ex.isCatExp()); break;
1430             case EXP.question:                  visitCond(ex.isCondExp()); break;
1431 
1432             default:                            visitExp(ex); break;
1433         }
1434 
1435         if (ex == ret)
1436             break;
1437     }
1438     return ret;
1439 }