d: Merge upstream dmd 4d1bfcf14, druntime 9ba9a6ae, phobos c0cc5e917.
[official-gcc.git] / gcc / d / dmd / ctfeexpr.d
blob11229d4bd4f5d24addb8dd97aff6712456ab9cbe
1 /**
2 * CTFE for expressions involving pointers, slices, array concatenation etc.
4 * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
5 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
6 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ctfeexpr.d, _ctfeexpr.d)
8 * Documentation: https://dlang.org/phobos/dmd_ctfeexpr.html
9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ctfeexpr.d
12 module dmd.ctfeexpr;
14 import core.stdc.stdio;
15 import core.stdc.stdlib;
16 import core.stdc.string;
17 import dmd.arraytypes;
18 import dmd.astenums;
19 import dmd.constfold;
20 import dmd.compiler;
21 import dmd.dclass;
22 import dmd.declaration;
23 import dmd.dinterpret;
24 import dmd.dstruct;
25 import dmd.dtemplate;
26 import dmd.errors;
27 import dmd.expression;
28 import dmd.func;
29 import dmd.globals;
30 import dmd.mtype;
31 import dmd.root.complex;
32 import dmd.root.ctfloat;
33 import dmd.root.port;
34 import dmd.root.rmem;
35 import dmd.tokens;
36 import dmd.visitor;
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).
43 extern (C++) final class ClassReferenceExp : Expression
45 StructLiteralExp value;
47 extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type)
49 super(loc, EXP.classReference, __traits(classInstanceSize, ClassReferenceExp));
50 assert(lit && lit.sd && lit.sd.isClassDeclaration());
51 this.value = lit;
52 this.type = type;
55 ClassDeclaration originalClass()
57 return value.sd.isClassDeclaration();
60 // Return index of the field, or -1 if not found
61 private int getFieldIndex(Type fieldtype, uint fieldoffset)
63 ClassDeclaration cd = originalClass();
64 uint fieldsSoFar = 0;
65 for (size_t j = 0; j < value.elements.dim; j++)
67 while (j - fieldsSoFar >= cd.fields.dim)
69 fieldsSoFar += cd.fields.dim;
70 cd = cd.baseClass;
72 VarDeclaration v2 = cd.fields[j - fieldsSoFar];
73 if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size())
75 return cast(int)(value.elements.dim - fieldsSoFar - cd.fields.dim + (j - fieldsSoFar));
78 return -1;
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)
85 ClassDeclaration cd = originalClass();
86 size_t fieldsSoFar = 0;
87 for (size_t j = 0; j < value.elements.dim; j++)
89 while (j - fieldsSoFar >= cd.fields.dim)
91 fieldsSoFar += cd.fields.dim;
92 cd = cd.baseClass;
94 VarDeclaration v2 = cd.fields[j - fieldsSoFar];
95 if (v == v2)
97 return cast(int)(value.elements.dim - fieldsSoFar - cd.fields.dim + (j - fieldsSoFar));
100 return -1;
103 override void accept(Visitor v)
105 v.visit(this);
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
114 int findFieldIndexByName(const StructDeclaration sd, const VarDeclaration v) pure
116 foreach (i, field; sd.fields)
118 if (field == v)
119 return cast(int)i;
121 return -1;
124 /***********************************************************
125 * Fake class which holds the thrown exception.
126 * Used for implementing exception handling.
128 extern (C++) final class ThrownExceptionExp : Expression
130 ClassReferenceExp thrown; // the thing being tossed
132 extern (D) this(const ref Loc loc, ClassReferenceExp victim)
134 super(loc, EXP.thrownException, __traits(classInstanceSize, ThrownExceptionExp));
135 this.thrown = victim;
136 this.type = victim.type;
139 override const(char)* toChars() const
141 return "CTFE ThrownException";
144 // Generate an error message when this exception is not caught
145 extern (D) void generateUncaughtError()
147 UnionExp ue = void;
148 Expression e = resolveSlice((*thrown.value.elements)[0], &ue);
149 StringExp se = e.toStringExp();
150 thrown.error("uncaught CTFE exception `%s(%s)`", thrown.type.toChars(), se ? se.toChars() : e.toChars());
151 /* Also give the line where the throw statement was. We won't have it
152 * in the case where the ThrowStatement is generated internally
153 * (eg, in ScopeStatement)
155 if (loc.isValid() && !loc.equals(thrown.loc))
156 .errorSupplemental(loc, "thrown from here");
159 override void accept(Visitor v)
161 v.visit(this);
165 /***********************************************************
166 * This type is only used by the interpreter.
168 extern (C++) final class CTFEExp : Expression
170 extern (D) this(EXP tok)
172 super(Loc.initial, tok, __traits(classInstanceSize, CTFEExp));
173 type = Type.tvoid;
176 override const(char)* toChars() const
178 switch (op)
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);
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.
205 extern (D) __gshared CTFEExp showcontext;
207 extern (D) static bool isCantExp(const Expression e)
209 return e && e.op == EXP.cantExpression;
212 extern (D) static bool isGotoExp(const Expression e)
214 return e && e.op == EXP.goto_;
218 // True if 'e' is CTFEExp::cantexp, or an exception
219 bool exceptionOrCantInterpret(const Expression e)
221 return e && (e.op == EXP.cantExpression || e.op == EXP.thrownException || e.op == EXP.showCtfeContext);
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)
229 Expression e = cast()expr;
230 for (;;)
232 switch (e.op)
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;
265 private Expressions* copyLiteralArray(Expressions* oldelems, Expression basis = null)
267 if (!oldelems)
268 return oldelems;
269 incArrayAllocs();
270 auto newelems = new Expressions(oldelems.dim);
271 foreach (i, el; *oldelems)
273 (*newelems)[i] = copyLiteral(el ? el : basis).copy();
275 return newelems;
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)
282 UnionExp ue = void;
283 if (auto se = e.isStringExp()) // syntaxCopy doesn't make a copy for StringExp!
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;
296 if (auto ale = e.isArrayLiteralExp())
298 auto elements = copyLiteralArray(ale.elements, ale.basis);
300 emplaceExp!(ArrayLiteralExp)(&ue, e.loc, e.type, elements);
302 ArrayLiteralExp r = ue.exp().isArrayLiteralExp();
303 r.ownedByCtfe = OwnedBy.ctfe;
304 return ue;
306 if (auto aae = e.isAssocArrayLiteralExp())
308 emplaceExp!(AssocArrayLiteralExp)(&ue, e.loc, copyLiteralArray(aae.keys), copyLiteralArray(aae.values));
309 AssocArrayLiteralExp r = ue.exp().isAssocArrayLiteralExp();
310 r.type = e.type;
311 r.ownedByCtfe = OwnedBy.ctfe;
312 return ue;
314 if (auto sle = e.isStructLiteralExp())
316 /* syntaxCopy doesn't work for struct literals, because of a nasty special
317 * case: block assignment is permitted inside struct literals, eg,
318 * an int[4] array can be initialized with a single int.
320 auto oldelems = sle.elements;
321 auto newelems = new Expressions(oldelems.dim);
322 foreach (i, ref el; *newelems)
324 // We need the struct definition to detect block assignment
325 auto v = sle.sd.fields[i];
326 auto m = (*oldelems)[i];
328 // If it is a void assignment, use the default initializer
329 if (!m)
330 m = voidInitLiteral(v.type, v).copy();
332 if (v.type.ty == Tarray || v.type.ty == Taarray)
334 // Don't have to copy array references
336 else
338 // Buzilla 15681: Copy the source element always.
339 m = copyLiteral(m).copy();
341 // Block assignment from inside struct literals
342 if (v.type.ty != m.type.ty && v.type.ty == Tsarray)
344 auto tsa = v.type.isTypeSArray();
345 auto len = cast(size_t)tsa.dim.toInteger();
346 m = createBlockDuplicatedArrayLiteral(&ue, e.loc, v.type, m, len);
347 if (m == ue.exp())
348 m = ue.copy();
351 el = m;
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;
360 if (e.op == EXP.function_ || e.op == EXP.delegate_ || e.op == EXP.symbolOffset || e.op == EXP.null_ || e.op == EXP.variable || e.op == EXP.dotVariable || e.op == EXP.int64 || e.op == EXP.float64 || e.op == EXP.char_ || e.op == EXP.complex80 || e.op == EXP.void_ || e.op == EXP.vector || e.op == EXP.typeid_)
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;
369 if (auto se = e.isSliceExp())
371 if (se.type.toBasetype().ty == Tsarray)
373 // same with resolveSlice()
374 if (se.e1.op == EXP.null_)
376 emplaceExp!(NullExp)(&ue, se.loc, se.type);
377 return ue;
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;
385 else
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;
394 if (isPointer(e.type))
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())
403 emplaceExp!(DotVarExp)(&ue, e.loc, dve.e1, dve.var, dve.hasOverloads);
405 else
406 assert(0);
408 Expression r = ue.exp();
409 r.type = e.type;
410 return ue;
412 if (auto cre = e.isClassReferenceExp())
414 emplaceExp!(ClassReferenceExp)(&ue, e.loc, cre.value, e.type);
415 return ue;
417 if (e.op == EXP.error)
419 emplaceExp!(UnionExp)(&ue, e);
420 return ue;
422 e.error("CTFE internal error: literal `%s`", e.toChars());
423 assert(0);
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.
432 Expression paintTypeOntoLiteral(Type type, Expression lit)
434 if (lit.type.equals(type))
435 return lit;
436 return paintTypeOntoLiteralCopy(type, lit).copy();
439 Expression paintTypeOntoLiteral(UnionExp* pue, Type type, Expression lit)
441 if (lit.type.equals(type))
442 return lit;
443 *pue = paintTypeOntoLiteralCopy(type, lit);
444 return pue.exp();
447 private UnionExp paintTypeOntoLiteralCopy(Type type, Expression lit)
449 UnionExp ue;
450 if (lit.type.equals(type))
452 emplaceExp!(UnionExp)(&ue, lit);
453 return ue;
455 // If it is a cast to inout, retain the original type of the referenced part.
456 if (type.hasWild())
458 emplaceExp!(UnionExp)(&ue, lit);
459 ue.exp().type = type;
460 return ue;
462 if (auto se = lit.isSliceExp())
464 emplaceExp!(SliceExp)(&ue, lit.loc, se.e1, se.lwr, se.upr);
466 else if (auto ie = lit.isIndexExp())
468 emplaceExp!(IndexExp)(&ue, lit.loc, ie.e1, ie.e2);
470 else if (lit.op == EXP.arrayLiteral)
472 emplaceExp!(SliceExp)(&ue, lit.loc, lit, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t), ArrayLength(Type.tsize_t, lit).copy());
474 else if (lit.op == EXP.string_)
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());
479 else if (auto aae = lit.isAssocArrayLiteralExp())
481 // TODO: we should be creating a reference to this AAExp, not
482 // just a ref to the keys and values.
483 OwnedBy wasOwned = aae.ownedByCtfe;
484 emplaceExp!(AssocArrayLiteralExp)(&ue, lit.loc, aae.keys, aae.values);
485 aae = ue.exp().isAssocArrayLiteralExp();
486 aae.ownedByCtfe = wasOwned;
488 else
490 // Can't type paint from struct to struct*; this needs another
491 // level of indirection
492 if (lit.op == EXP.structLiteral && isPointer(type))
493 lit.error("CTFE internal error: painting `%s`", type.toChars());
494 ue = copyLiteral(lit);
496 ue.exp().type = type;
497 return ue;
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
508 Expression resolveSlice(Expression e, UnionExp* pue = null)
510 SliceExp se = e.isSliceExp();
511 if (!se)
512 return e;
513 if (se.e1.op == EXP.null_)
514 return se.e1;
515 if (pue)
517 *pue = Slice(e.type, se.e1, se.lwr, se.upr);
518 return pue.exp();
520 else
521 return Slice(e.type, se.e1, se.lwr, se.upr).copy();
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.
529 uinteger_t resolveArrayLength(Expression e)
531 switch (e.op)
533 case EXP.vector:
534 return e.isVectorExp().dim;
536 case EXP.null_:
537 return 0;
539 case EXP.slice:
541 auto se = e.isSliceExp();
542 const ilo = se.lwr.toInteger();
543 const iup = se.upr.toInteger();
544 return iup - ilo;
547 case EXP.string_:
548 return e.isStringExp().len;
550 case EXP.arrayLiteral:
552 const ale = e.isArrayLiteralExp();
553 return ale.elements ? ale.elements.dim : 0;
556 case EXP.assocArrayLiteral:
558 return e.isAssocArrayLiteralExp().keys.dim;
561 default:
562 assert(0);
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
578 ArrayLiteralExp createBlockDuplicatedArrayLiteral(UnionExp* pue, const ref Loc loc, Type type, Expression elem, size_t dim)
580 if (type.ty == Tsarray && type.nextOf().ty == Tsarray && elem.type.ty != Tsarray)
582 // If it is a multidimensional array literal, do it recursively
583 auto tsa = type.nextOf().isTypeSArray();
584 const len = cast(size_t)tsa.dim.toInteger();
585 elem = createBlockDuplicatedArrayLiteral(pue, loc, type.nextOf(), elem, len);
586 if (elem == pue.exp())
587 elem = pue.copy();
590 // Buzilla 15681
591 const tb = elem.type.toBasetype();
592 const mustCopy = tb.ty == Tstruct || tb.ty == Tsarray;
594 auto elements = new Expressions(dim);
595 foreach (i, ref el; *elements)
597 el = mustCopy && i ? copyLiteral(elem).copy() : elem;
599 emplaceExp!(ArrayLiteralExp)(pue, loc, type, elements);
600 auto ale = pue.exp().isArrayLiteralExp();
601 ale.ownedByCtfe = OwnedBy.ctfe;
602 return ale;
605 /******************************
606 * Helper for NewExp
607 * Create a string literal consisting of 'value' duplicated 'dim' times.
609 StringExp createBlockDuplicatedStringLiteral(UnionExp* pue, const ref Loc loc, Type type, dchar value, size_t dim, ubyte sz)
611 auto s = cast(char*)mem.xcalloc(dim, sz);
612 foreach (elemi; 0 .. dim)
614 switch (sz)
616 case 1:
617 s[elemi] = cast(char)value;
618 break;
619 case 2:
620 (cast(wchar*)s)[elemi] = cast(wchar)value;
621 break;
622 case 4:
623 (cast(dchar*)s)[elemi] = value;
624 break;
625 default:
626 assert(0);
629 emplaceExp!(StringExp)(pue, loc, s[0 .. dim * sz], dim, sz);
630 auto se = pue.exp().isStringExp();
631 se.type = type;
632 se.committed = true;
633 se.ownedByCtfe = OwnedBy.ctfe;
634 return se;
637 // Return true if t is an AA
638 bool isAssocArray(Type t)
640 return t.toBasetype().isTypeAArray() !is null;
643 // Given a template AA type, extract the corresponding built-in AA type
644 TypeAArray toBuiltinAAType(Type t)
646 return t.toBasetype().isTypeAArray();
649 /************** TypeInfo operations ************************************/
650 // Return true if type is TypeInfo_Class
651 bool isTypeInfo_Class(const Type type)
653 auto tc = cast()type.isTypeClass();
654 return tc && (Type.dtypeinfo == tc.sym || Type.dtypeinfo.isBaseOf(tc.sym, null));
657 /************** Pointer operations ************************************/
658 // Return true if t is a pointer (not a function pointer)
659 bool isPointer(Type t)
661 Type tb = t.toBasetype();
662 return tb.ty == Tpointer && tb.nextOf().ty != Tfunction;
665 // For CTFE only. Returns true if 'e' is true or a non-null pointer.
666 bool isTrueBool(Expression e)
668 return e.toBool().hasValue(true) || ((e.type.ty == Tpointer || e.type.ty == Tclass) && e.op != EXP.null_);
671 /* Is it safe to convert from srcPointee* to destPointee* ?
672 * srcPointee is the genuine type (never void).
673 * destPointee may be void.
675 bool isSafePointerCast(Type srcPointee, Type destPointee)
677 // It's safe to cast S** to D** if it's OK to cast S* to D*
678 while (srcPointee.ty == Tpointer && destPointee.ty == Tpointer)
680 srcPointee = srcPointee.nextOf();
681 destPointee = destPointee.nextOf();
683 // It's OK if both are the same (modulo const)
684 if (srcPointee.constConv(destPointee))
685 return true;
687 // It's ok to cast from/to shared because CTFE is single threaded anyways
688 if (srcPointee.unSharedOf() == destPointee.unSharedOf())
689 return true;
691 // It's OK if function pointers differ only in safe/pure/nothrow
692 if (srcPointee.ty == Tfunction && destPointee.ty == Tfunction)
693 return srcPointee.covariant(destPointee) == Covariant.yes ||
694 destPointee.covariant(srcPointee) == Covariant.yes;
695 // it's OK to cast to void*
696 if (destPointee.ty == Tvoid)
697 return true;
698 // It's OK to cast from V[K] to void*
699 if (srcPointee.ty == Taarray && destPointee == Type.tvoidptr)
700 return true;
701 // It's OK if they are the same size (static array of) integers, eg:
702 // int* --> uint*
703 // int[5][] --> uint[5][]
704 if (srcPointee.ty == Tsarray && destPointee.ty == Tsarray)
706 if (srcPointee.size() != destPointee.size())
707 return false;
708 srcPointee = srcPointee.baseElemOf();
709 destPointee = destPointee.baseElemOf();
711 return srcPointee.isintegral() && destPointee.isintegral() && srcPointee.size() == destPointee.size();
714 Expression getAggregateFromPointer(Expression e, dinteger_t* ofs)
716 *ofs = 0;
717 if (auto ae = e.isAddrExp())
718 e = ae.e1;
719 if (auto soe = e.isSymOffExp())
720 *ofs = soe.offset;
721 if (auto dve = e.isDotVarExp())
723 auto ex = dve.e1;
724 const v = dve.var.isVarDeclaration();
725 assert(v);
726 StructLiteralExp se = (ex.op == EXP.classReference)
727 ? ex.isClassReferenceExp().value
728 : ex.isStructLiteralExp();
730 // We can't use getField, because it makes a copy
731 const i = (ex.op == EXP.classReference)
732 ? ex.isClassReferenceExp().getFieldIndex(e.type, v.offset)
733 : se.getFieldIndex(e.type, v.offset);
734 e = (*se.elements)[i];
736 if (auto ie = e.isIndexExp())
738 // Note that each AA element is part of its own memory block
739 if ((ie.e1.type.ty == Tarray || ie.e1.type.ty == Tsarray || ie.e1.op == EXP.string_ || ie.e1.op == EXP.arrayLiteral) && ie.e2.op == EXP.int64)
741 *ofs = ie.e2.toInteger();
742 return ie.e1;
745 if (auto se = e.isSliceExp())
747 if (se && e.type.toBasetype().ty == Tsarray &&
748 (se.e1.type.ty == Tarray || se.e1.type.ty == Tsarray || se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral) && se.lwr.op == EXP.int64)
750 *ofs = se.lwr.toInteger();
751 return se.e1;
755 // It can be a `null` disguised as a cast, e.g. `cast(void*)0`.
756 if (auto ie = e.isIntegerExp())
757 if (ie.type.ty == Tpointer && ie.getInteger() == 0)
758 return new NullExp(ie.loc, e.type.nextOf());
759 // Those casts are invalid, but let the rest of the code handle it,
760 // as it could be something like `x !is null`, which doesn't need
761 // to dereference the pointer, even if the pointer is `cast(void*)420`.
763 return e;
766 /** Return true if agg1 and agg2 are pointers to the same memory block
768 bool pointToSameMemoryBlock(Expression agg1, Expression agg2)
770 if (agg1 == agg2)
771 return true;
772 // For integers cast to pointers, we regard them as non-comparable
773 // unless they are identical. (This may be overly strict).
774 if (agg1.op == EXP.int64 && agg2.op == EXP.int64 && agg1.toInteger() == agg2.toInteger())
776 return true;
778 // Note that type painting can occur with VarExp, so we
779 // must compare the variables being pointed to.
780 if (agg1.op == EXP.variable && agg2.op == EXP.variable && agg1.isVarExp().var == agg2.isVarExp().var)
782 return true;
784 if (agg1.op == EXP.symbolOffset && agg2.op == EXP.symbolOffset && agg1.isSymOffExp().var == agg2.isSymOffExp().var)
786 return true;
788 return false;
791 // return e1 - e2 as an integer, or error if not possible
792 Expression pointerDifference(UnionExp* pue, const ref Loc loc, Type type, Expression e1, Expression e2)
794 dinteger_t ofs1, ofs2;
795 Expression agg1 = getAggregateFromPointer(e1, &ofs1);
796 Expression agg2 = getAggregateFromPointer(e2, &ofs2);
797 if (agg1 == agg2)
799 Type pointee = (cast(TypePointer)agg1.type).next;
800 const sz = pointee.size();
801 emplaceExp!(IntegerExp)(pue, loc, (ofs1 - ofs2) * sz, type);
803 else if (agg1.op == EXP.string_ && agg2.op == EXP.string_ &&
804 agg1.isStringExp().peekString().ptr == agg2.isStringExp().peekString().ptr)
806 Type pointee = (cast(TypePointer)agg1.type).next;
807 const sz = pointee.size();
808 emplaceExp!(IntegerExp)(pue, loc, (ofs1 - ofs2) * sz, type);
810 else if (agg1.op == EXP.symbolOffset && agg2.op == EXP.symbolOffset &&
811 agg1.isSymOffExp().var == agg2.isSymOffExp().var)
813 emplaceExp!(IntegerExp)(pue, loc, ofs1 - ofs2, type);
815 else
817 error(loc, "`%s - %s` cannot be interpreted at compile time: cannot subtract pointers to two different memory blocks", e1.toChars(), e2.toChars());
818 emplaceExp!(CTFEExp)(pue, EXP.cantExpression);
820 return pue.exp();
823 // Return eptr op e2, where eptr is a pointer, e2 is an integer,
824 // and op is EXP.add or EXP.min
825 Expression pointerArithmetic(UnionExp* pue, const ref Loc loc, EXP op, Type type, Expression eptr, Expression e2)
827 if (eptr.type.nextOf().ty == Tvoid)
829 error(loc, "cannot perform arithmetic on `void*` pointers at compile time");
830 Lcant:
831 emplaceExp!(CTFEExp)(pue, EXP.cantExpression);
832 return pue.exp();
834 if (eptr.op == EXP.address)
835 eptr = eptr.isAddrExp().e1;
836 dinteger_t ofs1;
837 Expression agg1 = getAggregateFromPointer(eptr, &ofs1);
838 if (agg1.op == EXP.symbolOffset)
840 if (agg1.isSymOffExp().var.type.ty != Tsarray)
842 error(loc, "cannot perform pointer arithmetic on arrays of unknown length at compile time");
843 goto Lcant;
846 else if (agg1.op != EXP.string_ && agg1.op != EXP.arrayLiteral)
848 error(loc, "cannot perform pointer arithmetic on non-arrays at compile time");
849 goto Lcant;
851 dinteger_t ofs2 = e2.toInteger();
852 Type pointee = (cast(TypeNext)agg1.type.toBasetype()).next;
853 dinteger_t sz = pointee.size();
854 sinteger_t indx;
855 dinteger_t len;
856 if (agg1.op == EXP.symbolOffset)
858 indx = ofs1 / sz;
859 len = (cast(TypeSArray)agg1.isSymOffExp().var.type).dim.toInteger();
861 else
863 Expression dollar = ArrayLength(Type.tsize_t, agg1).copy();
864 assert(!CTFEExp.isCantExp(dollar));
865 indx = ofs1;
866 len = dollar.toInteger();
868 if (op == EXP.add || op == EXP.addAssign || op == EXP.plusPlus)
869 indx += ofs2 / sz;
870 else if (op == EXP.min || op == EXP.minAssign || op == EXP.minusMinus)
871 indx -= ofs2 / sz;
872 else
874 error(loc, "CTFE internal error: bad pointer operation");
875 goto Lcant;
877 if (indx < 0 || len < indx)
879 error(loc, "cannot assign pointer to index %lld inside memory block `[0..%lld]`", indx, len);
880 goto Lcant;
882 if (agg1.op == EXP.symbolOffset)
884 emplaceExp!(SymOffExp)(pue, loc, agg1.isSymOffExp().var, indx * sz);
885 SymOffExp se = pue.exp().isSymOffExp();
886 se.type = type;
887 return pue.exp();
889 if (agg1.op != EXP.arrayLiteral && agg1.op != EXP.string_)
891 error(loc, "CTFE internal error: pointer arithmetic `%s`", agg1.toChars());
892 goto Lcant;
894 if (eptr.type.toBasetype().ty == Tsarray)
896 dinteger_t dim = (cast(TypeSArray)eptr.type.toBasetype()).dim.toInteger();
897 // Create a CTFE pointer &agg1[indx .. indx+dim]
898 auto se = ctfeEmplaceExp!SliceExp(loc, agg1,
899 ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t),
900 ctfeEmplaceExp!IntegerExp(loc, indx + dim, Type.tsize_t));
901 se.type = type.toBasetype().nextOf();
902 emplaceExp!(AddrExp)(pue, loc, se);
903 pue.exp().type = type;
904 return pue.exp();
906 // Create a CTFE pointer &agg1[indx]
907 auto ofs = ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t);
908 Expression ie = ctfeEmplaceExp!IndexExp(loc, agg1, ofs);
909 ie.type = type.toBasetype().nextOf(); // https://issues.dlang.org/show_bug.cgi?id=13992
910 emplaceExp!(AddrExp)(pue, loc, ie);
911 pue.exp().type = type;
912 return pue.exp();
915 // Return 1 if true, 0 if false
916 // -1 if comparison is illegal because they point to non-comparable memory blocks
917 int comparePointers(EXP op, Expression agg1, dinteger_t ofs1, Expression agg2, dinteger_t ofs2)
919 if (pointToSameMemoryBlock(agg1, agg2))
921 int n;
922 switch (op)
924 case EXP.lessThan:
925 n = (ofs1 < ofs2);
926 break;
927 case EXP.lessOrEqual:
928 n = (ofs1 <= ofs2);
929 break;
930 case EXP.greaterThan:
931 n = (ofs1 > ofs2);
932 break;
933 case EXP.greaterOrEqual:
934 n = (ofs1 >= ofs2);
935 break;
936 case EXP.identity:
937 case EXP.equal:
938 n = (ofs1 == ofs2);
939 break;
940 case EXP.notIdentity:
941 case EXP.notEqual:
942 n = (ofs1 != ofs2);
943 break;
944 default:
945 assert(0);
947 return n;
949 const null1 = (agg1.op == EXP.null_);
950 const null2 = (agg2.op == EXP.null_);
951 int cmp;
952 if (null1 || null2)
954 switch (op)
956 case EXP.lessThan:
957 cmp = null1 && !null2;
958 break;
959 case EXP.greaterThan:
960 cmp = !null1 && null2;
961 break;
962 case EXP.lessOrEqual:
963 cmp = null1;
964 break;
965 case EXP.greaterOrEqual:
966 cmp = null2;
967 break;
968 case EXP.identity:
969 case EXP.equal:
970 case EXP.notIdentity: // 'cmp' gets inverted below
971 case EXP.notEqual:
972 cmp = (null1 == null2);
973 break;
974 default:
975 assert(0);
978 else
980 switch (op)
982 case EXP.identity:
983 case EXP.equal:
984 case EXP.notIdentity: // 'cmp' gets inverted below
985 case EXP.notEqual:
986 cmp = 0;
987 break;
988 default:
989 return -1; // memory blocks are different
992 if (op == EXP.notIdentity || op == EXP.notEqual)
993 cmp ^= 1;
994 return cmp;
997 // True if conversion from type 'from' to 'to' involves a reinterpret_cast
998 // floating point -> integer or integer -> floating point
999 bool isFloatIntPaint(Type to, Type from)
1001 return from.size() == to.size() && (from.isintegral() && to.isfloating() || from.isfloating() && to.isintegral());
1004 // Reinterpret float/int value 'fromVal' as a float/integer of type 'to'.
1005 Expression paintFloatInt(UnionExp* pue, Expression fromVal, Type to)
1007 if (exceptionOrCantInterpret(fromVal))
1008 return fromVal;
1009 assert(to.size() == 4 || to.size() == 8);
1010 return Compiler.paintAsType(pue, fromVal, to);
1013 /******** Constant folding, with support for CTFE ***************************/
1014 /// Return true if non-pointer expression e can be compared
1015 /// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity
1016 bool isCtfeComparable(Expression e)
1018 if (e.op == EXP.slice)
1019 e = e.isSliceExp().e1;
1020 if (e.isConst() != 1)
1022 if (e.op == EXP.null_ || e.op == EXP.string_ || e.op == EXP.function_ || e.op == EXP.delegate_ || e.op == EXP.arrayLiteral || e.op == EXP.structLiteral || e.op == EXP.assocArrayLiteral || e.op == EXP.classReference)
1024 return true;
1026 // https://issues.dlang.org/show_bug.cgi?id=14123
1027 // TypeInfo object is comparable in CTFE
1028 if (e.op == EXP.typeid_)
1029 return true;
1030 return false;
1032 return true;
1035 /// Map EXP comparison ops
1036 private bool numCmp(N)(EXP op, N n1, N n2)
1038 switch (op)
1040 case EXP.lessThan:
1041 return n1 < n2;
1042 case EXP.lessOrEqual:
1043 return n1 <= n2;
1044 case EXP.greaterThan:
1045 return n1 > n2;
1046 case EXP.greaterOrEqual:
1047 return n1 >= n2;
1049 default:
1050 assert(0);
1054 /// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1055 bool specificCmp(EXP op, int rawCmp)
1057 return numCmp!int(op, rawCmp, 0);
1060 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1061 bool intUnsignedCmp(EXP op, dinteger_t n1, dinteger_t n2)
1063 return numCmp!dinteger_t(op, n1, n2);
1066 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1067 bool intSignedCmp(EXP op, sinteger_t n1, sinteger_t n2)
1069 return numCmp!sinteger_t(op, n1, n2);
1072 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1073 bool realCmp(EXP op, real_t r1, real_t r2)
1075 // Don't rely on compiler, handle NAN arguments separately
1076 if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered
1078 switch (op)
1080 case EXP.lessThan:
1081 case EXP.lessOrEqual:
1082 case EXP.greaterThan:
1083 case EXP.greaterOrEqual:
1084 return false;
1086 default:
1087 assert(0);
1090 else
1092 return numCmp!real_t(op, r1, r2);
1096 /* Conceptually the same as memcmp(e1, e2).
1097 * e1 and e2 may be strings, arrayliterals, or slices.
1098 * For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
1099 * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
1100 * Returns:
1101 * -1,0,1
1103 private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinteger_t len)
1105 // Resolve slices, if necessary
1106 uinteger_t lo1 = 0;
1107 uinteger_t lo2 = 0;
1109 Expression x1 = e1;
1110 if (auto sle1 = x1.isSliceExp())
1112 lo1 = sle1.lwr.toInteger();
1113 x1 = sle1.e1;
1115 auto se1 = x1.isStringExp();
1116 auto ae1 = x1.isArrayLiteralExp();
1118 Expression x2 = e2;
1119 if (auto sle2 = x2.isSliceExp())
1121 lo2 = sle2.lwr.toInteger();
1122 x2 = sle2.e1;
1124 auto se2 = x2.isStringExp();
1125 auto ae2 = x2.isArrayLiteralExp();
1127 // Now both must be either EXP.arrayLiteral or EXP.string_
1128 if (se1 && se2)
1129 return sliceCmpStringWithString(se1, se2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len);
1130 if (se1 && ae2)
1131 return sliceCmpStringWithArray(se1, ae2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len);
1132 if (se2 && ae1)
1133 return -sliceCmpStringWithArray(se2, ae1, cast(size_t)lo2, cast(size_t)lo1, cast(size_t)len);
1134 assert(ae1 && ae2);
1135 // Comparing two array literals. This case is potentially recursive.
1136 // If they aren't strings, we just need an equality check rather than
1137 // a full cmp.
1138 const bool needCmp = ae1.type.nextOf().isintegral();
1139 foreach (size_t i; 0 .. cast(size_t)len)
1141 Expression ee1 = (*ae1.elements)[cast(size_t)(lo1 + i)];
1142 Expression ee2 = (*ae2.elements)[cast(size_t)(lo2 + i)];
1143 if (needCmp)
1145 const sinteger_t c = ee1.toInteger() - ee2.toInteger();
1146 if (c > 0)
1147 return 1;
1148 if (c < 0)
1149 return -1;
1151 else
1153 if (ctfeRawCmp(loc, ee1, ee2))
1154 return 1;
1157 return 0;
1160 /* Given a delegate expression e, return .funcptr.
1161 * If e is NullExp, return NULL.
1163 private FuncDeclaration funcptrOf(Expression e)
1165 assert(e.type.ty == Tdelegate);
1166 if (auto de = e.isDelegateExp())
1167 return de.func;
1168 if (auto fe = e.isFuncExp())
1169 return fe.fd;
1170 assert(e.op == EXP.null_);
1171 return null;
1174 private bool isArray(const Expression e)
1176 return e.op == EXP.arrayLiteral || e.op == EXP.string_ || e.op == EXP.slice || e.op == EXP.null_;
1179 /*****
1180 * Params:
1181 * loc = source file location
1182 * e1 = left operand
1183 * e2 = right operand
1184 * identity = true for `is` identity comparisons
1185 * Returns:
1186 * For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
1187 * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
1189 private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool identity = false)
1191 if (e1.op == EXP.classReference || e2.op == EXP.classReference)
1193 if (e1.op == EXP.classReference && e2.op == EXP.classReference &&
1194 e1.isClassReferenceExp().value == e2.isClassReferenceExp().value)
1195 return 0;
1196 return 1;
1198 if (e1.op == EXP.typeid_ && e2.op == EXP.typeid_)
1200 // printf("e1: %s\n", e1.toChars());
1201 // printf("e2: %s\n", e2.toChars());
1202 Type t1 = isType(e1.isTypeidExp().obj);
1203 Type t2 = isType(e2.isTypeidExp().obj);
1204 assert(t1);
1205 assert(t2);
1206 return t1 != t2;
1208 // null == null, regardless of type
1209 if (e1.op == EXP.null_ && e2.op == EXP.null_)
1210 return 0;
1211 if (e1.type.ty == Tpointer && e2.type.ty == Tpointer)
1213 // Can only be an equality test.
1214 dinteger_t ofs1, ofs2;
1215 Expression agg1 = getAggregateFromPointer(e1, &ofs1);
1216 Expression agg2 = getAggregateFromPointer(e2, &ofs2);
1217 if ((agg1 == agg2) || (agg1.op == EXP.variable && agg2.op == EXP.variable && agg1.isVarExp().var == agg2.isVarExp().var))
1219 if (ofs1 == ofs2)
1220 return 0;
1222 return 1;
1224 if (e1.type.ty == Tdelegate && e2.type.ty == Tdelegate)
1226 // If .funcptr isn't the same, they are not equal
1227 if (funcptrOf(e1) != funcptrOf(e2))
1228 return 1;
1229 // If both are delegate literals, assume they have the
1230 // same closure pointer. TODO: We don't support closures yet!
1231 if (e1.op == EXP.function_ && e2.op == EXP.function_)
1232 return 0;
1233 assert(e1.op == EXP.delegate_ && e2.op == EXP.delegate_);
1234 // Same .funcptr. Do they have the same .ptr?
1235 Expression ptr1 = e1.isDelegateExp().e1;
1236 Expression ptr2 = e2.isDelegateExp().e1;
1237 dinteger_t ofs1, ofs2;
1238 Expression agg1 = getAggregateFromPointer(ptr1, &ofs1);
1239 Expression agg2 = getAggregateFromPointer(ptr2, &ofs2);
1240 // If they are EXP.variable, it means they are FuncDeclarations
1241 if ((agg1 == agg2 && ofs1 == ofs2) || (agg1.op == EXP.variable && agg2.op == EXP.variable && agg1.isVarExp().var == agg2.isVarExp().var))
1243 return 0;
1245 return 1;
1247 if (isArray(e1) && isArray(e2))
1249 const uinteger_t len1 = resolveArrayLength(e1);
1250 const uinteger_t len2 = resolveArrayLength(e2);
1251 // workaround for dmc optimizer bug calculating wrong len for
1252 // uinteger_t len = (len1 < len2 ? len1 : len2);
1253 // if (len == 0) ...
1254 if (len1 > 0 && len2 > 0)
1256 const uinteger_t len = (len1 < len2 ? len1 : len2);
1257 const int res = ctfeCmpArrays(loc, e1, e2, len);
1258 if (res != 0)
1259 return res;
1261 return cast(int)(len1 - len2);
1263 if (e1.type.isintegral())
1265 return e1.toInteger() != e2.toInteger();
1267 if (e1.type.isreal() || e1.type.isimaginary())
1269 real_t r1 = e1.type.isreal() ? e1.toReal() : e1.toImaginary();
1270 real_t r2 = e1.type.isreal() ? e2.toReal() : e2.toImaginary();
1271 if (identity)
1272 return !CTFloat.isIdentical(r1, r2);
1273 if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered
1275 return 1; // they are not equal
1277 else
1279 return (r1 != r2);
1282 else if (e1.type.iscomplex())
1284 auto c1 = e1.toComplex();
1285 auto c2 = e2.toComplex();
1286 if (identity)
1288 return !RealIdentical(c1.re, c2.re) && !RealIdentical(c1.im, c2.im);
1290 return c1 != c2;
1292 if (e1.op == EXP.structLiteral && e2.op == EXP.structLiteral)
1294 StructLiteralExp es1 = e1.isStructLiteralExp();
1295 StructLiteralExp es2 = e2.isStructLiteralExp();
1296 // For structs, we only need to return 0 or 1 (< and > aren't legal).
1297 if (es1.sd != es2.sd)
1298 return 1;
1299 else if ((!es1.elements || !es1.elements.dim) && (!es2.elements || !es2.elements.dim))
1300 return 0; // both arrays are empty
1301 else if (!es1.elements || !es2.elements)
1302 return 1;
1303 else if (es1.elements.dim != es2.elements.dim)
1304 return 1;
1305 else
1307 foreach (size_t i; 0 .. es1.elements.dim)
1309 Expression ee1 = (*es1.elements)[i];
1310 Expression ee2 = (*es2.elements)[i];
1312 // https://issues.dlang.org/show_bug.cgi?id=16284
1313 if (ee1.op == EXP.void_ && ee2.op == EXP.void_) // if both are VoidInitExp
1314 continue;
1316 if (ee1 == ee2)
1317 continue;
1318 if (!ee1 || !ee2)
1319 return 1;
1320 const int cmp = ctfeRawCmp(loc, ee1, ee2, identity);
1321 if (cmp)
1322 return 1;
1324 return 0; // All elements are equal
1327 if (e1.op == EXP.assocArrayLiteral && e2.op == EXP.assocArrayLiteral)
1329 AssocArrayLiteralExp es1 = e1.isAssocArrayLiteralExp();
1330 AssocArrayLiteralExp es2 = e2.isAssocArrayLiteralExp();
1331 size_t dim = es1.keys.dim;
1332 if (es2.keys.dim != dim)
1333 return 1;
1334 bool* used = cast(bool*)mem.xmalloc(bool.sizeof * dim);
1335 memset(used, 0, bool.sizeof * dim);
1336 foreach (size_t i; 0 .. dim)
1338 Expression k1 = (*es1.keys)[i];
1339 Expression v1 = (*es1.values)[i];
1340 Expression v2 = null;
1341 foreach (size_t j; 0 .. dim)
1343 if (used[j])
1344 continue;
1345 Expression k2 = (*es2.keys)[j];
1346 if (ctfeRawCmp(loc, k1, k2, identity))
1347 continue;
1348 used[j] = true;
1349 v2 = (*es2.values)[j];
1350 break;
1352 if (!v2 || ctfeRawCmp(loc, v1, v2, identity))
1354 mem.xfree(used);
1355 return 1;
1358 mem.xfree(used);
1359 return 0;
1361 else if (e1.op == EXP.assocArrayLiteral && e2.op == EXP.null_)
1363 return e1.isAssocArrayLiteralExp.keys.dim != 0;
1365 else if (e1.op == EXP.null_ && e2.op == EXP.assocArrayLiteral)
1367 return e2.isAssocArrayLiteralExp.keys.dim != 0;
1370 error(loc, "CTFE internal error: bad compare of `%s` and `%s`", e1.toChars(), e2.toChars());
1371 assert(0);
1374 /// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1
1375 bool ctfeEqual(const ref Loc loc, EXP op, Expression e1, Expression e2)
1377 return !ctfeRawCmp(loc, e1, e2) ^ (op == EXP.notEqual);
1380 /// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1
1381 bool ctfeIdentity(const ref Loc loc, EXP op, Expression e1, Expression e2)
1383 //printf("ctfeIdentity %s %s\n", e1.toChars(), e2.toChars());
1384 //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", EXPtoString(op).ptr,
1385 // EXPtoString(e1.op).ptr, e1.toChars(), EXPtoString(e2.op).ptr, e1.toChars());
1386 bool cmp;
1387 if (e1.op == EXP.null_)
1389 cmp = (e2.op == EXP.null_);
1391 else if (e2.op == EXP.null_)
1393 cmp = false;
1395 else if (e1.op == EXP.symbolOffset && e2.op == EXP.symbolOffset)
1397 SymOffExp es1 = e1.isSymOffExp();
1398 SymOffExp es2 = e2.isSymOffExp();
1399 cmp = (es1.var == es2.var && es1.offset == es2.offset);
1401 else if (e1.type.isreal())
1402 cmp = CTFloat.isIdentical(e1.toReal(), e2.toReal());
1403 else if (e1.type.isimaginary())
1404 cmp = RealIdentical(e1.toImaginary(), e2.toImaginary());
1405 else if (e1.type.iscomplex())
1407 complex_t v1 = e1.toComplex();
1408 complex_t v2 = e2.toComplex();
1409 cmp = RealIdentical(creall(v1), creall(v2)) && RealIdentical(cimagl(v1), cimagl(v1));
1411 else
1413 cmp = !ctfeRawCmp(loc, e1, e2, true);
1415 if (op == EXP.notIdentity || op == EXP.notEqual)
1416 cmp ^= true;
1417 return cmp;
1420 /// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1
1421 bool ctfeCmp(const ref Loc loc, EXP op, Expression e1, Expression e2)
1423 Type t1 = e1.type.toBasetype();
1424 Type t2 = e2.type.toBasetype();
1426 if (t1.isString() && t2.isString())
1427 return specificCmp(op, ctfeRawCmp(loc, e1, e2));
1428 else if (t1.isreal())
1429 return realCmp(op, e1.toReal(), e2.toReal());
1430 else if (t1.isimaginary())
1431 return realCmp(op, e1.toImaginary(), e2.toImaginary());
1432 else if (t1.isunsigned() || t2.isunsigned())
1433 return intUnsignedCmp(op, e1.toInteger(), e2.toInteger());
1434 else
1435 return intSignedCmp(op, e1.toInteger(), e2.toInteger());
1438 UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2)
1440 Type t1 = e1.type.toBasetype();
1441 Type t2 = e2.type.toBasetype();
1442 UnionExp ue;
1443 if (e2.op == EXP.string_ && e1.op == EXP.arrayLiteral && t1.nextOf().isintegral())
1445 // [chars] ~ string => string (only valid for CTFE)
1446 StringExp es1 = e2.isStringExp();
1447 ArrayLiteralExp es2 = e1.isArrayLiteralExp();
1448 const len = es1.len + es2.elements.dim;
1449 const sz = es1.sz;
1450 void* s = mem.xmalloc((len + 1) * sz);
1451 const data1 = es1.peekData();
1452 memcpy(cast(char*)s + sz * es2.elements.dim, data1.ptr, data1.length);
1453 foreach (size_t i; 0 .. es2.elements.dim)
1455 Expression es2e = (*es2.elements)[i];
1456 if (es2e.op != EXP.int64)
1458 emplaceExp!(CTFEExp)(&ue, EXP.cantExpression);
1459 return ue;
1461 dinteger_t v = es2e.toInteger();
1462 Port.valcpy(cast(char*)s + i * sz, v, sz);
1464 // Add terminating 0
1465 memset(cast(char*)s + len * sz, 0, sz);
1466 emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
1467 StringExp es = ue.exp().isStringExp();
1468 es.committed = 0;
1469 es.type = type;
1470 return ue;
1472 if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral && t2.nextOf().isintegral())
1474 // string ~ [chars] => string (only valid for CTFE)
1475 // Concatenate the strings
1476 StringExp es1 = e1.isStringExp();
1477 ArrayLiteralExp es2 = e2.isArrayLiteralExp();
1478 const len = es1.len + es2.elements.dim;
1479 const sz = es1.sz;
1480 void* s = mem.xmalloc((len + 1) * sz);
1481 auto slice = es1.peekData();
1482 memcpy(s, slice.ptr, slice.length);
1483 foreach (size_t i; 0 .. es2.elements.dim)
1485 Expression es2e = (*es2.elements)[i];
1486 if (es2e.op != EXP.int64)
1488 emplaceExp!(CTFEExp)(&ue, EXP.cantExpression);
1489 return ue;
1491 const v = es2e.toInteger();
1492 Port.valcpy(cast(char*)s + (es1.len + i) * sz, v, sz);
1494 // Add terminating 0
1495 memset(cast(char*)s + len * sz, 0, sz);
1496 emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
1497 StringExp es = ue.exp().isStringExp();
1498 es.sz = sz;
1499 es.committed = 0; //es1.committed;
1500 es.type = type;
1501 return ue;
1503 if (e1.op == EXP.arrayLiteral && e2.op == EXP.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
1505 // [ e1 ] ~ [ e2 ] ---> [ e1, e2 ]
1506 ArrayLiteralExp es1 = e1.isArrayLiteralExp();
1507 ArrayLiteralExp es2 = e2.isArrayLiteralExp();
1508 emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, type, copyLiteralArray(es1.elements));
1509 es1 = ue.exp().isArrayLiteralExp();
1510 es1.elements.insert(es1.elements.dim, copyLiteralArray(es2.elements));
1511 return ue;
1513 if (e1.op == EXP.arrayLiteral && e2.op == EXP.null_ && t1.nextOf().equals(t2.nextOf()))
1515 // [ e1 ] ~ null ----> [ e1 ].dup
1516 ue = paintTypeOntoLiteralCopy(type, copyLiteral(e1).copy());
1517 return ue;
1519 if (e1.op == EXP.null_ && e2.op == EXP.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
1521 // null ~ [ e2 ] ----> [ e2 ].dup
1522 ue = paintTypeOntoLiteralCopy(type, copyLiteral(e2).copy());
1523 return ue;
1525 ue = Cat(loc, type, e1, e2);
1526 return ue;
1529 /* Given an AA literal 'ae', and a key 'e2':
1530 * Return ae[e2] if present, or NULL if not found.
1532 Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, Expression e2)
1534 /* Search the keys backwards, in case there are duplicate keys
1536 for (size_t i = ae.keys.dim; i;)
1538 --i;
1539 Expression ekey = (*ae.keys)[i];
1540 const int eq = ctfeEqual(loc, EXP.equal, ekey, e2);
1541 if (eq)
1543 return (*ae.values)[i];
1546 return null;
1549 /* Same as for constfold.Index, except that it only works for static arrays,
1550 * dynamic arrays, and strings. We know that e1 is an
1551 * interpreted CTFE expression, so it cannot have side-effects.
1553 Expression ctfeIndex(UnionExp* pue, const ref Loc loc, Type type, Expression e1, uinteger_t indx)
1555 //printf("ctfeIndex(e1 = %s)\n", e1.toChars());
1556 assert(e1.type);
1557 if (auto es1 = e1.isStringExp())
1559 if (indx >= es1.len)
1561 error(loc, "string index %llu is out of bounds `[0 .. %llu]`", indx, cast(ulong)es1.len);
1562 return CTFEExp.cantexp;
1564 emplaceExp!IntegerExp(pue, loc, es1.getCodeUnit(cast(size_t) indx), type);
1565 return pue.exp();
1568 if (auto ale = e1.isArrayLiteralExp())
1570 if (indx >= ale.elements.dim)
1572 error(loc, "array index %llu is out of bounds `%s[0 .. %llu]`", indx, e1.toChars(), cast(ulong)ale.elements.dim);
1573 return CTFEExp.cantexp;
1575 Expression e = (*ale.elements)[cast(size_t)indx];
1576 return paintTypeOntoLiteral(pue, type, e);
1579 assert(0);
1582 Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expression e, bool explicitCast = false)
1584 Expression paint()
1586 return paintTypeOntoLiteral(pue, to, e);
1589 if (e.op == EXP.null_)
1590 return paint();
1592 if (e.op == EXP.classReference)
1594 // Disallow reinterpreting class casts. Do this by ensuring that
1595 // the original class can implicitly convert to the target class.
1596 // Also do not check 'alias this' for explicit cast expressions.
1597 auto tclass = e.isClassReferenceExp().originalClass().type.isTypeClass();
1598 auto match = explicitCast ? tclass.implicitConvToWithoutAliasThis(to.mutableOf())
1599 : tclass.implicitConvTo(to.mutableOf());
1600 if (match)
1601 return paint();
1602 else
1604 emplaceExp!(NullExp)(pue, loc, to);
1605 return pue.exp();
1609 // Allow TypeInfo type painting
1610 if (isTypeInfo_Class(e.type) && e.type.implicitConvTo(to))
1611 return paint();
1613 // Allow casting away const for struct literals
1614 if (e.op == EXP.structLiteral && e.type.toBasetype().castMod(0) == to.toBasetype().castMod(0))
1615 return paint();
1617 Expression r;
1618 if (e.type.equals(type) && type.equals(to))
1620 // necessary not to change e's address for pointer comparisons
1621 r = e;
1623 else if (to.toBasetype().ty == Tarray &&
1624 type.toBasetype().ty == Tarray &&
1625 to.toBasetype().nextOf().size() == type.toBasetype().nextOf().size())
1627 // https://issues.dlang.org/show_bug.cgi?id=12495
1628 // Array reinterpret casts: eg. string to immutable(ubyte)[]
1629 return paint();
1631 else
1633 *pue = Cast(loc, type, to, e);
1634 r = pue.exp();
1637 if (CTFEExp.isCantExp(r))
1638 error(loc, "cannot cast `%s` to `%s` at compile time", e.toChars(), to.toChars());
1640 if (auto ae = e.isArrayLiteralExp())
1641 ae.ownedByCtfe = OwnedBy.ctfe;
1643 if (auto se = e.isStringExp())
1644 se.ownedByCtfe = OwnedBy.ctfe;
1646 return r;
1649 /******** Assignment helper functions ***************************/
1650 /* Set dest = src, where both dest and src are container value literals
1651 * (ie, struct literals, or static arrays (can be an array literal or a string))
1652 * Assignment is recursively in-place.
1653 * Purpose: any reference to a member of 'dest' will remain valid after the
1654 * assignment.
1656 void assignInPlace(Expression dest, Expression src)
1658 if (!(dest.op == EXP.structLiteral || dest.op == EXP.arrayLiteral || dest.op == EXP.string_))
1660 printf("invalid op %d %d\n", src.op, dest.op);
1661 assert(0);
1663 Expressions* oldelems;
1664 Expressions* newelems;
1665 if (dest.op == EXP.structLiteral)
1667 assert(dest.op == src.op);
1668 oldelems = dest.isStructLiteralExp().elements;
1669 newelems = src.isStructLiteralExp().elements;
1670 auto sd = dest.isStructLiteralExp().sd;
1671 const nfields = sd.nonHiddenFields();
1672 const nvthis = sd.fields.dim - nfields;
1673 if (nvthis && oldelems.dim >= nfields && oldelems.dim < newelems.dim)
1674 foreach (_; 0 .. newelems.dim - oldelems.dim)
1675 oldelems.push(null);
1677 else if (dest.op == EXP.arrayLiteral && src.op == EXP.arrayLiteral)
1679 oldelems = dest.isArrayLiteralExp().elements;
1680 newelems = src.isArrayLiteralExp().elements;
1682 else if (dest.op == EXP.string_ && src.op == EXP.string_)
1684 sliceAssignStringFromString(dest.isStringExp(), src.isStringExp(), 0);
1685 return;
1687 else if (dest.op == EXP.arrayLiteral && src.op == EXP.string_)
1689 sliceAssignArrayLiteralFromString(dest.isArrayLiteralExp(), src.isStringExp(), 0);
1690 return;
1692 else if (src.op == EXP.arrayLiteral && dest.op == EXP.string_)
1694 sliceAssignStringFromArrayLiteral(dest.isStringExp(), src.isArrayLiteralExp(), 0);
1695 return;
1697 else
1699 printf("invalid op %d %d\n", src.op, dest.op);
1700 assert(0);
1702 assert(oldelems.dim == newelems.dim);
1703 foreach (size_t i; 0 .. oldelems.dim)
1705 Expression e = (*newelems)[i];
1706 Expression o = (*oldelems)[i];
1707 if (e.op == EXP.structLiteral)
1709 assert(o.op == e.op);
1710 assignInPlace(o, e);
1712 else if (e.type.ty == Tsarray && e.op != EXP.void_ && o.type.ty == Tsarray)
1714 assignInPlace(o, e);
1716 else
1718 (*oldelems)[i] = (*newelems)[i];
1723 // Given an AA literal aae, set aae[index] = newval and return newval.
1724 Expression assignAssocArrayElement(const ref Loc loc, AssocArrayLiteralExp aae, Expression index, Expression newval)
1726 /* Create new associative array literal reflecting updated key/value
1728 Expressions* keysx = aae.keys;
1729 Expressions* valuesx = aae.values;
1730 int updated = 0;
1731 for (size_t j = valuesx.dim; j;)
1733 j--;
1734 Expression ekey = (*aae.keys)[j];
1735 int eq = ctfeEqual(loc, EXP.equal, ekey, index);
1736 if (eq)
1738 (*valuesx)[j] = newval;
1739 updated = 1;
1742 if (!updated)
1744 // Append index/newval to keysx[]/valuesx[]
1745 valuesx.push(newval);
1746 keysx.push(index);
1748 return newval;
1751 /// Given array literal oldval of type ArrayLiteralExp or StringExp, of length
1752 /// oldlen, change its length to newlen. If the newlen is longer than oldlen,
1753 /// all new elements will be set to the default initializer for the element type.
1754 Expression changeArrayLiteralLength(UnionExp* pue, const ref Loc loc, TypeArray arrayType, Expression oldval, size_t oldlen, size_t newlen)
1756 Type elemType = arrayType.next;
1757 assert(elemType);
1758 Expression defaultElem = elemType.defaultInitLiteral(loc);
1759 auto elements = new Expressions(newlen);
1760 // Resolve slices
1761 size_t indxlo = 0;
1762 if (oldval.op == EXP.slice)
1764 indxlo = cast(size_t)oldval.isSliceExp().lwr.toInteger();
1765 oldval = oldval.isSliceExp().e1;
1767 size_t copylen = oldlen < newlen ? oldlen : newlen;
1768 if (oldval.op == EXP.string_)
1770 StringExp oldse = oldval.isStringExp();
1771 void* s = mem.xcalloc(newlen + 1, oldse.sz);
1772 const data = oldse.peekData();
1773 memcpy(s, data.ptr, copylen * oldse.sz);
1774 const defaultValue = cast(uint)defaultElem.toInteger();
1775 foreach (size_t elemi; copylen .. newlen)
1777 switch (oldse.sz)
1779 case 1:
1780 (cast(char*)s)[cast(size_t)(indxlo + elemi)] = cast(char)defaultValue;
1781 break;
1782 case 2:
1783 (cast(wchar*)s)[cast(size_t)(indxlo + elemi)] = cast(wchar)defaultValue;
1784 break;
1785 case 4:
1786 (cast(dchar*)s)[cast(size_t)(indxlo + elemi)] = cast(dchar)defaultValue;
1787 break;
1788 default:
1789 assert(0);
1792 emplaceExp!(StringExp)(pue, loc, s[0 .. newlen * oldse.sz], newlen, oldse.sz);
1793 StringExp se = pue.exp().isStringExp();
1794 se.type = arrayType;
1795 se.sz = oldse.sz;
1796 se.committed = oldse.committed;
1797 se.ownedByCtfe = OwnedBy.ctfe;
1799 else
1801 if (oldlen != 0)
1803 assert(oldval.op == EXP.arrayLiteral);
1804 ArrayLiteralExp ae = oldval.isArrayLiteralExp();
1805 foreach (size_t i; 0 .. copylen)
1806 (*elements)[i] = (*ae.elements)[indxlo + i];
1808 if (elemType.ty == Tstruct || elemType.ty == Tsarray)
1810 /* If it is an aggregate literal representing a value type,
1811 * we need to create a unique copy for each element
1813 foreach (size_t i; copylen .. newlen)
1814 (*elements)[i] = copyLiteral(defaultElem).copy();
1816 else
1818 foreach (size_t i; copylen .. newlen)
1819 (*elements)[i] = defaultElem;
1821 emplaceExp!(ArrayLiteralExp)(pue, loc, arrayType, elements);
1822 ArrayLiteralExp aae = pue.exp().isArrayLiteralExp();
1823 aae.ownedByCtfe = OwnedBy.ctfe;
1825 return pue.exp();
1828 /*************************** CTFE Sanity Checks ***************************/
1830 bool isCtfeValueValid(Expression newval)
1832 Type tb = newval.type.toBasetype();
1833 switch (newval.op)
1835 case EXP.int64:
1836 case EXP.float64:
1837 case EXP.char_:
1838 case EXP.complex80:
1839 return tb.isscalar();
1841 case EXP.null_:
1842 return tb.ty == Tnull ||
1843 tb.ty == Tpointer ||
1844 tb.ty == Tarray ||
1845 tb.ty == Taarray ||
1846 tb.ty == Tclass ||
1847 tb.ty == Tdelegate;
1849 case EXP.string_:
1850 return true; // CTFE would directly use the StringExp in AST.
1852 case EXP.arrayLiteral:
1853 return true; //((ArrayLiteralExp *)newval)->ownedByCtfe;
1855 case EXP.assocArrayLiteral:
1856 return true; //((AssocArrayLiteralExp *)newval)->ownedByCtfe;
1858 case EXP.structLiteral:
1859 return true; //((StructLiteralExp *)newval)->ownedByCtfe;
1861 case EXP.classReference:
1862 return true;
1864 case EXP.type:
1865 return true;
1867 case EXP.vector:
1868 return true; // vector literal
1870 case EXP.function_:
1871 return true; // function literal or delegate literal
1873 case EXP.delegate_:
1875 // &struct.func or &clasinst.func
1876 // &nestedfunc
1877 Expression ethis = newval.isDelegateExp().e1;
1878 return (ethis.op == EXP.structLiteral || ethis.op == EXP.classReference || ethis.op == EXP.variable && ethis.isVarExp().var == newval.isDelegateExp().func);
1881 case EXP.symbolOffset:
1883 // function pointer, or pointer to static variable
1884 Declaration d = newval.isSymOffExp().var;
1885 return d.isFuncDeclaration() || d.isDataseg();
1888 case EXP.typeid_:
1890 // always valid
1891 return true;
1894 case EXP.address:
1896 // e1 should be a CTFE reference
1897 Expression e1 = newval.isAddrExp().e1;
1898 return tb.ty == Tpointer &&
1900 (e1.op == EXP.structLiteral || e1.op == EXP.arrayLiteral) && isCtfeValueValid(e1) ||
1901 e1.op == EXP.variable ||
1902 e1.op == EXP.dotVariable && isCtfeReferenceValid(e1) ||
1903 e1.op == EXP.index && isCtfeReferenceValid(e1) ||
1904 e1.op == EXP.slice && e1.type.toBasetype().ty == Tsarray
1908 case EXP.slice:
1910 // e1 should be an array aggregate
1911 const SliceExp se = newval.isSliceExp();
1912 assert(se.lwr && se.lwr.op == EXP.int64);
1913 assert(se.upr && se.upr.op == EXP.int64);
1914 return (tb.ty == Tarray || tb.ty == Tsarray) && (se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral);
1917 case EXP.void_:
1918 return true; // uninitialized value
1920 default:
1921 newval.error("CTFE internal error: illegal CTFE value `%s`", newval.toChars());
1922 return false;
1926 bool isCtfeReferenceValid(Expression newval)
1928 switch (newval.op)
1930 case EXP.this_:
1931 return true;
1933 case EXP.variable:
1935 const VarDeclaration v = newval.isVarExp().var.isVarDeclaration();
1936 assert(v);
1937 // Must not be a reference to a reference
1938 return true;
1941 case EXP.index:
1943 const Expression eagg = newval.isIndexExp().e1;
1944 return eagg.op == EXP.string_ || eagg.op == EXP.arrayLiteral || eagg.op == EXP.assocArrayLiteral;
1947 case EXP.dotVariable:
1949 Expression eagg = newval.isDotVarExp().e1;
1950 return (eagg.op == EXP.structLiteral || eagg.op == EXP.classReference) && isCtfeValueValid(eagg);
1953 default:
1954 // Internally a ref variable may directly point a stack memory.
1955 // e.g. ref int v = 1;
1956 return isCtfeValueValid(newval);
1960 // Used for debugging only
1961 void showCtfeExpr(Expression e, int level = 0)
1963 for (int i = level; i > 0; --i)
1964 printf(" ");
1965 Expressions* elements = null;
1966 // We need the struct definition to detect block assignment
1967 StructDeclaration sd = null;
1968 ClassDeclaration cd = null;
1969 if (e.op == EXP.structLiteral)
1971 elements = e.isStructLiteralExp().elements;
1972 sd = e.isStructLiteralExp().sd;
1973 printf("STRUCT type = %s %p:\n", e.type.toChars(), e);
1975 else if (e.op == EXP.classReference)
1977 elements = e.isClassReferenceExp().value.elements;
1978 cd = e.isClassReferenceExp().originalClass();
1979 printf("CLASS type = %s %p:\n", e.type.toChars(), e.isClassReferenceExp().value);
1981 else if (e.op == EXP.arrayLiteral)
1983 elements = e.isArrayLiteralExp().elements;
1984 printf("ARRAY LITERAL type=%s %p:\n", e.type.toChars(), e);
1986 else if (e.op == EXP.assocArrayLiteral)
1988 printf("AA LITERAL type=%s %p:\n", e.type.toChars(), e);
1990 else if (e.op == EXP.string_)
1992 printf("STRING %s %p\n", e.toChars(), e.isStringExp.peekString.ptr);
1994 else if (e.op == EXP.slice)
1996 printf("SLICE %p: %s\n", e, e.toChars());
1997 showCtfeExpr(e.isSliceExp().e1, level + 1);
1999 else if (e.op == EXP.variable)
2001 printf("VAR %p %s\n", e, e.toChars());
2002 VarDeclaration v = e.isVarExp().var.isVarDeclaration();
2003 if (v && getValue(v))
2004 showCtfeExpr(getValue(v), level + 1);
2006 else if (e.op == EXP.address)
2008 // This is potentially recursive. We mustn't try to print the thing we're pointing to.
2009 printf("POINTER %p to %p: %s\n", e, e.isAddrExp().e1, e.toChars());
2011 else
2012 printf("VALUE %p: %s\n", e, e.toChars());
2013 if (elements)
2015 size_t fieldsSoFar = 0;
2016 for (size_t i = 0; i < elements.dim; i++)
2018 Expression z = null;
2019 VarDeclaration v = null;
2020 if (i > 15)
2022 printf("...(total %d elements)\n", cast(int)elements.dim);
2023 return;
2025 if (sd)
2027 v = sd.fields[i];
2028 z = (*elements)[i];
2030 else if (cd)
2032 while (i - fieldsSoFar >= cd.fields.dim)
2034 fieldsSoFar += cd.fields.dim;
2035 cd = cd.baseClass;
2036 for (int j = level; j > 0; --j)
2037 printf(" ");
2038 printf(" BASE CLASS: %s\n", cd.toChars());
2040 v = cd.fields[i - fieldsSoFar];
2041 assert((elements.dim + i) >= (fieldsSoFar + cd.fields.dim));
2042 size_t indx = (elements.dim - fieldsSoFar) - cd.fields.dim + i;
2043 assert(indx < elements.dim);
2044 z = (*elements)[indx];
2046 if (!z)
2048 for (int j = level; j > 0; --j)
2049 printf(" ");
2050 printf(" void\n");
2051 continue;
2053 if (v)
2055 // If it is a void assignment, use the default initializer
2056 if ((v.type.ty != z.type.ty) && v.type.ty == Tsarray)
2058 for (int j = level; --j;)
2059 printf(" ");
2060 printf(" field: block initialized static array\n");
2061 continue;
2064 showCtfeExpr(z, level + 1);
2069 /*************************** Void initialization ***************************/
2070 UnionExp voidInitLiteral(Type t, VarDeclaration var)
2072 UnionExp ue;
2073 if (t.ty == Tsarray)
2075 TypeSArray tsa = cast(TypeSArray)t;
2076 Expression elem = voidInitLiteral(tsa.next, var).copy();
2077 // For aggregate value types (structs, static arrays) we must
2078 // create an a separate copy for each element.
2079 const mustCopy = (elem.op == EXP.arrayLiteral || elem.op == EXP.structLiteral);
2080 const d = cast(size_t)tsa.dim.toInteger();
2081 auto elements = new Expressions(d);
2082 foreach (i; 0 .. d)
2084 if (mustCopy && i > 0)
2085 elem = copyLiteral(elem).copy();
2086 (*elements)[i] = elem;
2088 emplaceExp!(ArrayLiteralExp)(&ue, var.loc, tsa, elements);
2089 ArrayLiteralExp ae = ue.exp().isArrayLiteralExp();
2090 ae.ownedByCtfe = OwnedBy.ctfe;
2092 else if (t.ty == Tstruct)
2094 TypeStruct ts = cast(TypeStruct)t;
2095 auto exps = new Expressions(ts.sym.fields.dim);
2096 foreach (size_t i; 0 .. ts.sym.fields.dim)
2098 (*exps)[i] = voidInitLiteral(ts.sym.fields[i].type, ts.sym.fields[i]).copy();
2100 emplaceExp!(StructLiteralExp)(&ue, var.loc, ts.sym, exps);
2101 StructLiteralExp se = ue.exp().isStructLiteralExp();
2102 se.type = ts;
2103 se.ownedByCtfe = OwnedBy.ctfe;
2105 else
2106 emplaceExp!(VoidInitExp)(&ue, var);
2107 return ue;