1
/**
2
 * CTFE for expressions involving pointers, slices, array concatenation etc.
3
 *
4
 * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
5
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ctfeexpr.d, _ctfeexpr.d)
8
 * Documentation:  https://dlang.org/phobos/dmd_ctfeexpr.html
9
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ctfeexpr.d
10
 */
11

12
module dmd.ctfeexpr;
13

14
import core.stdc.stdio;
15
import core.stdc.stdlib;
16
import core.stdc.string;
17
import dmd.arraytypes;
18
import dmd.complex;
19
import dmd.constfold;
20
import dmd.compiler;
21
import dmd.dclass;
22
import dmd.declaration;
23
import dmd.dinterpret;
24
import dmd.dstruct;
25
import dmd.dtemplate;
26
import dmd.errors;
27
import dmd.expression;
28
import dmd.func;
29
import dmd.globals;
30
import dmd.mtype;
31
import dmd.root.ctfloat;
32
import dmd.root.port;
33
import dmd.root.rmem;
34
import dmd.tokens;
35
import dmd.visitor;
36

37

38
/***********************************************************
39
 * A reference to a class, or an interface. We need this when we
40
 * point to a base class (we must record what the type is).
41
 */
42
extern (C++) final class ClassReferenceExp : Expression
43
{
44
    StructLiteralExp value;
45

46 1
    extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type)
47
    {
48 1
        super(loc, TOK.classReference, __traits(classInstanceSize, ClassReferenceExp));
49 1
        assert(lit && lit.sd && lit.sd.isClassDeclaration());
50 1
        this.value = lit;
51 1
        this.type = type;
52
    }
53

54
    ClassDeclaration originalClass()
55
    {
56 1
        return value.sd.isClassDeclaration();
57
    }
58

59
    // Return index of the field, or -1 if not found
60
    private int getFieldIndex(Type fieldtype, uint fieldoffset)
61
    {
62 0
        ClassDeclaration cd = originalClass();
63 0
        uint fieldsSoFar = 0;
64 0
        for (size_t j = 0; j < value.elements.dim; j++)
65
        {
66 0
            while (j - fieldsSoFar >= cd.fields.dim)
67
            {
68 0
                fieldsSoFar += cd.fields.dim;
69 0
                cd = cd.baseClass;
70
            }
71 0
            VarDeclaration v2 = cd.fields[j - fieldsSoFar];
72 0
            if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size())
73
            {
74 0
                return cast(int)(value.elements.dim - fieldsSoFar - cd.fields.dim + (j - fieldsSoFar));
75
            }
76
        }
77 0
        return -1;
78
    }
79

80
    // Return index of the field, or -1 if not found
81
    // Same as getFieldIndex, but checks for a direct match with the VarDeclaration
82
    int findFieldIndexByName(VarDeclaration v)
83
    {
84 1
        ClassDeclaration cd = originalClass();
85 1
        size_t fieldsSoFar = 0;
86 1
        for (size_t j = 0; j < value.elements.dim; j++)
87
        {
88 1
            while (j - fieldsSoFar >= cd.fields.dim)
89
            {
90 1
                fieldsSoFar += cd.fields.dim;
91 1
                cd = cd.baseClass;
92
            }
93 1
            VarDeclaration v2 = cd.fields[j - fieldsSoFar];
94 1
            if (v == v2)
95
            {
96 1
                return cast(int)(value.elements.dim - fieldsSoFar - cd.fields.dim + (j - fieldsSoFar));
97
            }
98
        }
99 0
        return -1;
100
    }
101

102
    override void accept(Visitor v)
103
    {
104 1
        v.visit(this);
105
    }
106
}
107

108
/*************************
109
 * Same as getFieldIndex, but checks for a direct match with the VarDeclaration
110
 * Returns:
111
 *    index of the field, or -1 if not found
112
 */
113
int findFieldIndexByName(const StructDeclaration sd, const VarDeclaration v) pure
114
{
115 1
    foreach (i, field; sd.fields)
116
    {
117 1
        if (field == v)
118 1
            return cast(int)i;
119
    }
120 0
    return -1;
121
}
122

123
/***********************************************************
124
 * Fake class which holds the thrown exception.
125
 * Used for implementing exception handling.
126
 */
127
extern (C++) final class ThrownExceptionExp : Expression
128
{
129
    ClassReferenceExp thrown;   // the thing being tossed
130

131 1
    extern (D) this(const ref Loc loc, ClassReferenceExp victim)
132
    {
133 1
        super(loc, TOK.thrownException, __traits(classInstanceSize, ThrownExceptionExp));
134 1
        this.thrown = victim;
135 1
        this.type = victim.type;
136
    }
137

138
    override const(char)* toChars() const
139
    {
140 0
        return "CTFE ThrownException";
141
    }
142

143
    // Generate an error message when this exception is not caught
144
    extern (D) void generateUncaughtError()
145
    {
146 1
        UnionExp ue = void;
147 1
        Expression e = resolveSlice((*thrown.value.elements)[0], &ue);
148 1
        StringExp se = e.toStringExp();
149 1
        thrown.error("uncaught CTFE exception `%s(%s)`", thrown.type.toChars(), se ? se.toChars() : e.toChars());
150
        /* Also give the line where the throw statement was. We won't have it
151
         * in the case where the ThrowStatement is generated internally
152
         * (eg, in ScopeStatement)
153
         */
154 1
        if (loc.isValid() && !loc.equals(thrown.loc))
155 0
            .errorSupplemental(loc, "thrown from here");
156
    }
157

158
    override void accept(Visitor v)
159
    {
160 0
        v.visit(this);
161
    }
162
}
163

164
/***********************************************************
165
 * This type is only used by the interpreter.
166
 */
167
extern (C++) final class CTFEExp : Expression
168
{
169 1
    extern (D) this(TOK tok)
170
    {
171 1
        super(Loc.initial, tok, __traits(classInstanceSize, CTFEExp));
172 1
        type = Type.tvoid;
173
    }
174

175
    override const(char)* toChars() const
176
    {
177 1
        switch (op)
178
        {
179 0
        case TOK.cantExpression:
180 0
            return "<cant>";
181 0
        case TOK.voidExpression:
182 0
            return "<void>";
183 1
        case TOK.showCtfeContext:
184 1
            return "<error>";
185 0
        case TOK.break_:
186 0
            return "<break>";
187 0
        case TOK.continue_:
188 0
            return "<continue>";
189 0
        case TOK.goto_:
190 0
            return "<goto>";
191 0
        default:
192 0
            assert(0);
193
        }
194
    }
195

196
    extern (D) __gshared CTFEExp cantexp;
197
    extern (D) __gshared CTFEExp voidexp;
198
    extern (D) __gshared CTFEExp breakexp;
199
    extern (D) __gshared CTFEExp continueexp;
200
    extern (D) __gshared CTFEExp gotoexp;
201
    /* Used when additional information is needed regarding
202
     * a ctfe error.
203
     */
204
    extern (D) __gshared CTFEExp showcontext;
205

206
    extern (D) static bool isCantExp(const Expression e)
207
    {
208 1
        return e && e.op == TOK.cantExpression;
209
    }
210

211
    extern (D) static bool isGotoExp(const Expression e)
212
    {
213 1
        return e && e.op == TOK.goto_;
214
    }
215
}
216

217
// True if 'e' is CTFEExp::cantexp, or an exception
218
bool exceptionOrCantInterpret(const Expression e)
219
{
220 1
    return e && (e.op == TOK.cantExpression || e.op == TOK.thrownException || e.op == TOK.showCtfeContext);
221
}
222

223
/************** Aggregate literals (AA/string/array/struct) ******************/
224
// Given expr, which evaluates to an array/AA/string literal,
225
// return true if it needs to be copied
226
bool needToCopyLiteral(const Expression expr)
227
{
228 1
    Expression e = cast()expr;
229
    for (;;)
230
    {
231 1
        switch (e.op)
232
        {
233 1
        case TOK.arrayLiteral:
234 1
            return (cast(ArrayLiteralExp)e).ownedByCtfe == OwnedBy.code;
235 1
        case TOK.assocArrayLiteral:
236 1
            return (cast(AssocArrayLiteralExp)e).ownedByCtfe == OwnedBy.code;
237 1
        case TOK.structLiteral:
238 1
            return (cast(StructLiteralExp)e).ownedByCtfe == OwnedBy.code;
239 1
        case TOK.string_:
240 1
        case TOK.this_:
241 1
        case TOK.variable:
242 1
            return false;
243 0
        case TOK.assign:
244 0
            return false;
245 0
        case TOK.index:
246 0
        case TOK.dotVariable:
247 1
        case TOK.slice:
248 1
        case TOK.cast_:
249 1
            e = (cast(UnaExp)e).e1;
250 1
            continue;
251 0
        case TOK.concatenate:
252 0
            return needToCopyLiteral((cast(BinExp)e).e1) || needToCopyLiteral((cast(BinExp)e).e2);
253 0
        case TOK.concatenateAssign:
254 0
        case TOK.concatenateElemAssign:
255 0
        case TOK.concatenateDcharAssign:
256 0
            e = (cast(BinExp)e).e2;
257 0
            continue;
258 1
        default:
259 1
            return false;
260
        }
261
    }
262
}
263

264
private Expressions* copyLiteralArray(Expressions* oldelems, Expression basis = null)
265
{
266 1
    if (!oldelems)
267 0
        return oldelems;
268 1
    incArrayAllocs();
269 1
    auto newelems = new Expressions(oldelems.dim);
270 1
    foreach (i, el; *oldelems)
271
    {
272 1
        (*newelems)[i] = copyLiteral(el ? el : basis).copy();
273
    }
274 1
    return newelems;
275
}
276

