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