1 /**
2  * CTFE for expressions involving pointers, slices, array concatenation etc.
3  *
4  * Copyright:   Copyright (C) 1999-2021 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 "cast(void)0";
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 
750     // It can be a `null` disguised as a cast, e.g. `cast(void*)0`.
751     if (auto ie = e.isIntegerExp())
752         if (ie.type.ty == Tpointer && ie.getInteger() == 0)
753             return new NullExp(ie.loc, e.type.nextOf());
754     // Those casts are invalid, but let the rest of the code handle it,
755     // as it could be something like `x !is null`, which doesn't need
756     // to dereference the pointer, even if the pointer is `cast(void*)420`.
757 
758     return e;
759 }
760 
761 /** Return true if agg1 and agg2 are pointers to the same memory block
762  */
763 bool pointToSameMemoryBlock(Expression agg1, Expression agg2)
764 {
765     if (agg1 == agg2)
766         return true;
767     // For integers cast to pointers, we regard them as non-comparable
768     // unless they are identical. (This may be overly strict).
769     if (agg1.op == TOK.int64 && agg2.op == TOK.int64 && agg1.toInteger() == agg2.toInteger())
770     {
771         return true;
772     }
773     // Note that type painting can occur with VarExp, so we
774     // must compare the variables being pointed to.
775     if (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var)
776     {
777         return true;
778     }
779     if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset && (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var)
780     {
781         return true;
782     }
783     return false;
784 }
785 
786 // return e1 - e2 as an integer, or error if not possible
787 UnionExp pointerDifference(const ref Loc loc, Type type, Expression e1, Expression e2)
788 {
789     UnionExp ue = void;
790     dinteger_t ofs1, ofs2;
791     Expression agg1 = getAggregateFromPointer(e1, &ofs1);
792     Expression agg2 = getAggregateFromPointer(e2, &ofs2);
793     if (agg1 == agg2)
794     {
795         Type pointee = (cast(TypePointer)agg1.type).next;
796         const sz = pointee.size();
797         emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type);
798     }
799     else if (agg1.op == TOK.string_ && agg2.op == TOK.string_ &&
800              (cast(StringExp)agg1).peekString().ptr == (cast(StringExp)agg2).peekString().ptr)
801     {
802         Type pointee = (cast(TypePointer)agg1.type).next;
803         const sz = pointee.size();
804         emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type);
805     }
806     else if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset &&
807              (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var)
808     {
809         emplaceExp!(IntegerExp)(&ue, loc, ofs1 - ofs2, type);
810     }
811     else
812     {
813         error(loc, "`%s - %s` cannot be interpreted at compile time: cannot subtract pointers to two different memory blocks", e1.toChars(), e2.toChars());
814         emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
815     }
816     return ue;
817 }
818 
819 // Return eptr op e2, where eptr is a pointer, e2 is an integer,
820 // and op is TOK.add or TOK.min
821 UnionExp pointerArithmetic(const ref Loc loc, TOK op, Type type, Expression eptr, Expression e2)
822 {
823     UnionExp ue;
824     if (eptr.type.nextOf().ty == Tvoid)
825     {
826         error(loc, "cannot perform arithmetic on `void*` pointers at compile time");
827     Lcant:
828         emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
829         return ue;
830     }
831     if (eptr.op == TOK.address)
832         eptr = (cast(AddrExp)eptr).e1;
833     dinteger_t ofs1;
834     Expression agg1 = getAggregateFromPointer(eptr, &ofs1);
835     if (agg1.op == TOK.symbolOffset)
836     {
837         if ((cast(SymOffExp)agg1).var.type.ty != Tsarray)
838         {
839             error(loc, "cannot perform pointer arithmetic on arrays of unknown length at compile time");
840             goto Lcant;
841         }
842     }
843     else if (agg1.op != TOK.string_ && agg1.op != TOK.arrayLiteral)
844     {
845         error(loc, "cannot perform pointer arithmetic on non-arrays at compile time");
846         goto Lcant;
847     }
848     dinteger_t ofs2 = e2.toInteger();
849     Type pointee = (cast(TypeNext)agg1.type.toBasetype()).next;
850     dinteger_t sz = pointee.size();
851     sinteger_t indx;
852     dinteger_t len;
853     if (agg1.op == TOK.symbolOffset)
854     {
855         indx = ofs1 / sz;
856         len = (cast(TypeSArray)(cast(SymOffExp)agg1).var.type).dim.toInteger();
857     }
858     else
859     {
860         Expression dollar = ArrayLength(Type.tsize_t, agg1).copy();
861         assert(!CTFEExp.isCantExp(dollar));
862         indx = ofs1;
863         len = dollar.toInteger();
864     }
865     if (op == TOK.add || op == TOK.addAssign || op == TOK.plusPlus)
866         indx += ofs2 / sz;
867     else if (op == TOK.min || op == TOK.minAssign || op == TOK.minusMinus)
868         indx -= ofs2 / sz;
869     else
870     {
871         error(loc, "CTFE internal error: bad pointer operation");
872         goto Lcant;
873     }
874     if (indx < 0 || len < indx)
875     {
876         error(loc, "cannot assign pointer to index %lld inside memory block `[0..%lld]`", indx, len);
877         goto Lcant;
878     }
879     if (agg1.op == TOK.symbolOffset)
880     {
881         emplaceExp!(SymOffExp)(&ue, loc, (cast(SymOffExp)agg1).var, indx * sz);
882         SymOffExp se = cast(SymOffExp)ue.exp();
883         se.type = type;
884         return ue;
885     }
886     if (agg1.op != TOK.arrayLiteral && agg1.op != TOK.string_)
887     {
888         error(loc, "CTFE internal error: pointer arithmetic `%s`", agg1.toChars());
889         goto Lcant;
890     }
891     if (eptr.type.toBasetype().ty == Tsarray)
892     {
893         dinteger_t dim = (cast(TypeSArray)eptr.type.toBasetype()).dim.toInteger();
894         // Create a CTFE pointer &agg1[indx .. indx+dim]
895         auto se = ctfeEmplaceExp!SliceExp(loc, agg1,
896                 ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t),
897                 ctfeEmplaceExp!IntegerExp(loc, indx + dim, Type.tsize_t));
898         se.type = type.toBasetype().nextOf();
899         emplaceExp!(AddrExp)(&ue, loc, se);
900         ue.exp().type = type;
901         return ue;
902     }
903     // Create a CTFE pointer &agg1[indx]
904     auto ofs = ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t);
905     Expression ie = ctfeEmplaceExp!IndexExp(loc, agg1, ofs);
906     ie.type = type.toBasetype().nextOf(); // https://issues.dlang.org/show_bug.cgi?id=13992
907     emplaceExp!(AddrExp)(&ue, loc, ie);
908     ue.exp().type = type;
909     return ue;
910 }
911 
912 // Return 1 if true, 0 if false
913 // -1 if comparison is illegal because they point to non-comparable memory blocks
914 int comparePointers(TOK op, Expression agg1, dinteger_t ofs1, Expression agg2, dinteger_t ofs2)
915 {
916     if (pointToSameMemoryBlock(agg1, agg2))
917     {
918         int n;
919         switch (op)
920         {
921         case TOK.lessThan:
922             n = (ofs1 < ofs2);
923             break;
924         case TOK.lessOrEqual:
925             n = (ofs1 <= ofs2);
926             break;
927         case TOK.greaterThan:
928             n = (ofs1 > ofs2);
929             break;
930         case TOK.greaterOrEqual:
931             n = (ofs1 >= ofs2);
932             break;
933         case TOK.identity:
934         case TOK.equal:
935             n = (ofs1 == ofs2);
936             break;
937         case TOK.notIdentity:
938         case TOK.notEqual:
939             n = (ofs1 != ofs2);
940             break;
941         default:
942             assert(0);
943         }
944         return n;
945     }
946     const null1 = (agg1.op == TOK.null_);
947     const null2 = (agg2.op == TOK.null_);
948     int cmp;
949     if (null1 || null2)
950     {
951         switch (op)
952         {
953         case TOK.lessThan:
954             cmp = null1 && !null2;
955             break;
956         case TOK.greaterThan:
957             cmp = !null1 && null2;
958             break;
959         case TOK.lessOrEqual:
960             cmp = null1;
961             break;
962         case TOK.greaterOrEqual:
963             cmp = null2;
964             break;
965         case TOK.identity:
966         case TOK.equal:
967         case TOK.notIdentity: // 'cmp' gets inverted below
968         case TOK.notEqual:
969             cmp = (null1 == null2);
970             break;
971         default:
972             assert(0);
973         }
974     }
975     else
976     {
977         switch (op)
978         {
979         case TOK.identity:
980         case TOK.equal:
981         case TOK.notIdentity: // 'cmp' gets inverted below
982         case TOK.notEqual:
983             cmp = 0;
984             break;
985         default:
986             return -1; // memory blocks are different
987         }
988     }
989     if (op == TOK.notIdentity || op == TOK.notEqual)
990         cmp ^= 1;
991     return cmp;
992 }
993 
994 // True if conversion from type 'from' to 'to' involves a reinterpret_cast
995 // floating point -> integer or integer -> floating point
996 bool isFloatIntPaint(Type to, Type from)
997 {
998     return from.size() == to.size() && (from.isintegral() && to.isfloating() || from.isfloating() && to.isintegral());
999 }
1000 
1001 // Reinterpret float/int value 'fromVal' as a float/integer of type 'to'.
1002 Expression paintFloatInt(UnionExp* pue, Expression fromVal, Type to)
1003 {
1004     if (exceptionOrCantInterpret(fromVal))
1005         return fromVal;
1006     assert(to.size() == 4 || to.size() == 8);
1007     return Compiler.paintAsType(pue, fromVal, to);
1008 }
1009 
1010 /******** Constant folding, with support for CTFE ***************************/
1011 /// Return true if non-pointer expression e can be compared
1012 /// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity
1013 bool isCtfeComparable(Expression e)
1014 {
1015     if (e.op == TOK.slice)
1016         e = (cast(SliceExp)e).e1;
1017     if (e.isConst() != 1)
1018     {
1019         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)
1020         {
1021             return true;
1022         }
1023         // https://issues.dlang.org/show_bug.cgi?id=14123
1024         // TypeInfo object is comparable in CTFE
1025         if (e.op == TOK.typeid_)
1026             return true;
1027         return false;
1028     }
1029     return true;
1030 }
1031 
1032 /// Map TOK comparison ops
1033 private bool numCmp(N)(TOK op, N n1, N n2)
1034 {
1035     switch (op)
1036     {
1037     case TOK.lessThan:
1038         return n1 < n2;
1039     case TOK.lessOrEqual:
1040         return n1 <= n2;
1041     case TOK.greaterThan:
1042         return n1 > n2;
1043     case TOK.greaterOrEqual:
1044         return n1 >= n2;
1045 
1046     default:
1047         assert(0);
1048     }
1049 }
1050 
1051 /// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1052 bool specificCmp(TOK op, int rawCmp)
1053 {
1054     return numCmp!int(op, rawCmp, 0);
1055 }
1056 
1057 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1058 bool intUnsignedCmp(TOK op, dinteger_t n1, dinteger_t n2)
1059 {
1060     return numCmp!dinteger_t(op, n1, n2);
1061 }
1062 
1063 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1064 bool intSignedCmp(TOK op, sinteger_t n1, sinteger_t n2)
1065 {
1066     return numCmp!sinteger_t(op, n1, n2);
1067 }
1068 
1069 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1070 bool realCmp(TOK op, real_t r1, real_t r2)
1071 {
1072     // Don't rely on compiler, handle NAN arguments separately
1073     if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered
1074     {
1075         switch (op)
1076         {
1077         case TOK.lessThan:
1078         case TOK.lessOrEqual:
1079         case TOK.greaterThan:
1080         case TOK.greaterOrEqual:
1081             return false;
1082 
1083         default:
1084             assert(0);
1085         }
1086     }
1087     else
1088     {
1089         return numCmp!real_t(op, r1, r2);
1090     }
1091 }
1092 
1093 /* Conceptually the same as memcmp(e1, e2).
1094  * e1 and e2 may be strings, arrayliterals, or slices.
1095  * For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
1096  * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
1097  * Returns:
1098  *      -1,0,1
1099  */
1100 private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinteger_t len)
1101 {
1102     // Resolve slices, if necessary
1103     uinteger_t lo1 = 0;
1104     uinteger_t lo2 = 0;
1105 
1106     Expression x1 = e1;
1107     if (auto sle1 = x1.isSliceExp())
1108     {
1109         lo1 = sle1.lwr.toInteger();
1110         x1 = sle1.e1;
1111     }
1112     auto se1 = x1.isStringExp();
1113     auto ae1 = x1.isArrayLiteralExp();
1114 
1115     Expression x2 = e2;
1116     if (auto sle2 = x2.isSliceExp())
1117     {
1118         lo2 = sle2.lwr.toInteger();
1119         x2 = sle2.e1;
1120     }
1121     auto se2 = x2.isStringExp();
1122     auto ae2 = x2.isArrayLiteralExp();
1123 
1124     // Now both must be either TOK.arrayLiteral or TOK.string_
1125     if (se1 && se2)
1126         return sliceCmpStringWithString(se1, se2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len);
1127     if (se1 && ae2)
1128         return sliceCmpStringWithArray(se1, ae2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len);
1129     if (se2 && ae1)
1130         return -sliceCmpStringWithArray(se2, ae1, cast(size_t)lo2, cast(size_t)lo1, cast(size_t)len);
1131     assert(ae1 && ae2);
1132     // Comparing two array literals. This case is potentially recursive.
1133     // If they aren't strings, we just need an equality check rather than
1134     // a full cmp.
1135     const bool needCmp = ae1.type.nextOf().isintegral();
1136     foreach (size_t i; 0 .. cast(size_t)len)
1137     {
1138         Expression ee1 = (*ae1.elements)[cast(size_t)(lo1 + i)];
1139         Expression ee2 = (*ae2.elements)[cast(size_t)(lo2 + i)];
1140         if (needCmp)
1141         {
1142             const sinteger_t c = ee1.toInteger() - ee2.toInteger();
1143             if (c > 0)
1144                 return 1;
1145             if (c < 0)
1146                 return -1;
1147         }
1148         else
1149         {
1150             if (ctfeRawCmp(loc, ee1, ee2))
1151                 return 1;
1152         }
1153     }
1154     return 0;
1155 }
1156 
1157 /* Given a delegate expression e, return .funcptr.
1158  * If e is NullExp, return NULL.
1159  */
1160 private FuncDeclaration funcptrOf(Expression e)
1161 {
1162     assert(e.type.ty == Tdelegate);
1163     if (auto de = e.isDelegateExp())
1164         return de.func;
1165     if (auto fe = e.isFuncExp())
1166         return fe.fd;
1167     assert(e.op == TOK.null_);
1168     return null;
1169 }
1170 
1171 private bool isArray(const Expression e)
1172 {
1173     return e.op == TOK.arrayLiteral || e.op == TOK.string_ || e.op == TOK.slice || e.op == TOK.null_;
1174 }
1175 
1176 /*****
1177  * Params:
1178  *      loc = source file location
1179  *      e1 = left operand
1180  *      e2 = right operand
1181  *      identity = true for `is` identity comparisons
1182  * Returns:
1183  * For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
1184  * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
1185  */
1186 private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool identity = false)
1187 {
1188     if (e1.op == TOK.classReference || e2.op == TOK.classReference)
1189     {
1190         if (e1.op == TOK.classReference && e2.op == TOK.classReference &&
1191             (cast(ClassReferenceExp)e1).value == (cast(ClassReferenceExp)e2).value)
1192             return 0;
1193         return 1;
1194     }
1195     if (e1.op == TOK.typeid_ && e2.op == TOK.typeid_)
1196     {
1197         // printf("e1: %s\n", e1.toChars());
1198         // printf("e2: %s\n", e2.toChars());
1199         Type t1 = isType((cast(TypeidExp)e1).obj);
1200         Type t2 = isType((cast(TypeidExp)e2).obj);
1201         assert(t1);
1202         assert(t2);
1203         return t1 != t2;
1204     }
1205     // null == null, regardless of type
1206     if (e1.op == TOK.null_ && e2.op == TOK.null_)
1207         return 0;
1208     if (e1.type.ty == Tpointer && e2.type.ty == Tpointer)
1209     {
1210         // Can only be an equality test.
1211         dinteger_t ofs1, ofs2;
1212         Expression agg1 = getAggregateFromPointer(e1, &ofs1);
1213         Expression agg2 = getAggregateFromPointer(e2, &ofs2);
1214         if ((agg1 == agg2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var))
1215         {
1216             if (ofs1 == ofs2)
1217                 return 0;
1218         }
1219         return 1;
1220     }
1221     if (e1.type.ty == Tdelegate && e2.type.ty == Tdelegate)
1222     {
1223         // If .funcptr isn't the same, they are not equal
1224         if (funcptrOf(e1) != funcptrOf(e2))
1225             return 1;
1226         // If both are delegate literals, assume they have the
1227         // same closure pointer. TODO: We don't support closures yet!
1228         if (e1.op == TOK.function_ && e2.op == TOK.function_)
1229             return 0;
1230         assert(e1.op == TOK.delegate_ && e2.op == TOK.delegate_);
1231         // Same .funcptr. Do they have the same .ptr?
1232         Expression ptr1 = (cast(DelegateExp)e1).e1;
1233         Expression ptr2 = (cast(DelegateExp)e2).e1;
1234         dinteger_t ofs1, ofs2;
1235         Expression agg1 = getAggregateFromPointer(ptr1, &ofs1);
1236         Expression agg2 = getAggregateFromPointer(ptr2, &ofs2);
1237         // If they are TOK.variable, it means they are FuncDeclarations
1238         if ((agg1 == agg2 && ofs1 == ofs2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var))
1239         {
1240             return 0;
1241         }
1242         return 1;
1243     }
1244     if (isArray(e1) && isArray(e2))
1245     {
1246         const uinteger_t len1 = resolveArrayLength(e1);
1247         const uinteger_t len2 = resolveArrayLength(e2);
1248         // workaround for dmc optimizer bug calculating wrong len for
1249         // uinteger_t len = (len1 < len2 ? len1 : len2);
1250         // if (len == 0) ...
1251         if (len1 > 0 && len2 > 0)
1252         {
1253             const uinteger_t len = (len1 < len2 ? len1 : len2);
1254             const int res = ctfeCmpArrays(loc, e1, e2, len);
1255             if (res != 0)
1256                 return res;
1257         }
1258         return cast(int)(len1 - len2);
1259     }
1260     if (e1.type.isintegral())
1261     {
1262         return e1.toInteger() != e2.toInteger();
1263     }
1264     if (e1.type.isreal() || e1.type.isimaginary())
1265     {
1266         real_t r1 = e1.type.isreal() ? e1.toReal() : e1.toImaginary();
1267         real_t r2 = e1.type.isreal() ? e2.toReal() : e2.toImaginary();
1268         if (identity)
1269             return !RealIdentical(r1, r2);
1270         if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered
1271         {
1272             return 1;   // they are not equal
1273         }
1274         else
1275         {
1276             return (r1 != r2);
1277         }
1278     }
1279     else if (e1.type.iscomplex())
1280     {
1281         auto c1 = e1.toComplex();
1282         auto c2 = e2.toComplex();
1283         if (identity)
1284         {
1285             return !RealIdentical(c1.re, c2.re) && !RealIdentical(c1.im, c2.im);
1286         }
1287         return c1 != c2;
1288     }
1289     if (e1.op == TOK.structLiteral && e2.op == TOK.structLiteral)
1290     {
1291         StructLiteralExp es1 = cast(StructLiteralExp)e1;
1292         StructLiteralExp es2 = cast(StructLiteralExp)e2;
1293         // For structs, we only need to return 0 or 1 (< and > aren't legal).
1294         if (es1.sd != es2.sd)
1295             return 1;
1296         else if ((!es1.elements || !es1.elements.dim) && (!es2.elements || !es2.elements.dim))
1297             return 0; // both arrays are empty
1298         else if (!es1.elements || !es2.elements)
1299             return 1;
1300         else if (es1.elements.dim != es2.elements.dim)
1301             return 1;
1302         else
1303         {
1304             foreach (size_t i; 0 .. es1.elements.dim)
1305             {
1306                 Expression ee1 = (*es1.elements)[i];
1307                 Expression ee2 = (*es2.elements)[i];
1308 
1309                 // https://issues.dlang.org/show_bug.cgi?id=16284
1310                 if (ee1.op == TOK.void_ && ee2.op == TOK.void_) // if both are VoidInitExp
1311                     continue;
1312 
1313                 if (ee1 == ee2)
1314                     continue;
1315                 if (!ee1 || !ee2)
1316                     return 1;
1317                 const int cmp = ctfeRawCmp(loc, ee1, ee2, identity);
1318                 if (cmp)
1319                     return 1;
1320             }
1321             return 0; // All elements are equal
1322         }
1323     }
1324     if (e1.op == TOK.assocArrayLiteral && e2.op == TOK.assocArrayLiteral)
1325     {
1326         AssocArrayLiteralExp es1 = cast(AssocArrayLiteralExp)e1;
1327         AssocArrayLiteralExp es2 = cast(AssocArrayLiteralExp)e2;
1328         size_t dim = es1.keys.dim;
1329         if (es2.keys.dim != dim)
1330             return 1;
1331         bool* used = cast(bool*)mem.xmalloc(bool.sizeof * dim);
1332         memset(used, 0, bool.sizeof * dim);
1333         foreach (size_t i; 0 .. dim)
1334         {
1335             Expression k1 = (*es1.keys)[i];
1336             Expression v1 = (*es1.values)[i];
1337             Expression v2 = null;
1338             foreach (size_t j; 0 .. dim)
1339             {
1340                 if (used[j])
1341                     continue;
1342                 Expression k2 = (*es2.keys)[j];
1343                 if (ctfeRawCmp(loc, k1, k2, identity))
1344                     continue;
1345                 used[j] = true;
1346                 v2 = (*es2.values)[j];
1347                 break;
1348             }
1349             if (!v2 || ctfeRawCmp(loc, v1, v2, identity))
1350             {
1351                 mem.xfree(used);
1352                 return 1;
1353             }
1354         }
1355         mem.xfree(used);
1356         return 0;
1357     }
1358     error(loc, "CTFE internal error: bad compare of `%s` and `%s`", e1.toChars(), e2.toChars());
1359     assert(0);
1360 }
1361 
1362 /// Evaluate ==, !=.  Resolves slices before comparing. Returns 0 or 1
1363 bool ctfeEqual(const ref Loc loc, TOK op, Expression e1, Expression e2)
1364 {
1365     return !ctfeRawCmp(loc, e1, e2) ^ (op == TOK.notEqual);
1366 }
1367 
1368 /// Evaluate is, !is.  Resolves slices before comparing. Returns 0 or 1
1369 bool ctfeIdentity(const ref Loc loc, TOK op, Expression e1, Expression e2)
1370 {
1371     //printf("ctfeIdentity %s %s\n", e1.toChars(), e2.toChars());
1372     //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", Token::toChars(op),
1373     //    Token::toChars(e1.op), e1.toChars(), Token::toChars(e2.op), e1.toChars());
1374     bool cmp;
1375     if (e1.op == TOK.null_)
1376     {
1377         cmp = (e2.op == TOK.null_);
1378     }
1379     else if (e2.op == TOK.null_)
1380     {
1381         cmp = false;
1382     }
1383     else if (e1.op == TOK.symbolOffset && e2.op == TOK.symbolOffset)
1384     {
1385         SymOffExp es1 = cast(SymOffExp)e1;
1386         SymOffExp es2 = cast(SymOffExp)e2;
1387         cmp = (es1.var == es2.var && es1.offset == es2.offset);
1388     }
1389     else if (e1.type.isreal())
1390         cmp = RealIdentical(e1.toReal(), e2.toReal());
1391     else if (e1.type.isimaginary())
1392         cmp = RealIdentical(e1.toImaginary(), e2.toImaginary());
1393     else if (e1.type.iscomplex())
1394     {
1395         complex_t v1 = e1.toComplex();
1396         complex_t v2 = e2.toComplex();
1397         cmp = RealIdentical(creall(v1), creall(v2)) && RealIdentical(cimagl(v1), cimagl(v1));
1398     }
1399     else
1400     {
1401         cmp = !ctfeRawCmp(loc, e1, e2, true);
1402     }
1403     if (op == TOK.notIdentity || op == TOK.notEqual)
1404         cmp ^= true;
1405     return cmp;
1406 }
1407 
1408 /// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1
1409 bool ctfeCmp(const ref Loc loc, TOK op, Expression e1, Expression e2)
1410 {
1411     Type t1 = e1.type.toBasetype();
1412     Type t2 = e2.type.toBasetype();
1413 
1414     if (t1.isString() && t2.isString())
1415         return specificCmp(op, ctfeRawCmp(loc, e1, e2));
1416     else if (t1.isreal())
1417         return realCmp(op, e1.toReal(), e2.toReal());
1418     else if (t1.isimaginary())
1419         return realCmp(op, e1.toImaginary(), e2.toImaginary());
1420     else if (t1.isunsigned() || t2.isunsigned())
1421         return intUnsignedCmp(op, e1.toInteger(), e2.toInteger());
1422     else
1423         return intSignedCmp(op, e1.toInteger(), e2.toInteger());
1424 }
1425 
1426 UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2)
1427 {
1428     Type t1 = e1.type.toBasetype();
1429     Type t2 = e2.type.toBasetype();
1430     UnionExp ue;
1431     if (e2.op == TOK.string_ && e1.op == TOK.arrayLiteral && t1.nextOf().isintegral())
1432     {
1433         // [chars] ~ string => string (only valid for CTFE)
1434         StringExp es1 = cast(StringExp)e2;
1435         ArrayLiteralExp es2 = cast(ArrayLiteralExp)e1;
1436         const len = es1.len + es2.elements.dim;
1437         const sz = es1.sz;
1438         void* s = mem.xmalloc((len + 1) * sz);
1439         const data1 = es1.peekData();
1440         memcpy(cast(char*)s + sz * es2.elements.dim, data1.ptr, data1.length);
1441         foreach (size_t i; 0 .. es2.elements.dim)
1442         {
1443             Expression es2e = (*es2.elements)[i];
1444             if (es2e.op != TOK.int64)
1445             {
1446                 emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
1447                 return ue;
1448             }
1449             dinteger_t v = es2e.toInteger();
1450             Port.valcpy(cast(char*)s + i * sz, v, sz);
1451         }
1452         // Add terminating 0
1453         memset(cast(char*)s + len * sz, 0, sz);
1454         emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
1455         StringExp es = cast(StringExp)ue.exp();
1456         es.committed = 0;
1457         es.type = type;
1458         return ue;
1459     }
1460     if (e1.op == TOK.string_ && e2.op == TOK.arrayLiteral && t2.nextOf().isintegral())
1461     {
1462         // string ~ [chars] => string (only valid for CTFE)
1463         // Concatenate the strings
1464         StringExp es1 = cast(StringExp)e1;
1465         ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2;
1466         const len = es1.len + es2.elements.dim;
1467         const sz = es1.sz;
1468         void* s = mem.xmalloc((len + 1) * sz);
1469         auto slice = es1.peekData();
1470         memcpy(s, slice.ptr, slice.length);
1471         foreach (size_t i; 0 .. es2.elements.dim)
1472         {
1473             Expression es2e = (*es2.elements)[i];
1474             if (es2e.op != TOK.int64)
1475             {
1476                 emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
1477                 return ue;
1478             }
1479             const v = es2e.toInteger();
1480             Port.valcpy(cast(char*)s + (es1.len + i) * sz, v, sz);
1481         }
1482         // Add terminating 0
1483         memset(cast(char*)s + len * sz, 0, sz);
1484         emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
1485         StringExp es = cast(StringExp)ue.exp();
1486         es.sz = sz;
1487         es.committed = 0; //es1.committed;
1488         es.type = type;
1489         return ue;
1490     }
1491     if (e1.op == TOK.arrayLiteral && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
1492     {
1493         //  [ e1 ] ~ [ e2 ] ---> [ e1, e2 ]
1494         ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1;
1495         ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2;
1496         emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, type, copyLiteralArray(es1.elements));
1497         es1 = cast(ArrayLiteralExp)ue.exp();
1498         es1.elements.insert(es1.elements.dim, copyLiteralArray(es2.elements));
1499         return ue;
1500     }
1501     if (e1.op == TOK.arrayLiteral && e2.op == TOK.null_ && t1.nextOf().equals(t2.nextOf()))
1502     {
1503         //  [ e1 ] ~ null ----> [ e1 ].dup
1504         ue = paintTypeOntoLiteralCopy(type, copyLiteral(e1).copy());
1505         return ue;
1506     }
1507     if (e1.op == TOK.null_ && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
1508     {
1509         //  null ~ [ e2 ] ----> [ e2 ].dup
1510         ue = paintTypeOntoLiteralCopy(type, copyLiteral(e2).copy());
1511         return ue;
1512     }
1513     ue = Cat(type, e1, e2);
1514     return ue;
1515 }
1516 
1517 /*  Given an AA literal 'ae', and a key 'e2':
1518  *  Return ae[e2] if present, or NULL if not found.
1519  */
1520 Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, Expression e2)
1521 {
1522     /* Search the keys backwards, in case there are duplicate keys
1523      */
1524     for (size_t i = ae.keys.dim; i;)
1525     {
1526         --i;
1527         Expression ekey = (*ae.keys)[i];
1528         const int eq = ctfeEqual(loc, TOK.equal, ekey, e2);
1529         if (eq)
1530         {
1531             return (*ae.values)[i];
1532         }
1533     }
1534     return null;
1535 }
1536 
1537 /* Same as for constfold.Index, except that it only works for static arrays,
1538  * dynamic arrays, and strings. We know that e1 is an
1539  * interpreted CTFE expression, so it cannot have side-effects.
1540  */
1541 Expression ctfeIndex(UnionExp* pue, const ref Loc loc, Type type, Expression e1, uinteger_t indx)
1542 {
1543     //printf("ctfeIndex(e1 = %s)\n", e1.toChars());
1544     assert(e1.type);
1545     if (auto es1 = e1.isStringExp())
1546     {
1547         if (indx >= es1.len)
1548         {
1549             error(loc, "string index %llu is out of bounds `[0 .. %zu]`", indx, es1.len);
1550             return CTFEExp.cantexp;
1551         }
1552         emplaceExp!IntegerExp(pue, loc, es1.charAt(indx), type);
1553         return pue.exp();
1554     }
1555 
1556     if (auto ale = e1.isArrayLiteralExp())
1557     {
1558         if (indx >= ale.elements.dim)
1559         {
1560             error(loc, "array index %llu is out of bounds `%s[0 .. %zu]`", indx, e1.toChars(), ale.elements.dim);
1561             return CTFEExp.cantexp;
1562         }
1563         Expression e = (*ale.elements)[cast(size_t)indx];
1564         return paintTypeOntoLiteral(pue, type, e);
1565     }
1566 
1567     assert(0);
1568 }
1569 
1570 Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expression e)
1571 {
1572     Expression paint()
1573     {
1574         return paintTypeOntoLiteral(pue, to, e);
1575     }
1576 
1577     if (e.op == TOK.null_)
1578         return paint();
1579 
1580     if (e.op == TOK.classReference)
1581     {
1582         // Disallow reinterpreting class casts. Do this by ensuring that
1583         // the original class can implicitly convert to the target class
1584         ClassDeclaration originalClass = (cast(ClassReferenceExp)e).originalClass();
1585         if (originalClass.type.implicitConvTo(to.mutableOf()))
1586             return paint();
1587         else
1588         {
1589             emplaceExp!(NullExp)(pue, loc, to);
1590             return pue.exp();
1591         }
1592     }
1593 
1594     // Allow TypeInfo type painting
1595     if (isTypeInfo_Class(e.type) && e.type.implicitConvTo(to))
1596         return paint();
1597 
1598     // Allow casting away const for struct literals
1599     if (e.op == TOK.structLiteral && e.type.toBasetype().castMod(0) == to.toBasetype().castMod(0))
1600         return paint();
1601 
1602     Expression r;
1603     if (e.type.equals(type) && type.equals(to))
1604     {
1605         // necessary not to change e's address for pointer comparisons
1606         r = e;
1607     }
1608     else if (to.toBasetype().ty == Tarray &&
1609              type.toBasetype().ty == Tarray &&
1610              to.toBasetype().nextOf().size() == type.toBasetype().nextOf().size())
1611     {
1612         // https://issues.dlang.org/show_bug.cgi?id=12495
1613         // Array reinterpret casts: eg. string to immutable(ubyte)[]
1614         return paint();
1615     }
1616     else
1617     {
1618         *pue = Cast(loc, type, to, e);
1619         r = pue.exp();
1620     }
1621 
1622     if (CTFEExp.isCantExp(r))
1623         error(loc, "cannot cast `%s` to `%s` at compile time", e.toChars(), to.toChars());
1624 
1625     if (auto ae = e.isArrayLiteralExp())
1626         ae.ownedByCtfe = OwnedBy.ctfe;
1627 
1628     if (auto se = e.isStringExp())
1629         se.ownedByCtfe = OwnedBy.ctfe;
1630 
1631     return r;
1632 }
1633 
1634 /******** Assignment helper functions ***************************/
1635 /* Set dest = src, where both dest and src are container value literals
1636  * (ie, struct literals, or static arrays (can be an array literal or a string))
1637  * Assignment is recursively in-place.
1638  * Purpose: any reference to a member of 'dest' will remain valid after the
1639  * assignment.
1640  */
1641 void assignInPlace(Expression dest, Expression src)
1642 {
1643     if (!(dest.op == TOK.structLiteral || dest.op == TOK.arrayLiteral || dest.op == TOK.string_))
1644     {
1645         printf("invalid op %d %d\n", src.op, dest.op);
1646         assert(0);
1647     }
1648     Expressions* oldelems;
1649     Expressions* newelems;
1650     if (dest.op == TOK.structLiteral)
1651     {
1652         assert(dest.op == src.op);
1653         oldelems = (cast(StructLiteralExp)dest).elements;
1654         newelems = (cast(StructLiteralExp)src).elements;
1655         auto sd = (cast(StructLiteralExp)dest).sd;
1656         const nfields = sd.nonHiddenFields();
1657         const nvthis = sd.fields.dim - nfields;
1658         if (nvthis && oldelems.dim >= nfields && oldelems.dim < newelems.dim)
1659             foreach (_; 0 .. newelems.dim - oldelems.dim)
1660                 oldelems.push(null);
1661     }
1662     else if (dest.op == TOK.arrayLiteral && src.op == TOK.arrayLiteral)
1663     {
1664         oldelems = (cast(ArrayLiteralExp)dest).elements;
1665         newelems = (cast(ArrayLiteralExp)src).elements;
1666     }
1667     else if (dest.op == TOK.string_ && src.op == TOK.string_)
1668     {
1669         sliceAssignStringFromString(cast(StringExp)dest, cast(StringExp)src, 0);
1670         return;
1671     }
1672     else if (dest.op == TOK.arrayLiteral && src.op == TOK.string_)
1673     {
1674         sliceAssignArrayLiteralFromString(cast(ArrayLiteralExp)dest, cast(StringExp)src, 0);
1675         return;
1676     }
1677     else if (src.op == TOK.arrayLiteral && dest.op == TOK.string_)
1678     {
1679         sliceAssignStringFromArrayLiteral(cast(StringExp)dest, cast(ArrayLiteralExp)src, 0);
1680         return;
1681     }
1682     else
1683     {
1684         printf("invalid op %d %d\n", src.op, dest.op);
1685         assert(0);
1686     }
1687     assert(oldelems.dim == newelems.dim);
1688     foreach (size_t i; 0 .. oldelems.dim)
1689     {
1690         Expression e = (*newelems)[i];
1691         Expression o = (*oldelems)[i];
1692         if (e.op == TOK.structLiteral)
1693         {
1694             assert(o.op == e.op);
1695             assignInPlace(o, e);
1696         }
1697         else if (e.type.ty == Tsarray && e.op != TOK.void_ && o.type.ty == Tsarray)
1698         {
1699             assignInPlace(o, e);
1700         }
1701         else
1702         {
1703             (*oldelems)[i] = (*newelems)[i];
1704         }
1705     }
1706 }
1707 
1708 // Given an AA literal aae,  set aae[index] = newval and return newval.
1709 Expression assignAssocArrayElement(const ref Loc loc, AssocArrayLiteralExp aae, Expression index, Expression newval)
1710 {
1711     /* Create new associative array literal reflecting updated key/value
1712      */
1713     Expressions* keysx = aae.keys;
1714     Expressions* valuesx = aae.values;
1715     int updated = 0;
1716     for (size_t j = valuesx.dim; j;)
1717     {
1718         j--;
1719         Expression ekey = (*aae.keys)[j];
1720         int eq = ctfeEqual(loc, TOK.equal, ekey, index);
1721         if (eq)
1722         {
1723             (*valuesx)[j] = newval;
1724             updated = 1;
1725         }
1726     }
1727     if (!updated)
1728     {
1729         // Append index/newval to keysx[]/valuesx[]
1730         valuesx.push(newval);
1731         keysx.push(index);
1732     }
1733     return newval;
1734 }
1735 
1736 /// Given array literal oldval of type ArrayLiteralExp or StringExp, of length
1737 /// oldlen, change its length to newlen. If the newlen is longer than oldlen,
1738 /// all new elements will be set to the default initializer for the element type.
1739 UnionExp changeArrayLiteralLength(const ref Loc loc, TypeArray arrayType, Expression oldval, size_t oldlen, size_t newlen)
1740 {
1741     UnionExp ue;
1742     Type elemType = arrayType.next;
1743     assert(elemType);
1744     Expression defaultElem = elemType.defaultInitLiteral(loc);
1745     auto elements = new Expressions(newlen);
1746     // Resolve slices
1747     size_t indxlo = 0;
1748     if (oldval.op == TOK.slice)
1749     {
1750         indxlo = cast(size_t)(cast(SliceExp)oldval).lwr.toInteger();
1751         oldval = (cast(SliceExp)oldval).e1;
1752     }
1753     size_t copylen = oldlen < newlen ? oldlen : newlen;
1754     if (oldval.op == TOK.string_)
1755     {
1756         StringExp oldse = cast(StringExp)oldval;
1757         void* s = mem.xcalloc(newlen + 1, oldse.sz);
1758         const data = oldse.peekData();
1759         memcpy(s, data.ptr, copylen * oldse.sz);
1760         const defaultValue = cast(uint)defaultElem.toInteger();
1761         foreach (size_t elemi; copylen .. newlen)
1762         {
1763             switch (oldse.sz)
1764             {
1765             case 1:
1766                 (cast(char*)s)[cast(size_t)(indxlo + elemi)] = cast(char)defaultValue;
1767                 break;
1768             case 2:
1769                 (cast(wchar*)s)[cast(size_t)(indxlo + elemi)] = cast(wchar)defaultValue;
1770                 break;
1771             case 4:
1772                 (cast(dchar*)s)[cast(size_t)(indxlo + elemi)] = cast(dchar)defaultValue;
1773                 break;
1774             default:
1775                 assert(0);
1776             }
1777         }
1778         emplaceExp!(StringExp)(&ue, loc, s[0 .. newlen * oldse.sz], newlen, oldse.sz);
1779         StringExp se = cast(StringExp)ue.exp();
1780         se.type = arrayType;
1781         se.sz = oldse.sz;
1782         se.committed = oldse.committed;
1783         se.ownedByCtfe = OwnedBy.ctfe;
1784     }
1785     else
1786     {
1787         if (oldlen != 0)
1788         {
1789             assert(oldval.op == TOK.arrayLiteral);
1790             ArrayLiteralExp ae = cast(ArrayLiteralExp)oldval;
1791             foreach (size_t i; 0 .. copylen)
1792                 (*elements)[i] = (*ae.elements)[indxlo + i];
1793         }
1794         if (elemType.ty == Tstruct || elemType.ty == Tsarray)
1795         {
1796             /* If it is an aggregate literal representing a value type,
1797              * we need to create a unique copy for each element
1798              */
1799             foreach (size_t i; copylen .. newlen)
1800                 (*elements)[i] = copyLiteral(defaultElem).copy();
1801         }
1802         else
1803         {
1804             foreach (size_t i; copylen .. newlen)
1805                 (*elements)[i] = defaultElem;
1806         }
1807         emplaceExp!(ArrayLiteralExp)(&ue, loc, arrayType, elements);
1808         ArrayLiteralExp aae = cast(ArrayLiteralExp)ue.exp();
1809         aae.ownedByCtfe = OwnedBy.ctfe;
1810     }
1811     return ue;
1812 }
1813 
1814 /*************************** CTFE Sanity Checks ***************************/
1815 
1816 bool isCtfeValueValid(Expression newval)
1817 {
1818     Type tb = newval.type.toBasetype();
1819     switch (newval.op)
1820     {
1821         case TOK.int64:
1822         case TOK.float64:
1823         case TOK.char_:
1824         case TOK.complex80:
1825             return tb.isscalar();
1826 
1827         case TOK.null_:
1828             return tb.ty == Tnull    ||
1829                    tb.ty == Tpointer ||
1830                    tb.ty == Tarray   ||
1831                    tb.ty == Taarray  ||
1832                    tb.ty == Tclass   ||
1833                    tb.ty == Tdelegate;
1834 
1835         case TOK.string_:
1836             return true; // CTFE would directly use the StringExp in AST.
1837 
1838         case TOK.arrayLiteral:
1839             return true; //((ArrayLiteralExp *)newval)->ownedByCtfe;
1840 
1841         case TOK.assocArrayLiteral:
1842             return true; //((AssocArrayLiteralExp *)newval)->ownedByCtfe;
1843 
1844         case TOK.structLiteral:
1845             return true; //((StructLiteralExp *)newval)->ownedByCtfe;
1846 
1847         case TOK.classReference:
1848             return true;
1849 
1850         case TOK.type:
1851             return true;
1852 
1853         case TOK.vector:
1854             return true; // vector literal
1855 
1856         case TOK.function_:
1857             return true; // function literal or delegate literal
1858 
1859         case TOK.delegate_:
1860         {
1861             // &struct.func or &clasinst.func
1862             // &nestedfunc
1863             Expression ethis = (cast(DelegateExp)newval).e1;
1864             return (ethis.op == TOK.structLiteral || ethis.op == TOK.classReference || ethis.op == TOK.variable && (cast(VarExp)ethis).var == (cast(DelegateExp)newval).func);
1865         }
1866 
1867         case TOK.symbolOffset:
1868         {
1869             // function pointer, or pointer to static variable
1870             Declaration d = (cast(SymOffExp)newval).var;
1871             return d.isFuncDeclaration() || d.isDataseg();
1872         }
1873 
1874         case TOK.typeid_:
1875         {
1876             // always valid
1877             return true;
1878         }
1879 
1880         case TOK.address:
1881         {
1882             // e1 should be a CTFE reference
1883             Expression e1 = (cast(AddrExp)newval).e1;
1884             return tb.ty == Tpointer &&
1885             (
1886                 (e1.op == TOK.structLiteral || e1.op == TOK.arrayLiteral) && isCtfeValueValid(e1) ||
1887                  e1.op == TOK.variable ||
1888                  e1.op == TOK.dotVariable && isCtfeReferenceValid(e1) ||
1889                  e1.op == TOK.index && isCtfeReferenceValid(e1) ||
1890                  e1.op == TOK.slice && e1.type.toBasetype().ty == Tsarray
1891             );
1892         }
1893 
1894         case TOK.slice:
1895         {
1896             // e1 should be an array aggregate
1897             const SliceExp se = cast(SliceExp)newval;
1898             assert(se.lwr && se.lwr.op == TOK.int64);
1899             assert(se.upr && se.upr.op == TOK.int64);
1900             return (tb.ty == Tarray || tb.ty == Tsarray) && (se.e1.op == TOK.string_ || se.e1.op == TOK.arrayLiteral);
1901         }
1902 
1903         case TOK.void_:
1904             return true; // uninitialized value
1905 
1906         default:
1907             newval.error("CTFE internal error: illegal CTFE value `%s`", newval.toChars());
1908             return false;
1909     }
1910 }
1911 
1912 bool isCtfeReferenceValid(Expression newval)
1913 {
1914     switch (newval.op)
1915     {
1916         case TOK.this_:
1917             return true;
1918 
1919         case TOK.variable:
1920         {
1921             const VarDeclaration v = (cast(VarExp)newval).var.isVarDeclaration();
1922             assert(v);
1923             // Must not be a reference to a reference
1924             return true;
1925         }
1926 
1927         case TOK.index:
1928         {
1929             const Expression eagg = (cast(IndexExp)newval).e1;
1930             return eagg.op == TOK.string_ || eagg.op == TOK.arrayLiteral || eagg.op == TOK.assocArrayLiteral;
1931         }
1932 
1933         case TOK.dotVariable:
1934         {
1935             Expression eagg = (cast(DotVarExp)newval).e1;
1936             return (eagg.op == TOK.structLiteral || eagg.op == TOK.classReference) && isCtfeValueValid(eagg);
1937         }
1938 
1939         default:
1940             // Internally a ref variable may directly point a stack memory.
1941             // e.g. ref int v = 1;
1942             return isCtfeValueValid(newval);
1943     }
1944 }
1945 
1946 // Used for debugging only
1947 void showCtfeExpr(Expression e, int level = 0)
1948 {
1949     for (int i = level; i > 0; --i)
1950         printf(" ");
1951     Expressions* elements = null;
1952     // We need the struct definition to detect block assignment
1953     StructDeclaration sd = null;
1954     ClassDeclaration cd = null;
1955     if (e.op == TOK.structLiteral)
1956     {
1957         elements = (cast(StructLiteralExp)e).elements;
1958         sd = (cast(StructLiteralExp)e).sd;
1959         printf("STRUCT type = %s %p:\n", e.type.toChars(), e);
1960     }
1961     else if (e.op == TOK.classReference)
1962     {
1963         elements = (cast(ClassReferenceExp)e).value.elements;
1964         cd = (cast(ClassReferenceExp)e).originalClass();
1965         printf("CLASS type = %s %p:\n", e.type.toChars(), (cast(ClassReferenceExp)e).value);
1966     }
1967     else if (e.op == TOK.arrayLiteral)
1968     {
1969         elements = (cast(ArrayLiteralExp)e).elements;
1970         printf("ARRAY LITERAL type=%s %p:\n", e.type.toChars(), e);
1971     }
1972     else if (e.op == TOK.assocArrayLiteral)
1973     {
1974         printf("AA LITERAL type=%s %p:\n", e.type.toChars(), e);
1975     }
1976     else if (e.op == TOK.string_)
1977     {
1978         printf("STRING %s %p\n", e.toChars(), e.isStringExp.peekString.ptr);
1979     }
1980     else if (e.op == TOK.slice)
1981     {
1982         printf("SLICE %p: %s\n", e, e.toChars());
1983         showCtfeExpr((cast(SliceExp)e).e1, level + 1);
1984     }
1985     else if (e.op == TOK.variable)
1986     {
1987         printf("VAR %p %s\n", e, e.toChars());
1988         VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration();
1989         if (v && getValue(v))
1990             showCtfeExpr(getValue(v), level + 1);
1991     }
1992     else if (e.op == TOK.address)
1993     {
1994         // This is potentially recursive. We mustn't try to print the thing we're pointing to.
1995         printf("POINTER %p to %p: %s\n", e, (cast(AddrExp)e).e1, e.toChars());
1996     }
1997     else
1998         printf("VALUE %p: %s\n", e, e.toChars());
1999     if (elements)
2000     {
2001         size_t fieldsSoFar = 0;
2002         for (size_t i = 0; i < elements.dim; i++)
2003         {
2004             Expression z = null;
2005             VarDeclaration v = null;
2006             if (i > 15)
2007             {
2008                 printf("...(total %d elements)\n", cast(int)elements.dim);
2009                 return;
2010             }
2011             if (sd)
2012             {
2013                 v = sd.fields[i];
2014                 z = (*elements)[i];
2015             }
2016             else if (cd)
2017             {
2018                 while (i - fieldsSoFar >= cd.fields.dim)
2019                 {
2020                     fieldsSoFar += cd.fields.dim;
2021                     cd = cd.baseClass;
2022                     for (int j = level; j > 0; --j)
2023                         printf(" ");
2024                     printf(" BASE CLASS: %s\n", cd.toChars());
2025                 }
2026                 v = cd.fields[i - fieldsSoFar];
2027                 assert((elements.dim + i) >= (fieldsSoFar + cd.fields.dim));
2028                 size_t indx = (elements.dim - fieldsSoFar) - cd.fields.dim + i;
2029                 assert(indx < elements.dim);
2030                 z = (*elements)[indx];
2031             }
2032             if (!z)
2033             {
2034                 for (int j = level; j > 0; --j)
2035                     printf(" ");
2036                 printf(" void\n");
2037                 continue;
2038             }
2039             if (v)
2040             {
2041                 // If it is a void assignment, use the default initializer
2042                 if ((v.type.ty != z.type.ty) && v.type.ty == Tsarray)
2043                 {
2044                     for (int j = level; --j;)
2045                         printf(" ");
2046                     printf(" field: block initialized static array\n");
2047                     continue;
2048                 }
2049             }
2050             showCtfeExpr(z, level + 1);
2051         }
2052     }
2053 }
2054 
2055 /*************************** Void initialization ***************************/
2056 UnionExp voidInitLiteral(Type t, VarDeclaration var)
2057 {
2058     UnionExp ue;
2059     if (t.ty == Tsarray)
2060     {
2061         TypeSArray tsa = cast(TypeSArray)t;
2062         Expression elem = voidInitLiteral(tsa.next, var).copy();
2063         // For aggregate value types (structs, static arrays) we must
2064         // create an a separate copy for each element.
2065         const mustCopy = (elem.op == TOK.arrayLiteral || elem.op == TOK.structLiteral);
2066         const d = cast(size_t)tsa.dim.toInteger();
2067         auto elements = new Expressions(d);
2068         foreach (i; 0 .. d)
2069         {
2070             if (mustCopy && i > 0)
2071                 elem = copyLiteral(elem).copy();
2072             (*elements)[i] = elem;
2073         }
2074         emplaceExp!(ArrayLiteralExp)(&ue, var.loc, tsa, elements);
2075         ArrayLiteralExp ae = cast(ArrayLiteralExp)ue.exp();
2076         ae.ownedByCtfe = OwnedBy.ctfe;
2077     }
2078     else if (t.ty == Tstruct)
2079     {
2080         TypeStruct ts = cast(TypeStruct)t;
2081         auto exps = new Expressions(ts.sym.fields.dim);
2082         foreach (size_t i;  0 .. ts.sym.fields.dim)
2083         {
2084             (*exps)[i] = voidInitLiteral(ts.sym.fields[i].type, ts.sym.fields[i]).copy();
2085         }
2086         emplaceExp!(StructLiteralExp)(&ue, var.loc, ts.sym, exps);
2087         StructLiteralExp se = cast(StructLiteralExp)ue.exp();
2088         se.type = ts;
2089         se.ownedByCtfe = OwnedBy.ctfe;
2090     }
2091     else
2092         emplaceExp!(VoidInitExp)(&ue, var);
2093     return ue;
2094 }