
// Compiler implementation of the D programming language
// Copyright (c) 1999-2012 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>                     // mem{cpy|set|cmp}()
#include <math.h>

#if __DMC__
#include <complex.h>
#endif

#include "rmem.h"
#include "root.h"
#include "port.h"

#include "mtype.h"
#include "expression.h"
#include "aggregate.h"
#include "declaration.h"
#include "utf.h"

#define LOG 0

int RealEquals(real_t x1, real_t x2);

Expression *expType(Type *type, Expression *e)
{
    if (type != e->type)
    {
        e = e->copy();
        e->type = type;
    }
    return e;
}

/* ================================== isConst() ============================== */

int Expression::isConst()
{
    //printf("Expression::isConst(): %s\n", toChars());
    return 0;
}

int IntegerExp::isConst()
{
    return 1;
}

int RealExp::isConst()
{
    return 1;
}

int ComplexExp::isConst()
{
    return 1;
}

int NullExp::isConst()
{
    return 0;
}

int SymOffExp::isConst()
{
    return 2;
}

/* =============================== constFold() ============================== */

/* The constFold() functions were redundant with the optimize() ones,
 * and so have been folded in with them.
 */

/* ========================================================================== */

Expression *Neg(Type *type, Expression *e1)
{   Expression *e;
    Loc loc = e1->loc;

    if (e1->type->isreal())
    {
        e = new RealExp(loc, -e1->toReal(), type);
    }
    else if (e1->type->isimaginary())
    {
        e = new RealExp(loc, -e1->toImaginary(), type);
    }
    else if (e1->type->iscomplex())
    {
        e = new ComplexExp(loc, -e1->toComplex(), type);
    }
    else
        e = new IntegerExp(loc, -e1->toInteger(), type);
    return e;
}

Expression *Com(Type *type, Expression *e1)
{   Expression *e;
    Loc loc = e1->loc;

    e = new IntegerExp(loc, ~e1->toInteger(), type);
    return e;
}

Expression *Not(Type *type, Expression *e1)
{   Expression *e;
    Loc loc = e1->loc;

    e = new IntegerExp(loc, e1->isBool(0), type);
    return e;
}

Expression *Bool(Type *type, Expression *e1)
{   Expression *e;
    Loc loc = e1->loc;

    e = new IntegerExp(loc, e1->isBool(1), type);
    return e;
}

