"Fossies" - the Fresh Open Source Software Archive

Member "dmd2/src/dmd/dmd/ctfeexpr.d" (20 Nov 2020, 67989 Bytes) of package /linux/misc/dmd.2.094.2.linux.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) D source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

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