1 /**
2  * Perform constant folding.
3  *
4  * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 http://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.constfold;
17 import dmd.ctfeexpr;
18 import dmd.dclass;
19 import dmd.declaration;
20 import dmd.dsymbol;
21 import dmd.dsymbolsem;
22 import dmd.errors;
23 import dmd.expression;
24 import dmd.expressionsem;
25 import dmd.globals;
26 import dmd.init;
27 import dmd.mtype;
28 import dmd.root.ctfloat;
29 import dmd.sideeffect;
30 import dmd.tokens;
31 import dmd.visitor;
32 
33 /*************************************
34  * If variable has a const initializer,
35  * return that initializer.
36  * Returns:
37  *      initializer if there is one,
38  *      null if not,
39  *      ErrorExp if error
40  */
41 Expression expandVar(int result, VarDeclaration v)
42 {
43     //printf("expandVar(result = %d, v = %p, %s)\n", result, v, v ? v.toChars() : "null");
44 
45     /********
46      * Params:
47      *  e = initializer expression
48      */
49     Expression initializerReturn(Expression e)
50     {
51         if (e.type != v.type)
52         {
53             e = e.castTo(null, v.type);
54         }
55         v.inuse++;
56         e = e.optimize(result);
57         v.inuse--;
58         //if (e) printf("\te = %p, %s, e.type = %d, %s\n", e, e.toChars(), e.type.ty, e.type.toChars());
59         return e;
60     }
61 
62     static Expression nullReturn()
63     {
64         return null;
65     }
66 
67     static Expression errorReturn()
68     {
69         return ErrorExp.get();
70     }
71 
72     if (!v)
73         return nullReturn();
74     if (!v.originalType && v.semanticRun < PASS.semanticdone) // semantic() not yet run
75         v.dsymbolSemantic(null);
76     if (v.type &&
77         (v.isConst() || v.isImmutable() || v.storage_class & STC.manifest))
78     {
79         Type tb = v.type.toBasetype();
80         if (v.storage_class & STC.manifest ||
81             tb.isscalar() ||
82             ((result & WANTexpand) && (tb.ty != Tsarray && tb.ty != Tstruct)))
83         {
84             if (v._init)
85             {
86                 if (v.inuse)
87                 {
88                     if (v.storage_class & STC.manifest)
89                     {
90                         v.error("recursive initialization of constant");
91                         return errorReturn();
92                     }
93                     return nullReturn();
94                 }
95                 Expression ei = v.getConstInitializer();
96                 if (!ei)
97                 {
98                     if (v.storage_class & STC.manifest)
99                     {
100                         v.error("enum cannot be initialized with `%s`", v._init.toChars());
101                         return errorReturn();
102                     }
103                     return nullReturn();
104                 }
105                 if (ei.op == TOK.construct || ei.op == TOK.blit)
106                 {
107                     AssignExp ae = cast(AssignExp)ei;
108                     ei = ae.e2;
109                     if (ei.isConst() == 1)
110                     {
111                     }
112                     else if (ei.op == TOK.string_)
113                     {
114                         // https://issues.dlang.org/show_bug.cgi?id=14459
115                         // Do not constfold the string literal
116                         // if it's typed as a C string, because the value expansion
117                         // will drop the pointer identity.
118                         if (!(result & WANTexpand) && ei.type.toBasetype().ty == Tpointer)
119                             return nullReturn();
120                     }
121                     else
122                         return nullReturn();
123                     if (ei.type == v.type)
124                     {
125                         // const variable initialized with const expression
126                     }
127                     else if (ei.implicitConvTo(v.type) >= MATCH.constant)
128                     {
129                         // const var initialized with non-const expression
130                         ei = ei.implicitCastTo(null, v.type);
131                         ei = ei.expressionSemantic(null);
132                     }
133                     else
134                         return nullReturn();
135                 }
136                 else if (!(v.storage_class & STC.manifest) &&
137                          ei.isConst() != 1 &&
138                          ei.op != TOK.string_ &&
139                          ei.op != TOK.address)
140                 {
141                     return nullReturn();
142                 }
143 
144                 if (!ei.type)
145                 {
146                     return nullReturn();
147                 }
148                 else
149                 {
150                     // Should remove the copy() operation by
151                     // making all mods to expressions copy-on-write
152                     return initializerReturn(ei.copy());
153                 }
154             }
155             else
156             {
157                 // v does not have an initializer
158                 version (all)
159                 {
160                     return nullReturn();
161                 }
162                 else
163                 {
164                     // BUG: what if const is initialized in constructor?
165                     auto e = v.type.defaultInit();
166                     e.loc = e1.loc;
167                     return initializerReturn(e);
168                 }
169             }
170             assert(0);
171         }
172     }
173     return nullReturn();
174 }
175 
176 private Expression fromConstInitializer(int result, Expression e1)
177 {
178     //printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars());
179     //static int xx; if (xx++ == 10) assert(0);
180     Expression e = e1;
181     if (e1.op == TOK.variable)
182     {
183         VarExp ve = cast(VarExp)e1;
184         VarDeclaration v = ve.var.isVarDeclaration();
185         e = expandVar(result, v);
186         if (e)
187         {
188             // If it is a comma expression involving a declaration, we mustn't
189             // perform a copy -- we'd get two declarations of the same variable.
190             // See bugzilla 4465.
191             if (e.op == TOK.comma && (cast(CommaExp)e).e1.op == TOK.declaration)
192                 e = e1;
193             else if (e.type != e1.type && e1.type && e1.type.ty != Tident)
194             {
195                 // Type 'paint' operation
196                 e = e.copy();
197                 e.type = e1.type;
198             }
199             e.loc = e1.loc;
200         }
201         else
202         {
203             e = e1;
204         }
205     }
206     return e;
207 }
208 
209 /* It is possible for constant folding to change an array expression of
210  * unknown length, into one where the length is known.
211  * If the expression 'arr' is a literal, set lengthVar to be its length.
212  */
213 package void setLengthVarIfKnown(VarDeclaration lengthVar, Expression arr)
214 {
215     if (!lengthVar)
216         return;
217     if (lengthVar._init && !lengthVar._init.isVoidInitializer())
218         return; // we have previously calculated the length
219     size_t len;
220     if (arr.op == TOK.string_)
221         len = (cast(StringExp)arr).len;
222     else if (arr.op == TOK.arrayLiteral)
223         len = (cast(ArrayLiteralExp)arr).elements.dim;
224     else
225     {
226         Type t = arr.type.toBasetype();
227         if (t.ty == Tsarray)
228             len = cast(size_t)(cast(TypeSArray)t).dim.toInteger();
229         else
230             return; // we don't know the length yet
231     }
232     Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t);
233     lengthVar._init = new ExpInitializer(Loc.initial, dollar);
234     lengthVar.storage_class |= STC.static_ | STC.const_;
235 }
236 
237 /* Same as above, but determines the length from 'type'. */
238 package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type)
239 {
240     if (!lengthVar)
241         return;
242     if (lengthVar._init && !lengthVar._init.isVoidInitializer())
243         return; // we have previously calculated the length
244     size_t len;
245     Type t = type.toBasetype();
246     if (t.ty == Tsarray)
247         len = cast(size_t)(cast(TypeSArray)t).dim.toInteger();
248     else
249         return; // we don't know the length yet
250     Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t);
251     lengthVar._init = new ExpInitializer(Loc.initial, dollar);
252     lengthVar.storage_class |= STC.static_ | STC.const_;
253 }
254 
255 /*********************************
256  * Constant fold an Expression.
257  * Params:
258  *      e = expression to const fold; this may get modified in-place
259  *      result = WANTvalue, WANTexpand, or both
260  *      keepLvalue = `e` is an lvalue, and keep it as an lvalue since it is
261  *                   an argument to a `ref` or `out` parameter, or the operand of `&` operator
262  * Returns:
263  *      Constant folded version of `e`
264  */
265 Expression Expression_optimize(Expression e, int result, bool keepLvalue)
266 {
267     extern (C++) final class OptimizeVisitor : Visitor
268     {
269         alias visit = Visitor.visit;
270 
271         Expression ret;
272         private const int result;
273         private const bool keepLvalue;
274 
275         extern (D) this(Expression e, int result, bool keepLvalue)
276         {
277             this.ret = e;               // default result is original expression
278             this.result = result;
279             this.keepLvalue = keepLvalue;
280         }
281 
282         void error()
283         {
284             ret = ErrorExp.get();
285         }
286 
287         bool expOptimize(ref Expression e, int flags, bool keepLvalue = false)
288         {
289             if (!e)
290                 return false;
291             Expression ex = Expression_optimize(e, flags, keepLvalue);
292             if (ex.op == TOK.error)
293             {
294                 ret = ex; // store error result
295                 return true;
296             }
297             else
298             {
299                 e = ex; // modify original
300                 return false;
301             }
302         }
303 
304         bool unaOptimize(UnaExp e, int flags)
305         {
306             return expOptimize(e.e1, flags);
307         }
308 
309         bool binOptimize(BinExp e, int flags)
310         {
311             expOptimize(e.e1, flags);
312             expOptimize(e.e2, flags);
313             return ret.op == TOK.error;
314         }
315 
316         override void visit(Expression e)
317         {
318             //printf("Expression::optimize(result = x%x) %s\n", result, e.toChars());
319         }
320 
321         override void visit(VarExp e)
322         {
323             if (keepLvalue)
324             {
325                 VarDeclaration v = e.var.isVarDeclaration();
326                 if (v && !(v.storage_class & STC.manifest))
327                     return;
328             }
329             ret = fromConstInitializer(result, e);
330         }
331 
332         override void visit(TupleExp e)
333         {
334             expOptimize(e.e0, WANTvalue);
335             for (size_t i = 0; i < e.exps.dim; i++)
336             {
337                 expOptimize((*e.exps)[i], WANTvalue);
338             }
339         }
340 
341         override void visit(ArrayLiteralExp e)
342         {
343             if (e.elements)
344             {
345                 expOptimize(e.basis, result & WANTexpand);
346                 for (size_t i = 0; i < e.elements.dim; i++)
347                 {
348                     expOptimize((*e.elements)[i], result & WANTexpand);
349                 }
350             }
351         }
352 
353         override void visit(AssocArrayLiteralExp e)
354         {
355             assert(e.keys.dim == e.values.dim);
356             for (size_t i = 0; i < e.keys.dim; i++)
357             {
358                 expOptimize((*e.keys)[i], result & WANTexpand);
359                 expOptimize((*e.values)[i], result & WANTexpand);
360             }
361         }
362 
363         override void visit(StructLiteralExp e)
364         {
365             if (e.stageflags & stageOptimize)
366                 return;
367             int old = e.stageflags;
368             e.stageflags |= stageOptimize;
369             if (e.elements)
370             {
371                 for (size_t i = 0; i < e.elements.dim; i++)
372                 {
373                     expOptimize((*e.elements)[i], result & WANTexpand);
374                 }
375             }
376             e.stageflags = old;
377         }
378 
379         override void visit(UnaExp e)
380         {
381             //printf("UnaExp::optimize() %s\n", e.toChars());
382             if (unaOptimize(e, result))
383                 return;
384         }
385 
386         override void visit(NegExp e)
387         {
388             if (unaOptimize(e, result))
389                 return;
390             if (e.e1.isConst() == 1)
391             {
392                 ret = Neg(e.type, e.e1).copy();
393             }
394         }
395 
396         override void visit(ComExp e)
397         {
398             if (unaOptimize(e, result))
399                 return;
400             if (e.e1.isConst() == 1)
401             {
402                 ret = Com(e.type, e.e1).copy();
403             }
404         }
405 
406         override void visit(NotExp e)
407         {
408             if (unaOptimize(e, result))
409                 return;
410             if (e.e1.isConst() == 1)
411             {
412                 ret = Not(e.type, e.e1).copy();
413             }
414         }
415 
416         override void visit(SymOffExp e)
417         {
418             assert(e.var);
419         }
420 
421         override void visit(AddrExp e)
422         {
423             //printf("AddrExp::optimize(result = %d) %s\n", result, e.toChars());
424             /* Rewrite &(a,b) as (a,&b)
425              */
426             if (e.e1.op == TOK.comma)
427             {
428                 CommaExp ce = cast(CommaExp)e.e1;
429                 auto ae = new AddrExp(e.loc, ce.e2, e.type);
430                 ret = new CommaExp(ce.loc, ce.e1, ae);
431                 ret.type = e.type;
432                 return;
433             }
434             // Keep lvalue-ness
435             if (expOptimize(e.e1, result, true))
436                 return;
437             // Convert &*ex to ex
438             if (e.e1.op == TOK.star)
439             {
440                 Expression ex = (cast(PtrExp)e.e1).e1;
441                 if (e.type.equals(ex.type))
442                     ret = ex;
443                 else if (e.type.toBasetype().equivalent(ex.type.toBasetype()))
444                 {
445                     ret = ex.copy();
446                     ret.type = e.type;
447                 }
448                 return;
449             }
450             if (e.e1.op == TOK.variable)
451             {
452                 VarExp ve = cast(VarExp)e.e1;
453                 if (!ve.var.isOut() && !ve.var.isRef() && !ve.var.isImportedSymbol())
454                 {
455                     ret = new SymOffExp(e.loc, ve.var, 0, ve.hasOverloads);
456                     ret.type = e.type;
457                     return;
458                 }
459             }
460             if (e.e1.op == TOK.index)
461             {
462                 // Convert &array[n] to &array+n
463                 IndexExp ae = cast(IndexExp)e.e1;
464                 if (ae.e2.op == TOK.int64 && ae.e1.op == TOK.variable)
465                 {
466                     sinteger_t index = ae.e2.toInteger();
467                     VarExp ve = cast(VarExp)ae.e1;
468                     if (ve.type.ty == Tsarray && !ve.var.isImportedSymbol())
469                     {
470                         TypeSArray ts = cast(TypeSArray)ve.type;
471                         sinteger_t dim = ts.dim.toInteger();
472                         if (index < 0 || index >= dim)
473                         {
474                             e.error("array index %lld is out of bounds `[0..%lld]`", index, dim);
475                             return error();
476                         }
477 
478                         import core.checkedint : mulu;
479                         bool overflow;
480                         const offset = mulu(index, ts.nextOf().size(e.loc), overflow);
481                         if (overflow)
482                         {
483                             e.error("array offset overflow");
484                             return error();
485                         }
486 
487                         ret = new SymOffExp(e.loc, ve.var, offset);
488                         ret.type = e.type;
489                         return;
490                     }
491                 }
492             }
493         }
494 
495         override void visit(PtrExp e)
496         {
497             //printf("PtrExp::optimize(result = x%x) %s\n", result, e.toChars());
498             if (expOptimize(e.e1, result))
499                 return;
500             // Convert *&ex to ex
501             // But only if there is no type punning involved
502             if (e.e1.op == TOK.address)
503             {
504                 Expression ex = (cast(AddrExp)e.e1).e1;
505                 if (e.type.equals(ex.type))
506                     ret = ex;
507                 else if (e.type.toBasetype().equivalent(ex.type.toBasetype()))
508                 {
509                     ret = ex.copy();
510                     ret.type = e.type;
511                 }
512             }
513             if (keepLvalue)
514                 return;
515             // Constant fold *(&structliteral + offset)
516             if (e.e1.op == TOK.add)
517             {
518                 Expression ex = Ptr(e.type, e.e1).copy();
519                 if (!CTFEExp.isCantExp(ex))
520                 {
521                     ret = ex;
522                     return;
523                 }
524             }
525             if (e.e1.op == TOK.symbolOffset)
526             {
527                 SymOffExp se = cast(SymOffExp)e.e1;
528                 VarDeclaration v = se.var.isVarDeclaration();
529                 Expression ex = expandVar(result, v);
530                 if (ex && ex.op == TOK.structLiteral)
531                 {
532                     StructLiteralExp sle = cast(StructLiteralExp)ex;
533                     ex = sle.getField(e.type, cast(uint)se.offset);
534                     if (ex && !CTFEExp.isCantExp(ex))
535                     {
536                         ret = ex;
537                         return;
538                     }
539                 }
540             }
541         }
542 
543         override void visit(DotVarExp e)
544         {
545             //printf("DotVarExp::optimize(result = x%x) %s\n", result, e.toChars());
546             if (expOptimize(e.e1, result))
547                 return;
548             if (keepLvalue)
549                 return;
550             Expression ex = e.e1;
551             if (ex.op == TOK.variable)
552             {
553                 VarExp ve = cast(VarExp)ex;
554                 VarDeclaration v = ve.var.isVarDeclaration();
555                 ex = expandVar(result, v);
556             }
557             if (ex && ex.op == TOK.structLiteral)
558             {
559                 StructLiteralExp sle = cast(StructLiteralExp)ex;
560                 VarDeclaration vf = e.var.isVarDeclaration();
561                 if (vf && !vf.overlapped)
562                 {
563                     /* https://issues.dlang.org/show_bug.cgi?id=13021
564                      * Prevent optimization if vf has overlapped fields.
565                      */
566                     ex = sle.getField(e.type, vf.offset);
567                     if (ex && !CTFEExp.isCantExp(ex))
568                     {
569                         ret = ex;
570                         return;
571                     }
572                 }
573             }
574         }
575 
576         override void visit(NewExp e)
577         {
578             expOptimize(e.thisexp, WANTvalue);
579             // Optimize parameters
580             if (e.newargs)
581             {
582                 for (size_t i = 0; i < e.newargs.dim; i++)
583                 {
584                     expOptimize((*e.newargs)[i], WANTvalue);
585                 }
586             }
587             if (e.arguments)
588             {
589                 for (size_t i = 0; i < e.arguments.dim; i++)
590                 {
591                     expOptimize((*e.arguments)[i], WANTvalue);
592                 }
593             }
594         }
595 
596         override void visit(CallExp e)
597         {
598             //printf("CallExp::optimize(result = %d) %s\n", result, e.toChars());
599             // Optimize parameters with keeping lvalue-ness
600             if (expOptimize(e.e1, result))
601                 return;
602             if (e.arguments)
603             {
604                 Type t1 = e.e1.type.toBasetype();
605                 if (t1.ty == Tdelegate)
606                     t1 = t1.nextOf();
607                 assert(t1.ty == Tfunction);
608                 TypeFunction tf = cast(TypeFunction)t1;
609                 for (size_t i = 0; i < e.arguments.dim; i++)
610                 {
611                     Parameter p = tf.parameterList[i];
612                     bool keep = p && p.isReference();
613                     expOptimize((*e.arguments)[i], WANTvalue, keep);
614                 }
615             }
616         }
617 
618         override void visit(CastExp e)
619         {
620             //printf("CastExp::optimize(result = %d) %s\n", result, e.toChars());
621             //printf("from %s to %s\n", e.type.toChars(), e.to.toChars());
622             //printf("from %s\n", e.type.toChars());
623             //printf("e1.type %s\n", e.e1.type.toChars());
624             //printf("type = %p\n", e.type);
625             assert(e.type);
626             TOK op1 = e.e1.op;
627             Expression e1old = e.e1;
628             if (expOptimize(e.e1, result))
629                 return;
630             e.e1 = fromConstInitializer(result, e.e1);
631             if (e.e1 == e1old && e.e1.op == TOK.arrayLiteral && e.type.toBasetype().ty == Tpointer && e.e1.type.toBasetype().ty != Tsarray)
632             {
633                 // Casting this will result in the same expression, and
634                 // infinite loop because of Expression::implicitCastTo()
635                 return; // no change
636             }
637             if ((e.e1.op == TOK.string_ || e.e1.op == TOK.arrayLiteral) &&
638                 (e.type.ty == Tpointer || e.type.ty == Tarray))
639             {
640                 const esz  = e.type.nextOf().size(e.loc);
641                 const e1sz = e.e1.type.toBasetype().nextOf().size(e.e1.loc);
642                 if (esz == SIZE_INVALID || e1sz == SIZE_INVALID)
643                     return error();
644 
645                 if (e1sz == esz)
646                 {
647                     // https://issues.dlang.org/show_bug.cgi?id=12937
648                     // If target type is void array, trying to paint
649                     // e.e1 with that type will cause infinite recursive optimization.
650                     if (e.type.nextOf().ty == Tvoid)
651                         return;
652                     ret = e.e1.castTo(null, e.type);
653                     //printf(" returning1 %s\n", ret.toChars());
654                     return;
655                 }
656             }
657 
658             if (e.e1.op == TOK.structLiteral && e.e1.type.implicitConvTo(e.type) >= MATCH.constant)
659             {
660                 //printf(" returning2 %s\n", e.e1.toChars());
661             L1:
662                 // Returning e1 with changing its type
663                 ret = (e1old == e.e1 ? e.e1.copy() : e.e1);
664                 ret.type = e.type;
665                 return;
666             }
667             /* The first test here is to prevent infinite loops
668              */
669             if (op1 != TOK.arrayLiteral && e.e1.op == TOK.arrayLiteral)
670             {
671                 ret = e.e1.castTo(null, e.to);
672                 return;
673             }
674             if (e.e1.op == TOK.null_ && (e.type.ty == Tpointer || e.type.ty == Tclass || e.type.ty == Tarray))
675             {
676                 //printf(" returning3 %s\n", e.e1.toChars());
677                 goto L1;
678             }
679             if (e.type.ty == Tclass && e.e1.type.ty == Tclass)
680             {
681                 import dmd.aggregate : Sizeok;
682 
683                 // See if we can remove an unnecessary cast
684                 ClassDeclaration cdfrom = e.e1.type.isClassHandle();
685                 ClassDeclaration cdto = e.type.isClassHandle();
686                 if (cdto == ClassDeclaration.object && !cdfrom.isInterfaceDeclaration())
687                     goto L1;    // can always convert a class to Object
688                 // Need to determine correct offset before optimizing away the cast.
689                 // https://issues.dlang.org/show_bug.cgi?id=16980
690                 cdfrom.size(e.loc);
691                 assert(cdfrom.sizeok == Sizeok.done);
692                 assert(cdto.sizeok == Sizeok.done || !cdto.isBaseOf(cdfrom, null));
693                 int offset;
694                 if (cdto.isBaseOf(cdfrom, &offset) && offset == 0)
695                 {
696                     //printf(" returning4 %s\n", e.e1.toChars());
697                     goto L1;
698                 }
699             }
700             // We can convert 'head const' to mutable
701             if (e.to.mutableOf().constOf().equals(e.e1.type.mutableOf().constOf()))
702             {
703                 //printf(" returning5 %s\n", e.e1.toChars());
704                 goto L1;
705             }
706             if (e.e1.isConst())
707             {
708                 if (e.e1.op == TOK.symbolOffset)
709                 {
710                     if (e.type.toBasetype().ty != Tsarray)
711                     {
712                         const esz = e.type.size(e.loc);
713                         const e1sz = e.e1.type.size(e.e1.loc);
714                         if (esz == SIZE_INVALID ||
715                             e1sz == SIZE_INVALID)
716                             return error();
717 
718                         if (esz == e1sz)
719                             goto L1;
720                     }
721                     return;
722                 }
723                 if (e.to.toBasetype().ty != Tvoid)
724                 {
725                     if (e.e1.type.equals(e.type) && e.type.equals(e.to))
726                         ret = e.e1;
727                     else
728                         ret = Cast(e.loc, e.type, e.to, e.e1).copy();
729                 }
730             }
731             //printf(" returning6 %s\n", ret.toChars());
732         }
733 
734         override void visit(BinExp e)
735         {
736             //printf("BinExp::optimize(result = %d) %s\n", result, e.toChars());
737             // don't replace const variable with its initializer in e1
738             bool e2only = (e.op == TOK.construct || e.op == TOK.blit);
739             if (e2only ? expOptimize(e.e2, result) : binOptimize(e, result))
740                 return;
741             if (e.op == TOK.leftShiftAssign || e.op == TOK.rightShiftAssign || e.op == TOK.unsignedRightShiftAssign)
742             {
743                 if (e.e2.isConst() == 1)
744                 {
745                     sinteger_t i2 = e.e2.toInteger();
746                     d_uns64 sz = e.e1.type.size(e.e1.loc);
747                     assert(sz != SIZE_INVALID);
748                     sz *= 8;
749                     if (i2 < 0 || i2 >= sz)
750                     {
751                         e.error("shift assign by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1);
752                         return error();
753                     }
754                 }
755             }
756         }
757 
758         override void visit(AddExp e)
759         {
760             //printf("AddExp::optimize(%s)\n", e.toChars());
761             if (binOptimize(e, result))
762                 return;
763             if (e.e1.isConst() && e.e2.isConst())
764             {
765                 if (e.e1.op == TOK.symbolOffset && e.e2.op == TOK.symbolOffset)
766                     return;
767                 ret = Add(e.loc, e.type, e.e1, e.e2).copy();
768             }
769         }
770 
771         override void visit(MinExp e)
772         {
773             if (binOptimize(e, result))
774                 return;
775             if (e.e1.isConst() && e.e2.isConst())
776             {
777                 if (e.e2.op == TOK.symbolOffset)
778                     return;
779                 ret = Min(e.loc, e.type, e.e1, e.e2).copy();
780             }
781         }
782 
783         override void visit(MulExp e)
784         {
785             //printf("MulExp::optimize(result = %d) %s\n", result, e.toChars());
786             if (binOptimize(e, result))
787                 return;
788             if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
789             {
790                 ret = Mul(e.loc, e.type, e.e1, e.e2).copy();
791             }
792         }
793 
794         override void visit(DivExp e)
795         {
796             //printf("DivExp::optimize(%s)\n", e.toChars());
797             if (binOptimize(e, result))
798                 return;
799             if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
800             {
801                 ret = Div(e.loc, e.type, e.e1, e.e2).copy();
802             }
803         }
804 
805         override void visit(ModExp e)
806         {
807             if (binOptimize(e, result))
808                 return;
809             if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
810             {
811                 ret = Mod(e.loc, e.type, e.e1, e.e2).copy();
812             }
813         }
814 
815         extern (D) void shift_optimize(BinExp e, UnionExp function(const ref Loc, Type, Expression, Expression) shift)
816         {
817             if (binOptimize(e, result))
818                 return;
819             if (e.e2.isConst() == 1)
820             {
821                 sinteger_t i2 = e.e2.toInteger();
822                 d_uns64 sz = e.e1.type.size(e.e1.loc);
823                 assert(sz != SIZE_INVALID);
824                 sz *= 8;
825                 if (i2 < 0 || i2 >= sz)
826                 {
827                     e.error("shift by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1);
828                     return error();
829                 }
830                 if (e.e1.isConst() == 1)
831                     ret = (*shift)(e.loc, e.type, e.e1, e.e2).copy();
832             }
833         }
834 
835         override void visit(ShlExp e)
836         {
837             //printf("ShlExp::optimize(result = %d) %s\n", result, e.toChars());
838             shift_optimize(e, &Shl);
839         }
840 
841         override void visit(ShrExp e)
842         {
843             //printf("ShrExp::optimize(result = %d) %s\n", result, e.toChars());
844             shift_optimize(e, &Shr);
845         }
846 
847         override void visit(UshrExp e)
848         {
849             //printf("UshrExp::optimize(result = %d) %s\n", result, toChars());
850             shift_optimize(e, &Ushr);
851         }
852 
853         override void visit(AndExp e)
854         {
855             if (binOptimize(e, result))
856                 return;
857             if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
858                 ret = And(e.loc, e.type, e.e1, e.e2).copy();
859         }
860 
861         override void visit(OrExp e)
862         {
863             if (binOptimize(e, result))
864                 return;
865             if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
866                 ret = Or(e.loc, e.type, e.e1, e.e2).copy();
867         }
868 
869         override void visit(XorExp e)
870         {
871             if (binOptimize(e, result))
872                 return;
873             if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
874                 ret = Xor(e.loc, e.type, e.e1, e.e2).copy();
875         }
876 
877         override void visit(PowExp e)
878         {
879             if (binOptimize(e, result))
880                 return;
881             // All negative integral powers are illegal.
882             if (e.e1.type.isintegral() && (e.e2.op == TOK.int64) && cast(sinteger_t)e.e2.toInteger() < 0)
883             {
884                 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());
885                 return error();
886             }
887             // If e2 *could* have been an integer, make it one.
888             if (e.e2.op == TOK.float64 && e.e2.toReal() == real_t(cast(sinteger_t)e.e2.toReal()))
889             {
890                 // This only applies to floating point, or positive integral powers.
891                 if (e.e1.type.isfloating() || cast(sinteger_t)e.e2.toInteger() >= 0)
892                     e.e2 = new IntegerExp(e.loc, e.e2.toInteger(), Type.tint64);
893             }
894             if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
895             {
896                 Expression ex = Pow(e.loc, e.type, e.e1, e.e2).copy();
897                 if (!CTFEExp.isCantExp(ex))
898                 {
899                     ret = ex;
900                     return;
901                 }
902             }
903         }
904 
905         override void visit(CommaExp e)
906         {
907             //printf("CommaExp::optimize(result = %d) %s\n", result, e.toChars());
908             // Comma needs special treatment, because it may
909             // contain compiler-generated declarations. We can interpret them, but
910             // otherwise we must NOT attempt to constant-fold them.
911             // In particular, if the comma returns a temporary variable, it needs
912             // to be an lvalue (this is particularly important for struct constructors)
913             expOptimize(e.e1, WANTvalue);
914             expOptimize(e.e2, result, keepLvalue);
915             if (ret.op == TOK.error)
916                 return;
917             if (!e.e1 || e.e1.op == TOK.int64 || e.e1.op == TOK.float64 || !hasSideEffect(e.e1))
918             {
919                 ret = e.e2;
920                 if (ret)
921                     ret.type = e.type;
922             }
923             //printf("-CommaExp::optimize(result = %d) %s\n", result, e.e.toChars());
924         }
925 
926         override void visit(ArrayLengthExp e)
927         {
928             //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, e.toChars());
929             if (unaOptimize(e, WANTexpand))
930                 return;
931             // CTFE interpret static immutable arrays (to get better diagnostics)
932             if (e.e1.op == TOK.variable)
933             {
934                 VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration();
935                 if (v && (v.storage_class & STC.static_) && (v.storage_class & STC.immutable_) && v._init)
936                 {
937                     if (Expression ci = v.getConstInitializer())
938                         e.e1 = ci;
939                 }
940             }
941             if (e.e1.op == TOK.string_ || e.e1.op == TOK.arrayLiteral || e.e1.op == TOK.assocArrayLiteral || e.e1.type.toBasetype().ty == Tsarray)
942             {
943                 ret = ArrayLength(e.type, e.e1).copy();
944             }
945         }
946 
947         override void visit(EqualExp e)
948         {
949             //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars());
950             if (binOptimize(e, WANTvalue))
951                 return;
952             Expression e1 = fromConstInitializer(result, e.e1);
953             Expression e2 = fromConstInitializer(result, e.e2);
954             if (e1.op == TOK.error)
955             {
956                 ret = e1;
957                 return;
958             }
959             if (e2.op == TOK.error)
960             {
961                 ret = e2;
962                 return;
963             }
964             ret = Equal(e.op, e.loc, e.type, e1, e2).copy();
965             if (CTFEExp.isCantExp(ret))
966                 ret = e;
967         }
968 
969         override void visit(IdentityExp e)
970         {
971             //printf("IdentityExp::optimize(result = %d) %s\n", result, e.toChars());
972             if (binOptimize(e, WANTvalue))
973                 return;
974             if ((e.e1.isConst() && e.e2.isConst()) || (e.e1.op == TOK.null_ && e.e2.op == TOK.null_))
975             {
976                 ret = Identity(e.op, e.loc, e.type, e.e1, e.e2).copy();
977                 if (CTFEExp.isCantExp(ret))
978                     ret = e;
979             }
980         }
981 
982         override void visit(IndexExp e)
983         {
984             //printf("IndexExp::optimize(result = %d) %s\n", result, e.toChars());
985             if (expOptimize(e.e1, result & WANTexpand))
986                 return;
987             Expression ex = fromConstInitializer(result, e.e1);
988             // We might know $ now
989             setLengthVarIfKnown(e.lengthVar, ex);
990             if (expOptimize(e.e2, WANTvalue))
991                 return;
992             if (keepLvalue)
993                 return;
994             ret = Index(e.type, ex, e.e2).copy();
995             if (CTFEExp.isCantExp(ret))
996                 ret = e;
997         }
998 
999         override void visit(SliceExp e)
1000         {
1001             //printf("SliceExp::optimize(result = %d) %s\n", result, e.toChars());
1002             if (expOptimize(e.e1, result & WANTexpand))
1003                 return;
1004             if (!e.lwr)
1005             {
1006                 if (e.e1.op == TOK.string_)
1007                 {
1008                     // Convert slice of string literal into dynamic array
1009                     Type t = e.e1.type.toBasetype();
1010                     if (Type tn = t.nextOf())
1011                         ret = e.e1.castTo(null, tn.arrayOf());
1012                 }
1013             }
1014             else
1015             {
1016                 e.e1 = fromConstInitializer(result, e.e1);
1017                 // We might know $ now
1018                 setLengthVarIfKnown(e.lengthVar, e.e1);
1019                 expOptimize(e.lwr, WANTvalue);
1020                 expOptimize(e.upr, WANTvalue);
1021                 if (ret.op == TOK.error)
1022                     return;
1023                 ret = Slice(e.type, e.e1, e.lwr, e.upr).copy();
1024                 if (CTFEExp.isCantExp(ret))
1025                     ret = e;
1026             }
1027             // https://issues.dlang.org/show_bug.cgi?id=14649
1028             // Leave the slice form so it might be
1029             // a part of array operation.
1030             // Assume that the backend codegen will handle the form `e[]`
1031             // as an equal to `e` itself.
1032             if (ret.op == TOK.string_)
1033             {
1034                 e.e1 = ret;
1035                 e.lwr = null;
1036                 e.upr = null;
1037                 ret = e;
1038             }
1039             //printf("-SliceExp::optimize() %s\n", ret.toChars());
1040         }
1041 
1042         override void visit(LogicalExp e)
1043         {
1044             //printf("LogicalExp::optimize(%d) %s\n", result, e.toChars());
1045             if (expOptimize(e.e1, WANTvalue))
1046                 return;
1047             const oror = e.op == TOK.orOr;
1048             if (e.e1.isBool(oror))
1049             {
1050                 // Replace with (e1, oror)
1051                 ret = IntegerExp.createBool(oror);
1052                 ret = Expression.combine(e.e1, ret);
1053                 if (e.type.toBasetype().ty == Tvoid)
1054                 {
1055                     ret = new CastExp(e.loc, ret, Type.tvoid);
1056                     ret.type = e.type;
1057                 }
1058                 ret = Expression_optimize(ret, result, false);
1059                 return;
1060             }
1061             if (expOptimize(e.e2, WANTvalue))
1062                 return;
1063             if (e.e1.isConst())
1064             {
1065                 if (e.e2.isConst())
1066                 {
1067                     bool n1 = e.e1.isBool(true);
1068                     bool n2 = e.e2.isBool(true);
1069                     ret = new IntegerExp(e.loc, oror ? (n1 || n2) : (n1 && n2), e.type);
1070                 }
1071                 else if (e.e1.isBool(!oror))
1072                 {
1073                     if (e.type.toBasetype().ty == Tvoid)
1074                         ret = e.e2;
1075                     else
1076                     {
1077                         ret = new CastExp(e.loc, e.e2, e.type);
1078                         ret.type = e.type;
1079                     }
1080                 }
1081             }
1082         }
1083 
1084         override void visit(CmpExp e)
1085         {
1086             //printf("CmpExp::optimize() %s\n", e.toChars());
1087             if (binOptimize(e, WANTvalue))
1088                 return;
1089             Expression e1 = fromConstInitializer(result, e.e1);
1090             Expression e2 = fromConstInitializer(result, e.e2);
1091             ret = Cmp(e.op, e.loc, e.type, e1, e2).copy();
1092             if (CTFEExp.isCantExp(ret))
1093                 ret = e;
1094         }
1095 
1096         override void visit(CatExp e)
1097         {
1098             //printf("CatExp::optimize(%d) %s\n", result, e.toChars());
1099             if (binOptimize(e, result))
1100                 return;
1101             if (e.e1.op == TOK.concatenate)
1102             {
1103                 // https://issues.dlang.org/show_bug.cgi?id=12798
1104                 // optimize ((expr ~ str1) ~ str2)
1105                 CatExp ce1 = cast(CatExp)e.e1;
1106                 scope CatExp cex = new CatExp(e.loc, ce1.e2, e.e2);
1107                 cex.type = e.type;
1108                 Expression ex = Expression_optimize(cex, result, false);
1109                 if (ex != cex)
1110                 {
1111                     e.e1 = ce1.e1;
1112                     e.e2 = ex;
1113                 }
1114             }
1115             // optimize "str"[] -> "str"
1116             if (e.e1.op == TOK.slice)
1117             {
1118                 SliceExp se1 = cast(SliceExp)e.e1;
1119                 if (se1.e1.op == TOK.string_ && !se1.lwr)
1120                     e.e1 = se1.e1;
1121             }
1122             if (e.e2.op == TOK.slice)
1123             {
1124                 SliceExp se2 = cast(SliceExp)e.e2;
1125                 if (se2.e1.op == TOK.string_ && !se2.lwr)
1126                     e.e2 = se2.e1;
1127             }
1128             ret = Cat(e.type, e.e1, e.e2).copy();
1129             if (CTFEExp.isCantExp(ret))
1130                 ret = e;
1131         }
1132 
1133         override void visit(CondExp e)
1134         {
1135             if (expOptimize(e.econd, WANTvalue))
1136                 return;
1137             if (e.econd.isBool(true))
1138                 ret = Expression_optimize(e.e1, result, keepLvalue);
1139             else if (e.econd.isBool(false))
1140                 ret = Expression_optimize(e.e2, result, keepLvalue);
1141             else
1142             {
1143                 expOptimize(e.e1, result, keepLvalue);
1144                 expOptimize(e.e2, result, keepLvalue);
1145             }
1146         }
1147     }
1148 
1149     scope OptimizeVisitor v = new OptimizeVisitor(e, result, keepLvalue);
1150 
1151     // Optimize the expression until it can no longer be simplified.
1152     size_t b;
1153     while (1)
1154     {
1155         if (b++ == global.recursionLimit)
1156         {
1157             e.error("infinite loop while optimizing expression");
1158             fatal();
1159         }
1160         auto ex = v.ret;
1161         ex.accept(v);
1162         if (ex == v.ret)
1163             break;
1164     }
1165     return v.ret;
1166 }