277
// Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral.
278
// This value will be used for in-place modification.
279
UnionExp copyLiteral(Expression e)
280
{
281 1
    UnionExp ue = void;
282 1
    if (auto se = e.isStringExp()) // syntaxCopy doesn't make a copy for StringExp!
283
    {
284 1
        char* s = cast(char*)mem.xcalloc(se.len + 1, se.sz);
285 1
        const slice = se.peekData();
286 1
        memcpy(s, slice.ptr, slice.length);
287 1
        emplaceExp!(StringExp)(&ue, se.loc, s[0 .. se.len * se.sz], se.len, se.sz);
288 1
        StringExp se2 = cast(StringExp)ue.exp();
289 1
        se2.committed = se.committed;
290 1
        se2.postfix = se.postfix;
291 1
        se2.type = se.type;
292 1
        se2.ownedByCtfe = OwnedBy.ctfe;
293 1
        return ue;
294
    }
295 1
    if (auto ale = e.isArrayLiteralExp())
296
    {
297 1
        auto elements = copyLiteralArray(ale.elements, ale.basis);
298

299 1
        emplaceExp!(ArrayLiteralExp)(&ue, e.loc, e.type, elements);
300

301 1
        ArrayLiteralExp r = cast(ArrayLiteralExp)ue.exp();
302 1
        r.ownedByCtfe = OwnedBy.ctfe;
303 1
        return ue;
304
    }
305 1
    if (auto aae = e.isAssocArrayLiteralExp())
306
    {
307 1
        emplaceExp!(AssocArrayLiteralExp)(&ue, e.loc, copyLiteralArray(aae.keys), copyLiteralArray(aae.values));
308 1
        AssocArrayLiteralExp r = cast(AssocArrayLiteralExp)ue.exp();
309 1
        r.type = e.type;
310 1
        r.ownedByCtfe = OwnedBy.ctfe;
311 1
        return ue;
312
    }
313 1
    if (auto sle = e.isStructLiteralExp())
314
    {
315
        /* syntaxCopy doesn't work for struct literals, because of a nasty special
316
         * case: block assignment is permitted inside struct literals, eg,
317
         * an int[4] array can be initialized with a single int.
318
         */
319 1
        auto oldelems = sle.elements;
320 1
        auto newelems = new Expressions(oldelems.dim);
321 1
        foreach (i, ref el; *newelems)
322
        {
323
            // We need the struct definition to detect block assignment
324 1
            auto v = sle.sd.fields[i];
325 1
            auto m = (*oldelems)[i];
326

327
            // If it is a void assignment, use the default initializer
328 1
            if (!m)
329 0
                m = voidInitLiteral(v.type, v).copy();
330

331 1
            if (v.type.ty == Tarray || v.type.ty == Taarray)
332
            {
333
                // Don't have to copy array references
334
            }
335
            else
336
            {
337
                // Buzilla 15681: Copy the source element always.
338 1
                m = copyLiteral(m).copy();
339

340
                // Block assignment from inside struct literals
341 1
                if (v.type.ty != m.type.ty && v.type.ty == Tsarray)
342
                {
343 0
                    auto tsa = v.type.isTypeSArray();
344 0
                    auto len = cast(size_t)tsa.dim.toInteger();
345 0
                    UnionExp uex = void;
346 0
                    m = createBlockDuplicatedArrayLiteral(&uex, e.loc, v.type, m, len);
347 0
                    if (m == uex.exp())
348 0
                        m = uex.copy();
349
                }
350
            }
351 1
            el = m;
352
        }
353 1
        emplaceExp!(StructLiteralExp)(&ue, e.loc, sle.sd, newelems, sle.stype);
354 1
        auto r = ue.exp().isStructLiteralExp();
355 1
        r.type = e.type;
356 1
        r.ownedByCtfe = OwnedBy.ctfe;
357 1
        r.origin = sle.origin;
358 1
        return ue;
359
    }
360 1
    if (e.op == TOK.function_ || e.op == TOK.delegate_ || e.op == TOK.symbolOffset || e.op == TOK.null_ || e.op == TOK.variable || e.op == TOK.dotVariable || e.op == TOK.int64 || e.op == TOK.float64 || e.op == TOK.char_ || e.op == TOK.complex80 || e.op == TOK.void_ || e.op == TOK.vector || e.op == TOK.typeid_)
361
    {
362
        // Simple value types
363
        // Keep e1 for DelegateExp and DotVarExp
364 1
        emplaceExp!(UnionExp)(&ue, e);
365 1
        Expression r = ue.exp();
366 1
        r.type = e.type;
367 1
        return ue;
368
    }
369 1
    if (auto se = e.isSliceExp())
370
    {
371 1
        if (se.type.toBasetype().ty == Tsarray)
372
        {
373
            // same with resolveSlice()
374 1
            if (se.e1.op == TOK.null_)
375
            {
376 0
                emplaceExp!(NullExp)(&ue, se.loc, se.type);
377 0
                return ue;
378
            }
379 1
            ue = Slice(se.type, se.e1, se.lwr, se.upr);
380 1
            auto r = ue.exp().isArrayLiteralExp();
381 1
            r.elements = copyLiteralArray(r.elements);
382 1
            r.ownedByCtfe = OwnedBy.ctfe;
383 1
            return ue;
384
        }
385
        else
386
        {
387
            // Array slices only do a shallow copy
388 1
            emplaceExp!(SliceExp)(&ue, e.loc, se.e1, se.lwr, se.upr);
389 1
            Expression r = ue.exp();
390 1
            r.type = e.type;
391 1
            return ue;
392
        }
393
    }
394 1
    if (isPointer(e.type))
395
    {
396
        // For pointers, we only do a shallow copy.
397 1
        if (auto ae = e.isAddrExp())
398 1
            emplaceExp!(AddrExp)(&ue, e.loc, ae.e1);
399 0
        else if (auto ie = e.isIndexExp())
400 0
            emplaceExp!(IndexExp)(&ue, e.loc, ie.e1, ie.e2);
401 0
        else if (auto dve = e.isDotVarExp())
402
        {
403 0
            emplaceExp!(DotVarExp)(&ue, e.loc, dve.e1, dve.var, dve.hasOverloads);
404
        }
405
        else
406 0
            assert(0);
407

408 1
        Expression r = ue.exp();
409 1
        r.type = e.type;
410 1
        return ue;
411
    }
412 1
    if (auto cre = e.isClassReferenceExp())
413
    {
414 1
        emplaceExp!(ClassReferenceExp)(&ue, e.loc, cre.value, e.type);
415 1
        return ue;
416
    }
417 1
    if (e.op == TOK.error)
418
    {
419 1
        emplaceExp!(UnionExp)(&ue, e);
420 1
        return ue;
421
    }
422 0
    e.error("CTFE internal error: literal `%s`", e.toChars());
423 0
    assert(0);
424
}
425

426
/* Deal with type painting.
427
 * Type painting is a major nuisance: we can't just set
428
 * e.type = type, because that would change the original literal.
429
 * But, we can't simply copy the literal either, because that would change
430
 * the values of any pointers.
431
 */
432
Expression paintTypeOntoLiteral(Type type, Expression lit)
433
{
434 1
    if (lit.type.equals(type))
435 1
        return lit;
436 1
    return paintTypeOntoLiteralCopy(type, lit).copy();
437
}
438

439
Expression paintTypeOntoLiteral(UnionExp* pue, Type type, Expression lit)
440
{
441 1
    if (lit.type.equals(type))
442 1
        return lit;
443 1
    *pue = paintTypeOntoLiteralCopy(type, lit);
444 1
    return pue.exp();
445
}
446

447
private UnionExp paintTypeOntoLiteralCopy(Type type, Expression lit)
448
{
449 1
    UnionExp ue;
450 1
    if (lit.type.equals(type))
451
    {
452 1
        emplaceExp!(UnionExp)(&ue, lit);
453 1
        return ue;
454
    }
455
    // If it is a cast to inout, retain the original type of the referenced part.
456 1
    if (type.hasWild() && type.hasPointers())
457
    {
458 1
        emplaceExp!(UnionExp)(&ue, lit);
459 1
        ue.exp().type = type;
460 1
        return ue;
461
    }
462 1
    if (auto se = lit.isSliceExp())
463
    {
464 1
        emplaceExp!(SliceExp)(&ue, lit.loc, se.e1, se.lwr, se.upr);
465
    }
466 1
    else if (auto ie = lit.isIndexExp())
467
    {
468 1
        emplaceExp!(IndexExp)(&ue, lit.loc, ie.e1, ie.e2);
469
    }
470 1
    else if (lit.op == TOK.arrayLiteral)
471
    {
472 1
        emplaceExp!(SliceExp)(&ue, lit.loc, lit, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t), ArrayLength(Type.tsize_t, lit).copy());
473
    }
474 1
    else if (lit.op == TOK.string_)
475
    {
476
        // For strings, we need to introduce another level of indirection
477 1
        emplaceExp!(SliceExp)(&ue, lit.loc, lit, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t), ArrayLength(Type.tsize_t, lit).copy());
478
    }
479 1
    else if (auto aae = lit.isAssocArrayLiteralExp())
480
    {
481
        // TODO: we should be creating a reference to this AAExp, not
482
        // just a ref to the keys and values.
483 0
        OwnedBy wasOwned = aae.ownedByCtfe;
484 0
        emplaceExp!(AssocArrayLiteralExp)(&ue, lit.loc, aae.keys, aae.values);
485 0
        aae = cast(AssocArrayLiteralExp)ue.exp();
486 0
        aae.ownedByCtfe = wasOwned;
487
    }
488
    else
489
    {
490
        // Can't type paint from struct to struct*; this needs another
491
        // level of indirection
492 1
        if (lit.op == TOK.structLiteral && isPointer(type))
493 0
            lit.error("CTFE internal error: painting `%s`", type.toChars());
494 1
        ue = copyLiteral(lit);
495
    }
496 1
    ue.exp().type = type;
497 1
    return ue;
498
}
499

500
/*************************************
501
 * If e is a SliceExp, constant fold it.
502
 * Params:
503
 *      e = expression to resolve
504
 *      pue = if not null, store resulting expression here
505
 * Returns:
506
 *      resulting expression
507
 */
508
Expression resolveSlice(Expression e, UnionExp* pue = null)
509
{
510 1
    SliceExp se = e.isSliceExp();
511 1
    if (!se)
512 1
        return e;
513 1
    if (se.e1.op == TOK.null_)
514 0
        return se.e1;
515 1
    if (pue)
516
    {
517 1
        *pue = Slice(e.type, se.e1, se.lwr, se.upr);
518 1
        return pue.exp();
519
    }
520
    else
521 1
        return Slice(e.type, se.e1, se.lwr, se.upr).copy();
522
}
523

524
/* Determine the array length, without interpreting it.
525
 * e must be an array literal, or a slice
526
 * It's very wasteful to resolve the slice when we only
527
 * need the length.
528
 */
529
uinteger_t resolveArrayLength(const Expression e)
530
{
531 1
    switch (e.op)
532
    {
533 0
        case TOK.vector:
534 0
            return e.isVectorExp().dim;
535

536 1
        case TOK.null_:
537 1
            return 0;
538

539 1
        case TOK.slice:
540
        {
541 1
            auto se = cast(SliceExp)e;
542 1
            const ilo = se.lwr.toInteger();
543 1
            const iup = se.upr.toInteger();
544 1
            return iup - ilo;
545
        }
546

547 1
        case TOK.string_:
548 1
            return e.isStringExp().len;
549

550 1
        case TOK.arrayLiteral:
551
        {
552 1
            const ale = e.isArrayLiteralExp();
553 1
            return ale.elements ? ale.elements.dim : 0;
554
        }
555

556 0
        case TOK.assocArrayLiteral:
557
        {
558 0
            return e.isAssocArrayLiteralExp().keys.dim;
559
        }
560

561 0
        default:
562 0
            assert(0);
563
    }
564
}
565

566
/******************************
567
 * Helper for NewExp
568
 * Create an array literal consisting of 'elem' duplicated 'dim' times.
569
 * Params:
570
 *      pue = where to store result
571
 *      loc = source location where the interpretation occurs
572
 *      type = target type of the result
573
 *      elem = the source of array element, it will be owned by the result
574
 *      dim = element number of the result
575
 * Returns:
576
 *      Constructed ArrayLiteralExp
577
 */
