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