Home | History | Annotate | Line # | Download | only in dmd
ctfeexpr.d revision 1.1
      1 /**
      2  * CTFE for expressions involving pointers, slices, array concatenation etc.
      3  *
      4  * Copyright:   Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
      5  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
      6  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
      7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ctfeexpr.d, _ctfeexpr.d)
      8  * Documentation:  https://dlang.org/phobos/dmd_ctfeexpr.html
      9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ctfeexpr.d
     10  */
     11 
     12 module dmd.ctfeexpr;
     13 
     14 import core.stdc.stdio;
     15 import core.stdc.stdlib;
     16 import core.stdc.string;
     17 import dmd.arraytypes;
     18 import dmd.astenums;
     19 import dmd.constfold;
     20 import dmd.compiler;
     21 import dmd.dclass;
     22 import dmd.declaration;
     23 import dmd.dinterpret;
     24 import dmd.dstruct;
     25 import dmd.dtemplate;
     26 import dmd.errors;
     27 import dmd.expression;
     28 import dmd.func;
     29 import dmd.globals;
     30 import dmd.mtype;
     31 import dmd.root.complex;
     32 import dmd.root.ctfloat;
     33 import dmd.root.port;
     34 import dmd.root.rmem;
     35 import dmd.tokens;
     36 import dmd.visitor;
     37 
     38 
     39 /***********************************************************
     40  * A reference to a class, or an interface. We need this when we
     41  * point to a base class (we must record what the type is).
     42  */
     43 extern (C++) final class ClassReferenceExp : Expression
     44 {
     45     StructLiteralExp value;
     46 
     47     extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type)
     48     {
     49         super(loc, EXP.classReference, __traits(classInstanceSize, ClassReferenceExp));
     50         assert(lit && lit.sd && lit.sd.isClassDeclaration());
     51         this.value = lit;
     52         this.type = type;
     53     }
     54 
     55     ClassDeclaration originalClass()
     56     {
     57         return value.sd.isClassDeclaration();
     58     }
     59 
     60     // Return index of the field, or -1 if not found
     61     private int getFieldIndex(Type fieldtype, uint fieldoffset)
     62     {
     63         ClassDeclaration cd = originalClass();
     64         uint fieldsSoFar = 0;
     65         for (size_t j = 0; j < value.elements.dim; j++)
     66         {
     67             while (j - fieldsSoFar >= cd.fields.dim)
     68             {
     69                 fieldsSoFar += cd.fields.dim;
     70                 cd = cd.baseClass;
     71             }
     72             VarDeclaration v2 = cd.fields[j - fieldsSoFar];
     73             if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size())
     74             {
     75                 return cast(int)(value.elements.dim - fieldsSoFar - cd.fields.dim + (j - fieldsSoFar));
     76             }
     77         }
     78         return -1;
     79     }
     80 
     81     // Return index of the field, or -1 if not found
     82     // Same as getFieldIndex, but checks for a direct match with the VarDeclaration
     83     int findFieldIndexByName(VarDeclaration v)
     84     {
     85         ClassDeclaration cd = originalClass();
     86         size_t fieldsSoFar = 0;
     87         for (size_t j = 0; j < value.elements.dim; j++)
     88         {
     89             while (j - fieldsSoFar >= cd.fields.dim)
     90             {
     91                 fieldsSoFar += cd.fields.dim;
     92                 cd = cd.baseClass;
     93             }
     94             VarDeclaration v2 = cd.fields[j - fieldsSoFar];
     95             if (v == v2)
     96             {
     97                 return cast(int)(value.elements.dim - fieldsSoFar - cd.fields.dim + (j - fieldsSoFar));
     98             }
     99         }
    100         return -1;
    101     }
    102 
    103     override void accept(Visitor v)
    104     {
    105         v.visit(this);
    106     }
    107 }
    108 
    109 /*************************
    110  * Same as getFieldIndex, but checks for a direct match with the VarDeclaration
    111  * Returns:
    112  *    index of the field, or -1 if not found
    113  */
    114 int findFieldIndexByName(const StructDeclaration sd, const VarDeclaration v) pure
    115 {
    116     foreach (i, field; sd.fields)
    117     {
    118         if (field == v)
    119             return cast(int)i;
    120     }
    121     return -1;
    122 }
    123 
    124 /***********************************************************
    125  * Fake class which holds the thrown exception.
    126  * Used for implementing exception handling.
    127  */
    128 extern (C++) final class ThrownExceptionExp : Expression
    129 {
    130     ClassReferenceExp thrown;   // the thing being tossed
    131 
    132     extern (D) this(const ref Loc loc, ClassReferenceExp victim)
    133     {
    134         super(loc, EXP.thrownException, __traits(classInstanceSize, ThrownExceptionExp));
    135         this.thrown = victim;
    136         this.type = victim.type;
    137     }
    138 
    139     override const(char)* toChars() const
    140     {
    141         return "CTFE ThrownException";
    142     }
    143 
    144     // Generate an error message when this exception is not caught
    145     extern (D) void generateUncaughtError()
    146     {
    147         UnionExp ue = void;
    148         Expression e = resolveSlice((*thrown.value.elements)[0], &ue);
    149         StringExp se = e.toStringExp();
    150         thrown.error("uncaught CTFE exception `%s(%s)`", thrown.type.toChars(), se ? se.toChars() : e.toChars());
    151         /* Also give the line where the throw statement was. We won't have it
    152          * in the case where the ThrowStatement is generated internally
    153          * (eg, in ScopeStatement)
    154          */
    155         if (loc.isValid() && !loc.equals(thrown.loc))
    156             .errorSupplemental(loc, "thrown from here");
    157     }
    158 
    159     override void accept(Visitor v)
    160     {
    161         v.visit(this);
    162     }
    163 }
    164 
    165 /***********************************************************
    166  * This type is only used by the interpreter.
    167  */
    168 extern (C++) final class CTFEExp : Expression
    169 {
    170     extern (D) this(EXP tok)
    171     {
    172         super(Loc.initial, tok, __traits(classInstanceSize, CTFEExp));
    173         type = Type.tvoid;
    174     }
    175 
    176     override const(char)* toChars() const
    177     {
    178         switch (op)
    179         {
    180         case EXP.cantExpression:
    181             return "<cant>";
    182         case EXP.voidExpression:
    183             return "cast(void)0";
    184         case EXP.showCtfeContext:
    185             return "<error>";
    186         case EXP.break_:
    187             return "<break>";
    188         case EXP.continue_:
    189             return "<continue>";
    190         case EXP.goto_:
    191             return "<goto>";
    192         default:
    193             assert(0);
    194         }
    195     }
    196 
    197     extern (D) __gshared CTFEExp cantexp;
    198     extern (D) __gshared CTFEExp voidexp;
    199     extern (D) __gshared CTFEExp breakexp;
    200     extern (D) __gshared CTFEExp continueexp;
    201     extern (D) __gshared CTFEExp gotoexp;
    202     /* Used when additional information is needed regarding
    203      * a ctfe error.
    204      */
    205     extern (D) __gshared CTFEExp showcontext;
    206 
    207     extern (D) static bool isCantExp(const Expression e)
    208     {
    209         return e && e.op == EXP.cantExpression;
    210     }
    211 
    212     extern (D) static bool isGotoExp(const Expression e)
    213     {
    214         return e && e.op == EXP.goto_;
    215     }
    216 }
    217 
    218 // True if 'e' is CTFEExp::cantexp, or an exception
    219 bool exceptionOrCantInterpret(const Expression e)
    220 {
    221     return e && (e.op == EXP.cantExpression || e.op == EXP.thrownException || e.op == EXP.showCtfeContext);
    222 }
    223 
    224 /************** Aggregate literals (AA/string/array/struct) ******************/
    225 // Given expr, which evaluates to an array/AA/string literal,
    226 // return true if it needs to be copied
    227 bool needToCopyLiteral(const Expression expr)
    228 {
    229     Expression e = cast()expr;
    230     for (;;)
    231     {
    232         switch (e.op)
    233         {
    234         case EXP.arrayLiteral:
    235             return e.isArrayLiteralExp().ownedByCtfe == OwnedBy.code;
    236         case EXP.assocArrayLiteral:
    237             return e.isAssocArrayLiteralExp().ownedByCtfe == OwnedBy.code;
    238         case EXP.structLiteral:
    239             return e.isStructLiteralExp().ownedByCtfe == OwnedBy.code;
    240         case EXP.string_:
    241         case EXP.this_:
    242         case EXP.variable:
    243             return false;
    244         case EXP.assign:
    245             return false;
    246         case EXP.index:
    247         case EXP.dotVariable:
    248         case EXP.slice:
    249         case EXP.cast_:
    250             e = e.isUnaExp().e1;
    251             continue;
    252         case EXP.concatenate:
    253             return needToCopyLiteral(e.isBinExp().e1) || needToCopyLiteral(e.isBinExp().e2);
    254         case EXP.concatenateAssign:
    255         case EXP.concatenateElemAssign:
    256         case EXP.concatenateDcharAssign:
    257             e = e.isBinExp().e2;
    258             continue;
    259         default:
    260             return false;
    261         }
    262     }
    263 }
    264 
    265 private Expressions* copyLiteralArray(Expressions* oldelems, Expression basis = null)
    266 {
    267     if (!oldelems)
    268         return oldelems;
    269     incArrayAllocs();
    270     auto newelems = new Expressions(oldelems.dim);
    271     foreach (i, el; *oldelems)
    272     {
    273         (*newelems)[i] = copyLiteral(el ? el : basis).copy();
    274     }
    275     return newelems;
    276 }
    277 
    278 // Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral.
    279 // This value will be used for in-place modification.
    280 UnionExp copyLiteral(Expression e)
    281 {
    282     UnionExp ue = void;
    283     if (auto se = e.isStringExp()) // syntaxCopy doesn't make a copy for StringExp!
    284     {
    285         char* s = cast(char*)mem.xcalloc(se.len + 1, se.sz);
    286         const slice = se.peekData();
    287         memcpy(s, slice.ptr, slice.length);
    288         emplaceExp!(StringExp)(&ue, se.loc, s[0 .. se.len * se.sz], se.len, se.sz);
    289         StringExp se2 = ue.exp().isStringExp();
    290         se2.committed = se.committed;
    291         se2.postfix = se.postfix;
    292         se2.type = se.type;
    293         se2.ownedByCtfe = OwnedBy.ctfe;
    294         return ue;
    295     }
    296     if (auto ale = e.isArrayLiteralExp())
    297     {
    298         auto elements = copyLiteralArray(ale.elements, ale.basis);
    299 
    300         emplaceExp!(ArrayLiteralExp)(&ue, e.loc, e.type, elements);
    301 
    302         ArrayLiteralExp r = ue.exp().isArrayLiteralExp();
    303         r.ownedByCtfe = OwnedBy.ctfe;
    304         return ue;
    305     }
    306     if (auto aae = e.isAssocArrayLiteralExp())
    307     {
    308         emplaceExp!(AssocArrayLiteralExp)(&ue, e.loc, copyLiteralArray(aae.keys), copyLiteralArray(aae.values));
    309         AssocArrayLiteralExp r = ue.exp().isAssocArrayLiteralExp();
    310         r.type = e.type;
    311         r.ownedByCtfe = OwnedBy.ctfe;
    312         return ue;
    313     }
    314     if (auto sle = e.isStructLiteralExp())
    315     {
    316         /* syntaxCopy doesn't work for struct literals, because of a nasty special
    317          * case: block assignment is permitted inside struct literals, eg,
    318          * an int[4] array can be initialized with a single int.
    319          */
    320         auto oldelems = sle.elements;
    321         auto newelems = new Expressions(oldelems.dim);
    322         foreach (i, ref el; *newelems)
    323         {
    324             // We need the struct definition to detect block assignment
    325             auto v = sle.sd.fields[i];
    326             auto m = (*oldelems)[i];
    327 
    328             // If it is a void assignment, use the default initializer
    329             if (!m)
    330                 m = voidInitLiteral(v.type, v).copy();
    331 
    332             if (v.type.ty == Tarray || v.type.ty == Taarray)
    333             {
    334                 // Don't have to copy array references
    335             }
    336             else
    337             {
    338                 // Buzilla 15681: Copy the source element always.
    339                 m = copyLiteral(m).copy();
    340 
    341                 // Block assignment from inside struct literals
    342                 if (v.type.ty != m.type.ty && v.type.ty == Tsarray)
    343                 {
    344                     auto tsa = v.type.isTypeSArray();
    345                     auto len = cast(size_t)tsa.dim.toInteger();
    346                     m = createBlockDuplicatedArrayLiteral(&ue, e.loc, v.type, m, len);
    347                     if (m == ue.exp())
    348                         m = ue.copy();
    349                 }
    350             }
    351             el = m;
    352         }
    353         emplaceExp!(StructLiteralExp)(&ue, e.loc, sle.sd, newelems, sle.stype);
    354         auto r = ue.exp().isStructLiteralExp();
    355         r.type = e.type;
    356         r.ownedByCtfe = OwnedBy.ctfe;
    357         r.origin = sle.origin;
    358         return ue;
    359     }
    360     if (e.op == EXP.function_ || e.op == EXP.delegate_ || e.op == EXP.symbolOffset || e.op == EXP.null_ || e.op == EXP.variable || e.op == EXP.dotVariable || e.op == EXP.int64 || e.op == EXP.float64 || e.op == EXP.char_ || e.op == EXP.complex80 || e.op == EXP.void_ || e.op == EXP.vector || e.op == EXP.typeid_)
    361     {
    362         // Simple value types
    363         // Keep e1 for DelegateExp and DotVarExp
    364         emplaceExp!(UnionExp)(&ue, e);
    365         Expression r = ue.exp();
    366         r.type = e.type;
    367         return ue;
    368     }
    369     if (auto se = e.isSliceExp())
    370     {
    371         if (se.type.toBasetype().ty == Tsarray)
    372         {
    373             // same with resolveSlice()
    374             if (se.e1.op == EXP.null_)
    375             {
    376                 emplaceExp!(NullExp)(&ue, se.loc, se.type);
    377                 return ue;
    378             }
    379             ue = Slice(se.type, se.e1, se.lwr, se.upr);
    380             auto r = ue.exp().isArrayLiteralExp();
    381             r.elements = copyLiteralArray(r.elements);
    382             r.ownedByCtfe = OwnedBy.ctfe;
    383             return ue;
    384         }
    385         else
    386         {
    387             // Array slices only do a shallow copy
    388             emplaceExp!(SliceExp)(&ue, e.loc, se.e1, se.lwr, se.upr);
    389             Expression r = ue.exp();
    390             r.type = e.type;
    391             return ue;
    392         }
    393     }
    394     if (isPointer(e.type))
    395     {
    396         // For pointers, we only do a shallow copy.
    397         if (auto ae = e.isAddrExp())
    398             emplaceExp!(AddrExp)(&ue, e.loc, ae.e1);
    399         else if (auto ie = e.isIndexExp())
    400             emplaceExp!(IndexExp)(&ue, e.loc, ie.e1, ie.e2);
    401         else if (auto dve = e.isDotVarExp())
    402         {
    403             emplaceExp!(DotVarExp)(&ue, e.loc, dve.e1, dve.var, dve.hasOverloads);
    404         }
    405         else
    406             assert(0);
    407 
    408         Expression r = ue.exp();
    409         r.type = e.type;
    410         return ue;
    411     }
    412     if (auto cre = e.isClassReferenceExp())
    413     {
    414         emplaceExp!(ClassReferenceExp)(&ue, e.loc, cre.value, e.type);
    415         return ue;
    416     }
    417     if (e.op == EXP.error)
    418     {
    419         emplaceExp!(UnionExp)(&ue, e);
    420         return ue;
    421     }
    422     e.error("CTFE internal error: literal `%s`", e.toChars());
    423     assert(0);
    424 }
    425 
    426 /* Deal with type painting.
    427  * Type painting is a major nuisance: we can't just set
    428  * e.type = type, because that would change the original literal.
    429  * But, we can't simply copy the literal either, because that would change
    430  * the values of any pointers.
    431  */
    432 Expression paintTypeOntoLiteral(Type type, Expression lit)
    433 {
    434     if (lit.type.equals(type))
    435         return lit;
    436     return paintTypeOntoLiteralCopy(type, lit).copy();
    437 }
    438 
    439 Expression paintTypeOntoLiteral(UnionExp* pue, Type type, Expression lit)
    440 {
    441     if (lit.type.equals(type))
    442         return lit;
    443     *pue = paintTypeOntoLiteralCopy(type, lit);
    444     return pue.exp();
    445 }
    446 
    447 private UnionExp paintTypeOntoLiteralCopy(Type type, Expression lit)
    448 {
    449     UnionExp ue;
    450     if (lit.type.equals(type))
    451     {
    452         emplaceExp!(UnionExp)(&ue, lit);
    453         return ue;
    454     }
    455     // If it is a cast to inout, retain the original type of the referenced part.
    456     if (type.hasWild())
    457     {
    458         emplaceExp!(UnionExp)(&ue, lit);
    459         ue.exp().type = type;
    460         return ue;
    461     }
    462     if (auto se = lit.isSliceExp())
    463     {
    464         emplaceExp!(SliceExp)(&ue, lit.loc, se.e1, se.lwr, se.upr);
    465     }
    466     else if (auto ie = lit.isIndexExp())
    467     {
    468         emplaceExp!(IndexExp)(&ue, lit.loc, ie.e1, ie.e2);
    469     }
    470     else if (lit.op == EXP.arrayLiteral)
    471     {
    472         emplaceExp!(SliceExp)(&ue, lit.loc, lit, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t), ArrayLength(Type.tsize_t, lit).copy());
    473     }
    474     else if (lit.op == EXP.string_)
    475     {
    476         // For strings, we need to introduce another level of indirection
    477         emplaceExp!(SliceExp)(&ue, lit.loc, lit, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t), ArrayLength(Type.tsize_t, lit).copy());
    478     }
    479     else if (auto aae = lit.isAssocArrayLiteralExp())
    480     {
    481         // TODO: we should be creating a reference to this AAExp, not
    482         // just a ref to the keys and values.
    483         OwnedBy wasOwned = aae.ownedByCtfe;
    484         emplaceExp!(AssocArrayLiteralExp)(&ue, lit.loc, aae.keys, aae.values);
    485         aae = ue.exp().isAssocArrayLiteralExp();
    486         aae.ownedByCtfe = wasOwned;
    487     }
    488     else
    489     {
    490         // Can't type paint from struct to struct*; this needs another
    491         // level of indirection
    492         if (lit.op == EXP.structLiteral && isPointer(type))
    493             lit.error("CTFE internal error: painting `%s`", type.toChars());
    494         ue = copyLiteral(lit);
    495     }
    496     ue.exp().type = type;
    497     return ue;
    498 }
    499 
    500 /*************************************
    501  * If e is a SliceExp, constant fold it.
    502  * Params:
    503  *      e = expression to resolve
    504  *      pue = if not null, store resulting expression here
    505  * Returns:
    506  *      resulting expression
    507  */
    508 Expression resolveSlice(Expression e, UnionExp* pue = null)
    509 {
    510     SliceExp se = e.isSliceExp();
    511     if (!se)
    512         return e;
    513     if (se.e1.op == EXP.null_)
    514         return se.e1;
    515     if (pue)
    516     {
    517         *pue = Slice(e.type, se.e1, se.lwr, se.upr);
    518         return pue.exp();
    519     }
    520     else
    521         return Slice(e.type, se.e1, se.lwr, se.upr).copy();
    522 }
    523 
    524 /* Determine the array length, without interpreting it.
    525  * e must be an array literal, or a slice
    526  * It's very wasteful to resolve the slice when we only
    527  * need the length.
    528  */
    529 uinteger_t resolveArrayLength(Expression e)
    530 {
    531     switch (e.op)
    532     {
    533         case EXP.vector:
    534             return e.isVectorExp().dim;
    535 
    536         case EXP.null_:
    537             return 0;
    538 
    539         case EXP.slice:
    540         {
    541             auto se = e.isSliceExp();
    542             const ilo = se.lwr.toInteger();
    543             const iup = se.upr.toInteger();
    544             return iup - ilo;
    545         }
    546 
    547         case EXP.string_:
    548             return e.isStringExp().len;
    549 
    550         case EXP.arrayLiteral:
    551         {
    552             const ale = e.isArrayLiteralExp();
    553             return ale.elements ? ale.elements.dim : 0;
    554         }
    555 
    556         case EXP.assocArrayLiteral:
    557         {
    558             return e.isAssocArrayLiteralExp().keys.dim;
    559         }
    560 
    561         default:
    562             assert(0);
    563     }
    564 }
    565 
    566 /******************************
    567  * Helper for NewExp
    568  * Create an array literal consisting of 'elem' duplicated 'dim' times.
    569  * Params:
    570  *      pue = where to store result
    571  *      loc = source location where the interpretation occurs
    572  *      type = target type of the result
    573  *      elem = the source of array element, it will be owned by the result
    574  *      dim = element number of the result
    575  * Returns:
    576  *      Constructed ArrayLiteralExp
    577  */
    578 ArrayLiteralExp createBlockDuplicatedArrayLiteral(UnionExp* pue, const ref Loc loc, Type type, Expression elem, size_t dim)
    579 {
    580     if (type.ty == Tsarray && type.nextOf().ty == Tsarray && elem.type.ty != Tsarray)
    581     {
    582         // If it is a multidimensional array literal, do it recursively
    583         auto tsa = type.nextOf().isTypeSArray();
    584         const len = cast(size_t)tsa.dim.toInteger();
    585         elem = createBlockDuplicatedArrayLiteral(pue, loc, type.nextOf(), elem, len);
    586         if (elem == pue.exp())
    587             elem = pue.copy();
    588     }
    589 
    590     // Buzilla 15681
    591     const tb = elem.type.toBasetype();
    592     const mustCopy = tb.ty == Tstruct || tb.ty == Tsarray;
    593 
    594     auto elements = new Expressions(dim);
    595     foreach (i, ref el; *elements)
    596     {
    597         el = mustCopy && i ? copyLiteral(elem).copy() : elem;
    598     }
    599     emplaceExp!(ArrayLiteralExp)(pue, loc, type, elements);
    600     auto ale = pue.exp().isArrayLiteralExp();
    601     ale.ownedByCtfe = OwnedBy.ctfe;
    602     return ale;
    603 }
    604 
    605 /******************************
    606  * Helper for NewExp
    607  * Create a string literal consisting of 'value' duplicated 'dim' times.
    608  */
    609 StringExp createBlockDuplicatedStringLiteral(UnionExp* pue, const ref Loc loc, Type type, dchar value, size_t dim, ubyte sz)
    610 {
    611     auto s = cast(char*)mem.xcalloc(dim, sz);
    612     foreach (elemi; 0 .. dim)
    613     {
    614         switch (sz)
    615         {
    616         case 1:
    617             s[elemi] = cast(char)value;
    618             break;
    619         case 2:
    620             (cast(wchar*)s)[elemi] = cast(wchar)value;
    621             break;
    622         case 4:
    623             (cast(dchar*)s)[elemi] = value;
    624             break;
    625         default:
    626             assert(0);
    627         }
    628     }
    629     emplaceExp!(StringExp)(pue, loc, s[0 .. dim * sz], dim, sz);
    630     auto se = pue.exp().isStringExp();
    631     se.type = type;
    632     se.committed = true;
    633     se.ownedByCtfe = OwnedBy.ctfe;
    634     return se;
    635 }
    636 
    637 // Return true if t is an AA
    638 bool isAssocArray(Type t)
    639 {
    640     return t.toBasetype().isTypeAArray() !is null;
    641 }
    642 
    643 // Given a template AA type, extract the corresponding built-in AA type
    644 TypeAArray toBuiltinAAType(Type t)
    645 {
    646     return t.toBasetype().isTypeAArray();
    647 }
    648 
    649 /************** TypeInfo operations ************************************/
    650 // Return true if type is TypeInfo_Class
    651 bool isTypeInfo_Class(const Type type)
    652 {
    653     auto tc = cast()type.isTypeClass();
    654     return tc && (Type.dtypeinfo == tc.sym || Type.dtypeinfo.isBaseOf(tc.sym, null));
    655 }
    656 
    657 /************** Pointer operations ************************************/
    658 // Return true if t is a pointer (not a function pointer)
    659 bool isPointer(Type t)
    660 {
    661     Type tb = t.toBasetype();
    662     return tb.ty == Tpointer && tb.nextOf().ty != Tfunction;
    663 }
    664 
    665 // For CTFE only. Returns true if 'e' is true or a non-null pointer.
    666 bool isTrueBool(Expression e)
    667 {
    668     return e.toBool().hasValue(true) || ((e.type.ty == Tpointer || e.type.ty == Tclass) && e.op != EXP.null_);
    669 }
    670 
    671 /* Is it safe to convert from srcPointee* to destPointee* ?
    672  * srcPointee is the genuine type (never void).
    673  * destPointee may be void.
    674  */
    675 bool isSafePointerCast(Type srcPointee, Type destPointee)
    676 {
    677     // It's safe to cast S** to D** if it's OK to cast S* to D*
    678     while (srcPointee.ty == Tpointer && destPointee.ty == Tpointer)
    679     {
    680         srcPointee = srcPointee.nextOf();
    681         destPointee = destPointee.nextOf();
    682     }
    683     // It's OK if both are the same (modulo const)
    684     if (srcPointee.constConv(destPointee))
    685         return true;
    686 
    687     // It's ok to cast from/to shared because CTFE is single threaded anyways
    688     if (srcPointee.unSharedOf() == destPointee.unSharedOf())
    689         return true;
    690 
    691     // It's OK if function pointers differ only in safe/pure/nothrow
    692     if (srcPointee.ty == Tfunction && destPointee.ty == Tfunction)
    693         return srcPointee.covariant(destPointee) == Covariant.yes ||
    694             destPointee.covariant(srcPointee) == Covariant.yes;
    695     // it's OK to cast to void*
    696     if (destPointee.ty == Tvoid)
    697         return true;
    698     // It's OK to cast from V[K] to void*
    699     if (srcPointee.ty == Taarray && destPointee == Type.tvoidptr)
    700         return true;
    701     // It's OK if they are the same size (static array of) integers, eg:
    702     //     int*     --> uint*
    703     //     int[5][] --> uint[5][]
    704     if (srcPointee.ty == Tsarray && destPointee.ty == Tsarray)
    705     {
    706         if (srcPointee.size() != destPointee.size())
    707             return false;
    708         srcPointee = srcPointee.baseElemOf();
    709         destPointee = destPointee.baseElemOf();
    710     }
    711     return srcPointee.isintegral() && destPointee.isintegral() && srcPointee.size() == destPointee.size();
    712 }
    713 
    714 Expression getAggregateFromPointer(Expression e, dinteger_t* ofs)
    715 {
    716     *ofs = 0;
    717     if (auto ae = e.isAddrExp())
    718         e = ae.e1;
    719     if (auto soe = e.isSymOffExp())
    720         *ofs = soe.offset;
    721     if (auto dve = e.isDotVarExp())
    722     {
    723         auto ex = dve.e1;
    724         const v = dve.var.isVarDeclaration();
    725         assert(v);
    726         StructLiteralExp se = (ex.op == EXP.classReference)
    727             ? ex.isClassReferenceExp().value
    728             : ex.isStructLiteralExp();
    729 
    730         // We can't use getField, because it makes a copy
    731         const i = (ex.op == EXP.classReference)
    732             ? ex.isClassReferenceExp().getFieldIndex(e.type, v.offset)
    733             : se.getFieldIndex(e.type, v.offset);
    734         e = (*se.elements)[i];
    735     }
    736     if (auto ie = e.isIndexExp())
    737     {
    738         // Note that each AA element is part of its own memory block
    739         if ((ie.e1.type.ty == Tarray || ie.e1.type.ty == Tsarray || ie.e1.op == EXP.string_ || ie.e1.op == EXP.arrayLiteral) && ie.e2.op == EXP.int64)
    740         {
    741             *ofs = ie.e2.toInteger();
    742             return ie.e1;
    743         }
    744     }
    745     if (auto se = e.isSliceExp())
    746     {
    747         if (se && e.type.toBasetype().ty == Tsarray &&
    748            (se.e1.type.ty == Tarray || se.e1.type.ty == Tsarray || se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral) && se.lwr.op == EXP.int64)
    749         {
    750             *ofs = se.lwr.toInteger();
    751             return se.e1;
    752         }
    753     }
    754 
    755     // It can be a `null` disguised as a cast, e.g. `cast(void*)0`.
    756     if (auto ie = e.isIntegerExp())
    757         if (ie.type.ty == Tpointer && ie.getInteger() == 0)
    758             return new NullExp(ie.loc, e.type.nextOf());
    759     // Those casts are invalid, but let the rest of the code handle it,
    760     // as it could be something like `x !is null`, which doesn't need
    761     // to dereference the pointer, even if the pointer is `cast(void*)420`.
    762 
    763     return e;
    764 }
    765 
    766 /** Return true if agg1 and agg2 are pointers to the same memory block
    767  */
    768 bool pointToSameMemoryBlock(Expression agg1, Expression agg2)
    769 {
    770     if (agg1 == agg2)
    771         return true;
    772     // For integers cast to pointers, we regard them as non-comparable
    773     // unless they are identical. (This may be overly strict).
    774     if (agg1.op == EXP.int64 && agg2.op == EXP.int64 && agg1.toInteger() == agg2.toInteger())
    775     {
    776         return true;
    777     }
    778     // Note that type painting can occur with VarExp, so we
    779     // must compare the variables being pointed to.
    780     if (agg1.op == EXP.variable && agg2.op == EXP.variable && agg1.isVarExp().var == agg2.isVarExp().var)
    781     {
    782         return true;
    783     }
    784     if (agg1.op == EXP.symbolOffset && agg2.op == EXP.symbolOffset && agg1.isSymOffExp().var == agg2.isSymOffExp().var)
    785     {
    786         return true;
    787     }
    788     return false;
    789 }
    790 
    791 // return e1 - e2 as an integer, or error if not possible
    792 Expression pointerDifference(UnionExp* pue, const ref Loc loc, Type type, Expression e1, Expression e2)
    793 {
    794     dinteger_t ofs1, ofs2;
    795     Expression agg1 = getAggregateFromPointer(e1, &ofs1);
    796     Expression agg2 = getAggregateFromPointer(e2, &ofs2);
    797     if (agg1 == agg2)
    798     {
    799         Type pointee = (cast(TypePointer)agg1.type).next;
    800         const sz = pointee.size();
    801         emplaceExp!(IntegerExp)(pue, loc, (ofs1 - ofs2) * sz, type);
    802     }
    803     else if (agg1.op == EXP.string_ && agg2.op == EXP.string_ &&
    804              agg1.isStringExp().peekString().ptr == agg2.isStringExp().peekString().ptr)
    805     {
    806         Type pointee = (cast(TypePointer)agg1.type).next;
    807         const sz = pointee.size();
    808         emplaceExp!(IntegerExp)(pue, loc, (ofs1 - ofs2) * sz, type);
    809     }
    810     else if (agg1.op == EXP.symbolOffset && agg2.op == EXP.symbolOffset &&
    811              agg1.isSymOffExp().var == agg2.isSymOffExp().var)
    812     {
    813         emplaceExp!(IntegerExp)(pue, loc, ofs1 - ofs2, type);
    814     }
    815     else
    816     {
    817         error(loc, "`%s - %s` cannot be interpreted at compile time: cannot subtract pointers to two different memory blocks", e1.toChars(), e2.toChars());
    818         emplaceExp!(CTFEExp)(pue, EXP.cantExpression);
    819     }
    820     return pue.exp();
    821 }
    822 
    823 // Return eptr op e2, where eptr is a pointer, e2 is an integer,
    824 // and op is EXP.add or EXP.min
    825 Expression pointerArithmetic(UnionExp* pue, const ref Loc loc, EXP op, Type type, Expression eptr, Expression e2)
    826 {
    827     if (eptr.type.nextOf().ty == Tvoid)
    828     {
    829         error(loc, "cannot perform arithmetic on `void*` pointers at compile time");
    830     Lcant:
    831         emplaceExp!(CTFEExp)(pue, EXP.cantExpression);
    832         return pue.exp();
    833     }
    834     if (eptr.op == EXP.address)
    835         eptr = eptr.isAddrExp().e1;
    836     dinteger_t ofs1;
    837     Expression agg1 = getAggregateFromPointer(eptr, &ofs1);
    838     if (agg1.op == EXP.symbolOffset)
    839     {
    840         if (agg1.isSymOffExp().var.type.ty != Tsarray)
    841         {
    842             error(loc, "cannot perform pointer arithmetic on arrays of unknown length at compile time");
    843             goto Lcant;
    844         }
    845     }
    846     else if (agg1.op != EXP.string_ && agg1.op != EXP.arrayLiteral)
    847     {
    848         error(loc, "cannot perform pointer arithmetic on non-arrays at compile time");
    849         goto Lcant;
    850     }
    851     dinteger_t ofs2 = e2.toInteger();
    852     Type pointee = (cast(TypeNext)agg1.type.toBasetype()).next;
    853     dinteger_t sz = pointee.size();
    854     sinteger_t indx;
    855     dinteger_t len;
    856     if (agg1.op == EXP.symbolOffset)
    857     {
    858         indx = ofs1 / sz;
    859         len = (cast(TypeSArray)agg1.isSymOffExp().var.type).dim.toInteger();
    860     }
    861     else
    862     {
    863         Expression dollar = ArrayLength(Type.tsize_t, agg1).copy();
    864         assert(!CTFEExp.isCantExp(dollar));
    865         indx = ofs1;
    866         len = dollar.toInteger();
    867     }
    868     if (op == EXP.add || op == EXP.addAssign || op == EXP.plusPlus)
    869         indx += ofs2 / sz;
    870     else if (op == EXP.min || op == EXP.minAssign || op == EXP.minusMinus)
    871         indx -= ofs2 / sz;
    872     else
    873     {
    874         error(loc, "CTFE internal error: bad pointer operation");
    875         goto Lcant;
    876     }
    877     if (indx < 0 || len < indx)
    878     {
    879         error(loc, "cannot assign pointer to index %lld inside memory block `[0..%lld]`", indx, len);
    880         goto Lcant;
    881     }
    882     if (agg1.op == EXP.symbolOffset)
    883     {
    884         emplaceExp!(SymOffExp)(pue, loc, agg1.isSymOffExp().var, indx * sz);
    885         SymOffExp se = pue.exp().isSymOffExp();
    886         se.type = type;
    887         return pue.exp();
    888     }
    889     if (agg1.op != EXP.arrayLiteral && agg1.op != EXP.string_)
    890     {
    891         error(loc, "CTFE internal error: pointer arithmetic `%s`", agg1.toChars());
    892         goto Lcant;
    893     }
    894     if (eptr.type.toBasetype().ty == Tsarray)
    895     {
    896         dinteger_t dim = (cast(TypeSArray)eptr.type.toBasetype()).dim.toInteger();
    897         // Create a CTFE pointer &agg1[indx .. indx+dim]
    898         auto se = ctfeEmplaceExp!SliceExp(loc, agg1,
    899                 ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t),
    900                 ctfeEmplaceExp!IntegerExp(loc, indx + dim, Type.tsize_t));
    901         se.type = type.toBasetype().nextOf();
    902         emplaceExp!(AddrExp)(pue, loc, se);
    903         pue.exp().type = type;
    904         return pue.exp();
    905     }
    906     // Create a CTFE pointer &agg1[indx]
    907     auto ofs = ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t);
    908     Expression ie = ctfeEmplaceExp!IndexExp(loc, agg1, ofs);
    909     ie.type = type.toBasetype().nextOf(); // https://issues.dlang.org/show_bug.cgi?id=13992
    910     emplaceExp!(AddrExp)(pue, loc, ie);
    911     pue.exp().type = type;
    912     return pue.exp();
    913 }
    914 
    915 // Return 1 if true, 0 if false
    916 // -1 if comparison is illegal because they point to non-comparable memory blocks
    917 int comparePointers(EXP op, Expression agg1, dinteger_t ofs1, Expression agg2, dinteger_t ofs2)
    918 {
    919     if (pointToSameMemoryBlock(agg1, agg2))
    920     {
    921         int n;
    922         switch (op)
    923         {
    924         case EXP.lessThan:
    925             n = (ofs1 < ofs2);
    926             break;
    927         case EXP.lessOrEqual:
    928             n = (ofs1 <= ofs2);
    929             break;
    930         case EXP.greaterThan:
    931             n = (ofs1 > ofs2);
    932             break;
    933         case EXP.greaterOrEqual:
    934             n = (ofs1 >= ofs2);
    935             break;
    936         case EXP.identity:
    937         case EXP.equal:
    938             n = (ofs1 == ofs2);
    939             break;
    940         case EXP.notIdentity:
    941         case EXP.notEqual:
    942             n = (ofs1 != ofs2);
    943             break;
    944         default:
    945             assert(0);
    946         }
    947         return n;
    948     }
    949     const null1 = (agg1.op == EXP.null_);
    950     const null2 = (agg2.op == EXP.null_);
    951     int cmp;
    952     if (null1 || null2)
    953     {
    954         switch (op)
    955         {
    956         case EXP.lessThan:
    957             cmp = null1 && !null2;
    958             break;
    959         case EXP.greaterThan:
    960             cmp = !null1 && null2;
    961             break;
    962         case EXP.lessOrEqual:
    963             cmp = null1;
    964             break;
    965         case EXP.greaterOrEqual:
    966             cmp = null2;
    967             break;
    968         case EXP.identity:
    969         case EXP.equal:
    970         case EXP.notIdentity: // 'cmp' gets inverted below
    971         case EXP.notEqual:
    972             cmp = (null1 == null2);
    973             break;
    974         default:
    975             assert(0);
    976         }
    977     }
    978     else
    979     {
    980         switch (op)
    981         {
    982         case EXP.identity:
    983         case EXP.equal:
    984         case EXP.notIdentity: // 'cmp' gets inverted below
    985         case EXP.notEqual:
    986             cmp = 0;
    987             break;
    988         default:
    989             return -1; // memory blocks are different
    990         }
    991     }
    992     if (op == EXP.notIdentity || op == EXP.notEqual)
    993         cmp ^= 1;
    994     return cmp;
    995 }
    996 
    997 // True if conversion from type 'from' to 'to' involves a reinterpret_cast
    998 // floating point -> integer or integer -> floating point
    999 bool isFloatIntPaint(Type to, Type from)
   1000 {
   1001     return from.size() == to.size() && (from.isintegral() && to.isfloating() || from.isfloating() && to.isintegral());
   1002 }
   1003 
   1004 // Reinterpret float/int value 'fromVal' as a float/integer of type 'to'.
   1005 Expression paintFloatInt(UnionExp* pue, Expression fromVal, Type to)
   1006 {
   1007     if (exceptionOrCantInterpret(fromVal))
   1008         return fromVal;
   1009     assert(to.size() == 4 || to.size() == 8);
   1010     return Compiler.paintAsType(pue, fromVal, to);
   1011 }
   1012 
   1013 /******** Constant folding, with support for CTFE ***************************/
   1014 /// Return true if non-pointer expression e can be compared
   1015 /// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity
   1016 bool isCtfeComparable(Expression e)
   1017 {
   1018     if (e.op == EXP.slice)
   1019         e = e.isSliceExp().e1;
   1020     if (e.isConst() != 1)
   1021     {
   1022         if (e.op == EXP.null_ || e.op == EXP.string_ || e.op == EXP.function_ || e.op == EXP.delegate_ || e.op == EXP.arrayLiteral || e.op == EXP.structLiteral || e.op == EXP.assocArrayLiteral || e.op == EXP.classReference)
   1023         {
   1024             return true;
   1025         }
   1026         // https://issues.dlang.org/show_bug.cgi?id=14123
   1027         // TypeInfo object is comparable in CTFE
   1028         if (e.op == EXP.typeid_)
   1029             return true;
   1030         return false;
   1031     }
   1032     return true;
   1033 }
   1034 
   1035 /// Map EXP comparison ops
   1036 private bool numCmp(N)(EXP op, N n1, N n2)
   1037 {
   1038     switch (op)
   1039     {
   1040     case EXP.lessThan:
   1041         return n1 < n2;
   1042     case EXP.lessOrEqual:
   1043         return n1 <= n2;
   1044     case EXP.greaterThan:
   1045         return n1 > n2;
   1046     case EXP.greaterOrEqual:
   1047         return n1 >= n2;
   1048 
   1049     default:
   1050         assert(0);
   1051     }
   1052 }
   1053 
   1054 /// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1
   1055 bool specificCmp(EXP op, int rawCmp)
   1056 {
   1057     return numCmp!int(op, rawCmp, 0);
   1058 }
   1059 
   1060 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
   1061 bool intUnsignedCmp(EXP op, dinteger_t n1, dinteger_t n2)
   1062 {
   1063     return numCmp!dinteger_t(op, n1, n2);
   1064 }
   1065 
   1066 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
   1067 bool intSignedCmp(EXP op, sinteger_t n1, sinteger_t n2)
   1068 {
   1069     return numCmp!sinteger_t(op, n1, n2);
   1070 }
   1071 
   1072 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
   1073 bool realCmp(EXP op, real_t r1, real_t r2)
   1074 {
   1075     // Don't rely on compiler, handle NAN arguments separately
   1076     if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered
   1077     {
   1078         switch (op)
   1079         {
   1080         case EXP.lessThan:
   1081         case EXP.lessOrEqual:
   1082         case EXP.greaterThan:
   1083         case EXP.greaterOrEqual:
   1084             return false;
   1085 
   1086         default:
   1087             assert(0);
   1088         }
   1089     }
   1090     else
   1091     {
   1092         return numCmp!real_t(op, r1, r2);
   1093     }
   1094 }
   1095 
   1096 /* Conceptually the same as memcmp(e1, e2).
   1097  * e1 and e2 may be strings, arrayliterals, or slices.
   1098  * For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
   1099  * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
   1100  * Returns:
   1101  *      -1,0,1
   1102  */
   1103 private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinteger_t len)
   1104 {
   1105     // Resolve slices, if necessary
   1106     uinteger_t lo1 = 0;
   1107     uinteger_t lo2 = 0;
   1108 
   1109     Expression x1 = e1;
   1110     if (auto sle1 = x1.isSliceExp())
   1111     {
   1112         lo1 = sle1.lwr.toInteger();
   1113         x1 = sle1.e1;
   1114     }
   1115     auto se1 = x1.isStringExp();
   1116     auto ae1 = x1.isArrayLiteralExp();
   1117 
   1118     Expression x2 = e2;
   1119     if (auto sle2 = x2.isSliceExp())
   1120     {
   1121         lo2 = sle2.lwr.toInteger();
   1122         x2 = sle2.e1;
   1123     }
   1124     auto se2 = x2.isStringExp();
   1125     auto ae2 = x2.isArrayLiteralExp();
   1126 
   1127     // Now both must be either EXP.arrayLiteral or EXP.string_
   1128     if (se1 && se2)
   1129         return sliceCmpStringWithString(se1, se2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len);
   1130     if (se1 && ae2)
   1131         return sliceCmpStringWithArray(se1, ae2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len);
   1132     if (se2 && ae1)
   1133         return -sliceCmpStringWithArray(se2, ae1, cast(size_t)lo2, cast(size_t)lo1, cast(size_t)len);
   1134     assert(ae1 && ae2);
   1135     // Comparing two array literals. This case is potentially recursive.
   1136     // If they aren't strings, we just need an equality check rather than
   1137     // a full cmp.
   1138     const bool needCmp = ae1.type.nextOf().isintegral();
   1139     foreach (size_t i; 0 .. cast(size_t)len)
   1140     {
   1141         Expression ee1 = (*ae1.elements)[cast(size_t)(lo1 + i)];
   1142         Expression ee2 = (*ae2.elements)[cast(size_t)(lo2 + i)];
   1143         if (needCmp)
   1144         {
   1145             const sinteger_t c = ee1.toInteger() - ee2.toInteger();
   1146             if (c > 0)
   1147                 return 1;
   1148             if (c < 0)
   1149                 return -1;
   1150         }
   1151         else
   1152         {
   1153             if (ctfeRawCmp(loc, ee1, ee2))
   1154                 return 1;
   1155         }
   1156     }
   1157     return 0;
   1158 }
   1159 
   1160 /* Given a delegate expression e, return .funcptr.
   1161  * If e is NullExp, return NULL.
   1162  */
   1163 private FuncDeclaration funcptrOf(Expression e)
   1164 {
   1165     assert(e.type.ty == Tdelegate);
   1166     if (auto de = e.isDelegateExp())
   1167         return de.func;
   1168     if (auto fe = e.isFuncExp())
   1169         return fe.fd;
   1170     assert(e.op == EXP.null_);
   1171     return null;
   1172 }
   1173 
   1174 private bool isArray(const Expression e)
   1175 {
   1176     return e.op == EXP.arrayLiteral || e.op == EXP.string_ || e.op == EXP.slice || e.op == EXP.null_;
   1177 }
   1178 
   1179 /*****
   1180  * Params:
   1181  *      loc = source file location
   1182  *      e1 = left operand
   1183  *      e2 = right operand
   1184  *      identity = true for `is` identity comparisons
   1185  * Returns:
   1186  * For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
   1187  * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
   1188  */
   1189 private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool identity = false)
   1190 {
   1191     if (e1.op == EXP.classReference || e2.op == EXP.classReference)
   1192     {
   1193         if (e1.op == EXP.classReference && e2.op == EXP.classReference &&
   1194             e1.isClassReferenceExp().value == e2.isClassReferenceExp().value)
   1195             return 0;
   1196         return 1;
   1197     }
   1198     if (e1.op == EXP.typeid_ && e2.op == EXP.typeid_)
   1199     {
   1200         // printf("e1: %s\n", e1.toChars());
   1201         // printf("e2: %s\n", e2.toChars());
   1202         Type t1 = isType(e1.isTypeidExp().obj);
   1203         Type t2 = isType(e2.isTypeidExp().obj);
   1204         assert(t1);
   1205         assert(t2);
   1206         return t1 != t2;
   1207     }
   1208     // null == null, regardless of type
   1209     if (e1.op == EXP.null_ && e2.op == EXP.null_)
   1210         return 0;
   1211     if (e1.type.ty == Tpointer && e2.type.ty == Tpointer)
   1212     {
   1213         // Can only be an equality test.
   1214         dinteger_t ofs1, ofs2;
   1215         Expression agg1 = getAggregateFromPointer(e1, &ofs1);
   1216         Expression agg2 = getAggregateFromPointer(e2, &ofs2);
   1217         if ((agg1 == agg2) || (agg1.op == EXP.variable && agg2.op == EXP.variable && agg1.isVarExp().var == agg2.isVarExp().var))
   1218         {
   1219             if (ofs1 == ofs2)
   1220                 return 0;
   1221         }
   1222         return 1;
   1223     }
   1224     if (e1.type.ty == Tdelegate && e2.type.ty == Tdelegate)
   1225     {
   1226         // If .funcptr isn't the same, they are not equal
   1227         if (funcptrOf(e1) != funcptrOf(e2))
   1228             return 1;
   1229         // If both are delegate literals, assume they have the
   1230         // same closure pointer. TODO: We don't support closures yet!
   1231         if (e1.op == EXP.function_ && e2.op == EXP.function_)
   1232             return 0;
   1233         assert(e1.op == EXP.delegate_ && e2.op == EXP.delegate_);
   1234         // Same .funcptr. Do they have the same .ptr?
   1235         Expression ptr1 = e1.isDelegateExp().e1;
   1236         Expression ptr2 = e2.isDelegateExp().e1;
   1237         dinteger_t ofs1, ofs2;
   1238         Expression agg1 = getAggregateFromPointer(ptr1, &ofs1);
   1239         Expression agg2 = getAggregateFromPointer(ptr2, &ofs2);
   1240         // If they are EXP.variable, it means they are FuncDeclarations
   1241         if ((agg1 == agg2 && ofs1 == ofs2) || (agg1.op == EXP.variable && agg2.op == EXP.variable && agg1.isVarExp().var == agg2.isVarExp().var))
   1242         {
   1243             return 0;
   1244         }
   1245         return 1;
   1246     }
   1247     if (isArray(e1) && isArray(e2))
   1248     {
   1249         const uinteger_t len1 = resolveArrayLength(e1);
   1250         const uinteger_t len2 = resolveArrayLength(e2);
   1251         // workaround for dmc optimizer bug calculating wrong len for
   1252         // uinteger_t len = (len1 < len2 ? len1 : len2);
   1253         // if (len == 0) ...
   1254         if (len1 > 0 && len2 > 0)
   1255         {
   1256             const uinteger_t len = (len1 < len2 ? len1 : len2);
   1257             const int res = ctfeCmpArrays(loc, e1, e2, len);
   1258             if (res != 0)
   1259                 return res;
   1260         }
   1261         return cast(int)(len1 - len2);
   1262     }
   1263     if (e1.type.isintegral())
   1264     {
   1265         return e1.toInteger() != e2.toInteger();
   1266     }
   1267     if (e1.type.isreal() || e1.type.isimaginary())
   1268     {
   1269         real_t r1 = e1.type.isreal() ? e1.toReal() : e1.toImaginary();
   1270         real_t r2 = e1.type.isreal() ? e2.toReal() : e2.toImaginary();
   1271         if (identity)
   1272             return !CTFloat.isIdentical(r1, r2);
   1273         if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered
   1274         {
   1275             return 1;   // they are not equal
   1276         }
   1277         else
   1278         {
   1279             return (r1 != r2);
   1280         }
   1281     }
   1282     else if (e1.type.iscomplex())
   1283     {
   1284         auto c1 = e1.toComplex();
   1285         auto c2 = e2.toComplex();
   1286         if (identity)
   1287         {
   1288             return !RealIdentical(c1.re, c2.re) && !RealIdentical(c1.im, c2.im);
   1289         }
   1290         return c1 != c2;
   1291     }
   1292     if (e1.op == EXP.structLiteral && e2.op == EXP.structLiteral)
   1293     {
   1294         StructLiteralExp es1 = e1.isStructLiteralExp();
   1295         StructLiteralExp es2 = e2.isStructLiteralExp();
   1296         // For structs, we only need to return 0 or 1 (< and > aren't legal).
   1297         if (es1.sd != es2.sd)
   1298             return 1;
   1299         else if ((!es1.elements || !es1.elements.dim) && (!es2.elements || !es2.elements.dim))
   1300             return 0; // both arrays are empty
   1301         else if (!es1.elements || !es2.elements)
   1302             return 1;
   1303         else if (es1.elements.dim != es2.elements.dim)
   1304             return 1;
   1305         else
   1306         {
   1307             foreach (size_t i; 0 .. es1.elements.dim)
   1308             {
   1309                 Expression ee1 = (*es1.elements)[i];
   1310                 Expression ee2 = (*es2.elements)[i];
   1311 
   1312                 // https://issues.dlang.org/show_bug.cgi?id=16284
   1313                 if (ee1.op == EXP.void_ && ee2.op == EXP.void_) // if both are VoidInitExp
   1314                     continue;
   1315 
   1316                 if (ee1 == ee2)
   1317                     continue;
   1318                 if (!ee1 || !ee2)
   1319                     return 1;
   1320                 const int cmp = ctfeRawCmp(loc, ee1, ee2, identity);
   1321                 if (cmp)
   1322                     return 1;
   1323             }
   1324             return 0; // All elements are equal
   1325         }
   1326     }
   1327     if (e1.op == EXP.assocArrayLiteral && e2.op == EXP.assocArrayLiteral)
   1328     {
   1329         AssocArrayLiteralExp es1 = e1.isAssocArrayLiteralExp();
   1330         AssocArrayLiteralExp es2 = e2.isAssocArrayLiteralExp();
   1331         size_t dim = es1.keys.dim;
   1332         if (es2.keys.dim != dim)
   1333             return 1;
   1334         bool* used = cast(bool*)mem.xmalloc(bool.sizeof * dim);
   1335         memset(used, 0, bool.sizeof * dim);
   1336         foreach (size_t i; 0 .. dim)
   1337         {
   1338             Expression k1 = (*es1.keys)[i];
   1339             Expression v1 = (*es1.values)[i];
   1340             Expression v2 = null;
   1341             foreach (size_t j; 0 .. dim)
   1342             {
   1343                 if (used[j])
   1344                     continue;
   1345                 Expression k2 = (*es2.keys)[j];
   1346                 if (ctfeRawCmp(loc, k1, k2, identity))
   1347                     continue;
   1348                 used[j] = true;
   1349                 v2 = (*es2.values)[j];
   1350                 break;
   1351             }
   1352             if (!v2 || ctfeRawCmp(loc, v1, v2, identity))
   1353             {
   1354                 mem.xfree(used);
   1355                 return 1;
   1356             }
   1357         }
   1358         mem.xfree(used);
   1359         return 0;
   1360     }
   1361     else if (e1.op == EXP.assocArrayLiteral && e2.op == EXP.null_)
   1362     {
   1363         return e1.isAssocArrayLiteralExp.keys.dim != 0;
   1364     }
   1365     else if (e1.op == EXP.null_ && e2.op == EXP.assocArrayLiteral)
   1366     {
   1367         return e2.isAssocArrayLiteralExp.keys.dim != 0;
   1368     }
   1369 
   1370     error(loc, "CTFE internal error: bad compare of `%s` and `%s`", e1.toChars(), e2.toChars());
   1371     assert(0);
   1372 }
   1373 
   1374 /// Evaluate ==, !=.  Resolves slices before comparing. Returns 0 or 1
   1375 bool ctfeEqual(const ref Loc loc, EXP op, Expression e1, Expression e2)
   1376 {
   1377     return !ctfeRawCmp(loc, e1, e2) ^ (op == EXP.notEqual);
   1378 }
   1379 
   1380 /// Evaluate is, !is.  Resolves slices before comparing. Returns 0 or 1
   1381 bool ctfeIdentity(const ref Loc loc, EXP op, Expression e1, Expression e2)
   1382 {
   1383     //printf("ctfeIdentity %s %s\n", e1.toChars(), e2.toChars());
   1384     //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", EXPtoString(op).ptr,
   1385     //    EXPtoString(e1.op).ptr, e1.toChars(), EXPtoString(e2.op).ptr, e1.toChars());
   1386     bool cmp;
   1387     if (e1.op == EXP.null_)
   1388     {
   1389         cmp = (e2.op == EXP.null_);
   1390     }
   1391     else if (e2.op == EXP.null_)
   1392     {
   1393         cmp = false;
   1394     }
   1395     else if (e1.op == EXP.symbolOffset && e2.op == EXP.symbolOffset)
   1396     {
   1397         SymOffExp es1 = e1.isSymOffExp();
   1398         SymOffExp es2 = e2.isSymOffExp();
   1399         cmp = (es1.var == es2.var && es1.offset == es2.offset);
   1400     }
   1401     else if (e1.type.isreal())
   1402         cmp = CTFloat.isIdentical(e1.toReal(), e2.toReal());
   1403     else if (e1.type.isimaginary())
   1404         cmp = RealIdentical(e1.toImaginary(), e2.toImaginary());
   1405     else if (e1.type.iscomplex())
   1406     {
   1407         complex_t v1 = e1.toComplex();
   1408         complex_t v2 = e2.toComplex();
   1409         cmp = RealIdentical(creall(v1), creall(v2)) && RealIdentical(cimagl(v1), cimagl(v1));
   1410     }
   1411     else
   1412     {
   1413         cmp = !ctfeRawCmp(loc, e1, e2, true);
   1414     }
   1415     if (op == EXP.notIdentity || op == EXP.notEqual)
   1416         cmp ^= true;
   1417     return cmp;
   1418 }
   1419 
   1420 /// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1
   1421 bool ctfeCmp(const ref Loc loc, EXP op, Expression e1, Expression e2)
   1422 {
   1423     Type t1 = e1.type.toBasetype();
   1424     Type t2 = e2.type.toBasetype();
   1425 
   1426     if (t1.isString() && t2.isString())
   1427         return specificCmp(op, ctfeRawCmp(loc, e1, e2));
   1428     else if (t1.isreal())
   1429         return realCmp(op, e1.toReal(), e2.toReal());
   1430     else if (t1.isimaginary())
   1431         return realCmp(op, e1.toImaginary(), e2.toImaginary());
   1432     else if (t1.isunsigned() || t2.isunsigned())
   1433         return intUnsignedCmp(op, e1.toInteger(), e2.toInteger());
   1434     else
   1435         return intSignedCmp(op, e1.toInteger(), e2.toInteger());
   1436 }
   1437 
   1438 UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2)
   1439 {
   1440     Type t1 = e1.type.toBasetype();
   1441     Type t2 = e2.type.toBasetype();
   1442     UnionExp ue;
   1443     if (e2.op == EXP.string_ && e1.op == EXP.arrayLiteral && t1.nextOf().isintegral())
   1444     {
   1445         // [chars] ~ string => string (only valid for CTFE)
   1446         StringExp es1 = e2.isStringExp();
   1447         ArrayLiteralExp es2 = e1.isArrayLiteralExp();
   1448         const len = es1.len + es2.elements.dim;
   1449         const sz = es1.sz;
   1450         void* s = mem.xmalloc((len + 1) * sz);
   1451         const data1 = es1.peekData();
   1452         memcpy(cast(char*)s + sz * es2.elements.dim, data1.ptr, data1.length);
   1453         foreach (size_t i; 0 .. es2.elements.dim)
   1454         {
   1455             Expression es2e = (*es2.elements)[i];
   1456             if (es2e.op != EXP.int64)
   1457             {
   1458                 emplaceExp!(CTFEExp)(&ue, EXP.cantExpression);
   1459                 return ue;
   1460             }
   1461             dinteger_t v = es2e.toInteger();
   1462             Port.valcpy(cast(char*)s + i * sz, v, sz);
   1463         }
   1464         // Add terminating 0
   1465         memset(cast(char*)s + len * sz, 0, sz);
   1466         emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
   1467         StringExp es = ue.exp().isStringExp();
   1468         es.committed = 0;
   1469         es.type = type;
   1470         return ue;
   1471     }
   1472     if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral && t2.nextOf().isintegral())
   1473     {
   1474         // string ~ [chars] => string (only valid for CTFE)
   1475         // Concatenate the strings
   1476         StringExp es1 = e1.isStringExp();
   1477         ArrayLiteralExp es2 = e2.isArrayLiteralExp();
   1478         const len = es1.len + es2.elements.dim;
   1479         const sz = es1.sz;
   1480         void* s = mem.xmalloc((len + 1) * sz);
   1481         auto slice = es1.peekData();
   1482         memcpy(s, slice.ptr, slice.length);
   1483         foreach (size_t i; 0 .. es2.elements.dim)
   1484         {
   1485             Expression es2e = (*es2.elements)[i];
   1486             if (es2e.op != EXP.int64)
   1487             {
   1488                 emplaceExp!(CTFEExp)(&ue, EXP.cantExpression);
   1489                 return ue;
   1490             }
   1491             const v = es2e.toInteger();
   1492             Port.valcpy(cast(char*)s + (es1.len + i) * sz, v, sz);
   1493         }
   1494         // Add terminating 0
   1495         memset(cast(char*)s + len * sz, 0, sz);
   1496         emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
   1497         StringExp es = ue.exp().isStringExp();
   1498         es.sz = sz;
   1499         es.committed = 0; //es1.committed;
   1500         es.type = type;
   1501         return ue;
   1502     }
   1503     if (e1.op == EXP.arrayLiteral && e2.op == EXP.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
   1504     {
   1505         //  [ e1 ] ~ [ e2 ] ---> [ e1, e2 ]
   1506         ArrayLiteralExp es1 = e1.isArrayLiteralExp();
   1507         ArrayLiteralExp es2 = e2.isArrayLiteralExp();
   1508         emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, type, copyLiteralArray(es1.elements));
   1509         es1 = ue.exp().isArrayLiteralExp();
   1510         es1.elements.insert(es1.elements.dim, copyLiteralArray(es2.elements));
   1511         return ue;
   1512     }
   1513     if (e1.op == EXP.arrayLiteral && e2.op == EXP.null_ && t1.nextOf().equals(t2.nextOf()))
   1514     {
   1515         //  [ e1 ] ~ null ----> [ e1 ].dup
   1516         ue = paintTypeOntoLiteralCopy(type, copyLiteral(e1).copy());
   1517         return ue;
   1518     }
   1519     if (e1.op == EXP.null_ && e2.op == EXP.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
   1520     {
   1521         //  null ~ [ e2 ] ----> [ e2 ].dup
   1522         ue = paintTypeOntoLiteralCopy(type, copyLiteral(e2).copy());
   1523         return ue;
   1524     }
   1525     ue = Cat(loc, type, e1, e2);
   1526     return ue;
   1527 }
   1528 
   1529 /*  Given an AA literal 'ae', and a key 'e2':
   1530  *  Return ae[e2] if present, or NULL if not found.
   1531  */
   1532 Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, Expression e2)
   1533 {
   1534     /* Search the keys backwards, in case there are duplicate keys
   1535      */
   1536     for (size_t i = ae.keys.dim; i;)
   1537     {
   1538         --i;
   1539         Expression ekey = (*ae.keys)[i];
   1540         const int eq = ctfeEqual(loc, EXP.equal, ekey, e2);
   1541         if (eq)
   1542         {
   1543             return (*ae.values)[i];
   1544         }
   1545     }
   1546     return null;
   1547 }
   1548 
   1549 /* Same as for constfold.Index, except that it only works for static arrays,
   1550  * dynamic arrays, and strings. We know that e1 is an
   1551  * interpreted CTFE expression, so it cannot have side-effects.
   1552  */
   1553 Expression ctfeIndex(UnionExp* pue, const ref Loc loc, Type type, Expression e1, uinteger_t indx)
   1554 {
   1555     //printf("ctfeIndex(e1 = %s)\n", e1.toChars());
   1556     assert(e1.type);
   1557     if (auto es1 = e1.isStringExp())
   1558     {
   1559         if (indx >= es1.len)
   1560         {
   1561             error(loc, "string index %llu is out of bounds `[0 .. %llu]`", indx, cast(ulong)es1.len);
   1562             return CTFEExp.cantexp;
   1563         }
   1564         emplaceExp!IntegerExp(pue, loc, es1.getCodeUnit(cast(size_t) indx), type);
   1565         return pue.exp();
   1566     }
   1567 
   1568     if (auto ale = e1.isArrayLiteralExp())
   1569     {
   1570         if (indx >= ale.elements.dim)
   1571         {
   1572             error(loc, "array index %llu is out of bounds `%s[0 .. %llu]`", indx, e1.toChars(), cast(ulong)ale.elements.dim);
   1573             return CTFEExp.cantexp;
   1574         }
   1575         Expression e = (*ale.elements)[cast(size_t)indx];
   1576         return paintTypeOntoLiteral(pue, type, e);
   1577     }
   1578 
   1579     assert(0);
   1580 }
   1581 
   1582 Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expression e, bool explicitCast = false)
   1583 {
   1584     Expression paint()
   1585     {
   1586         return paintTypeOntoLiteral(pue, to, e);
   1587     }
   1588 
   1589     if (e.op == EXP.null_)
   1590         return paint();
   1591 
   1592     if (e.op == EXP.classReference)
   1593     {
   1594         // Disallow reinterpreting class casts. Do this by ensuring that
   1595         // the original class can implicitly convert to the target class.
   1596         // Also do not check 'alias this' for explicit cast expressions.
   1597         auto tclass = e.isClassReferenceExp().originalClass().type.isTypeClass();
   1598         auto match = explicitCast ? tclass.implicitConvToWithoutAliasThis(to.mutableOf())
   1599                                   : tclass.implicitConvTo(to.mutableOf());
   1600         if (match)
   1601             return paint();
   1602         else
   1603         {
   1604             emplaceExp!(NullExp)(pue, loc, to);
   1605             return pue.exp();
   1606         }
   1607     }
   1608 
   1609     // Allow TypeInfo type painting
   1610     if (isTypeInfo_Class(e.type) && e.type.implicitConvTo(to))
   1611         return paint();
   1612 
   1613     // Allow casting away const for struct literals
   1614     if (e.op == EXP.structLiteral && e.type.toBasetype().castMod(0) == to.toBasetype().castMod(0))
   1615         return paint();
   1616 
   1617     Expression r;
   1618     if (e.type.equals(type) && type.equals(to))
   1619     {
   1620         // necessary not to change e's address for pointer comparisons
   1621         r = e;
   1622     }
   1623     else if (to.toBasetype().ty == Tarray &&
   1624              type.toBasetype().ty == Tarray &&
   1625              to.toBasetype().nextOf().size() == type.toBasetype().nextOf().size())
   1626     {
   1627         // https://issues.dlang.org/show_bug.cgi?id=12495
   1628         // Array reinterpret casts: eg. string to immutable(ubyte)[]
   1629         return paint();
   1630     }
   1631     else
   1632     {
   1633         *pue = Cast(loc, type, to, e);
   1634         r = pue.exp();
   1635     }
   1636 
   1637     if (CTFEExp.isCantExp(r))
   1638         error(loc, "cannot cast `%s` to `%s` at compile time", e.toChars(), to.toChars());
   1639 
   1640     if (auto ae = e.isArrayLiteralExp())
   1641         ae.ownedByCtfe = OwnedBy.ctfe;
   1642 
   1643     if (auto se = e.isStringExp())
   1644         se.ownedByCtfe = OwnedBy.ctfe;
   1645 
   1646     return r;
   1647 }
   1648 
   1649 /******** Assignment helper functions ***************************/
   1650 /* Set dest = src, where both dest and src are container value literals
   1651  * (ie, struct literals, or static arrays (can be an array literal or a string))
   1652  * Assignment is recursively in-place.
   1653  * Purpose: any reference to a member of 'dest' will remain valid after the
   1654  * assignment.
   1655  */
   1656 void assignInPlace(Expression dest, Expression src)
   1657 {
   1658     if (!(dest.op == EXP.structLiteral || dest.op == EXP.arrayLiteral || dest.op == EXP.string_))
   1659     {
   1660         printf("invalid op %d %d\n", src.op, dest.op);
   1661         assert(0);
   1662     }
   1663     Expressions* oldelems;
   1664     Expressions* newelems;
   1665     if (dest.op == EXP.structLiteral)
   1666     {
   1667         assert(dest.op == src.op);
   1668         oldelems = dest.isStructLiteralExp().elements;
   1669         newelems = src.isStructLiteralExp().elements;
   1670         auto sd = dest.isStructLiteralExp().sd;
   1671         const nfields = sd.nonHiddenFields();
   1672         const nvthis = sd.fields.dim - nfields;
   1673         if (nvthis && oldelems.dim >= nfields && oldelems.dim < newelems.dim)
   1674             foreach (_; 0 .. newelems.dim - oldelems.dim)
   1675                 oldelems.push(null);
   1676     }
   1677     else if (dest.op == EXP.arrayLiteral && src.op == EXP.arrayLiteral)
   1678     {
   1679         oldelems = dest.isArrayLiteralExp().elements;
   1680         newelems = src.isArrayLiteralExp().elements;
   1681     }
   1682     else if (dest.op == EXP.string_ && src.op == EXP.string_)
   1683     {
   1684         sliceAssignStringFromString(dest.isStringExp(), src.isStringExp(), 0);
   1685         return;
   1686     }
   1687     else if (dest.op == EXP.arrayLiteral && src.op == EXP.string_)
   1688     {
   1689         sliceAssignArrayLiteralFromString(dest.isArrayLiteralExp(), src.isStringExp(), 0);
   1690         return;
   1691     }
   1692     else if (src.op == EXP.arrayLiteral && dest.op == EXP.string_)
   1693     {
   1694         sliceAssignStringFromArrayLiteral(dest.isStringExp(), src.isArrayLiteralExp(), 0);
   1695         return;
   1696     }
   1697     else
   1698     {
   1699         printf("invalid op %d %d\n", src.op, dest.op);
   1700         assert(0);
   1701     }
   1702     assert(oldelems.dim == newelems.dim);
   1703     foreach (size_t i; 0 .. oldelems.dim)
   1704     {
   1705         Expression e = (*newelems)[i];
   1706         Expression o = (*oldelems)[i];
   1707         if (e.op == EXP.structLiteral)
   1708         {
   1709             assert(o.op == e.op);
   1710             assignInPlace(o, e);
   1711         }
   1712         else if (e.type.ty == Tsarray && e.op != EXP.void_ && o.type.ty == Tsarray)
   1713         {
   1714             assignInPlace(o, e);
   1715         }
   1716         else
   1717         {
   1718             (*oldelems)[i] = (*newelems)[i];
   1719         }
   1720     }
   1721 }
   1722 
   1723 // Given an AA literal aae,  set aae[index] = newval and return newval.
   1724 Expression assignAssocArrayElement(const ref Loc loc, AssocArrayLiteralExp aae, Expression index, Expression newval)
   1725 {
   1726     /* Create new associative array literal reflecting updated key/value
   1727      */
   1728     Expressions* keysx = aae.keys;
   1729     Expressions* valuesx = aae.values;
   1730     int updated = 0;
   1731     for (size_t j = valuesx.dim; j;)
   1732     {
   1733         j--;
   1734         Expression ekey = (*aae.keys)[j];
   1735         int eq = ctfeEqual(loc, EXP.equal, ekey, index);
   1736         if (eq)
   1737         {
   1738             (*valuesx)[j] = newval;
   1739             updated = 1;
   1740         }
   1741     }
   1742     if (!updated)
   1743     {
   1744         // Append index/newval to keysx[]/valuesx[]
   1745         valuesx.push(newval);
   1746         keysx.push(index);
   1747     }
   1748     return newval;
   1749 }
   1750 
   1751 /// Given array literal oldval of type ArrayLiteralExp or StringExp, of length
   1752 /// oldlen, change its length to newlen. If the newlen is longer than oldlen,
   1753 /// all new elements will be set to the default initializer for the element type.
   1754 Expression changeArrayLiteralLength(UnionExp* pue, const ref Loc loc, TypeArray arrayType, Expression oldval, size_t oldlen, size_t newlen)
   1755 {
   1756     Type elemType = arrayType.next;
   1757     assert(elemType);
   1758     Expression defaultElem = elemType.defaultInitLiteral(loc);
   1759     auto elements = new Expressions(newlen);
   1760     // Resolve slices
   1761     size_t indxlo = 0;
   1762     if (oldval.op == EXP.slice)
   1763     {
   1764         indxlo = cast(size_t)oldval.isSliceExp().lwr.toInteger();
   1765         oldval = oldval.isSliceExp().e1;
   1766     }
   1767     size_t copylen = oldlen < newlen ? oldlen : newlen;
   1768     if (oldval.op == EXP.string_)
   1769     {
   1770         StringExp oldse = oldval.isStringExp();
   1771         void* s = mem.xcalloc(newlen + 1, oldse.sz);
   1772         const data = oldse.peekData();
   1773         memcpy(s, data.ptr, copylen * oldse.sz);
   1774         const defaultValue = cast(uint)defaultElem.toInteger();
   1775         foreach (size_t elemi; copylen .. newlen)
   1776         {
   1777             switch (oldse.sz)
   1778             {
   1779             case 1:
   1780                 (cast(char*)s)[cast(size_t)(indxlo + elemi)] = cast(char)defaultValue;
   1781                 break;
   1782             case 2:
   1783                 (cast(wchar*)s)[cast(size_t)(indxlo + elemi)] = cast(wchar)defaultValue;
   1784                 break;
   1785             case 4:
   1786                 (cast(dchar*)s)[cast(size_t)(indxlo + elemi)] = cast(dchar)defaultValue;
   1787                 break;
   1788             default:
   1789                 assert(0);
   1790             }
   1791         }
   1792         emplaceExp!(StringExp)(pue, loc, s[0 .. newlen * oldse.sz], newlen, oldse.sz);
   1793         StringExp se = pue.exp().isStringExp();
   1794         se.type = arrayType;
   1795         se.sz = oldse.sz;
   1796         se.committed = oldse.committed;
   1797         se.ownedByCtfe = OwnedBy.ctfe;
   1798     }
   1799     else
   1800     {
   1801         if (oldlen != 0)
   1802         {
   1803             assert(oldval.op == EXP.arrayLiteral);
   1804             ArrayLiteralExp ae = oldval.isArrayLiteralExp();
   1805             foreach (size_t i; 0 .. copylen)
   1806                 (*elements)[i] = (*ae.elements)[indxlo + i];
   1807         }
   1808         if (elemType.ty == Tstruct || elemType.ty == Tsarray)
   1809         {
   1810             /* If it is an aggregate literal representing a value type,
   1811              * we need to create a unique copy for each element
   1812              */
   1813             foreach (size_t i; copylen .. newlen)
   1814                 (*elements)[i] = copyLiteral(defaultElem).copy();
   1815         }
   1816         else
   1817         {
   1818             foreach (size_t i; copylen .. newlen)
   1819                 (*elements)[i] = defaultElem;
   1820         }
   1821         emplaceExp!(ArrayLiteralExp)(pue, loc, arrayType, elements);
   1822         ArrayLiteralExp aae = pue.exp().isArrayLiteralExp();
   1823         aae.ownedByCtfe = OwnedBy.ctfe;
   1824     }
   1825     return pue.exp();
   1826 }
   1827 
   1828 /*************************** CTFE Sanity Checks ***************************/
   1829 
   1830 bool isCtfeValueValid(Expression newval)
   1831 {
   1832     Type tb = newval.type.toBasetype();
   1833     switch (newval.op)
   1834     {
   1835         case EXP.int64:
   1836         case EXP.float64:
   1837         case EXP.char_:
   1838         case EXP.complex80:
   1839             return tb.isscalar();
   1840 
   1841         case EXP.null_:
   1842             return tb.ty == Tnull    ||
   1843                    tb.ty == Tpointer ||
   1844                    tb.ty == Tarray   ||
   1845                    tb.ty == Taarray  ||
   1846                    tb.ty == Tclass   ||
   1847                    tb.ty == Tdelegate;
   1848 
   1849         case EXP.string_:
   1850             return true; // CTFE would directly use the StringExp in AST.
   1851 
   1852         case EXP.arrayLiteral:
   1853             return true; //((ArrayLiteralExp *)newval)->ownedByCtfe;
   1854 
   1855         case EXP.assocArrayLiteral:
   1856             return true; //((AssocArrayLiteralExp *)newval)->ownedByCtfe;
   1857 
   1858         case EXP.structLiteral:
   1859             return true; //((StructLiteralExp *)newval)->ownedByCtfe;
   1860 
   1861         case EXP.classReference:
   1862             return true;
   1863 
   1864         case EXP.type:
   1865             return true;
   1866 
   1867         case EXP.vector:
   1868             return true; // vector literal
   1869 
   1870         case EXP.function_:
   1871             return true; // function literal or delegate literal
   1872 
   1873         case EXP.delegate_:
   1874         {
   1875             // &struct.func or &clasinst.func
   1876             // &nestedfunc
   1877             Expression ethis = newval.isDelegateExp().e1;
   1878             return (ethis.op == EXP.structLiteral || ethis.op == EXP.classReference || ethis.op == EXP.variable && ethis.isVarExp().var == newval.isDelegateExp().func);
   1879         }
   1880 
   1881         case EXP.symbolOffset:
   1882         {
   1883             // function pointer, or pointer to static variable
   1884             Declaration d = newval.isSymOffExp().var;
   1885             return d.isFuncDeclaration() || d.isDataseg();
   1886         }
   1887 
   1888         case EXP.typeid_:
   1889         {
   1890             // always valid
   1891             return true;
   1892         }
   1893 
   1894         case EXP.address:
   1895         {
   1896             // e1 should be a CTFE reference
   1897             Expression e1 = newval.isAddrExp().e1;
   1898             return tb.ty == Tpointer &&
   1899             (
   1900                 (e1.op == EXP.structLiteral || e1.op == EXP.arrayLiteral) && isCtfeValueValid(e1) ||
   1901                  e1.op == EXP.variable ||
   1902                  e1.op == EXP.dotVariable && isCtfeReferenceValid(e1) ||
   1903                  e1.op == EXP.index && isCtfeReferenceValid(e1) ||
   1904                  e1.op == EXP.slice && e1.type.toBasetype().ty == Tsarray
   1905             );
   1906         }
   1907 
   1908         case EXP.slice:
   1909         {
   1910             // e1 should be an array aggregate
   1911             const SliceExp se = newval.isSliceExp();
   1912             assert(se.lwr && se.lwr.op == EXP.int64);
   1913             assert(se.upr && se.upr.op == EXP.int64);
   1914             return (tb.ty == Tarray || tb.ty == Tsarray) && (se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral);
   1915         }
   1916 
   1917         case EXP.void_:
   1918             return true; // uninitialized value
   1919 
   1920         default:
   1921             newval.error("CTFE internal error: illegal CTFE value `%s`", newval.toChars());
   1922             return false;
   1923     }
   1924 }
   1925 
   1926 bool isCtfeReferenceValid(Expression newval)
   1927 {
   1928     switch (newval.op)
   1929     {
   1930         case EXP.this_:
   1931             return true;
   1932 
   1933         case EXP.variable:
   1934         {
   1935             const VarDeclaration v = newval.isVarExp().var.isVarDeclaration();
   1936             assert(v);
   1937             // Must not be a reference to a reference
   1938             return true;
   1939         }
   1940 
   1941         case EXP.index:
   1942         {
   1943             const Expression eagg = newval.isIndexExp().e1;
   1944             return eagg.op == EXP.string_ || eagg.op == EXP.arrayLiteral || eagg.op == EXP.assocArrayLiteral;
   1945         }
   1946 
   1947         case EXP.dotVariable:
   1948         {
   1949             Expression eagg = newval.isDotVarExp().e1;
   1950             return (eagg.op == EXP.structLiteral || eagg.op == EXP.classReference) && isCtfeValueValid(eagg);
   1951         }
   1952 
   1953         default:
   1954             // Internally a ref variable may directly point a stack memory.
   1955             // e.g. ref int v = 1;
   1956             return isCtfeValueValid(newval);
   1957     }
   1958 }
   1959 
   1960 // Used for debugging only
   1961 void showCtfeExpr(Expression e, int level = 0)
   1962 {
   1963     for (int i = level; i > 0; --i)
   1964         printf(" ");
   1965     Expressions* elements = null;
   1966     // We need the struct definition to detect block assignment
   1967     StructDeclaration sd = null;
   1968     ClassDeclaration cd = null;
   1969     if (e.op == EXP.structLiteral)
   1970     {
   1971         elements = e.isStructLiteralExp().elements;
   1972         sd = e.isStructLiteralExp().sd;
   1973         printf("STRUCT type = %s %p:\n", e.type.toChars(), e);
   1974     }
   1975     else if (e.op == EXP.classReference)
   1976     {
   1977         elements = e.isClassReferenceExp().value.elements;
   1978         cd = e.isClassReferenceExp().originalClass();
   1979         printf("CLASS type = %s %p:\n", e.type.toChars(), e.isClassReferenceExp().value);
   1980     }
   1981     else if (e.op == EXP.arrayLiteral)
   1982     {
   1983         elements = e.isArrayLiteralExp().elements;
   1984         printf("ARRAY LITERAL type=%s %p:\n", e.type.toChars(), e);
   1985     }
   1986     else if (e.op == EXP.assocArrayLiteral)
   1987     {
   1988         printf("AA LITERAL type=%s %p:\n", e.type.toChars(), e);
   1989     }
   1990     else if (e.op == EXP.string_)
   1991     {
   1992         printf("STRING %s %p\n", e.toChars(), e.isStringExp.peekString.ptr);
   1993     }
   1994     else if (e.op == EXP.slice)
   1995     {
   1996         printf("SLICE %p: %s\n", e, e.toChars());
   1997         showCtfeExpr(e.isSliceExp().e1, level + 1);
   1998     }
   1999     else if (e.op == EXP.variable)
   2000     {
   2001         printf("VAR %p %s\n", e, e.toChars());
   2002         VarDeclaration v = e.isVarExp().var.isVarDeclaration();
   2003         if (v && getValue(v))
   2004             showCtfeExpr(getValue(v), level + 1);
   2005     }
   2006     else if (e.op == EXP.address)
   2007     {
   2008         // This is potentially recursive. We mustn't try to print the thing we're pointing to.
   2009         printf("POINTER %p to %p: %s\n", e, e.isAddrExp().e1, e.toChars());
   2010     }
   2011     else
   2012         printf("VALUE %p: %s\n", e, e.toChars());
   2013     if (elements)
   2014     {
   2015         size_t fieldsSoFar = 0;
   2016         for (size_t i = 0; i < elements.dim; i++)
   2017         {
   2018             Expression z = null;
   2019             VarDeclaration v = null;
   2020             if (i > 15)
   2021             {
   2022                 printf("...(total %d elements)\n", cast(int)elements.dim);
   2023                 return;
   2024             }
   2025             if (sd)
   2026             {
   2027                 v = sd.fields[i];
   2028                 z = (*elements)[i];
   2029             }
   2030             else if (cd)
   2031             {
   2032                 while (i - fieldsSoFar >= cd.fields.dim)
   2033                 {
   2034                     fieldsSoFar += cd.fields.dim;
   2035                     cd = cd.baseClass;
   2036                     for (int j = level; j > 0; --j)
   2037                         printf(" ");
   2038                     printf(" BASE CLASS: %s\n", cd.toChars());
   2039                 }
   2040                 v = cd.fields[i - fieldsSoFar];
   2041                 assert((elements.dim + i) >= (fieldsSoFar + cd.fields.dim));
   2042                 size_t indx = (elements.dim - fieldsSoFar) - cd.fields.dim + i;
   2043                 assert(indx < elements.dim);
   2044                 z = (*elements)[indx];
   2045             }
   2046             if (!z)
   2047             {
   2048                 for (int j = level; j > 0; --j)
   2049                     printf(" ");
   2050                 printf(" void\n");
   2051                 continue;
   2052             }
   2053             if (v)
   2054             {
   2055                 // If it is a void assignment, use the default initializer
   2056                 if ((v.type.ty != z.type.ty) && v.type.ty == Tsarray)
   2057                 {
   2058                     for (int j = level; --j;)
   2059                         printf(" ");
   2060                     printf(" field: block initialized static array\n");
   2061                     continue;
   2062                 }
   2063             }
   2064             showCtfeExpr(z, level + 1);
   2065         }
   2066     }
   2067 }
   2068 
   2069 /*************************** Void initialization ***************************/
   2070 UnionExp voidInitLiteral(Type t, VarDeclaration var)
   2071 {
   2072     UnionExp ue;
   2073     if (t.ty == Tsarray)
   2074     {
   2075         TypeSArray tsa = cast(TypeSArray)t;
   2076         Expression elem = voidInitLiteral(tsa.next, var).copy();
   2077         // For aggregate value types (structs, static arrays) we must
   2078         // create an a separate copy for each element.
   2079         const mustCopy = (elem.op == EXP.arrayLiteral || elem.op == EXP.structLiteral);
   2080         const d = cast(size_t)tsa.dim.toInteger();
   2081         auto elements = new Expressions(d);
   2082         foreach (i; 0 .. d)
   2083         {
   2084             if (mustCopy && i > 0)
   2085                 elem = copyLiteral(elem).copy();
   2086             (*elements)[i] = elem;
   2087         }
   2088         emplaceExp!(ArrayLiteralExp)(&ue, var.loc, tsa, elements);
   2089         ArrayLiteralExp ae = ue.exp().isArrayLiteralExp();
   2090         ae.ownedByCtfe = OwnedBy.ctfe;
   2091     }
   2092     else if (t.ty == Tstruct)
   2093     {
   2094         TypeStruct ts = cast(TypeStruct)t;
   2095         auto exps = new Expressions(ts.sym.fields.dim);
   2096         foreach (size_t i;  0 .. ts.sym.fields.dim)
   2097         {
   2098             (*exps)[i] = voidInitLiteral(ts.sym.fields[i].type, ts.sym.fields[i]).copy();
   2099         }
   2100         emplaceExp!(StructLiteralExp)(&ue, var.loc, ts.sym, exps);
   2101         StructLiteralExp se = ue.exp().isStructLiteralExp();
   2102         se.type = ts;
   2103         se.ownedByCtfe = OwnedBy.ctfe;
   2104     }
   2105     else
   2106         emplaceExp!(VoidInitExp)(&ue, var);
   2107     return ue;
   2108 }
   2109