578
ArrayLiteralExp createBlockDuplicatedArrayLiteral(UnionExp* pue, const ref Loc loc, Type type, Expression elem, size_t dim)
579
{
580 1
    if (type.ty == Tsarray && type.nextOf().ty == Tsarray && elem.type.ty != Tsarray)
581
    {
582
        // If it is a multidimensional array literal, do it recursively
583 1
        auto tsa = type.nextOf().isTypeSArray();
584 1
        const len = cast(size_t)tsa.dim.toInteger();
585 1
        UnionExp ue = void;
586 1
        elem = createBlockDuplicatedArrayLiteral(&ue, loc, type.nextOf(), elem, len);
587 1
        if (elem == ue.exp())
588 1
            elem = ue.copy();
589
    }
590

591
    // Buzilla 15681
592 1
    const tb = elem.type.toBasetype();
593 1
    const mustCopy = tb.ty == Tstruct || tb.ty == Tsarray;
594

595 1
    auto elements = new Expressions(dim);
596 1
    foreach (i, ref el; *elements)
597
    {
598 1
        el = mustCopy && i ? copyLiteral(elem).copy() : elem;
599
    }
600 1
    emplaceExp!(ArrayLiteralExp)(pue, loc, type, elements);
601 1
    auto ale = pue.exp().isArrayLiteralExp();
602 1
    ale.ownedByCtfe = OwnedBy.ctfe;
603 1
    return ale;
604
}
605

606
/******************************
607
 * Helper for NewExp
608
 * Create a string literal consisting of 'value' duplicated 'dim' times.
609
 */
610
StringExp createBlockDuplicatedStringLiteral(UnionExp* pue, const ref Loc loc, Type type, dchar value, size_t dim, ubyte sz)
611
{
612 1
    auto s = cast(char*)mem.xcalloc(dim, sz);
613 1
    foreach (elemi; 0 .. dim)
614
    {
615 1
        switch (sz)
616
        {
617 1
        case 1:
618 1
            s[elemi] = cast(char)value;
619 1
            break;
620 0
        case 2:
621 0
            (cast(wchar*)s)[elemi] = cast(wchar)value;
622 0
            break;
623 0
        case 4:
624 0
            (cast(dchar*)s)[elemi] = value;
625 0
            break;
626 0
        default:
627 0
            assert(0);
628
        }
629
    }
630 1
    emplaceExp!(StringExp)(pue, loc, s[0 .. dim * sz], dim, sz);
631 1
    auto se = pue.exp().isStringExp();
632 1
    se.type = type;
633 1
    se.committed = true;
634 1
    se.ownedByCtfe = OwnedBy.ctfe;
635 1
    return se;
636
}
637

638
// Return true if t is an AA
639
bool isAssocArray(Type t)
640
{
641 1
    return t.toBasetype().isTypeAArray() !is null;
642
}
643

644
// Given a template AA type, extract the corresponding built-in AA type
645
TypeAArray toBuiltinAAType(Type t)
646
{
647 1
    return t.toBasetype().isTypeAArray();
648
}
649

650
/************** TypeInfo operations ************************************/
651
// Return true if type is TypeInfo_Class
652
bool isTypeInfo_Class(const Type type)
653
{
654 1
    auto tc = cast()type.isTypeClass();
655 1
    return tc && (Type.dtypeinfo == tc.sym || Type.dtypeinfo.isBaseOf(tc.sym, null));
656
}
657

658
/************** Pointer operations ************************************/
659
// Return true if t is a pointer (not a function pointer)
660
bool isPointer(Type t)
661
{
662 1
    Type tb = t.toBasetype();
663 1
    return tb.ty == Tpointer && tb.nextOf().ty != Tfunction;
664
}
665

666
// For CTFE only. Returns true if 'e' is true or a non-null pointer.
667
bool isTrueBool(Expression e)
668
{
669 1
    return e.isBool(true) || ((e.type.ty == Tpointer || e.type.ty == Tclass) && e.op != TOK.null_);
670
}
671

672
/* Is it safe to convert from srcPointee* to destPointee* ?
673
 * srcPointee is the genuine type (never void).
674
 * destPointee may be void.
675
 */
676
bool isSafePointerCast(Type srcPointee, Type destPointee)
677
{
678
    // It's safe to cast S** to D** if it's OK to cast S* to D*
679 1
    while (srcPointee.ty == Tpointer && destPointee.ty == Tpointer)
680
    {
681 1
        srcPointee = srcPointee.nextOf();
682 1
        destPointee = destPointee.nextOf();
683
    }
684
    // It's OK if both are the same (modulo const)
685 1
    if (srcPointee.constConv(destPointee))
686 1
        return true;
687
    // It's OK if function pointers differ only in safe/pure/nothrow
688 1
    if (srcPointee.ty == Tfunction && destPointee.ty == Tfunction)
689 0
        return srcPointee.covariant(destPointee) == 1;
690
    // it's OK to cast to void*
691 1
    if (destPointee.ty == Tvoid)
692 1
        return true;
693
    // It's OK to cast from V[K] to void*
694 1
    if (srcPointee.ty == Taarray && destPointee == Type.tvoidptr)
695 0
        return true;
696
    // It's OK if they are the same size (static array of) integers, eg:
697
    //     int*     --> uint*
698
    //     int[5][] --> uint[5][]
699 1
    if (srcPointee.ty == Tsarray && destPointee.ty == Tsarray)
700
    {
701 1
        if (srcPointee.size() != destPointee.size())
702 0
            return false;
703 1
        srcPointee = srcPointee.baseElemOf();
704 1
        destPointee = destPointee.baseElemOf();
705
    }
706 1
    return srcPointee.isintegral() && destPointee.isintegral() && srcPointee.size() == destPointee.size();
707
}
708

709
Expression getAggregateFromPointer(Expression e, dinteger_t* ofs)
710
{
711 1
    *ofs = 0;
712 1
    if (auto ae = e.isAddrExp())
713 1
        e = ae.e1;
714 1
    if (auto soe = e.isSymOffExp())
715 1
        *ofs = soe.offset;
716 1
    if (auto dve = e.isDotVarExp())
717
    {
718 0
        const ex = dve.e1;
719 0
        const v = dve.var.isVarDeclaration();
720 0
        assert(v);
721 0
        StructLiteralExp se = (ex.op == TOK.classReference)
722 0
            ? (cast(ClassReferenceExp)ex).value
723 0
            : cast(StructLiteralExp)ex;
724

725
        // We can't use getField, because it makes a copy
726 0
        const i = (ex.op == TOK.classReference)
727 0
            ? (cast(ClassReferenceExp)ex).getFieldIndex(e.type, v.offset)
728 0
            : se.getFieldIndex(e.type, v.offset);
729 0
        e = (*se.elements)[i];
730
    }
731 1
    if (auto ie = e.isIndexExp())
732
    {
733
        // Note that each AA element is part of its own memory block
734 1
        if ((ie.e1.type.ty == Tarray || ie.e1.type.ty == Tsarray || ie.e1.op == TOK.string_ || ie.e1.op == TOK.arrayLiteral) && ie.e2.op == TOK.int64)
735
        {
736 1
            *ofs = ie.e2.toInteger();
737 1
            return ie.e1;
738
        }
739
    }
740 1
    if (auto se = e.isSliceExp())
741
    {
742 1
        if (se && e.type.toBasetype().ty == Tsarray &&
743 1
           (se.e1.type.ty == Tarray || se.e1.type.ty == Tsarray || se.e1.op == TOK.string_ || se.e1.op == TOK.arrayLiteral) && se.lwr.op == TOK.int64)
744
        {
745 1
            *ofs = se.lwr.toInteger();
746 1
            return se.e1;
747
        }
748
    }
749 1
    return e;
750
}
751

752
/** Return true if agg1 and agg2 are pointers to the same memory block
753
 */
754
bool pointToSameMemoryBlock(Expression agg1, Expression agg2)
755
{
756 1
    if (agg1 == agg2)
757 1
        return true;
758
    // For integers cast to pointers, we regard them as non-comparable
759
    // unless they are identical. (This may be overly strict).
760 1
    if (agg1.op == TOK.int64 && agg2.op == TOK.int64 && agg1.toInteger() == agg2.toInteger())
761
    {
762 0
        return true;
763
    }
764
    // Note that type painting can occur with VarExp, so we
765
    // must compare the variables being pointed to.
766 1
    if (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var)
767
    {
768 1
        return true;
769
    }
770 1
    if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset && (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var)
771
    {
772 1
        return true;
773
    }
774 1
    return false;
775
}
776

777
// return e1 - e2 as an integer, or error if not possible
778
UnionExp pointerDifference(const ref Loc loc, Type type, Expression e1, Expression e2)
779
{
780 1
    UnionExp ue = void;
781 1
    dinteger_t ofs1, ofs2;
782 1
    Expression agg1 = getAggregateFromPointer(e1, &ofs1);
783 1
    Expression agg2 = getAggregateFromPointer(e2, &ofs2);
784 1
    if (agg1 == agg2)
785
    {
786 1
        Type pointee = (cast(TypePointer)agg1.type).next;
787 1
        const sz = pointee.size();
788 1
        emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type);
789
    }
790 1
    else if (agg1.op == TOK.string_ && agg2.op == TOK.string_ &&
791 0
             (cast(StringExp)agg1).peekString().ptr == (cast(StringExp)agg2).peekString().ptr)
792
    {
793 0
        Type pointee = (cast(TypePointer)agg1.type).next;
794 0
        const sz = pointee.size();
795 0
        emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type);
796
    }
797 1
    else if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset &&
798 1
             (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var)
799
    {
800 1
        emplaceExp!(IntegerExp)(&ue, loc, ofs1 - ofs2, type);
801
    }
802
    else
803
    {
804 0
        error(loc, "`%s - %s` cannot be interpreted at compile time: cannot subtract pointers to two different memory blocks", e1.toChars(), e2.toChars());
805 0
        emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
806
    }
807 1
    return ue;
808
}
809