Expression *Add(Type *type, Expression *e1, Expression *e2)
{   Expression *e;
    Loc loc = e1->loc;

#if LOG
    printf("Add(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars());
#endif
    if (type->isreal())
    {
        e = new RealExp(loc, e1->toReal() + e2->toReal(), type);
    }
    else if (type->isimaginary())
    {
        e = new RealExp(loc, e1->toImaginary() + e2->toImaginary(), type);
    }
    else if (type->iscomplex())
    {
        // This rigamarole is necessary so that -0.0 doesn't get
        // converted to +0.0 by doing an extraneous add with +0.0
        complex_t c1;
        real_t r1;
        real_t i1;

        complex_t c2;
        real_t r2;
        real_t i2;

        complex_t v;
        int x;

        if (e1->type->isreal())
        {   r1 = e1->toReal();
            x = 0;
        }
        else if (e1->type->isimaginary())
        {   i1 = e1->toImaginary();
            x = 3;
        }
        else
        {   c1 = e1->toComplex();
            x = 6;
        }

        if (e2->type->isreal())
        {   r2 = e2->toReal();
        }
        else if (e2->type->isimaginary())
        {   i2 = e2->toImaginary();
            x += 1;
        }
        else
        {   c2 = e2->toComplex();
            x += 2;
        }

        switch (x)
        {
#if __DMC__
            case 0+0:   v = (complex_t) (r1 + r2);      break;
            case 0+1:   v = r1 + i2 * I;                break;
            case 0+2:   v = r1 + c2;                    break;
            case 3+0:   v = i1 * I + r2;                break;
            case 3+1:   v = (complex_t) ((i1 + i2) * I); break;
            case 3+2:   v = i1 * I + c2;                break;
            case 6+0:   v = c1 + r2;                    break;
            case 6+1:   v = c1 + i2 * I;                break;
            case 6+2:   v = c1 + c2;                    break;
#else
            case 0+0:   v = complex_t(r1 + r2, 0);      break;
            case 0+1:   v = complex_t(r1, i2);          break;
            case 0+2:   v = complex_t(r1 + creall(c2), cimagl(c2));     break;
            case 3+0:   v = complex_t(r2, i1);          break;
            case 3+1:   v = complex_t(0, i1 + i2);      break;
            case 3+2:   v = complex_t(creall(c2), i1 + cimagl(c2));     break;
            case 6+0:   v = complex_t(creall(c1) + r2, cimagl(c2));     break;
            case 6+1:   v = complex_t(creall(c1), cimagl(c1) + i2);     break;
            case 6+2:   v = c1 + c2;                    break;
#endif
            default: assert(0);
        }
        e = new ComplexExp(loc, v, type);
    }
    else if (e1->op == TOKsymoff)
    {
        SymOffExp *soe = (SymOffExp *)e1;
        e = new SymOffExp(loc, soe->var, soe->offset + e2->toInteger());
        e->type = type;
    }
    else if (e2->op == TOKsymoff)
    {
        SymOffExp *soe = (SymOffExp *)e2;
        e = new SymOffExp(loc, soe->var, soe->offset + e1->toInteger());
        e->type = type;
    }
    else
        e = new IntegerExp(loc, e1->toInteger() + e2->toInteger(), type);
    return e;
}


Expression *Min(Type *type, Expression *e1, Expression *e2)
{   Expression *e;
    Loc loc = e1->loc;

    if (type->isreal())
    {
        e = new RealExp(loc, e1->toReal() - e2->toReal(), type);
    }
    else if (type->isimaginary())
    {
        e = new RealExp(loc, e1->toImaginary() - e2->toImaginary(), type);
    }
    else if (type->iscomplex())
    {
        // This rigamarole is necessary so that -0.0 doesn't get
        // converted to +0.0 by doing an extraneous add with +0.0
        complex_t c1;
        real_t r1;
        real_t i1;

        complex_t c2;
        real_t r2;
        real_t i2;

        complex_t v;
        int x;

        if (e1->type->isreal())
        {   r1 = e1->toReal();
            x = 0;
        }
        else if (e1->type->isimaginary())
        {   i1 = e1->toImaginary();
            x = 3;
        }
        else
        {   c1 = e1->toComplex();
            x = 6;
        }

        if (e2->type->isreal())
        {   r2 = e2->toReal();
        }
        else if (e2->type->isimaginary())
        {   i2 = e2->toImaginary();
            x += 1;
        }
        else
        {   c2 = e2->toComplex();
            x += 2;
        }

        switch (x)
        {
#if __DMC__
            case 0+0:   v = (complex_t) (r1 - r2);      break;
            case 0+1:   v = r1 - i2 * I;                break;
            case 0+2:   v = r1 - c2;                    break;
            case 3+0:   v = i1 * I - r2;                break;
            case 3+1:   v = (complex_t) ((i1 - i2) * I); break;
            case 3+2:   v = i1 * I - c2;                break;
            case 6+0:   v = c1 - r2;                    break;
            case 6+1:   v = c1 - i2 * I;                break;
            case 6+2:   v = c1 - c2;                    break;
#else
            case 0+0:   v = complex_t(r1 - r2, 0);      break;
            case 0+1:   v = complex_t(r1, -i2);         break;
            case 0+2:   v = complex_t(r1 - creall(c2), -cimagl(c2));    break;
            case 3+0:   v = complex_t(-r2, i1);         break;
            case 3+1:   v = complex_t(0, i1 - i2);      break;
            case 3+2:   v = complex_t(-creall(c2), i1 - cimagl(c2));    break;
            case 6+0:   v = complex_t(creall(c1) - r2, cimagl(c1));     break;
            case 6+1:   v = complex_t(creall(c1), cimagl(c1) - i2);     break;
            case 6+2:   v = c1 - c2;                    break;
#endif
            default: assert(0);
        }
        e = new ComplexExp(loc, v, type);
    }
    else if (e1->op == TOKsymoff)
    {
        SymOffExp *soe = (SymOffExp *)e1;
        e = new SymOffExp(loc, soe->var, soe->offset - e2->toInteger());
        e->type = type;
    }
    else
    {
        e = new IntegerExp(loc, e1->toInteger() - e2->toInteger(), type);
    }
    return e;
}

Expression *Mul(Type *type, Expression *e1, Expression *e2)
{   Expression *e;
    Loc loc = e1->loc;

    if (type->isfloating())
    {   complex_t c;
#ifdef IN_GCC
        real_t r;
#else
        d_float80 r;
#endif

        if (e1->type->isreal())
        {
#if __DMC__
            c = e1->toReal() * e2->toComplex();
#else
            r = e1->toReal();
            c = e2->toComplex();
            c = complex_t(r * creall(c), r * cimagl(c));
#endif
        }
        else if (e1->type->isimaginary())
        {
#if __DMC__
            c = e1->toImaginary() * I * e2->toComplex();
#else
            r = e1->toImaginary();
            c = e2->toComplex();
            c = complex_t(-r * cimagl(c), r * creall(c));
#endif
        }
        else if (e2->type->isreal())
        {
#if __DMC__
            c = e2->toReal() * e1->toComplex();
#else
            r = e2->toReal();
            c = e1->toComplex();
            c = complex_t(r * creall(c), r * cimagl(c));
#endif
        }
        else if (e2->type->isimaginary())
        {
#if __DMC__
            c = e1->toComplex() * e2->toImaginary() * I;
#else
            r = e2->toImaginary();
            c = e1->toComplex();
            c = complex_t(-r * cimagl(c), r * creall(c));
#endif
        }
        else
            c = e1->toComplex() * e2->toComplex();

        if (type->isreal())
            e = new RealExp(loc, creall(c), type);
        else if (type->isimaginary())
            e = new RealExp(loc, cimagl(c), type);
        else if (type->iscomplex())
            e = new ComplexExp(loc, c, type);
        else
            assert(0);
    }
    else
    {
        e = new IntegerExp(loc, e1->toInteger() * e2->toInteger(), type);
    }
    return e;
}

Expression *Div(Type *type, Expression *e1, Expression *e2)
{   Expression *e;
    Loc loc = e1->loc;

    if (type->isfloating())
    {   complex_t c;
#ifdef IN_GCC
        real_t r;
#else
        d_float80 r;
#endif

        //e1->type->print();
        //e2->type->print();
        if (e2->type->isreal())
        {
            if (e1->type->isreal())
            {
                e = new RealExp(loc, e1->toReal() / e2->toReal(), type);
                return e;
            }
#if __DMC__
            //r = e2->toReal();
            //c = e1->toComplex();
            //printf("(%Lg + %Lgi) / %Lg\n", creall(c), cimagl(c), r);

            c = e1->toComplex() / e2->toReal();
#else
            r = e2->toReal();
            c = e1->toComplex();
            c = complex_t(creall(c) / r, cimagl(c) / r);
#endif
        }
        else if (e2->type->isimaginary())
        {
#if __DMC__
            //r = e2->toImaginary();
            //c = e1->toComplex();
            //printf("(%Lg + %Lgi) / %Lgi\n", creall(c), cimagl(c), r);

            c = e1->toComplex() / (e2->toImaginary() * I);
#else
            r = e2->toImaginary();
            c = e1->toComplex();
            c = complex_t(cimagl(c) / r, -creall(c) / r);
#endif
        }
        else
        {
            c = e1->toComplex() / e2->toComplex();
        }

        if (type->isreal())
            e = new RealExp(loc, creall(c), type);
        else if (type->isimaginary())
            e = new RealExp(loc, cimagl(c), type);
        else if (type->iscomplex())
            e = new ComplexExp(loc, c, type);
        else
            assert(0);
    }
    else
    {   sinteger_t n1;
        sinteger_t n2;
        sinteger_t n;

        n1 = e1->toInteger();
        n2 = e2->toInteger();
        if (n2 == 0)
        {   e2->error("divide by 0");
            e2 = new IntegerExp(loc, 1, e2->type);
            n2 = 1;
        }
        if (e1->type->isunsigned() || e2->type->isunsigned())
            n = ((d_uns64) n1) / ((d_uns64) n2);
        else
            n = n1 / n2;
        e = new IntegerExp(loc, n, type);
    }
    return e;
}

Expression *Mod(Type *type, Expression *e1, Expression *e2)
{   Expression *e;
    Loc loc = e1->loc;

    if (type->isfloating())
    {
        complex_t c;

        if (e2->type->isreal())
        {   real_t r2 = e2->toReal();

#ifdef __DMC__
            c = Port::fmodl(e1->toReal(), r2) + Port::fmodl(e1->toImaginary(), r2) * I;
#elif defined(IN_GCC)
            c = complex_t(e1->toReal() % r2, e1->toImaginary() % r2);
#elif (defined(__FreeBSD__) && __FreeBSD_version < 800000) || defined(__arm__) || defined(__thumb__)
            // freebsd is kinda messed up. the STABLE branch doesn't support C99's fmodl !?!
            // arm also doesn't like fmodl
            c = complex_t(fmod(e1->toReal(), r2), fmod(e1->toImaginary(), r2));
#else
            c = complex_t(Port::fmodl(e1->toReal(), r2), Port::fmodl(e1->toImaginary(), r2));
#endif
        }
        else if (e2->type->isimaginary())
        {   real_t i2 = e2->toImaginary();

#ifdef __DMC__
            c = Port::fmodl(e1->toReal(), i2) + Port::fmodl(e1->toImaginary(), i2) * I;
#elif defined(IN_GCC)
            c = complex_t(e1->toReal() % i2, e1->toImaginary() % i2);
#elif (defined(__FreeBSD__) && __FreeBSD_version < 800000) || defined(__arm__) || defined(__thumb__)
            // freebsd is kinda messed up. the STABLE branch doesn't support C99's fmodl !?!
            // arm also doesn't like fmodl
            c = complex_t(fmod(e1->toReal(), i2), fmod(e1->toImaginary(), i2));
#else
            c = complex_t(Port::fmodl(e1->toReal(), i2), Port::fmodl(e1->toImaginary(), i2));
#endif
        }
        else
            assert(0);

        if (type->isreal())
            e = new RealExp(loc, creall(c), type);
        else if (type->isimaginary())
            e = new RealExp(loc, cimagl(c), type);
        else if (type->iscomplex())
            e = new ComplexExp(loc, c, type);
        else
            assert(0);
    }
    else
    {   sinteger_t n1;
        sinteger_t n2;
        sinteger_t n;

        n1 = e1->toInteger();
        n2 = e2->toInteger();
        if (n2 == 0)
        {   e2->error("divide by 0");
            e2 = new IntegerExp(loc, 1, e2->type);
            n2 = 1;
        }
        if (n2 == -1 && !type->isunsigned())
        {    // Check for int.min % -1
            if (n1 == 0xFFFFFFFF80000000ULL && type->toBasetype()->ty != Tint64)
            {
                e2->error("integer overflow: int.min % -1");
                e2 = new IntegerExp(loc, 1, e2->type);
                n2 = 1;
            }
            else if (n1 == 0x8000000000000000LL) // long.min % -1
            {
                e2->error("integer overflow: long.min % -1");
                e2 = new IntegerExp(loc, 1, e2->type);
                n2 = 1;
            }
        }
        if (e1->type->isunsigned() || e2->type->isunsigned())
            n = ((d_uns64) n1) % ((d_uns64) n2);
        else
            n = n1 % n2;
        e = new IntegerExp(loc, n, type);
    }
    return e;
}

Expression *Pow(Type *type, Expression *e1, Expression *e2)
{   Expression *e;
    Loc loc = e1->loc;

    // Handle integer power operations.
    if (e2->type->isintegral())
    {
        Expression * r;
        Expression * v;
        dinteger_t n = e2->toInteger();
        bool neg;

        if (!e2->type->isunsigned() && (sinteger_t)n < 0)
        {
            if (e1->type->isintegral())
                return EXP_CANT_INTERPRET;

            // Don't worry about overflow, from now on n is unsigned.
            neg = true;
            n = -n;
        }
        else
            neg = false;

        if (e1->type->isfloating())
        {
            r = new RealExp(loc, e1->toReal(), e1->type);
            v = new RealExp(loc, ldouble(1.0), e1->type);
        }
        else
        {
            r = new RealExp(loc, e1->toReal(), Type::tfloat64);
            v = new RealExp(loc, ldouble(1.0), Type::tfloat64);
        }

        while (n != 0)
        {
            if (n & 1)
                v = Mul(v->type, v, r);
            n >>= 1;
            r = Mul(r->type, r, r);
        }

        if (neg)
            v = Div(v->type, new RealExp(loc, ldouble(1.0), v->type), v);

        if (type->isintegral())
            e = new IntegerExp(loc, v->toInteger(), type);
        else
            e = new RealExp(loc, v->toReal(), type);
    }
    else if (e2->type->isfloating())
    {
        // x ^^ y for x < 0 and y not an integer is not defined
        if (e1->toReal() < 0.0)
        {
            e = new RealExp(loc, ldouble(Port::nan), type);
        }
        else if (e2->toReal() == 0.5)
        {
            // Special case: call sqrt directly.
            Expressions args;
            args.setDim(1);
            args[0] = e1;
            e = eval_builtin(loc, BUILTINsqrt, &args);
            if (!e)
                e = EXP_CANT_INTERPRET;
        }
        else
            e = EXP_CANT_INTERPRET;
    }
    else
        e = EXP_CANT_INTERPRET;

    return e;
}

Expression *Shl(Type *type, Expression *e1, Expression *e2)
{   Expression *e;
    Loc loc = e1->loc;

    e = new IntegerExp(loc, e1->toInteger() << e2->toInteger(), type);
    return e;
}

Expression *Shr(Type *type, Expression *e1, Expression *e2)
{
    Loc loc = e1->loc;

    dinteger_t value = e1->toInteger();
    dinteger_t dcount = e2->toInteger();
    assert(dcount <= 0xFFFFFFFF);
    unsigned count = (unsigned)dcount;
    switch (e1->type->toBasetype()->ty)
    {
        case Tint8:
                value = (d_int8)(value) >> count;
                break;

        case Tuns8:
        case Tchar:
                value = (d_uns8)(value) >> count;
                break;

        case Tint16:
                value = (d_int16)(value) >> count;
                break;

        case Tuns16:
        case Twchar:
                value = (d_uns16)(value) >> count;
                break;

        case Tint32:
                value = (d_int32)(value) >> count;
                break;

        case Tuns32:
        case Tdchar:
                value = (d_uns32)(value) >> count;
                break;

        case Tint64:
                value = (d_int64)(value) >> count;
                break;

        case Tuns64:
                value = (d_uns64)(value) >> count;
                break;

        case Terror:
                return e1;

        default:
                assert(0);
    }
    Expression *e = new IntegerExp(loc, value, type);
    return e;
}

Expression *Ushr(Type *type, Expression *e1, Expression *e2)
{
    Loc loc = e1->loc;

    dinteger_t value = e1->toInteger();
    dinteger_t dcount = e2->toInteger();
    assert(dcount <= 0xFFFFFFFF);
    unsigned count = (unsigned)dcount;
    switch (e1->type->toBasetype()->ty)
    {
        case Tint8:
        case Tuns8:
        case Tchar:
                // Possible only with >>>=. >>> always gets promoted to int.
                value = (value & 0xFF) >> count;
                break;

        case Tint16:
        case Tuns16:
        case Twchar:
                // Possible only with >>>=. >>> always gets promoted to int.
                value = (value & 0xFFFF) >> count;
                break;

        case Tint32:
        case Tuns32:
        case Tdchar:
                value = (value & 0xFFFFFFFF) >> count;
                break;

        case Tint64:
        case Tuns64:
                value = (d_uns64)(value) >> count;
                break;

        case Terror:
                return e1;

        default:
                assert(0);
    }
    Expression *e = new IntegerExp(loc, value, type);
    return e;
}

Expression *And(Type *type, Expression *e1, Expression *e2)
{
    Expression *e;
    e = new IntegerExp(e1->loc, e1->toInteger() & e2->toInteger(), type);
    return e;
}

Expression *Or(Type *type, Expression *e1, Expression *e2)
{   Expression *e;
    e = new IntegerExp(e1->loc, e1->toInteger() | e2->toInteger(), type);
    return e;
}

Expression *Xor(Type *type, Expression *e1, Expression *e2)
{   Expression *e;
    e = new IntegerExp(e1->loc, e1->toInteger() ^ e2->toInteger(), type);
    return e;
}

/* Also returns EXP_CANT_INTERPRET if cannot be computed.
 */
Expression *Equal(enum TOK op, Type *type, Expression *e1, Expression *e2)
{   Expression *e;
    Loc loc = e1->loc;
    int cmp;
    real_t r1;
    real_t r2;

    //printf("Equal(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars());

    assert(op == TOKequal || op == TOKnotequal);

    if (e1->op == TOKnull)
    {
        if (e2->op == TOKnull)
            cmp = 1;
        else if (e2->op == TOKstring)
        {   StringExp *es2 = (StringExp *)e2;
            cmp = (0 == es2->len);
        }
        else if (e2->op == TOKarrayliteral)
        {   ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2;
            cmp = !es2->elements || (0 == es2->elements->dim);
        }
        else
            return EXP_CANT_INTERPRET;
    }
    else if (e2->op == TOKnull)
    {
        if (e1->op == TOKstring)
        {   StringExp *es1 = (StringExp *)e1;
            cmp = (0 == es1->len);
        }
        else if (e1->op == TOKarrayliteral)
        {   ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1;
            cmp = !es1->elements || (0 == es1->elements->dim);
        }
        else
            return EXP_CANT_INTERPRET;
    }
    else if (e1->op == TOKstring && e2->op == TOKstring)
    {   StringExp *es1 = (StringExp *)e1;
        StringExp *es2 = (StringExp *)e2;

        if (es1->sz != es2->sz)
        {
            assert(global.errors);
            return EXP_CANT_INTERPRET;
        }
        if (es1->len == es2->len &&
            memcmp(es1->string, es2->string, es1->sz * es1->len) == 0)
            cmp = 1;
        else
            cmp = 0;
    }
    else if (e1->op == TOKarrayliteral && e2->op == TOKarrayliteral)
    {   ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1;
        ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2;

        if ((!es1->elements || !es1->elements->dim) &&
            (!es2->elements || !es2->elements->dim))
            cmp = 1;            // both arrays are empty
        else if (!es1->elements || !es2->elements)
            cmp = 0;
        else if (es1->elements->dim != es2->elements->dim)
            cmp = 0;
        else
        {
            for (size_t i = 0; i < es1->elements->dim; i++)
            {   Expression *ee1 = (*es1->elements)[i];
                Expression *ee2 = (*es2->elements)[i];

                Expression *v = Equal(TOKequal, Type::tint32, ee1, ee2);
                if (v == EXP_CANT_INTERPRET)
                    return EXP_CANT_INTERPRET;
                cmp = v->toInteger();
                if (cmp == 0)
                    break;
            }
        }
    }
    else if (e1->op == TOKarrayliteral && e2->op == TOKstring)
    {   // Swap operands and use common code
        Expression *etmp = e1;
        e1 = e2;
        e2 = etmp;
        goto Lsa;
    }
    else if (e1->op == TOKstring && e2->op == TOKarrayliteral)
    {
     Lsa:
        StringExp *es1 = (StringExp *)e1;
        ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2;
        size_t dim1 = es1->len;
        size_t dim2 = es2->elements ? es2->elements->dim : 0;
        if (dim1 != dim2)
            cmp = 0;
        else
        {
            cmp = 1;            // if dim1 winds up being 0
            for (size_t i = 0; i < dim1; i++)
            {
                uinteger_t c = es1->charAt(i);
                Expression *ee2 = (*es2->elements)[i];
                if (ee2->isConst() != 1)
                    return EXP_CANT_INTERPRET;
                cmp = (c == ee2->toInteger());
                if (cmp == 0)
                    break;
            }
        }
    }
    else if (e1->op == TOKstructliteral && e2->op == TOKstructliteral)
    {   StructLiteralExp *es1 = (StructLiteralExp *)e1;
        StructLiteralExp *es2 = (StructLiteralExp *)e2;

        if (es1->sd != es2->sd)
            cmp = 0;
        else if ((!es1->elements || !es1->elements->dim) &&
            (!es2->elements || !es2->elements->dim))
            cmp = 1;            // both arrays are empty
        else if (!es1->elements || !es2->elements)
            cmp = 0;
        else if (es1->elements->dim != es2->elements->dim)
            cmp = 0;
        else
        {
            cmp = 1;
            for (size_t i = 0; i < es1->elements->dim; i++)
            {   Expression *ee1 = (*es1->elements)[i];
                Expression *ee2 = (*es2->elements)[i];

                if (ee1 == ee2)
                    continue;
                if (!ee1 || !ee2)
                {   cmp = 0;
                    break;
                }
                Expression *v = Equal(TOKequal, Type::tint32, ee1, ee2);
                if (v == EXP_CANT_INTERPRET)
                    return EXP_CANT_INTERPRET;
                cmp = v->toInteger();
                if (cmp == 0)
                    break;
            }
        }
    }
#if 0 // Should handle this
    else if (e1->op == TOKarrayliteral && e2->op == TOKstring)
    {
    }
#endif
    else if (e1->isConst() != 1 || e2->isConst() != 1)
        return EXP_CANT_INTERPRET;
    else if (e1->type->isreal())
    {
        r1 = e1->toReal();
        r2 = e2->toReal();
        goto L1;
    }
    else if (e1->type->isimaginary())
    {
        r1 = e1->toImaginary();
        r2 = e2->toImaginary();
     L1:
#if __DMC__
        cmp = (r1 == r2);
#else
        if (Port::isNan(r1) || Port::isNan(r2)) // if unordered
        {
            cmp = 0;
        }
        else
        {
            cmp = (r1 == r2);
        }
#endif
    }
    else if (e1->type->iscomplex())
    {
        cmp = e1->toComplex() == e2->toComplex();
    }
    else if (e1->type->isintegral() || e1->type->toBasetype()->ty == Tpointer)
    {
        cmp = (e1->toInteger() == e2->toInteger());
    }
    else
        return EXP_CANT_INTERPRET;
    if (op == TOKnotequal)
        cmp ^= 1;
    e = new IntegerExp(loc, cmp, type);
    return e;
}

Expression *Identity(enum TOK op, Type *type, Expression *e1, Expression *e2)
{
    Loc loc = e1->loc;
    int cmp;

    if (e1->op == TOKnull)
    {
        cmp = (e2->op == TOKnull);
    }
    else if (e2->op == TOKnull)
    {
        cmp = 0;
    }
    else if (e1->op == TOKsymoff && e2->op == TOKsymoff)
    {
        SymOffExp *es1 = (SymOffExp *)e1;
        SymOffExp *es2 = (SymOffExp *)e2;

        cmp = (es1->var == es2->var && es1->offset == es2->offset);
    }
    else
    {
       if (e1->type->isreal())
       {
           cmp = RealEquals(e1->toReal(), e2->toReal());
       }
       else if (e1->type->isimaginary())
       {
           cmp = RealEquals(e1->toImaginary(), e2->toImaginary());
       }
       else if (e1->type->iscomplex())
       {
           complex_t v1 = e1->toComplex();
           complex_t v2 = e2->toComplex();
           cmp = RealEquals(creall(v1), creall(v2)) &&
                 RealEquals(cimagl(v1), cimagl(v1));
       }
       else
           return Equal((op == TOKidentity) ? TOKequal : TOKnotequal,
                   type, e1, e2);
    }
    if (op == TOKnotidentity)
        cmp ^= 1;
    return new IntegerExp(loc, cmp, type);
}


Expression *Cmp(enum TOK op, Type *type, Expression *e1, Expression *e2)
{   Expression *e;
    Loc loc = e1->loc;
    dinteger_t n;
    real_t r1;
    real_t r2;

    //printf("Cmp(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars());

    if (e1->op == TOKstring && e2->op == TOKstring)
    {   StringExp *es1 = (StringExp *)e1;
        StringExp *es2 = (StringExp *)e2;
        size_t sz = es1->sz;
        assert(sz == es2->sz);

        size_t len = es1->len;
        if (es2->len < len)
            len = es2->len;

        int cmp = memcmp(es1->string, es2->string, sz * len);
        if (cmp == 0)
            cmp = es1->len - es2->len;

        switch (op)
        {
            case TOKlt: n = cmp <  0;   break;
            case TOKle: n = cmp <= 0;   break;
            case TOKgt: n = cmp >  0;   break;
            case TOKge: n = cmp >= 0;   break;

            case TOKleg:   n = 1;               break;
            case TOKlg:    n = cmp != 0;        break;
            case TOKunord: n = 0;               break;
            case TOKue:    n = cmp == 0;        break;
            case TOKug:    n = cmp >  0;        break;
            case TOKuge:   n = cmp >= 0;        break;
            case TOKul:    n = cmp <  0;        break;
            case TOKule:   n = cmp <= 0;        break;

            default:
                assert(0);
        }
    }
    else if (e1->isConst() != 1 || e2->isConst() != 1)
        return EXP_CANT_INTERPRET;
    else if (e1->type->isreal())
    {
        r1 = e1->toReal();
        r2 = e2->toReal();
        goto L1;
    }
    else if (e1->type->isimaginary())
    {
        r1 = e1->toImaginary();
        r2 = e2->toImaginary();
     L1:
#if __DMC__
        // DMC is the only compiler I know of that handles NAN arguments
        // correctly in comparisons.
        switch (op)
        {
            case TOKlt:    n = r1 <  r2;        break;
            case TOKle:    n = r1 <= r2;        break;
            case TOKgt:    n = r1 >  r2;        break;
            case TOKge:    n = r1 >= r2;        break;

            case TOKleg:   n = r1 <>=  r2;      break;
            case TOKlg:    n = r1 <>   r2;      break;
            case TOKunord: n = r1 !<>= r2;      break;
            case TOKue:    n = r1 !<>  r2;      break;
            case TOKug:    n = r1 !<=  r2;      break;
            case TOKuge:   n = r1 !<   r2;      break;
            case TOKul:    n = r1 !>=  r2;      break;
            case TOKule:   n = r1 !>   r2;      break;

            default:
                assert(0);
        }
#else
        // Don't rely on compiler, handle NAN arguments separately
        if (Port::isNan(r1) || Port::isNan(r2)) // if unordered
        {
            switch (op)
            {
                case TOKlt:     n = 0;  break;
                case TOKle:     n = 0;  break;
                case TOKgt:     n = 0;  break;
                case TOKge:     n = 0;  break;

                case TOKleg:    n = 0;  break;
                case TOKlg:     n = 0;  break;
                case TOKunord:  n = 1;  break;
                case TOKue:     n = 1;  break;
                case TOKug:     n = 1;  break;
                case TOKuge:    n = 1;  break;
                case TOKul:     n = 1;  break;
                case TOKule:    n = 1;  break;

                default:
                    assert(0);
            }
        }
        else
        {
            switch (op)
            {
                case TOKlt:     n = r1 <  r2;   break;
                case TOKle:     n = r1 <= r2;   break;
                case TOKgt:     n = r1 >  r2;   break;
                case TOKge:     n = r1 >= r2;   break;

                case TOKleg:    n = 1;          break;
                case TOKlg:     n = r1 != r2;   break;
                case TOKunord:  n = 0;          break;
                case TOKue:     n = r1 == r2;   break;
                case TOKug:     n = r1 >  r2;   break;
                case TOKuge:    n = r1 >= r2;   break;
                case TOKul:     n = r1 <  r2;   break;
                case TOKule:    n = r1 <= r2;   break;

                default:
                    assert(0);
            }
        }
#endif
    }
    else if (e1->type->iscomplex())
    {
        assert(0);
    }
    else
    {   sinteger_t n1;
        sinteger_t n2;

        n1 = e1->toInteger();
        n2 = e2->toInteger();
        if (e1->type->isunsigned() || e2->type->isunsigned())
        {
            switch (op)
            {
                case TOKlt:     n = ((d_uns64) n1) <  ((d_uns64) n2);   break;
                case TOKle:     n = ((d_uns64) n1) <= ((d_uns64) n2);   break;
                case TOKgt:     n = ((d_uns64) n1) >  ((d_uns64) n2);   break;
                case TOKge:     n = ((d_uns64) n1) >= ((d_uns64) n2);   break;

                case TOKleg:    n = 1;                                  break;
                case TOKlg:     n = ((d_uns64) n1) != ((d_uns64) n2);   break;
                case TOKunord:  n = 0;                                  break;
                case TOKue:     n = ((d_uns64) n1) == ((d_uns64) n2);   break;
                case TOKug:     n = ((d_uns64) n1) >  ((d_uns64) n2);   break;
                case TOKuge:    n = ((d_uns64) n1) >= ((d_uns64) n2);   break;
                case TOKul:     n = ((d_uns64) n1) <  ((d_uns64) n2);   break;
                case TOKule:    n = ((d_uns64) n1) <= ((d_uns64) n2);   break;

                default:
                    assert(0);
            }
        }
        else
        {
            switch (op)
            {
                case TOKlt:     n = n1 <  n2;   break;
                case TOKle:     n = n1 <= n2;   break;
                case TOKgt:     n = n1 >  n2;   break;
                case TOKge:     n = n1 >= n2;   break;

                case TOKleg:    n = 1;          break;
                case TOKlg:     n = n1 != n2;   break;
                case TOKunord:  n = 0;          break;
                case TOKue:     n = n1 == n2;   break;
                case TOKug:     n = n1 >  n2;   break;
                case TOKuge:    n = n1 >= n2;   break;
                case TOKul:     n = n1 <  n2;   break;
                case TOKule:    n = n1 <= n2;   break;

                default:
                    assert(0);
            }
        }
    }
    e = new IntegerExp(loc, n, type);
    return e;
}

/* Also returns EXP_CANT_INTERPRET if cannot be computed.
 *  to: type to cast to
 *  type: type to paint the result
 */

Expression *Cast(Type *type, Type *to, Expression *e1)
{   Expression *e = EXP_CANT_INTERPRET;
    Loc loc = e1->loc;

    //printf("Cast(type = %s, to = %s, e1 = %s)\n", type->toChars(), to->toChars(), e1->toChars());
    //printf("\te1->type = %s\n", e1->type->toChars());
    if (e1->type->equals(type) && type->equals(to))
        return e1;
    if (e1->type->implicitConvTo(to) >= MATCHconst ||
        to->implicitConvTo(e1->type) >= MATCHconst)
        return expType(to, e1);

    // Allow covariant converions of delegates
    // (Perhaps implicit conversion from pure to impure should be a MATCHconst,
    // then we wouldn't need this extra check.)
    if (e1->type->toBasetype()->ty == Tdelegate &&
        e1->type->implicitConvTo(to) == MATCHconvert)
        return expType(to, e1);

    Type *tb = to->toBasetype();
    Type *typeb = type->toBasetype();

    /* Allow casting from one string type to another
     */
    if (e1->op == TOKstring)
    {
        if (tb->ty == Tarray && typeb->ty == Tarray &&
            tb->nextOf()->size() == typeb->nextOf()->size())
        {
            return expType(to, e1);
        }
    }

    if (e1->op == TOKarrayliteral && typeb == tb)
        return e1;

    if (e1->isConst() != 1)
        return EXP_CANT_INTERPRET;

    if (tb->ty == Tbool)
        e = new IntegerExp(loc, e1->toInteger() != 0, type);
    else if (type->isintegral())
    {
        if (e1->type->isfloating())
        {   dinteger_t result;
            real_t r = e1->toReal();

            switch (typeb->ty)
            {
                case Tint8:     result = (d_int8)r;     break;
                case Tchar:
                case Tuns8:     result = (d_uns8)r;     break;
                case Tint16:    result = (d_int16)r;    break;
                case Twchar:
                case Tuns16:    result = (d_uns16)r;    break;
                case Tint32:    result = (d_int32)r;    break;
                case Tdchar:
                case Tuns32:    result = (d_uns32)r;    break;
                case Tint64:    result = (d_int64)r;    break;
                case Tuns64:    result = (d_uns64)r;    break;
                default:
                    assert(0);
            }

            e = new IntegerExp(loc, result, type);
        }
        else if (type->isunsigned())
            e = new IntegerExp(loc, e1->toUInteger(), type);
        else
            e = new IntegerExp(loc, e1->toInteger(), type);
    }
    else if (tb->isreal())
    {   real_t value = e1->toReal();

        e = new RealExp(loc, value, type);
    }
    else if (tb->isimaginary())
    {   real_t value = e1->toImaginary();

        e = new RealExp(loc, value, type);
    }
    else if (tb->iscomplex())
    {   complex_t value = e1->toComplex();

        e = new ComplexExp(loc, value, type);
    }
    else if (tb->isscalar())
        e = new IntegerExp(loc, e1->toInteger(), type);
    else if (tb->ty == Tvoid)
        e = EXP_CANT_INTERPRET;
    else if (tb->ty == Tstruct && e1->op == TOKint64)
    {   // Struct = 0;
        StructDeclaration *sd = tb->toDsymbol(NULL)->isStructDeclaration();
        assert(sd);
        Expressions *elements = new Expressions;
        for (size_t i = 0; i < sd->fields.dim; i++)
        {   Dsymbol *s = sd->fields[i];
            VarDeclaration *v = s->isVarDeclaration();
            assert(v);

            Expression *exp = new IntegerExp(0);
            exp = Cast(v->type, v->type, exp);
            if (exp == EXP_CANT_INTERPRET)
                return exp;
            elements->push(exp);
        }
        e = new StructLiteralExp(loc, sd, elements);
        e->type = type;
    }
    else
    {
        if (type != Type::terror)
            error(loc, "cannot cast %s to %s", e1->type->toChars(), type->toChars());
        e = new ErrorExp();
    }
    return e;
}


Expression *ArrayLength(Type *type, Expression *e1)
{   Expression *e;
    Loc loc = e1->loc;

    if (e1->op == TOKstring)
    {   StringExp *es1 = (StringExp *)e1;

        e = new IntegerExp(loc, es1->len, type);
    }
    else if (e1->op == TOKarrayliteral)
    {   ArrayLiteralExp *ale = (ArrayLiteralExp *)e1;
        size_t dim;

        dim = ale->elements ? ale->elements->dim : 0;
        e = new IntegerExp(loc, dim, type);
    }
    else if (e1->op == TOKassocarrayliteral)
    {   AssocArrayLiteralExp *ale = (AssocArrayLiteralExp *)e1;
        size_t dim = ale->keys->dim;

        e = new IntegerExp(loc, dim, type);
    }
    else
        e = EXP_CANT_INTERPRET;
    return e;
}

/* Also return EXP_CANT_INTERPRET if this fails
 */
Expression *Index(Type *type, Expression *e1, Expression *e2)
{   Expression *e = EXP_CANT_INTERPRET;
    Loc loc = e1->loc;

    //printf("Index(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars());
    assert(e1->type);
    if (e1->op == TOKstring && e2->op == TOKint64)
    {   StringExp *es1 = (StringExp *)e1;
        uinteger_t i = e2->toInteger();

        if (i >= es1->len)
        {
            e1->error("string index %llu is out of bounds [0 .. %llu]", i, (ulonglong)es1->len);
            e = new ErrorExp();
        }
        else
        {
            e = new IntegerExp(loc, es1->charAt(i), type);
        }
    }
    else if (e1->type->toBasetype()->ty == Tsarray && e2->op == TOKint64)
    {   TypeSArray *tsa = (TypeSArray *)e1->type->toBasetype();
        uinteger_t length = tsa->dim->toInteger();
        uinteger_t i = e2->toInteger();

        if (i >= length)
        {
            e1->error("array index %llu is out of bounds %s[0 .. %llu]", i, e1->toChars(), length);
            e = new ErrorExp();
        }
        else if (e1->op == TOKarrayliteral)
        {   ArrayLiteralExp *ale = (ArrayLiteralExp *)e1;
            e = (*ale->elements)[i];
            e->type = type;
            if (e->hasSideEffect())
                e = EXP_CANT_INTERPRET;
        }
    }
    else if (e1->type->toBasetype()->ty == Tarray && e2->op == TOKint64)
    {
        uinteger_t i = e2->toInteger();

        if (e1->op == TOKarrayliteral)
        {   ArrayLiteralExp *ale = (ArrayLiteralExp *)e1;
            if (i >= ale->elements->dim)
            {
                e1->error("array index %llu is out of bounds %s[0 .. %u]", i, e1->toChars(), ale->elements->dim);
                e = new ErrorExp();
            }
            else
            {   e = (*ale->elements)[i];
                e->type = type;
                if (e->hasSideEffect())
                    e = EXP_CANT_INTERPRET;
            }
        }
    }
    else if (e1->op == TOKassocarrayliteral)
    {
        AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e1;
        /* Search the keys backwards, in case there are duplicate keys
         */
        for (size_t i = ae->keys->dim; i;)
        {
            i--;
            Expression *ekey = (*ae->keys)[i];
            Expression *ex = Equal(TOKequal, Type::tbool, ekey, e2);
            if (ex == EXP_CANT_INTERPRET)
                return ex;
            if (ex->isBool(TRUE))
            {   e = (*ae->values)[i];
                e->type = type;
                if (e->hasSideEffect())
                    e = EXP_CANT_INTERPRET;
                break;
            }
        }
    }
    return e;
}

/* Also return EXP_CANT_INTERPRET if this fails
 */
Expression *Slice(Type *type, Expression *e1, Expression *lwr, Expression *upr)
{   Expression *e = EXP_CANT_INTERPRET;
    Loc loc = e1->loc;

#if LOG
    printf("Slice()\n");
    if (lwr)
    {   printf("\te1 = %s\n", e1->toChars());
        printf("\tlwr = %s\n", lwr->toChars());
        printf("\tupr = %s\n", upr->toChars());
    }
#endif
    if (e1->op == TOKstring && lwr->op == TOKint64 && upr->op == TOKint64)
    {   StringExp *es1 = (StringExp *)e1;
        uinteger_t ilwr = lwr->toInteger();
        uinteger_t iupr = upr->toInteger();

        if (iupr > es1->len || ilwr > iupr)
        {
            e1->error("string slice [%llu .. %llu] is out of bounds", ilwr, iupr);
            e = new ErrorExp();
        }
        else
        {
            void *s;
            size_t len = iupr - ilwr;
            int sz = es1->sz;
            StringExp *es;

            s = mem.malloc((len + 1) * sz);
            memcpy((unsigned char *)s, (unsigned char *)es1->string + ilwr * sz, len * sz);
            memset((unsigned char *)s + len * sz, 0, sz);

            es = new StringExp(loc, s, len, es1->postfix);
            es->sz = sz;
            es->committed = 1;
            es->type = type;
            e = es;
        }
    }
    else if (e1->op == TOKarrayliteral &&
            lwr->op == TOKint64 && upr->op == TOKint64 &&
            !e1->hasSideEffect())
    {   ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1;
        uinteger_t ilwr = lwr->toInteger();
        uinteger_t iupr = upr->toInteger();

        if (iupr > es1->elements->dim || ilwr > iupr)
        {
            e1->error("array slice [%llu .. %llu] is out of bounds", ilwr, iupr);
            e = new ErrorExp();
        }
        else
        {
            Expressions *elements = new Expressions();
            elements->setDim(iupr - ilwr);
            memcpy(elements->tdata(),
                   es1->elements->tdata() + ilwr,
                   (iupr - ilwr) * sizeof((*es1->elements)[0]));
            e = new ArrayLiteralExp(e1->loc, elements);
            e->type = type;
        }
    }
    return e;
}

/* Set a slice of char array literal 'existingAE' from a string 'newval'.
 * existingAE[firstIndex..firstIndex+newval.length] = newval.
 */
void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, int firstIndex)
{
    size_t newlen =  newval->len;
    size_t sz = newval->sz;
    unsigned char *s = (unsigned char *)newval->string;
    Type *elemType = existingAE->type->nextOf();
    for (size_t j = 0; j < newlen; j++)
    {
        dinteger_t val;
        switch (sz)
        {
            case 1: val = s[j]; break;
            case 2: val = ((unsigned short *)s)[j]; break;
            case 4: val = ((unsigned *)s)[j]; break;
            default:
                assert(0);
                break;
        }
        (*existingAE->elements)[j+firstIndex]
            = new IntegerExp(newval->loc, val, elemType);
    }
}

