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