810
// Return eptr op e2, where eptr is a pointer, e2 is an integer,
811
// and op is TOK.add or TOK.min
812
UnionExp pointerArithmetic(const ref Loc loc, TOK op, Type type, Expression eptr, Expression e2)
813
{
814 1
    UnionExp ue;
815 1
    if (eptr.type.nextOf().ty == Tvoid)
816
    {
817 1
        error(loc, "cannot perform arithmetic on `void*` pointers at compile time");
818
    Lcant:
819 1
        emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
820 1
        return ue;
821
    }
822 1
    if (eptr.op == TOK.address)
823 1
        eptr = (cast(AddrExp)eptr).e1;
824 1
    dinteger_t ofs1;
825 1
    Expression agg1 = getAggregateFromPointer(eptr, &ofs1);
826 1
    if (agg1.op == TOK.symbolOffset)
827
    {
828 1
        if ((cast(SymOffExp)agg1).var.type.ty != Tsarray)
829
        {
830 0
            error(loc, "cannot perform pointer arithmetic on arrays of unknown length at compile time");
831 0
            goto Lcant;
832
        }
833
    }
834 1
    else if (agg1.op != TOK.string_ && agg1.op != TOK.arrayLiteral)
835
    {
836 0
        error(loc, "cannot perform pointer arithmetic on non-arrays at compile time");
837 0
        goto Lcant;
838
    }
839 1
    dinteger_t ofs2 = e2.toInteger();
840 1
    Type pointee = (cast(TypeNext)agg1.type.toBasetype()).next;
841 1
    dinteger_t sz = pointee.size();
842 1
    sinteger_t indx;
843 1
    dinteger_t len;
844 1
    if (agg1.op == TOK.symbolOffset)
845
    {
846 1
        indx = ofs1 / sz;
847 1
        len = (cast(TypeSArray)(cast(SymOffExp)agg1).var.type).dim.toInteger();
848
    }
849
    else
850
    {
851 1
        Expression dollar = ArrayLength(Type.tsize_t, agg1).copy();
852 1
        assert(!CTFEExp.isCantExp(dollar));
853 1
        indx = ofs1;
854 1
        len = dollar.toInteger();
855
    }
856 1
    if (op == TOK.add || op == TOK.addAssign || op == TOK.plusPlus)
857 1
        indx += ofs2 / sz;
858 1
    else if (op == TOK.min || op == TOK.minAssign || op == TOK.minusMinus)
859 1
        indx -= ofs2 / sz;
860
    else
861
    {
862 0
        error(loc, "CTFE internal error: bad pointer operation");
863 0
        goto Lcant;
864
    }
865 1
    if (indx < 0 || len < indx)
866
    {
867 1
        error(loc, "cannot assign pointer to index %lld inside memory block `[0..%lld]`", indx, len);
868 1
        goto Lcant;
869
    }
870 1
    if (agg1.op == TOK.symbolOffset)
871
    {
872 1
        emplaceExp!(SymOffExp)(&ue, loc, (cast(SymOffExp)agg1).var, indx * sz);
873 1
        SymOffExp se = cast(SymOffExp)ue.exp();
874 1
        se.type = type;
875 1
        return ue;
876
    }
877 1
    if (agg1.op != TOK.arrayLiteral && agg1.op != TOK.string_)
878
    {
879 0
        error(loc, "CTFE internal error: pointer arithmetic `%s`", agg1.toChars());
880 0
        goto Lcant;
881
    }
882 1
    if (eptr.type.toBasetype().ty == Tsarray)
883
    {
884 1
        dinteger_t dim = (cast(TypeSArray)eptr.type.toBasetype()).dim.toInteger();
885
        // Create a CTFE pointer &agg1[indx .. indx+dim]
886 1
        auto se = ctfeEmplaceExp!SliceExp(loc, agg1,
887
                ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t),
888
                ctfeEmplaceExp!IntegerExp(loc, indx + dim, Type.tsize_t));
889 1
        se.type = type.toBasetype().nextOf();
890 1
        emplaceExp!(AddrExp)(&ue, loc, se);
891 1
        ue.exp().type = type;
892 1
        return ue;
893
    }
894
    // Create a CTFE pointer &agg1[indx]
895 1
    auto ofs = ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t);
896 1
    Expression ie = ctfeEmplaceExp!IndexExp(loc, agg1, ofs);
897 1
    ie.type = type.toBasetype().nextOf(); // https://issues.dlang.org/show_bug.cgi?id=13992
898 1
    emplaceExp!(AddrExp)(&ue, loc, ie);
899 1
    ue.exp().type = type;
900 1
    return ue;
901
}
902

903
// Return 1 if true, 0 if false
904
// -1 if comparison is illegal because they point to non-comparable memory blocks
905
int comparePointers(TOK op, Expression agg1, dinteger_t ofs1, Expression agg2, dinteger_t ofs2)
906
{
907 1
    if (pointToSameMemoryBlock(agg1, agg2))
908
    {
909 1
        int n;
910 1
        switch (op)
911
        {
912 1
        case TOK.lessThan:
913 1
            n = (ofs1 < ofs2);
914 1
            break;
915 1
        case TOK.lessOrEqual:
916 1
            n = (ofs1 <= ofs2);
917 1
            break;
918 1
        case TOK.greaterThan:
919 1
            n = (ofs1 > ofs2);
920 1
            break;
921 1
        case TOK.greaterOrEqual:
922 1
            n = (ofs1 >= ofs2);
923 1
            break;
924 1
        case TOK.identity:
925 1
        case TOK.equal:
926 1
            n = (ofs1 == ofs2);
927 1
            break;
928 1
        case TOK.notIdentity:
929 1
        case TOK.notEqual:
930 1
            n = (ofs1 != ofs2);
931 1
            break;
932 0
        default:
933 0
            assert(0);
934
        }
935 1
        return n;
936
    }
937 1
    const null1 = (agg1.op == TOK.null_);
938 1
    const null2 = (agg2.op == TOK.null_);
939 1
    int cmp;
940 1
    if (null1 || null2)
941
    {
942 1
        switch (op)
943
        {
944 0
        case TOK.lessThan:
945 0
            cmp = null1 && !null2;
946 0
            break;
947 1
        case TOK.greaterThan:
948 1
            cmp = !null1 && null2;
949 1
            break;
950 1
        case TOK.lessOrEqual:
951 1
            cmp = null1;
952 1
            break;
953 1
        case TOK.greaterOrEqual:
954 1
            cmp = null2;
955 1
            break;
956 1
        case TOK.identity:
957 1
        case TOK.equal:
958 1
        case TOK.notIdentity: // 'cmp' gets inverted below
959 1
        case TOK.notEqual:
960 1
            cmp = (null1 == null2);
961 1
            break;
962 0
        default:
963 0
            assert(0);
964
        }
965
    }
966
    else
967
    {
968 1
        switch (op)
969
        {
970 1
        case TOK.identity:
971 1
        case TOK.equal:
972 1
        case TOK.notIdentity: // 'cmp' gets inverted below
973 1
        case TOK.notEqual:
974 1
            cmp = 0;
975 1
            break;
976 1
        default:
977 1
            return -1; // memory blocks are different
978
        }
979
    }
980 1
    if (op == TOK.notIdentity || op == TOK.notEqual)
981 1
        cmp ^= 1;
982 1
    return cmp;
983
}
984

985
// True if conversion from type 'from' to 'to' involves a reinterpret_cast
986
// floating point -> integer or integer -> floating point
987
bool isFloatIntPaint(Type to, Type from)
988
{
989 1
    return from.size() == to.size() && (from.isintegral() && to.isfloating() || from.isfloating() && to.isintegral());
990
}
991

992
// Reinterpret float/int value 'fromVal' as a float/integer of type 'to'.
993
Expression paintFloatInt(UnionExp* pue, Expression fromVal, Type to)
994
{
995 1
    if (exceptionOrCantInterpret(fromVal))
996 0
        return fromVal;
997 1
    assert(to.size() == 4 || to.size() == 8);
998 1
    return Compiler.paintAsType(pue, fromVal, to);
999
}
1000

1001
/******** Constant folding, with support for CTFE ***************************/
1002
/// Return true if non-pointer expression e can be compared
1003
/// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity
1004
bool isCtfeComparable(Expression e)
1005
{
1006 1
    if (e.op == TOK.slice)
1007 1
        e = (cast(SliceExp)e).e1;
1008 1
    if (e.isConst() != 1)
1009
    {
1010 1
        if (e.op == TOK.null_ || e.op == TOK.string_ || e.op == TOK.function_ || e.op == TOK.delegate_ || e.op == TOK.arrayLiteral || e.op == TOK.structLiteral || e.op == TOK.assocArrayLiteral || e.op == TOK.classReference)
1011
        {
1012 1
            return true;
1013
        }
1014
        // https://issues.dlang.org/show_bug.cgi?id=14123
1015
        // TypeInfo object is comparable in CTFE
1016 1
        if (e.op == TOK.typeid_)
1017 1
            return true;
1018 0
        return false;
1019
    }
1020 1
    return true;
1021
}
1022

1023
/// Map TOK comparison ops
1024
private bool numCmp(N)(TOK op, N n1, N n2)
1025
{
1026 1
    switch (op)
1027
    {
1028 1
    case TOK.lessThan:
1029 1
        return n1 < n2;
1030 1
    case TOK.lessOrEqual:
1031 1
        return n1 <= n2;
1032 1
    case TOK.greaterThan:
1033 1
        return n1 > n2;
1034 1
    case TOK.greaterOrEqual:
1035 1
        return n1 >= n2;
1036

1037 0
    default:
1038 0
        assert(0);
1039
    }
1040
}
1041

1042
/// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1043
bool specificCmp(TOK op, int rawCmp)
1044
{
1045 0
    return numCmp!int(op, rawCmp, 0);
1046
}
1047

1048
/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1049
bool intUnsignedCmp(TOK op, dinteger_t n1, dinteger_t n2)
1050
{
1051 1
    return numCmp!dinteger_t(op, n1, n2);
1052
}
1053

1054
/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1055
bool intSignedCmp(TOK op, sinteger_t n1, sinteger_t n2)
1056
{
1057 1
    return numCmp!sinteger_t(op, n1, n2);
1058
}
1059

1060
/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1061
bool realCmp(TOK op, real_t r1, real_t r2)
1062
{
1063
    // Don't rely on compiler, handle NAN arguments separately
1064 1
    if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered
1065
    {
1066 1
        switch (op)
1067
        {
1068 1
        case TOK.lessThan:
1069 1
        case TOK.lessOrEqual:
1070 1
        case TOK.greaterThan:
1071 1
        case TOK.greaterOrEqual:
1072 1
            return false;
1073

1074 0
        default:
1075 0
            assert(0);
1076
        }
1077
    }
1078
    else
1079
    {
1080 1
        return numCmp!real_t(op, r1, r2);
1081
    }
1082
}
1083

1084
/* Conceptually the same as memcmp(e1, e2).
1085
 * e1 and e2 may be strings, arrayliterals, or slices.
1086
 * For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
1087
 * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
1088
 * Returns:
1089
 *      -1,0,1
1090
 */