/* Set a slice of string 'existingSE' from a char array literal 'newae'.
 *   existingSE[firstIndex..firstIndex+newae.length] = newae.
 */
void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *newae, int firstIndex)
{
    unsigned char *s = (unsigned char *)existingSE->string;
    for (size_t j = 0; j < newae->elements->dim; j++)
    {
        unsigned value = (unsigned)((*newae->elements)[j]->toInteger());
        switch (existingSE->sz)
        {
            case 1: s[j+firstIndex] = value; break;
            case 2: ((unsigned short *)s)[j+firstIndex] = value; break;
            case 4: ((unsigned *)s)[j+firstIndex] = value; break;
            default:
                assert(0);
                break;
        }
    }
}

/* Set a slice of string 'existingSE' from a string 'newstr'.
 *   existingSE[firstIndex..firstIndex+newstr.length] = newstr.
 */
void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, int firstIndex)
{
    unsigned char *s = (unsigned char *)existingSE->string;
    size_t sz = existingSE->sz;
    assert(sz == newstr->sz);
    memcpy(s + firstIndex * sz, newstr->string, sz * newstr->len);
}

/* Compare a string slice with another string slice.
 * Conceptually equivalent to memcmp( se1[lo1..lo1+len],  se2[lo2..lo2+len])
 */