1091
private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinteger_t len)
1092
{
1093
    // Resolve slices, if necessary
1094 1
    uinteger_t lo1 = 0;
1095 1
    uinteger_t lo2 = 0;
1096

1097 1
    Expression x1 = e1;
1098 1
    if (auto sle1 = x1.isSliceExp())
1099
    {
1100 1
        lo1 = sle1.lwr.toInteger();
1101 1
        x1 = sle1.e1;
1102
    }
1103 1
    auto se1 = x1.isStringExp();
1104 1
    auto ae1 = x1.isArrayLiteralExp();
1105

1106 1
    Expression x2 = e2;
1107 1
    if (auto sle2 = x2.isSliceExp())
1108
    {
1109 1
        lo2 = sle2.lwr.toInteger();
1110 1
        x2 = sle2.e1;
1111
    }
1112 1
    auto se2 = x2.isStringExp();
1113 1
    auto ae2 = x2.isArrayLiteralExp();
1114

1115
    // Now both must be either TOK.arrayLiteral or TOK.string_
1116 1
    if (se1 && se2)
1117 1
        return sliceCmpStringWithString(se1, se2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len);
1118 1
    if (se1 && ae2)
1119 0
        return sliceCmpStringWithArray(se1, ae2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len);
1120 1
    if (se2 && ae1)
1121 0
        return -sliceCmpStringWithArray(se2, ae1, cast(size_t)lo2, cast(size_t)lo1, cast(size_t)len);
1122 1
    assert(ae1 && ae2);
1123
    // Comparing two array literals. This case is potentially recursive.
1124
    // If they aren't strings, we just need an equality check rather than
1125
    // a full cmp.
1126 1
    const bool needCmp = ae1.type.nextOf().isintegral();
1127 1
    foreach (size_t i; 0 .. cast(size_t)len)
1128
    {
1129 1
        Expression ee1 = (*ae1.elements)[cast(size_t)(lo1 + i)];
1130 1
        Expression ee2 = (*ae2.elements)[cast(size_t)(lo2 + i)];
1131 1
        if (needCmp)
1132
        {
1133 1
            const sinteger_t c = ee1.toInteger() - ee2.toInteger();
1134 1
            if (c > 0)
1135 1
                return 1;
1136 1
            if (c < 0)
1137 1
                return -1;
1138
        }
1139
        else
1140
        {
1141 1
            if (ctfeRawCmp(loc, ee1, ee2))
1142 1
                return 1;
1143
        }
1144
    }
1145 1
    return 0;
1146
}
1147

1148
/* Given a delegate expression e, return .funcptr.
1149
 * If e is NullExp, return NULL.
1150
 */
1151
private FuncDeclaration funcptrOf(Expression e)
1152
{
1153 1
    assert(e.type.ty == Tdelegate);
1154 1
    if (auto de = e.isDelegateExp())
1155 1
        return de.func;
1156 1
    if (auto fe = e.isFuncExp())
1157 1
        return fe.fd;
1158 0
    assert(e.op == TOK.null_);
1159 0
    return null;
1160
}
1161

1162
private bool isArray(const Expression e)
1163
{
1164 1
    return e.op == TOK.arrayLiteral || e.op == TOK.string_ || e.op == TOK.slice || e.op == TOK.null_;
1165
}
1166

1167
/*****
1168
 * Params:
1169
 *      loc = source file location
1170
 *      e1 = left operand
1171
 *      e2 = right operand
1172
 *      identity = true for `is` identity comparisons
1173
 * Returns:
1174
 * For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
1175
 * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
1176
 */
1177
private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool identity = false)
1178
{
1179 1
    if (e1.op == TOK.classReference || e2.op == TOK.classReference)
1180
    {
1181 1
        if (e1.op == TOK.classReference && e2.op == TOK.classReference &&
1182 1
            (cast(ClassReferenceExp)e1).value == (cast(ClassReferenceExp)e2).value)
1183 1
            return 0;
1184 1
        return 1;
1185
    }
1186 1
    if (e1.op == TOK.typeid_ && e2.op == TOK.typeid_)
1187
    {
1188
        // printf("e1: %s\n", e1.toChars());
1189
        // printf("e2: %s\n", e2.toChars());
1190 1
        Type t1 = isType((cast(TypeidExp)e1).obj);
1191 1
        Type t2 = isType((cast(TypeidExp)e2).obj);
1192 1
        assert(t1);
1193 1
        assert(t2);
1194 1
        return t1 != t2;
1195
    }
1196
    // null == null, regardless of type
1197 1
    if (e1.op == TOK.null_ && e2.op == TOK.null_)
1198 1
        return 0;
1199 1
    if (e1.type.ty == Tpointer && e2.type.ty == Tpointer)
1200
    {
1201
        // Can only be an equality test.
1202 1
        dinteger_t ofs1, ofs2;
1203 1
        Expression agg1 = getAggregateFromPointer(e1, &ofs1);
1204 1
        Expression agg2 = getAggregateFromPointer(e2, &ofs2);
1205 1
        if ((agg1 == agg2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var))
1206
        {
1207 1
            if (ofs1 == ofs2)
1208 1
                return 0;
1209
        }
1210 0
        return 1;
1211
    }
1212 1
    if (e1.type.ty == Tdelegate && e2.type.ty == Tdelegate)
1213
    {
1214
        // If .funcptr isn't the same, they are not equal
1215 1
        if (funcptrOf(e1) != funcptrOf(e2))
1216 1
            return 1;
1217
        // If both are delegate literals, assume they have the
1218
        // same closure pointer. TODO: We don't support closures yet!
1219 1
        if (e1.op == TOK.function_ && e2.op == TOK.function_)
1220 1
            return 0;
1221 1
        assert(e1.op == TOK.delegate_ && e2.op == TOK.delegate_);
1222
        // Same .funcptr. Do they have the same .ptr?
1223 1
        Expression ptr1 = (cast(DelegateExp)e1).e1;
1224 1
        Expression ptr2 = (cast(DelegateExp)e2).e1;
1225 1
        dinteger_t ofs1, ofs2;
1226 1
        Expression agg1 = getAggregateFromPointer(ptr1, &ofs1);
1227 1
        Expression agg2 = getAggregateFromPointer(ptr2, &ofs2);
1228
        // If they are TOK.variable, it means they are FuncDeclarations
1229 1
        if ((agg1 == agg2 && ofs1 == ofs2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var))
1230
        {
1231 1
            return 0;
1232
        }
1233 1
        return 1;
1234
    }
1235 1
    if (isArray(e1) && isArray(e2))
1236
    {
1237 1
        const uinteger_t len1 = resolveArrayLength(e1);
1238 1
        const uinteger_t len2 = resolveArrayLength(e2);
1239
        // workaround for dmc optimizer bug calculating wrong len for
1240
        // uinteger_t len = (len1 < len2 ? len1 : len2);
1241
        // if (len == 0) ...
1242 1
        if (len1 > 0 && len2 > 0)
1243
        {
1244 1
            const uinteger_t len = (len1 < len2 ? len1 : len2);
1245 1
            const int res = ctfeCmpArrays(loc, e1, e2, len);
1246 1
            if (res != 0)
1247 1
                return res;
1248
        }
1249 1
        return cast(int)(len1 - len2);
1250
    }
1251 1
    if (e1.type.isintegral())
1252
    {
1253 1
        return e1.toInteger() != e2.toInteger();
1254
    }
1255 1
    if (e1.type.isreal() || e1.type.isimaginary())
1256
    {
1257 1
        real_t r1 = e1.type.isreal() ? e1.toReal() : e1.toImaginary();
1258 1
        real_t r2 = e1.type.isreal() ? e2.toReal() : e2.toImaginary();
1259 1
        if (identity)
1260 1
            return !RealIdentical(r1, r2);
1261 1
        if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered
1262
        {
1263 1
            return 1;   // they are not equal
1264
        }
1265
        else
1266
        {
1267 1
            return (r1 != r2);
1268
        }
1269
    }
1270 1
    else if (e1.type.iscomplex())
1271
    {
1272 1
        auto c1 = e1.toComplex();
1273 1
        auto c2 = e2.toComplex();
1274 1
        if (identity)
1275
        {
1276 1
            return !RealIdentical(c1.re, c2.re) && !RealIdentical(c1.im, c2.im);
1277
        }
1278 1
        return c1 != c2;
1279
    }
1280 1
    if (e1.op == TOK.structLiteral && e2.op == TOK.structLiteral)
1281
    {
1282 1
        StructLiteralExp es1 = cast(StructLiteralExp)e1;
1283 1
        StructLiteralExp es2 = cast(StructLiteralExp)e2;
1284
        // For structs, we only need to return 0 or 1 (< and > aren't legal).
1285 1
        if (es1.sd != es2.sd)
1286 0
            return 1;
1287 1
        else if ((!es1.elements || !es1.elements.dim) && (!es2.elements || !es2.elements.dim))
1288 1
            return 0; // both arrays are empty
1289 1
        else if (!es1.elements || !es2.elements)
1290 0
            return 1;
1291 1
        else if (es1.elements.dim != es2.elements.dim)
1292 0
            return 1;
1293
        else
1294
        {
1295 1
            foreach (size_t i; 0 .. es1.elements.dim)
1296
            {
1297 1
                Expression ee1 = (*es1.elements)[i];
1298 1
                Expression ee2 = (*es2.elements)[i];
1299

1300
                // https://issues.dlang.org/show_bug.cgi?id=16284
1301 1
                if (ee1.op == TOK.void_ && ee2.op == TOK.void_) // if both are VoidInitExp
1302 0
                    continue;
1303

1304 1
                if (ee1 == ee2)
1305 1
                    continue;
1306 1
                if (!ee1 || !ee2)
1307 0
                    return 1;
1308 1
                const int cmp = ctfeRawCmp(loc, ee1, ee2, identity);
1309 1
                if (cmp)
1310 1
                    return 1;
1311
            }
1312 1
            return 0; // All elements are equal
1313
        }
1314
    }
1315 1
    if (e1.op == TOK.assocArrayLiteral && e2.op == TOK.assocArrayLiteral)
1316
    {
1317 1
        AssocArrayLiteralExp es1 = cast(AssocArrayLiteralExp)e1;
1318 1
        AssocArrayLiteralExp es2 = cast(AssocArrayLiteralExp)e2;
1319 1
        size_t dim = es1.keys.dim;
1320 1
        if (es2.keys.dim != dim)
1321 1
            return 1;
1322 1
        bool* used = cast(bool*)mem.xmalloc(bool.sizeof * dim);
1323 1
        memset(used, 0, bool.sizeof * dim);
1324 1
        foreach (size_t i; 0 .. dim)
1325
        {
1326 1
            Expression k1 = (*es1.keys)[i];
1327 1
            Expression v1 = (*es1.values)[i];
1328 1
            Expression v2 = null;
1329 1
            foreach (size_t j; 0 .. dim)
1330
            {
1331 1
                if (used[j])
1332 1
                    continue;
1333 1
                Expression k2 = (*es2.keys)[j];
1334 1
                if (ctfeRawCmp(loc, k1, k2, identity))
1335 1
                    continue;
1336 1
                used[j] = true;
1337 1
                v2 = (*es2.values)[j];
1338 1
                break;
1339
            }
1340 1
            if (!v2 || ctfeRawCmp(loc, v1, v2, identity))
1341
            {
1342 1
                mem.xfree(used);
1343 1
                return 1;
1344
            }
1345
        }
1346 1
        mem.xfree(used);
1347 1
        return 0;
1348
    }
1349 0
    error(loc, "CTFE internal error: bad compare of `%s` and `%s`", e1.toChars(), e2.toChars());
1350 0
    assert(0);
1351
}
1352

1353
/// Evaluate ==, !=.  Resolves slices before comparing. Returns 0 or 1
1354
bool ctfeEqual(const ref Loc loc, TOK op, Expression e1, Expression e2)
1355
{
1356 1
    return !ctfeRawCmp(loc, e1, e2) ^ (op == TOK.notEqual);
1357
}
1358

1359
/// Evaluate is, !is.  Resolves slices before comparing. Returns 0 or 1
1360
bool ctfeIdentity(const ref Loc loc, TOK op, Expression e1, Expression e2)
1361
{
1362
    //printf("ctfeIdentity %s %s\n", e1.toChars(), e2.toChars());
1363
    //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", Token::toChars(op),
1364
    //    Token::toChars(e1.op), e1.toChars(), Token::toChars(e2.op), e1.toChars());
1365 1
    bool cmp;
1366 1
    if (e1.op == TOK.null_)
1367
    {
1368 1
        cmp = (e2.op == TOK.null_);
1369
    }
1370 1
    else if (e2.op == TOK.null_)
1371
    {
1372 1
        cmp = false;
1373
    }
1374 1
    else if (e1.op == TOK.symbolOffset && e2.op == TOK.symbolOffset)
1375
    {
1376 0
        SymOffExp es1 = cast(SymOffExp)e1;
1377 0
        SymOffExp es2 = cast(SymOffExp)e2;
1378 0
        cmp = (es1.var == es2.var && es1.offset == es2.offset);
1379
    }
1380 1
    else if (e1.type.isreal())
1381 0
        cmp = RealIdentical(e1.toReal(), e2.toReal());
1382 1
    else if (e1.type.isimaginary())
1383 0
        cmp = RealIdentical(e1.toImaginary(), e2.toImaginary());
1384 1
    else if (e1.type.iscomplex())
1385
    {
1386 0
        complex_t v1 = e1.toComplex();
1387 0
        complex_t v2 = e2.toComplex();
1388 0
        cmp = RealIdentical(creall(v1), creall(v2)) && RealIdentical(cimagl(v1), cimagl(v1));
1389
    }
1390
    else
1391
    {
1392 1
        cmp = !ctfeRawCmp(loc, e1, e2, true);
1393
    }
1394 1
    if (op == TOK.notIdentity || op == TOK.notEqual)
1395 1
        cmp ^= true;
1396 1
    return cmp;
1397
}
1398

1399
/// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1
1400
bool ctfeCmp(const ref Loc loc, TOK op, Expression e1, Expression e2)
1401
{
1402 1
    Type t1 = e1.type.toBasetype();
1403 1
    Type t2 = e2.type.toBasetype();
1404

1405 1
    if (t1.isString() && t2.isString())
1406 0
        return specificCmp(op, ctfeRawCmp(loc, e1, e2));
1407 1
    else if (t1.isreal())
1408 1
        return realCmp(op, e1.toReal(), e2.toReal());
1409 1
    else if (t1.isimaginary())
1410 0
        return realCmp(op, e1.toImaginary(), e2.toImaginary());
1411 1
    else if (t1.isunsigned() || t2.isunsigned())
1412 1
        return intUnsignedCmp(op, e1.toInteger(), e2.toInteger());
1413
    else
1414 1
        return intSignedCmp(op, e1.toInteger(), e2.toInteger());
1415
}
1416

1417
UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2)
1418
{
1419 1
    Type t1 = e1.type.toBasetype();
1420 1
    Type t2 = e2.type.toBasetype();
1421 1
    UnionExp ue;
1422 1
    if (e2.op == TOK.string_ && e1.op == TOK.arrayLiteral && t1.nextOf().isintegral())
1423
    {
1424
        // [chars] ~ string => string (only valid for CTFE)
1425 1
        StringExp es1 = cast(StringExp)e2;
1426 1
        ArrayLiteralExp es2 = cast(ArrayLiteralExp)e1;
1427 1
        const len = es1.len + es2.elements.dim;
1428 1
        const sz = es1.sz;
1429 1
        void* s = mem.xmalloc((len + 1) * sz);
1430 1
        const data1 = es1.peekData();
1431 1
        memcpy(cast(char*)s + sz * es2.elements.dim, data1.ptr, data1.length);
1432 1
        foreach (size_t i; 0 .. es2.elements.dim)
1433
        {
1434 1
            Expression es2e = (*es2.elements)[i];
1435 1
            if (es2e.op != TOK.int64)
1436
            {
1437 0
                emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
1438 0
                return ue;
1439
            }
1440 1
            dinteger_t v = es2e.toInteger();
1441 1
            Port.valcpy(cast(char*)s + i * sz, v, sz);
1442
        }
1443
        // Add terminating 0
1444 1
        memset(cast(char*)s + len * sz, 0, sz);
1445 1
        emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
1446 1
        StringExp es = cast(StringExp)ue.exp();
1447 1
        es.committed = 0;
1448 1
        es.type = type;
1449 1
        return ue;
1450
    }
1451 1
    if (e1.op == TOK.string_ && e2.op == TOK.arrayLiteral && t2.nextOf().isintegral())
1452
    {
1453
        // string ~ [chars] => string (only valid for CTFE)
1454
        // Concatenate the strings
1455 1
        StringExp es1 = cast(StringExp)e1;
1456 1
        ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2;
1457 1
        const len = es1.len + es2.elements.dim;
1458 1
        const sz = es1.sz;
1459 1
        void* s = mem.xmalloc((len + 1) * sz);
1460 1
        auto slice = es1.peekData();
1461 1
        memcpy(s, slice.ptr, slice.length);
1462 1
        foreach (size_t i; 0 .. es2.elements.dim)
1463
        {
1464 1
            Expression es2e = (*es2.elements)[i];
1465 1
            if (es2e.op != TOK.int64)
1466
            {
1467 0
                emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
1468 0
                return ue;
1469
            }
1470 1
            const v = es2e.toInteger();
1471 1
            Port.valcpy(cast(char*)s + (es1.len + i) * sz, v, sz);
1472
        }
1473
        // Add terminating 0
1474 1
        memset(cast(char*)s + len * sz, 0, sz);
1475 1
        emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
1476 1
        StringExp es = cast(StringExp)ue.exp();
1477 1
        es.sz = sz;
1478 1
        es.committed = 0; //es1.committed;
1479 1
        es.type = type;
1480 1
        return ue;
1481
    }
1482 1
    if (e1.op == TOK.arrayLiteral && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
1483
    {
1484
        //  [ e1 ] ~ [ e2 ] ---> [ e1, e2 ]
1485 1
        ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1;
1486 1
        ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2;
1487 1
        emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, type, copyLiteralArray(es1.elements));
1488 1
        es1 = cast(ArrayLiteralExp)ue.exp();
1489 1
        es1.elements.insert(es1.elements.dim, copyLiteralArray(es2.elements));
1490 1
        return ue;
1491
    }
1492 1
    if (e1.op == TOK.arrayLiteral && e2.op == TOK.null_ && t1.nextOf().equals(t2.nextOf()))
1493
    {
1494
        //  [ e1 ] ~ null ----> [ e1 ].dup
1495 1
        ue = paintTypeOntoLiteralCopy(type, copyLiteral(e1).copy());
1496 1
        return ue;
1497
    }
1498 1
    if (e1.op == TOK.null_ && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
1499
    {
1500
        //  null ~ [ e2 ] ----> [ e2 ].dup
1501 1
        ue = paintTypeOntoLiteralCopy(type, copyLiteral(e2).copy());
1502 1
        return ue;
1503
    }
1504 1
    ue = Cat(type, e1, e2);
1505 1
    return ue;
1506
}
1507

1508
/*  Given an AA literal 'ae', and a key 'e2':
1509
 *  Return ae[e2] if present, or NULL if not found.
1510
 */
1511
Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, Expression e2)
1512
{
1513
    /* Search the keys backwards, in case there are duplicate keys
1514
     */
1515 1
    for (size_t i = ae.keys.dim; i;)
1516
    {
1517 1
        --i;
1518 1
        Expression ekey = (*ae.keys)[i];
1519 1
        const int eq = ctfeEqual(loc, TOK.equal, ekey, e2);
1520 1
        if (eq)
1521
        {
1522 1
            return (*ae.values)[i];
1523
        }
1524
    }
1525 1
    return null;
1526
}
1527

1528
/* Same as for constfold.Index, except that it only works for static arrays,
1529
 * dynamic arrays, and strings. We know that e1 is an
1530
 * interpreted CTFE expression, so it cannot have side-effects.
1531
 */
1532
Expression ctfeIndex(UnionExp* pue, const ref Loc loc, Type type, Expression e1, uinteger_t indx)
1533
{
1534
    //printf("ctfeIndex(e1 = %s)\n", e1.toChars());
1535 1
    assert(e1.type);
1536 1
    if (auto es1 = e1.isStringExp())
1537
    {
1538 1
        if (indx >= es1.len)
1539
        {
1540 0
            error(loc, "string index %llu is out of bounds `[0 .. %llu]`", indx, cast(ulong)es1.len);
1541 0
            return CTFEExp.cantexp;
1542
        }
1543 1
        emplaceExp!IntegerExp(pue, loc, es1.charAt(indx), type);
1544 1
        return pue.exp();
1545
    }
1546

1547 1
    if (auto ale = e1.isArrayLiteralExp())
1548
    {
1549 1
        if (indx >= ale.elements.dim)
1550
        {
1551 0
            error(loc, "array index %llu is out of bounds `%s[0 .. %llu]`", indx, e1.toChars(), cast(ulong)ale.elements.dim);
1552 0
            return CTFEExp.cantexp;
1553
        }
1554 1
        Expression e = (*ale.elements)[cast(size_t)indx];
1555 1
        return paintTypeOntoLiteral(pue, type, e);
1556
    }
1557

1558 0
    assert(0);
1559
}
1560