int sliceCmpStringWithString(StringExp *se1, StringExp *se2, size_t lo1, size_t lo2, size_t len)
{
    unsigned char *s1 = (unsigned char *)se1->string;
    unsigned char *s2 = (unsigned char *)se2->string;
    size_t sz = se1->sz;
    assert(sz == se2->sz);

    return memcmp(s1 + sz * lo1, s2 + sz * lo2, sz * len);
}

/* Compare a string slice with an array literal slice
 * Conceptually equivalent to memcmp( se1[lo1..lo1+len],  ae2[lo2..lo2+len])
 */
int sliceCmpStringWithArray(StringExp *se1, ArrayLiteralExp *ae2, size_t lo1, size_t lo2, size_t len)
{
    unsigned char *s = (unsigned char *)se1->string;
    size_t sz = se1->sz;

    int c = 0;

    for (size_t j = 0; j < len; j++)
    {
        unsigned value = (unsigned)((*ae2->elements)[j + lo2]->toInteger());
        unsigned svalue;
        switch (sz)
        {
            case 1: svalue = s[j + lo1]; break;
            case 2: svalue = ((unsigned short *)s)[j+lo1]; break;
            case 4: svalue = ((unsigned *)s)[j + lo1]; break;
            default:
                assert(0);
        }
        int c = svalue - value;
        if (c)
            return c;
    }
    return 0;
}