1561
Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expression e)
1562
{
1563
    Expression paint()
1564
    {
1565 1
        return paintTypeOntoLiteral(pue, to, e);
1566
    }
1567

1568 1
    if (e.op == TOK.null_)
1569 1
        return paint();
1570

1571 1
    if (e.op == TOK.classReference)
1572
    {
1573
        // Disallow reinterpreting class casts. Do this by ensuring that
1574
        // the original class can implicitly convert to the target class
1575 1
        ClassDeclaration originalClass = (cast(ClassReferenceExp)e).originalClass();
1576 1
        if (originalClass.type.implicitConvTo(to.mutableOf()))
1577 1
            return paint();
1578
        else
1579
        {
1580 1
            emplaceExp!(NullExp)(pue, loc, to);
1581 1
            return pue.exp();
1582
        }
1583
    }
1584

1585
    // Allow TypeInfo type painting
1586 1
    if (isTypeInfo_Class(e.type) && e.type.implicitConvTo(to))
1587 1
        return paint();
1588

1589
    // Allow casting away const for struct literals
1590 1
    if (e.op == TOK.structLiteral && e.type.toBasetype().castMod(0) == to.toBasetype().castMod(0))
1591 1
        return paint();
1592

1593 1
    Expression r;
1594 1
    if (e.type.equals(type) && type.equals(to))
1595
    {
1596
        // necessary not to change e's address for pointer comparisons
1597 1
        r = e;
1598
    }
1599 1
    else if (to.toBasetype().ty == Tarray &&
1600 1
             type.toBasetype().ty == Tarray &&
1601 1
             to.toBasetype().nextOf().size() == type.toBasetype().nextOf().size())
1602
    {
1603
        // https://issues.dlang.org/show_bug.cgi?id=12495
1604
        // Array reinterpret casts: eg. string to immutable(ubyte)[]
1605 1
        return paint();
1606
    }
1607
    else
1608
    {
1609 1
        *pue = Cast(loc, type, to, e);
1610 1
        r = pue.exp();
1611
    }
1612

1613 1
    if (CTFEExp.isCantExp(r))
1614 1
        error(loc, "cannot cast `%s` to `%s` at compile time", e.toChars(), to.toChars());
1615

1616 1
    if (auto ae = e.isArrayLiteralExp())
1617 1
        ae.ownedByCtfe = OwnedBy.ctfe;
1618

1619 1
    if (auto se = e.isStringExp())
1620 1
        se.ownedByCtfe = OwnedBy.ctfe;
1621

1622 1
    return r;
1623
}
1624

1625
/******** Assignment helper functions ***************************/
1626
/* Set dest = src, where both dest and src are container value literals
1627
 * (ie, struct literals, or static arrays (can be an array literal or a string))
1628
 * Assignment is recursively in-place.
1629
 * Purpose: any reference to a member of 'dest' will remain valid after the
1630
 * assignment.
1631
 */
1632
void assignInPlace(Expression dest, Expression src)
1633
{
1634 1
    if (!(dest.op == TOK.structLiteral || dest.op == TOK.arrayLiteral || dest.op == TOK.string_))
1635
    {
1636 0
        printf("invalid op %d %d\n", src.op, dest.op);
1637 0
        assert(0);
1638
    }
1639 1
    Expressions* oldelems;
1640 1
    Expressions* newelems;
1641 1
    if (dest.op == TOK.structLiteral)
1642
    {
1643 1
        assert(dest.op == src.op);
1644 1
        oldelems = (cast(StructLiteralExp)dest).elements;
1645 1
        newelems = (cast(StructLiteralExp)src).elements;
1646 1
        auto sd = (cast(StructLiteralExp)dest).sd;
1647 1
        const nfields = sd.nonHiddenFields();
1648 1
        const nvthis = sd.fields.dim - nfields;
1649 1
        if (nvthis && oldelems.dim >= nfields && oldelems.dim < newelems.dim)
1650 0
            foreach (_; 0 .. newelems.dim - oldelems.dim)
1651 0
                oldelems.push(null);
1652
    }
1653 1
    else if (dest.op == TOK.arrayLiteral && src.op == TOK.arrayLiteral)
1654
    {
1655 1
        oldelems = (cast(ArrayLiteralExp)dest).elements;
1656 1
        newelems = (cast(ArrayLiteralExp)src).elements;
1657
    }
1658 1
    else if (dest.op == TOK.string_ && src.op == TOK.string_)
1659
    {
1660 1
        sliceAssignStringFromString(cast(StringExp)dest, cast(StringExp)src, 0);
1661 1
        return;
1662
    }
1663 1
    else if (dest.op == TOK.arrayLiteral && src.op == TOK.string_)
1664
    {
1665 0
        sliceAssignArrayLiteralFromString(cast(ArrayLiteralExp)dest, cast(StringExp)src, 0);
1666 0
        return;
1667
    }
1668 1
    else if (src.op == TOK.arrayLiteral && dest.op == TOK.string_)
1669
    {
1670 1
        sliceAssignStringFromArrayLiteral(cast(StringExp)dest, cast(ArrayLiteralExp)src, 0);
1671 1
        return;
1672
    }
1673
    else
1674
    {
1675 0
        printf("invalid op %d %d\n", src.op, dest.op);
1676 0
        assert(0);
1677
    }
1678 1
    assert(oldelems.dim == newelems.dim);
1679 1
    foreach (size_t i; 0 .. oldelems.dim)
1680
    {
1681 1
        Expression e = (*newelems)[i];
1682 1
        Expression o = (*oldelems)[i];
1683 1
        if (e.op == TOK.structLiteral)
1684
        {
1685 1
            assert(o.op == e.op);
1686 1
            assignInPlace(o, e);
1687
        }
1688 1
        else if (e.type.ty == Tsarray && e.op != TOK.void_ && o.type.ty == Tsarray)
1689
        {
1690 1
            assignInPlace(o, e);
1691
        }
1692
        else
1693
        {
1694 1
            (*oldelems)[i] = (*newelems)[i];
1695
        }
1696
    }
1697
}
1698

1699
// Given an AA literal aae,  set aae[index] = newval and return newval.
1700
Expression assignAssocArrayElement(const ref Loc loc, AssocArrayLiteralExp aae, Expression index, Expression newval)
1701
{
1702
    /* Create new associative array literal reflecting updated key/value
1703
     */
1704 1
    Expressions* keysx = aae.keys;
1705 1
    Expressions* valuesx = aae.values;
1706 1
    int updated = 0;
1707 1
    for (size_t j = valuesx.dim; j;)
1708
    {
1709 1
        j--;
1710 1
        Expression ekey = (*aae.keys)[j];
1711 1
        int eq = ctfeEqual(loc, TOK.equal, ekey, index);
1712 1
        if (eq)
1713
        {
1714 1
            (*valuesx)[j] = newval;
1715 1
            updated = 1;
1716
        }
1717
    }
1718 1
    if (!updated)
1719
    {
1720
        // Append index/newval to keysx[]/valuesx[]
1721 1
        valuesx.push(newval);
1722 1
        keysx.push(index);
1723
    }
1724 1
    return newval;
1725
}
1726

1727
/// Given array literal oldval of type ArrayLiteralExp or StringExp, of length
1728
/// oldlen, change its length to newlen. If the newlen is longer than oldlen,
1729
/// all new elements will be set to the default initializer for the element type.
1730
UnionExp changeArrayLiteralLength(const ref Loc loc, TypeArray arrayType, Expression oldval, size_t oldlen, size_t newlen)
1731
{
1732 1
    UnionExp ue;
1733 1
    Type elemType = arrayType.next;
1734 1
    assert(elemType);
1735 1
    Expression defaultElem = elemType.defaultInitLiteral(loc);
1736 1
    auto elements = new Expressions(newlen);
1737
    // Resolve slices
1738 1
    size_t indxlo = 0;
1739 1
    if (oldval.op == TOK.slice)
1740
    {
1741 0
        indxlo = cast(size_t)(cast(SliceExp)oldval).lwr.toInteger();
1742 0
        oldval = (cast(SliceExp)oldval).e1;
1743
    }
1744 1
    size_t copylen = oldlen < newlen ? oldlen : newlen;
1745 1
    if (oldval.op == TOK.string_)
1746
    {
1747 1
        StringExp oldse = cast(StringExp)oldval;
1748 1
        void* s = mem.xcalloc(newlen + 1, oldse.sz);
1749 1
        const data = oldse.peekData();
1750 1
        memcpy(s, data.ptr, copylen * oldse.sz);
1751 1
        const defaultValue = cast(uint)defaultElem.toInteger();
1752 1
        foreach (size_t elemi; copylen .. newlen)
1753
        {
1754 1
            switch (oldse.sz)
1755
            {
1756 1
            case 1:
1757 1
                (cast(char*)s)[cast(size_t)(indxlo + elemi)] = cast(char)defaultValue;
1758 1
                break;
1759 0
            case 2:
1760 0
                (cast(wchar*)s)[cast(size_t)(indxlo + elemi)] = cast(wchar)defaultValue;
1761 0
                break;
1762 0
            case 4:
1763 0
                (cast(dchar*)s)[cast(size_t)(indxlo + elemi)] = cast(dchar)defaultValue;
1764 0
                break;
1765 0
            default:
1766 0
                assert(0);
1767
            }
1768
        }
1769 1
        emplaceExp!(StringExp)(&ue, loc, s[0 .. newlen * oldse.sz], newlen, oldse.sz);
1770 1
        StringExp se = cast(StringExp)ue.exp();
1771 1
        se.type = arrayType;
1772 1
        se.sz = oldse.sz;
1773 1
        se.committed = oldse.committed;
1774 1
        se.ownedByCtfe = OwnedBy.ctfe;
1775
    }
1776
    else
1777
    {
1778 1
        if (oldlen != 0)
1779
        {
1780 1
            assert(oldval.op == TOK.arrayLiteral);
1781 1
            ArrayLiteralExp ae = cast(ArrayLiteralExp)oldval;
1782 1
            foreach (size_t i; 0 .. copylen)
1783 1
                (*elements)[i] = (*ae.elements)[indxlo + i];
1784
        }
1785 1
        if (elemType.ty == Tstruct || elemType.ty == Tsarray)
1786
        {
1787
            /* If it is an aggregate literal representing a value type,
1788
             * we need to create a unique copy for each element
1789
             */
1790 1
            foreach (size_t i; copylen .. newlen)
1791 1
                (*elements)[i] = copyLiteral(defaultElem).copy();
1792
        }
1793
        else
1794
        {
1795 1
            foreach (size_t i; copylen .. newlen)
1796 1
                (*elements)[i] = defaultElem;
1797
        }
1798 1
        emplaceExp!(ArrayLiteralExp)(&ue, loc, arrayType, elements);
1799 1
        ArrayLiteralExp aae = cast(ArrayLiteralExp)ue.exp();
1800 1
        aae.ownedByCtfe = OwnedBy.ctfe;
1801
    }
1802 1
    return ue;
1803
}
1804

1805
/*************************** CTFE Sanity Checks ***************************/
1806