/* Also return EXP_CANT_INTERPRET if this fails
 */
Expression *Cat(Type *type, Expression *e1, Expression *e2)
{   Expression *e = EXP_CANT_INTERPRET;
    Loc loc = e1->loc;
    Type *t;
    Type *t1 = e1->type->toBasetype();
    Type *t2 = e2->type->toBasetype();

    //printf("Cat(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars());
    //printf("\tt1 = %s, t2 = %s, type = %s\n", t1->toChars(), t2->toChars(), type->toChars());

    if (e1->op == TOKnull && (e2->op == TOKint64 || e2->op == TOKstructliteral))
    {   e = e2;
        t = t1;
        goto L2;
    }
    else if ((e1->op == TOKint64 || e1->op == TOKstructliteral) && e2->op == TOKnull)
    {   e = e1;
        t = t2;
     L2:
        Type *tn = e->type->toBasetype();
        if (tn->ty == Tchar || tn->ty == Twchar || tn->ty == Tdchar)
        {
            // Create a StringExp
            void *s;
            StringExp *es;
            if (t->nextOf())
                t = t->nextOf()->toBasetype();
            int sz = t->size();

            dinteger_t v = e->toInteger();

            size_t len = (t->ty == tn->ty) ? 1 : utf_codeLength(sz, v);
            s = mem.malloc((len + 1) * sz);
            if (t->ty == tn->ty)
#if IN_LLVM
#if __LITTLE_ENDIAN__
                memcpy((unsigned char *)s, &v, sz);
#else
                memcpy((unsigned char *)s,
                       (unsigned char *)&v + (sizeof(dinteger_t) - sz), sz);
#endif
#else
                memcpy((unsigned char *)s, &v, sz);
#endif
            else
                utf_encode(sz, s, v);

            // Add terminating 0
            memset((unsigned char *)s + len * sz, 0, sz);

            es = new StringExp(loc, s, len);
            es->sz = sz;
            es->committed = 1;
            e = es;
        }
        else
        {   // Create an ArrayLiteralExp
            Expressions *elements = new Expressions();
            elements->push(e);
            e = new ArrayLiteralExp(e->loc, elements);
        }
        e->type = type;
        return e;
    }
    else if (e1->op == TOKnull && e2->op == TOKnull)
    {
        if (type == e1->type)
        {
            // Handle null ~= null
            if (t1->ty == Tarray && t2 == t1->nextOf())
            {
                e = new ArrayLiteralExp(e1->loc, e2);
                e->type = type;
                return e;
            }
            else
                return e1;
        }
        if (type == e2->type)
            return e2;
        return new NullExp(e1->loc, type);
    }
    else if (e1->op == TOKstring && e2->op == TOKstring)
    {
        // Concatenate the strings
        void *s;
        StringExp *es1 = (StringExp *)e1;
        StringExp *es2 = (StringExp *)e2;
        StringExp *es;
        size_t len = es1->len + es2->len;
        int sz = es1->sz;

        if (sz != es2->sz)
        {
            /* Can happen with:
             *   auto s = "foo"d ~ "bar"c;
             */
            assert(global.errors);
            return e;
        }
        s = mem.malloc((len + 1) * sz);
        memcpy(s, es1->string, es1->len * sz);
        memcpy((unsigned char *)s + es1->len * sz, es2->string, es2->len * sz);

        // Add terminating 0
        memset((unsigned char *)s + len * sz, 0, sz);

        es = new StringExp(loc, s, len);
        es->sz = sz;
        es->committed = es1->committed | es2->committed;
        es->type = type;
        e = es;
    }
    else if (e2->op == TOKstring && e1->op == TOKarrayliteral &&
        t1->nextOf()->isintegral())
    {
        // [chars] ~ string --> [chars]
        StringExp *es = (StringExp *)e2;
        ArrayLiteralExp *ea = (ArrayLiteralExp *)e1;
        size_t len = es->len + ea->elements->dim;
        Expressions * elems = new Expressions;
        elems->setDim(len);
        for (size_t i= 0; i < ea->elements->dim; ++i)
        {
            (*elems)[i] = (*ea->elements)[i];
        }
        ArrayLiteralExp *dest = new ArrayLiteralExp(e1->loc, elems);
        dest->type = type;
        sliceAssignArrayLiteralFromString(dest, es, ea->elements->dim);
        return dest;
    }
    else if (e1->op == TOKstring && e2->op == TOKarrayliteral &&
        t2->nextOf()->isintegral())
    {
        // string ~ [chars] --> [chars]
        StringExp *es = (StringExp *)e1;
        ArrayLiteralExp *ea = (ArrayLiteralExp *)e2;
        size_t len = es->len + ea->elements->dim;
        Expressions * elems = new Expressions;
        elems->setDim(len);
        for (size_t i= 0; i < ea->elements->dim; ++i)
        {
            (*elems)[es->len + i] = (*ea->elements)[i];
        }
        ArrayLiteralExp *dest = new ArrayLiteralExp(e1->loc, elems);
        dest->type = type;
        sliceAssignArrayLiteralFromString(dest, es, 0);
        return dest;
    }
    else if (e1->op == TOKstring && e2->op == TOKint64)
    {
        // string ~ char --> string
        void *s;
        StringExp *es1 = (StringExp *)e1;
        StringExp *es;
        int sz = es1->sz;
        dinteger_t v = e2->toInteger();

        // Is it a concatentation of homogenous types?
        // (char[] ~ char, wchar[]~wchar, or dchar[]~dchar)
        bool homoConcat = (sz == t2->size());
        size_t len = es1->len;
        len += homoConcat ? 1 : utf_codeLength(sz, v);

        s = mem.malloc((len + 1) * sz);
        memcpy(s, es1->string, es1->len * sz);
        if (homoConcat)
#if IN_LLVM
#if __LITTLE_ENDIAN__
             memcpy((unsigned char *)s + (sz * es1->len), &v, sz);
#else
             memcpy((unsigned char *)s + (sz * es1->len),
                    (unsigned char *)&v + (sizeof(dinteger_t) - sz), sz);
#endif
#else
             memcpy((unsigned char *)s + (sz * es1->len), &v, sz);
#endif
        else
             utf_encode(sz, (unsigned char *)s + (sz * es1->len), v);

        // Add terminating 0
        memset((unsigned char *)s + len * sz, 0, sz);

        es = new StringExp(loc, s, len);
        es->sz = sz;
        es->committed = es1->committed;
        es->type = type;
        e = es;
    }
    else if (e1->op == TOKint64 && e2->op == TOKstring)
    {
        // Concatenate the strings
        void *s;
        StringExp *es2 = (StringExp *)e2;
        StringExp *es;
        size_t len = 1 + es2->len;
        int sz = es2->sz;
        dinteger_t v = e1->toInteger();

        s = mem.malloc((len + 1) * sz);
#if IN_LLVM
#if __LITTLE_ENDIAN__
        memcpy((unsigned char *)s, &v, sz);
#else
        memcpy((unsigned char *)s,
               (unsigned char *)&v + (sizeof(dinteger_t) - sz), sz);
#endif
#else
        memcpy((unsigned char *)s, &v, sz);
#endif
        memcpy((unsigned char *)s + sz, es2->string, es2->len * sz);

        // Add terminating 0
        memset((unsigned char *)s + len * sz, 0, sz);

        es = new StringExp(loc, s, len);
        es->sz = sz;
        es->committed = es2->committed;
        es->type = type;
        e = es;
    }
    else if (e1->op == TOKarrayliteral && e2->op == TOKarrayliteral &&
        t1->nextOf()->equals(t2->nextOf()))
    {
        // Concatenate the arrays
        ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1;
        ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2;

        es1 = new ArrayLiteralExp(es1->loc, (Expressions *)es1->elements->copy());
        es1->elements->insert(es1->elements->dim, es2->elements);
        e = es1;

        if (type->toBasetype()->ty == Tsarray)
        {
            e->type = new TypeSArray(t1->nextOf(), new IntegerExp(loc, es1->elements->dim, Type::tindex));
            e->type = e->type->semantic(loc, NULL);
        }
        else
            e->type = type;
    }
    else if (e1->op == TOKarrayliteral && e2->op == TOKnull &&
        t1->nextOf()->equals(t2->nextOf()))
    {
        e = e1;
        goto L3;
    }
    else if (e1->op == TOKnull && e2->op == TOKarrayliteral &&
        t1->nextOf()->equals(t2->nextOf()))
    {
        e = e2;
     L3:
        // Concatenate the array with null
        ArrayLiteralExp *es = (ArrayLiteralExp *)e;

        es = new ArrayLiteralExp(es->loc, (Expressions *)es->elements->copy());
        e = es;

        if (type->toBasetype()->ty == Tsarray)
        {
            e->type = new TypeSArray(t1->nextOf(), new IntegerExp(loc, es->elements->dim, Type::tindex));
            e->type = e->type->semantic(loc, NULL);
        }
        else
            e->type = type;
    }
    else if ((e1->op == TOKarrayliteral || e1->op == TOKnull) &&
        e1->type->toBasetype()->nextOf()->equals(e2->type))
    {
        ArrayLiteralExp *es1;
        if (e1->op == TOKarrayliteral)
        {   es1 = (ArrayLiteralExp *)e1;
            es1 = new ArrayLiteralExp(es1->loc, (Expressions *)es1->elements->copy());
            es1->elements->push(e2);
        }
        else
        {
            es1 = new ArrayLiteralExp(e1->loc, e2);
        }
        e = es1;

        if (type->toBasetype()->ty == Tsarray)
        {
            e->type = new TypeSArray(e2->type, new IntegerExp(loc, es1->elements->dim, Type::tindex));
            e->type = e->type->semantic(loc, NULL);
        }
        else
            e->type = type;
    }
    else if (e2->op == TOKarrayliteral &&
        e2->type->toBasetype()->nextOf()->equals(e1->type))
    {
        ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2;

        es2 = new ArrayLiteralExp(es2->loc, (Expressions *)es2->elements->copy());
        es2->elements->shift(e1);
        e = es2;

        if (type->toBasetype()->ty == Tsarray)
        {
            e->type = new TypeSArray(e1->type, new IntegerExp(loc, es2->elements->dim, Type::tindex));
            e->type = e->type->semantic(loc, NULL);
        }
        else
            e->type = type;
    }
    else if (e1->op == TOKnull && e2->op == TOKstring)
    {
        t = e1->type;
        e = e2;
        goto L1;
    }
    else if (e1->op == TOKstring && e2->op == TOKnull)
    {   e = e1;
        t = e2->type;
      L1:
        Type *tb = t->toBasetype();
        if (tb->ty == Tarray && tb->nextOf()->equals(e->type))
        {   Expressions *expressions = new Expressions();
            expressions->push(e);
            e = new ArrayLiteralExp(loc, expressions);
            e->type = t;
        }
        if (!e->type->equals(type))
        {   StringExp *se = (StringExp *)e->copy();
            e = se->castTo(NULL, type);
        }
    }
    return e;
}

Expression *Ptr(Type *type, Expression *e1)
{
    //printf("Ptr(e1 = %s)\n", e1->toChars());
    if (e1->op == TOKadd)
    {   AddExp *ae = (AddExp *)e1;
        if (ae->e1->op == TOKaddress && ae->e2->op == TOKint64)
        {   AddrExp *ade = (AddrExp *)ae->e1;
            if (ade->e1->op == TOKstructliteral)
            {   StructLiteralExp *se = (StructLiteralExp *)ade->e1;
                unsigned offset = ae->e2->toInteger();
                Expression *e = se->getField(type, offset);
                if (!e)
                    e = EXP_CANT_INTERPRET;
                return e;
            }
        }
    }
    return EXP_CANT_INTERPRET;
}