1807
bool isCtfeValueValid(Expression newval)
1808
{
1809 1
    Type tb = newval.type.toBasetype();
1810 1
    switch (newval.op)
1811
    {
1812 1
        case TOK.int64:
1813 1
        case TOK.float64:
1814 1
        case TOK.char_:
1815 1
        case TOK.complex80:
1816 1
            return tb.isscalar();
1817

1818 1
        case TOK.null_:
1819 1
            return tb.ty == Tnull    ||
1820 1
                   tb.ty == Tpointer ||
1821 1
                   tb.ty == Tarray   ||
1822 1
                   tb.ty == Taarray  ||
1823 1
                   tb.ty == Tclass   ||
1824 1
                   tb.ty == Tdelegate;
1825

1826 1
        case TOK.string_:
1827 1
            return true; // CTFE would directly use the StringExp in AST.
1828

1829 1
        case TOK.arrayLiteral:
1830 1
            return true; //((ArrayLiteralExp *)newval)->ownedByCtfe;
1831

1832 1
        case TOK.assocArrayLiteral:
1833 1
            return true; //((AssocArrayLiteralExp *)newval)->ownedByCtfe;
1834

1835 1
        case TOK.structLiteral:
1836 1
            return true; //((StructLiteralExp *)newval)->ownedByCtfe;
1837

1838 1
        case TOK.classReference:
1839 1
            return true;
1840

1841 1
        case TOK.type:
1842 1
            return true;
1843

1844 1
        case TOK.vector:
1845 1
            return true; // vector literal
1846

1847 1
        case TOK.function_:
1848 1
            return true; // function literal or delegate literal
1849

1850 1
        case TOK.delegate_:
1851
        {
1852
            // &struct.func or &clasinst.func
1853
            // &nestedfunc
1854 1
            Expression ethis = (cast(DelegateExp)newval).e1;
1855 1
            return (ethis.op == TOK.structLiteral || ethis.op == TOK.classReference || ethis.op == TOK.variable && (cast(VarExp)ethis).var == (cast(DelegateExp)newval).func);
1856
        }
1857

1858 1
        case TOK.symbolOffset:
1859
        {
1860
            // function pointer, or pointer to static variable
1861 1
            Declaration d = (cast(SymOffExp)newval).var;
1862 1
            return d.isFuncDeclaration() || d.isDataseg();
1863
        }
1864

1865 1
        case TOK.typeid_:
1866
        {
1867
            // always valid
1868 1
            return true;
1869
        }
1870

1871 1
        case TOK.address:
1872
        {
1873
            // e1 should be a CTFE reference
1874 1
            Expression e1 = (cast(AddrExp)newval).e1;
1875 1
            return tb.ty == Tpointer &&
1876
            (
1877 1
                (e1.op == TOK.structLiteral || e1.op == TOK.arrayLiteral) && isCtfeValueValid(e1) ||
1878 1
                 e1.op == TOK.variable ||
1879 1
                 e1.op == TOK.dotVariable && isCtfeReferenceValid(e1) ||
1880 1
                 e1.op == TOK.index && isCtfeReferenceValid(e1) ||
1881 1
                 e1.op == TOK.slice && e1.type.toBasetype().ty == Tsarray
1882
            );
1883
        }
1884

1885 1
        case TOK.slice:
1886
        {
1887
            // e1 should be an array aggregate
1888 1
            const SliceExp se = cast(SliceExp)newval;
1889 1
            assert(se.lwr && se.lwr.op == TOK.int64);
1890 1
            assert(se.upr && se.upr.op == TOK.int64);
1891 1
            return (tb.ty == Tarray || tb.ty == Tsarray) && (se.e1.op == TOK.string_ || se.e1.op == TOK.arrayLiteral);
1892
        }
1893

1894 1
        case TOK.void_:
1895 1
            return true; // uninitialized value
1896

1897 0
        default:
1898 0
            newval.error("CTFE internal error: illegal CTFE value `%s`", newval.toChars());
1899 0
            return false;
1900
    }
1901
}
1902

1903
bool isCtfeReferenceValid(Expression newval)
1904
{
1905 1
    switch (newval.op)
1906
    {
1907 0
        case TOK.this_:
1908 0
            return true;
1909

1910 1
        case TOK.variable:
1911
        {
1912 1
            const VarDeclaration v = (cast(VarExp)newval).var.isVarDeclaration();
1913 1
            assert(v);
1914
            // Must not be a reference to a reference
1915 1
            return true;
1916
        }
1917

1918 1
        case TOK.index:
1919
        {
1920 1
            const Expression eagg = (cast(IndexExp)newval).e1;
1921 1
            return eagg.op == TOK.string_ || eagg.op == TOK.arrayLiteral || eagg.op == TOK.assocArrayLiteral;
1922
        }
1923

1924 1
        case TOK.dotVariable:
1925
        {
1926 1
            Expression eagg = (cast(DotVarExp)newval).e1;
1927 1
            return (eagg.op == TOK.structLiteral || eagg.op == TOK.classReference) && isCtfeValueValid(eagg);
1928
        }
1929

1930 1
        default:
1931
            // Internally a ref variable may directly point a stack memory.
1932
            // e.g. ref int v = 1;
1933 1
            return isCtfeValueValid(newval);
1934
    }
1935
}
1936

1937
// Used for debugging only
1938
void showCtfeExpr(Expression e, int level = 0)
1939
{
1940 0
    for (int i = level; i > 0; --i)
1941 0
        printf(" ");
1942 0
    Expressions* elements = null;
1943
    // We need the struct definition to detect block assignment
1944 0
    StructDeclaration sd = null;
1945 0
    ClassDeclaration cd = null;
1946 0
    if (e.op == TOK.structLiteral)
1947
    {
1948 0
        elements = (cast(StructLiteralExp)e).elements;
1949 0
        sd = (cast(StructLiteralExp)e).sd;
1950 0
        printf("STRUCT type = %s %p:\n", e.type.toChars(), e);
1951
    }
1952 0
    else if (e.op == TOK.classReference)
1953
    {
1954 0
        elements = (cast(ClassReferenceExp)e).value.elements;
1955 0
        cd = (cast(ClassReferenceExp)e).originalClass();
1956 0
        printf("CLASS type = %s %p:\n", e.type.toChars(), (cast(ClassReferenceExp)e).value);
1957
    }
1958 0
    else if (e.op == TOK.arrayLiteral)
1959
    {
1960 0
        elements = (cast(ArrayLiteralExp)e).elements;
1961 0
        printf("ARRAY LITERAL type=%s %p:\n", e.type.toChars(), e);
1962
    }
1963 0
    else if (e.op == TOK.assocArrayLiteral)
1964
    {
1965 0
        printf("AA LITERAL type=%s %p:\n", e.type.toChars(), e);
1966
    }
1967 0
    else if (e.op == TOK.string_)
1968
    {
1969 0
        printf("STRING %s %p\n", e.toChars(), e.isStringExp.peekString.ptr);
1970
    }
1971 0
    else if (e.op == TOK.slice)
1972
    {
1973 0
        printf("SLICE %p: %s\n", e, e.toChars());
1974 0
        showCtfeExpr((cast(SliceExp)e).e1, level + 1);
1975
    }
1976 0
    else if (e.op == TOK.variable)
1977
    {
1978 0
        printf("VAR %p %s\n", e, e.toChars());
1979 0
        VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration();
1980 0
        if (v && getValue(v))
1981 0
            showCtfeExpr(getValue(v), level + 1);
1982
    }
1983 0
    else if (e.op == TOK.address)
1984
    {
1985
        // This is potentially recursive. We mustn't try to print the thing we're pointing to.
1986 0
        printf("POINTER %p to %p: %s\n", e, (cast(AddrExp)e).e1, e.toChars());
1987
    }
1988
    else
1989 0
        printf("VALUE %p: %s\n", e, e.toChars());
1990 0
    if (elements)
1991
    {
1992 0
        size_t fieldsSoFar = 0;
1993 0
        for (size_t i = 0; i < elements.dim; i++)
1994
        {
1995 0
            Expression z = null;
1996 0
            VarDeclaration v = null;
1997 0
            if (i > 15)
1998
            {
1999 0
                printf("...(total %d elements)\n", cast(int)elements.dim);
2000 0
                return;
2001
            }
2002 0
            if (sd)
2003
            {
2004 0
                v = sd.fields[i];
2005 0
                z = (*elements)[i];
2006
            }
2007 0
            else if (cd)
2008
            {
2009 0
                while (i - fieldsSoFar >= cd.fields.dim)
2010
                {
2011 0
                    fieldsSoFar += cd.fields.dim;
2012 0
                    cd = cd.baseClass;
2013 0
                    for (int j = level; j > 0; --j)
2014 0
                        printf(" ");
2015 0
                    printf(" BASE CLASS: %s\n", cd.toChars());
2016
                }
2017 0
                v = cd.fields[i - fieldsSoFar];
2018 0
                assert((elements.dim + i) >= (fieldsSoFar + cd.fields.dim));
2019 0
                size_t indx = (elements.dim - fieldsSoFar) - cd.fields.dim + i;
2020 0
                assert(indx < elements.dim);
2021 0
                z = (*elements)[indx];
2022
            }
2023 0
            if (!z)
2024
            {
2025 0
                for (int j = level; j > 0; --j)
2026 0
                    printf(" ");
2027 0
                printf(" void\n");
2028 0
                continue;
2029
            }
2030 0
            if (v)
2031
            {
2032
                // If it is a void assignment, use the default initializer
2033 0
                if ((v.type.ty != z.type.ty) && v.type.ty == Tsarray)
2034
                {
2035 0
                    for (int j = level; --j;)
2036 0
                        printf(" ");
2037 0
                    printf(" field: block initialized static array\n");
2038 0
                    continue;
2039
                }
2040
            }
2041 0
            showCtfeExpr(z, level + 1);
2042
        }
2043
    }
2044
}
2045

2046
/*************************** Void initialization ***************************/
2047
UnionExp voidInitLiteral(Type t, VarDeclaration var)
2048
{
2049 1
    UnionExp ue;
2050 1
    if (t.ty == Tsarray)
2051
    {
2052 1
        TypeSArray tsa = cast(TypeSArray)t;
2053 1
        Expression elem = voidInitLiteral(tsa.next, var).copy();
2054
        // For aggregate value types (structs, static arrays) we must
2055
        // create an a separate copy for each element.
2056 1
        const mustCopy = (elem.op == TOK.arrayLiteral || elem.op == TOK.structLiteral);
2057 1
        const d = cast(size_t)tsa.dim.toInteger();
2058 1
        auto elements = new Expressions(d);
2059 1
        foreach (i; 0 .. d)
2060
        {
2061 1
            if (mustCopy && i > 0)
2062 1
                elem = copyLiteral(elem).copy();
2063 1
            (*elements)[i] = elem;
2064
        }
2065 1
        emplaceExp!(ArrayLiteralExp)(&ue, var.loc, tsa, elements);
2066 1
        ArrayLiteralExp ae = cast(ArrayLiteralExp)ue.exp();
2067 1
        ae.ownedByCtfe = OwnedBy.ctfe;
2068
    }
2069 1
    else if (t.ty == Tstruct)
2070
    {
2071 1
        TypeStruct ts = cast(TypeStruct)t;
2072 1
        auto exps = new Expressions(ts.sym.fields.dim);
2073 1
        foreach (size_t i;  0 .. ts.sym.fields.dim)
2074
        {
2075 1
            (*exps)[i] = voidInitLiteral(ts.sym.fields[i].type, ts.sym.fields[i]).copy();
2076
        }
2077 1
        emplaceExp!(StructLiteralExp)(&ue, var.loc, ts.sym, exps);
2078 1
        StructLiteralExp se = cast(StructLiteralExp)ue.exp();
2079 1
        se.type = ts;
2080 1
        se.ownedByCtfe = OwnedBy.ctfe;
2081
    }
2082
    else
2083 1
        emplaceExp!(VoidInitExp)(&ue, var);
2084 1
    return ue;
2085
}

Read our documentation on viewing source code .

Loading