/**
 * Compiler implementation of the
 * $(LINK2 http://www.dlang.org, D programming language).
 *
 * Copyright:   Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
 * Source:      $(DMDSRC _expression.d)
 */

module ddmd.expression;

import core.stdc.stdarg;
import core.stdc.stdio;
import core.stdc.string;

import ddmd.access;
import ddmd.aggregate;
import ddmd.aliasthis;
import ddmd.apply;
import ddmd.argtypes;
import ddmd.arrayop;
import ddmd.arraytypes;
import ddmd.attrib;
import ddmd.gluelayer;
import ddmd.canthrow;
import ddmd.complex;
import ddmd.constfold;
import ddmd.ctfeexpr;
import ddmd.dcast;
import ddmd.dclass;
import ddmd.declaration;
import ddmd.delegatize;
import ddmd.denum;
import ddmd.dimport;
import ddmd.dinterpret;
import ddmd.dmangle;
import ddmd.dmodule;
import ddmd.dscope;
import ddmd.dstruct;
import ddmd.dsymbol;
import ddmd.dtemplate;
import ddmd.errors;
import ddmd.func;
import ddmd.globals;
import ddmd.hdrgen;
import ddmd.id;
import ddmd.identifier;
import ddmd.imphint;
import ddmd.inline;
import ddmd.intrange;
import ddmd.mtype;
import ddmd.nspace;
import ddmd.opover;
import ddmd.optimize;
import ddmd.parse;
import ddmd.root.ctfloat;
import ddmd.root.file;
import ddmd.root.filename;
import ddmd.root.outbuffer;
import ddmd.root.rmem;
import ddmd.root.rootobject;
import ddmd.safe;
import ddmd.sideeffect;
import ddmd.target;
import ddmd.tokens;
import ddmd.traits;
import ddmd.typinf;
import ddmd.utf;
import ddmd.utils;
import ddmd.visitor;

enum LOGSEMANTIC = false;
void emplaceExp(T : Expression, Args...)(void* p, Args args)
{
    scope tmp = new T(args);
    memcpy(p, cast(void*)tmp, __traits(classInstanceSize, T));
}

void emplaceExp(T : UnionExp)(T* p, Expression e)
{
    memcpy(p, cast(void*)e, e.size);
}

/*************************************************************
 * Given var, we need to get the
 * right 'this' pointer if var is in an outer class, but our
 * existing 'this' pointer is in an inner class.
 * Input:
 *      e1      existing 'this'
 *      ad      struct or class we need the correct 'this' for
 *      var     the specific member of ad we're accessing
 */
extern (C++) Expression getRightThis(Loc loc, Scope* sc, AggregateDeclaration ad, Expression e1, Declaration var, int flag = 0)
{
    //printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1->toChars(), ad->toChars(), var->toChars());
L1:
    Type t = e1.type.toBasetype();
    //printf("e1->type = %s, var->type = %s\n", e1->type->toChars(), var->type->toChars());

    /* If e1 is not the 'this' pointer for ad
     */
    if (ad && !(t.ty == Tpointer && t.nextOf().ty == Tstruct && (cast(TypeStruct)t.nextOf()).sym == ad) && !(t.ty == Tstruct && (cast(TypeStruct)t).sym == ad))
    {
        ClassDeclaration cd = ad.isClassDeclaration();
        ClassDeclaration tcd = t.isClassHandle();

        /* e1 is the right this if ad is a base class of e1
         */
        if (!cd || !tcd || !(tcd == cd || cd.isBaseOf(tcd, null)))
        {
            /* Only classes can be inner classes with an 'outer'
             * member pointing to the enclosing class instance
             */
            if (tcd && tcd.isNested())
            {
                /* e1 is the 'this' pointer for an inner class: tcd.
                 * Rewrite it as the 'this' pointer for the outer class.
                 */
                e1 = new DotVarExp(loc, e1, tcd.vthis);
                e1.type = tcd.vthis.type;
                e1.type = e1.type.addMod(t.mod);
                // Do not call checkNestedRef()
                //e1 = e1->semantic(sc);

                // Skip up over nested functions, and get the enclosing
                // class type.
                int n = 0;
                Dsymbol s;
                for (s = tcd.toParent(); s && s.isFuncDeclaration(); s = s.toParent())
                {
                    FuncDeclaration f = s.isFuncDeclaration();
                    if (f.vthis)
                    {
                        //printf("rewriting e1 to %s's this\n", f->toChars());
                        n++;
                        e1 = new VarExp(loc, f.vthis);
                    }
                    else
                    {
                        e1.error("need 'this' of type %s to access member %s from static function %s", ad.toChars(), var.toChars(), f.toChars());
                        e1 = new ErrorExp();
                        return e1;
                    }
                }
                if (s && s.isClassDeclaration())
                {
                    e1.type = s.isClassDeclaration().type;
                    e1.type = e1.type.addMod(t.mod);
                    if (n > 1)
                        e1 = e1.semantic(sc);
                }
                else
                    e1 = e1.semantic(sc);
                goto L1;
            }

            /* Can't find a path from e1 to ad
             */
            if (flag)
                return null;
            e1.error("this for %s needs to be type %s not type %s", var.toChars(), ad.toChars(), t.toChars());
            return new ErrorExp();
        }
    }
    return e1;
}

/*****************************************
 * Determine if 'this' is available.
 * If it is, return the FuncDeclaration that has it.
 */
extern (C++) FuncDeclaration hasThis(Scope* sc)
{
    //printf("hasThis()\n");
    Dsymbol p = sc.parent;
    while (p && p.isTemplateMixin())
        p = p.parent;
    FuncDeclaration fdthis = p ? p.isFuncDeclaration() : null;
    //printf("fdthis = %p, '%s'\n", fdthis, fdthis ? fdthis->toChars() : "");

    // Go upwards until we find the enclosing member function
    FuncDeclaration fd = fdthis;
    while (1)
    {
        if (!fd)
        {
            goto Lno;
        }
        if (!fd.isNested())
            break;

        Dsymbol parent = fd.parent;
        while (1)
        {
            if (!parent)
                goto Lno;
            TemplateInstance ti = parent.isTemplateInstance();
            if (ti)
                parent = ti.parent;
            else
                break;
        }
        fd = parent.isFuncDeclaration();
    }

    if (!fd.isThis())
    {
        //printf("test '%s'\n", fd->toChars());
        goto Lno;
    }

    assert(fd.vthis);
    return fd;

Lno:
    return null; // don't have 'this' available
}

extern (C++) bool isNeedThisScope(Scope* sc, Declaration d)
{
    if (sc.intypeof == 1)
        return false;

    AggregateDeclaration ad = d.isThis();
    if (!ad)
        return false;
    //printf("d = %s, ad = %s\n", d.toChars(), ad.toChars());

    for (Dsymbol s = sc.parent; s; s = s.toParent2())
    {
        //printf("\ts = %s %s, toParent2() = %p\n", s.kind(), s.toChars(), s.toParent2());
        if (AggregateDeclaration ad2 = s.isAggregateDeclaration())
        {
            if (ad2 == ad)
                return false;
            else if (ad2.isNested())
                continue;
            else
                return true;
        }
        if (FuncDeclaration f = s.isFuncDeclaration())
        {
            if (f.isMember2())
                break;
        }
    }
    return true;
}

/***************************************
 * Pull out any properties.
 */
extern (C++) Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null)
{
    //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", Token.toChars(e1.op), e1.toChars(), e2 ? e2.toChars() : null);
    Loc loc = e1.loc;

    OverloadSet os;
    Dsymbol s;
    Objects* tiargs;
    Type tthis;
    if (e1.op == TOKdot)
    {
        DotExp de = cast(DotExp)e1;
        if (de.e2.op == TOKoverloadset)
        {
            tiargs = null;
            tthis = de.e1.type;
            os = (cast(OverExp)de.e2).vars;
            goto Los;
        }
    }
    else if (e1.op == TOKoverloadset)
    {
        tiargs = null;
        tthis = null;
        os = (cast(OverExp)e1).vars;
    Los:
        assert(os);
        FuncDeclaration fd = null;
        if (e2)
        {
            e2 = e2.semantic(sc);
            if (e2.op == TOKerror)
                return new ErrorExp();
            e2 = resolveProperties(sc, e2);

            Expressions a;
            a.push(e2);

            for (size_t i = 0; i < os.a.dim; i++)
            {
                FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, &a, 1);
                if (f)
                {
                    if (f.errors)
                        return new ErrorExp();
                    fd = f;
                    assert(fd.type.ty == Tfunction);
                    TypeFunction tf = cast(TypeFunction)fd.type;
                }
            }
            if (fd)
            {
                Expression e = new CallExp(loc, e1, e2);
                return e.semantic(sc);
            }
        }
        {
            for (size_t i = 0; i < os.a.dim; i++)
            {
                FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, null, 1);
                if (f)
                {
                    if (f.errors)
                        return new ErrorExp();
                    fd = f;
                    assert(fd.type.ty == Tfunction);
                    TypeFunction tf = cast(TypeFunction)fd.type;
                    if (!tf.isref && e2)
                        goto Leproplvalue;
                }
            }
            if (fd)
            {
                Expression e = new CallExp(loc, e1);
                if (e2)
                    e = new AssignExp(loc, e, e2);
                return e.semantic(sc);
            }
        }
        if (e2)
            goto Leprop;
    }
    else if (e1.op == TOKdotti)
    {
        DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)e1;
        if (!dti.findTempDecl(sc))
            goto Leprop;
        if (!dti.ti.semanticTiargs(sc))
            goto Leprop;
        tiargs = dti.ti.tiargs;
        tthis = dti.e1.type;
        if ((os = dti.ti.tempdecl.isOverloadSet()) !is null)
            goto Los;
        if ((s = dti.ti.tempdecl) !is null)
            goto Lfd;
    }
    else if (e1.op == TOKdottd)
    {
        DotTemplateExp dte = cast(DotTemplateExp)e1;
        s = dte.td;
        tiargs = null;
        tthis = dte.e1.type;
        goto Lfd;
    }
    else if (e1.op == TOKscope)
    {
        s = (cast(ScopeExp)e1).sds;
        TemplateInstance ti = s.isTemplateInstance();
        if (ti && !ti.semanticRun && ti.tempdecl)
        {
            //assert(ti.needsTypeInference(sc));
            if (!ti.semanticTiargs(sc))
                goto Leprop;
            tiargs = ti.tiargs;
            tthis = null;
            if ((os = ti.tempdecl.isOverloadSet()) !is null)
                goto Los;
            if ((s = ti.tempdecl) !is null)
                goto Lfd;
        }
    }
    else if (e1.op == TOKtemplate)
    {
        s = (cast(TemplateExp)e1).td;
        tiargs = null;
        tthis = null;
        goto Lfd;
    }
    else if (e1.op == TOKdotvar && e1.type && e1.type.toBasetype().ty == Tfunction)
    {
        DotVarExp dve = cast(DotVarExp)e1;
        s = dve.var.isFuncDeclaration();
        tiargs = null;
        tthis = dve.e1.type;
        goto Lfd;
    }
    else if (e1.op == TOKvar && e1.type && e1.type.toBasetype().ty == Tfunction)
    {
        s = (cast(VarExp)e1).var.isFuncDeclaration();
        tiargs = null;
        tthis = null;
    Lfd:
        assert(s);
        if (e2)
        {
            e2 = e2.semantic(sc);
            if (e2.op == TOKerror)
                return new ErrorExp();
            e2 = resolveProperties(sc, e2);

            Expressions a;
            a.push(e2);

            FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, &a, 1);
            if (fd && fd.type)
            {
                if (fd.errors)
                    return new ErrorExp();
                assert(fd.type.ty == Tfunction);
                TypeFunction tf = cast(TypeFunction)fd.type;
                Expression e = new CallExp(loc, e1, e2);
                return e.semantic(sc);
            }
        }
        {
            FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, null, 1);
            if (fd && fd.type)
            {
                if (fd.errors)
                    return new ErrorExp();
                assert(fd.type.ty == Tfunction);
                TypeFunction tf = cast(TypeFunction)fd.type;
                if (!e2 || tf.isref)
                {
                    Expression e = new CallExp(loc, e1);
                    if (e2)
                        e = new AssignExp(loc, e, e2);
                    return e.semantic(sc);
                }
            }
        }
        if (FuncDeclaration fd = s.isFuncDeclaration())
        {
            // Keep better diagnostic message for invalid property usage of functions
            assert(fd.type.ty == Tfunction);
            TypeFunction tf = cast(TypeFunction)fd.type;
            Expression e = new CallExp(loc, e1, e2);
            return e.semantic(sc);
        }
        if (e2)
            goto Leprop;
    }
    if (e1.op == TOKvar)
    {
        VarExp ve = cast(VarExp)e1;
        VarDeclaration v = ve.var.isVarDeclaration();
        if (v && ve.checkPurity(sc, v))
            return new ErrorExp();
    }
    if (e2)
        return null;

    if (e1.type && e1.op != TOKtype) // function type is not a property
    {
        /* Look for e1 being a lazy parameter; rewrite as delegate call
         */
        if (e1.op == TOKvar)
        {
            VarExp ve = cast(VarExp)e1;
            if (ve.var.storage_class & STClazy)
            {
                Expression e = new CallExp(loc, e1);
                return e.semantic(sc);
            }
        }
        else if (e1.op == TOKdotvar)
        {
            // Check for reading overlapped pointer field in @safe code.
            if (checkUnsafeAccess(sc, e1, true, true))
                return new ErrorExp();
        }
        else if (e1.op == TOKdot)
        {
            e1.error("expression has no value");
            return new ErrorExp();
        }
        else if (e1.op == TOKcall)
        {
            CallExp ce = cast(CallExp)e1;
            // Check for reading overlapped pointer field in @safe code.
            if (checkUnsafeAccess(sc, ce.e1, true, true))
                return new ErrorExp();
        }
    }

    if (!e1.type)
    {
        error(loc, "cannot resolve type for %s", e1.toChars());
        e1 = new ErrorExp();
    }
    return e1;

Leprop:
    error(loc, "not a property %s", e1.toChars());
    return new ErrorExp();

Leproplvalue:
    error(loc, "%s is not an lvalue", e1.toChars());
    return new ErrorExp();
}

extern (C++) Expression resolveProperties(Scope* sc, Expression e)
{
    //printf("resolveProperties(%s)\n", e->toChars());
    e = resolvePropertiesX(sc, e);
    if (e.checkRightThis(sc))
        return new ErrorExp();
    return e;
}

/******************************
 * Check the tail CallExp is really property function call.
 */
extern (C++) bool checkPropertyCall(Expression e, Expression emsg)
{
    while (e.op == TOKcomma)
        e = (cast(CommaExp)e).e2;

    if (e.op == TOKcall)
    {
        CallExp ce = cast(CallExp)e;
        TypeFunction tf;
        if (ce.f)
        {
            tf = cast(TypeFunction)ce.f.type;
            /* If a forward reference to ce->f, try to resolve it
             */
            if (!tf.deco && ce.f._scope)
            {
                ce.f.semantic(ce.f._scope);
                tf = cast(TypeFunction)ce.f.type;
            }
        }
        else if (ce.e1.type.ty == Tfunction)
            tf = cast(TypeFunction)ce.e1.type;
        else if (ce.e1.type.ty == Tdelegate)
            tf = cast(TypeFunction)ce.e1.type.nextOf();
        else if (ce.e1.type.ty == Tpointer && ce.e1.type.nextOf().ty == Tfunction)
            tf = cast(TypeFunction)ce.e1.type.nextOf();
        else
            assert(0);
    }
    return false;
}

/******************************
 * If e1 is a property function (template), resolve it.
 */
extern (C++) Expression resolvePropertiesOnly(Scope* sc, Expression e1)
{
    //printf("e1 = %s %s\n", Token::toChars(e1->op), e1->toChars());
    OverloadSet os;
    FuncDeclaration fd;
    TemplateDeclaration td;

    if (e1.op == TOKdot)
    {
        DotExp de = cast(DotExp)e1;
        if (de.e2.op == TOKoverloadset)
        {
            os = (cast(OverExp)de.e2).vars;
            goto Los;
        }
    }
    else if (e1.op == TOKoverloadset)
    {
        os = (cast(OverExp)e1).vars;
    Los:
        assert(os);
        for (size_t i = 0; i < os.a.dim; i++)
        {
            Dsymbol s = os.a[i];
            fd = s.isFuncDeclaration();
            td = s.isTemplateDeclaration();
            if (fd)
            {
                if ((cast(TypeFunction)fd.type).isproperty)
                    return resolveProperties(sc, e1);
            }
            else if (td && td.onemember && (fd = td.onemember.isFuncDeclaration()) !is null)
            {
                if ((cast(TypeFunction)fd.type).isproperty || (fd.storage_class2 & STCproperty) || (td._scope.stc & STCproperty))
                {
                    return resolveProperties(sc, e1);
                }
            }
        }
    }
    else if (e1.op == TOKdotti)
    {
        DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)e1;
        if (dti.ti.tempdecl && (td = dti.ti.tempdecl.isTemplateDeclaration()) !is null)
            goto Ltd;
    }
    else if (e1.op == TOKdottd)
    {
        td = (cast(DotTemplateExp)e1).td;
        goto Ltd;
    }
    else if (e1.op == TOKscope)
    {
        Dsymbol s = (cast(ScopeExp)e1).sds;
        TemplateInstance ti = s.isTemplateInstance();
        if (ti && !ti.semanticRun && ti.tempdecl)
        {
            if ((td = ti.tempdecl.isTemplateDeclaration()) !is null)
                goto Ltd;
        }
    }
    else if (e1.op == TOKtemplate)
    {
        td = (cast(TemplateExp)e1).td;
    Ltd:
        assert(td);
        if (td.onemember && (fd = td.onemember.isFuncDeclaration()) !is null)
        {
            if ((cast(TypeFunction)fd.type).isproperty || (fd.storage_class2 & STCproperty) || (td._scope.stc & STCproperty))
            {
                return resolveProperties(sc, e1);
            }
        }
    }
    else if (e1.op == TOKdotvar && e1.type.ty == Tfunction)
    {
        DotVarExp dve = cast(DotVarExp)e1;
        fd = dve.var.isFuncDeclaration();
        goto Lfd;
    }
    else if (e1.op == TOKvar && e1.type.ty == Tfunction && (sc.intypeof || !(cast(VarExp)e1).var.needThis()))
    {
        fd = (cast(VarExp)e1).var.isFuncDeclaration();
    Lfd:
        assert(fd);
        if ((cast(TypeFunction)fd.type).isproperty)
            return resolveProperties(sc, e1);
    }
    return e1;
}

/******************************
 * Find symbol in accordance with the UFCS name look up rule
 */
extern (C++) Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident)
{
    //printf("searchUFCS(ident = %s)\n", ident.toChars());
    Loc loc = ue.loc;

    // TODO: merge with Scope.search.searchScopes()
    Dsymbol searchScopes(int flags)
    {
        Dsymbol s = null;
        for (Scope* scx = sc; scx; scx = scx.enclosing)
        {
            if (!scx.scopesym)
                continue;
            if (scx.scopesym.isModule())
                flags |= SearchUnqualifiedModule;    // tell Module.search() that SearchLocalsOnly is to be obeyed
            s = scx.scopesym.search(loc, ident, flags);
            if (s)
            {
                // overload set contains only module scope symbols.
                if (s.isOverloadSet())
                    break;
                // selective/renamed imports also be picked up
                if (AliasDeclaration ad = s.isAliasDeclaration())
                {
                    if (ad._import)
                        break;
                }
                // See only module scope symbols for UFCS target.
                Dsymbol p = s.toParent2();
                if (p && p.isModule())
                    break;
            }
            s = null;

            // Stop when we hit a module, but keep going if that is not just under the global scope
            if (scx.scopesym.isModule() && !(scx.enclosing && !scx.enclosing.enclosing))
                break;
        }
        return s;
    }

    int flags = 0;
    Dsymbol s;

    if (sc.flags & SCOPEignoresymbolvisibility)
        flags |= IgnoreSymbolVisibility;

    Dsymbol sold = void;
    if (global.params.bug10378 || global.params.check10378)
    {
        sold = searchScopes(flags | IgnoreSymbolVisibility);
        if (!global.params.check10378)
        {
            s = sold;
            goto Lsearchdone;
        }
    }

    // First look in local scopes
    s = searchScopes(flags | SearchLocalsOnly);
    if (!s)
    {
        // Second look in imported modules
        s = searchScopes(flags | SearchImportsOnly);

        /** Still find private symbols, so that symbols that weren't access
         * checked by the compiler remain usable.  Once the deprecation is over,
         * this should be moved to search_correct instead.
         */
        if (!s && !(flags & IgnoreSymbolVisibility))
        {
            s = searchScopes(flags | SearchLocalsOnly | IgnoreSymbolVisibility);
            if (!s)
                s = searchScopes(flags | SearchImportsOnly | IgnoreSymbolVisibility);

            if (s)
                .deprecation(loc, "%s is not visible from module %s", s.toPrettyChars(), sc._module.toChars());
        }
    }
    if (global.params.check10378)
    {
        alias snew = s;
        if (sold !is snew)
            Scope.deprecation10378(loc, sold, snew);
        if (global.params.bug10378)
            s = sold;
    }
Lsearchdone:

    if (!s)
        return ue.e1.type.Type.getProperty(loc, ident, 0);

    FuncDeclaration f = s.isFuncDeclaration();
    if (f)
    {
        TemplateDeclaration td = getFuncTemplateDecl(f);
        if (td)
        {
            if (td.overroot)
                td = td.overroot;
            s = td;
        }
    }

    if (ue.op == TOKdotti)
    {
        DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)ue;
        auto ti = new TemplateInstance(loc, s.ident, dti.ti.tiargs);
        if (!ti.updateTempDecl(sc, s))
            return new ErrorExp();
        return new ScopeExp(loc, ti);
    }
    else
    {
        //printf("-searchUFCS() %s\n", s.toChars());
        return new DsymbolExp(loc, s);
    }
}

/******************************
 * check e is exp.opDispatch!(tiargs) or not
 * It's used to switch to UFCS the semantic analysis path
 */
extern (C++) bool isDotOpDispatch(Expression e)
{
    return e.op == TOKdotti && (cast(DotTemplateInstanceExp)e).ti.name == Id.opDispatch;
}

/******************************
 * Pull out callable entity with UFCS.
 */
extern (C++) Expression resolveUFCS(Scope* sc, CallExp ce)
{
    Loc loc = ce.loc;
    Expression eleft;
    Expression e;

    if (ce.e1.op == TOKdotid)
    {
        DotIdExp die = cast(DotIdExp)ce.e1;
        Identifier ident = die.ident;

        Expression ex = die.semanticX(sc);
        if (ex != die)
        {
            ce.e1 = ex;
            return null;
        }
        eleft = die.e1;

        Type t = eleft.type.toBasetype();
        if (t.ty == Tarray || t.ty == Tsarray || t.ty == Tnull || (t.isTypeBasic() && t.ty != Tvoid))
        {
            /* Built-in types and arrays have no callable properties, so do shortcut.
             * It is necessary in: e.init()
             */
        }
        else if (t.ty == Taarray)
        {
            if (ident == Id.remove)
            {
                /* Transform:
                 *  aa.remove(arg) into delete aa[arg]
                 */
                if (!ce.arguments || ce.arguments.dim != 1)
                {
                    ce.error("expected key as argument to aa.remove()");
                    return new ErrorExp();
                }
                if (!eleft.type.isMutable())
                {
                    ce.error("cannot remove key from %s associative array %s", MODtoChars(t.mod), eleft.toChars());
                    return new ErrorExp();
                }
                Expression key = (*ce.arguments)[0];
                key = key.semantic(sc);
                key = resolveProperties(sc, key);

                TypeAArray taa = cast(TypeAArray)t;
                key = key.implicitCastTo(sc, taa.index);

                if (key.checkValue())
                    return new ErrorExp();

                semanticTypeInfo(sc, taa.index);

                return new RemoveExp(loc, eleft, key);
            }
        }
        else
        {
            if (Expression ey = die.semanticY(sc, 1))
            {
                if (ey.op == TOKerror)
                    return ey;
                ce.e1 = ey;
                if (isDotOpDispatch(ey))
                {
                    uint errors = global.startGagging();
                    e = ce.syntaxCopy().semantic(sc);
                    if (!global.endGagging(errors))
                        return e;
                    /* fall down to UFCS */
                }
                else
                    return null;
            }
        }
        e = searchUFCS(sc, die, ident);
    }
    else if (ce.e1.op == TOKdotti)
    {
        DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)ce.e1;
        if (Expression ey = dti.semanticY(sc, 1))
        {
            ce.e1 = ey;
            return null;
        }
        eleft = dti.e1;
        e = searchUFCS(sc, dti, dti.ti.name);
    }
    else
        return null;

    // Rewrite
    ce.e1 = e;
    if (!ce.arguments)
        ce.arguments = new Expressions();
    ce.arguments.shift(eleft);

    return null;
}

/******************************
 * Pull out property with UFCS.
 */
extern (C++) Expression resolveUFCSProperties(Scope* sc, Expression e1, Expression e2 = null)
{
    Loc loc = e1.loc;
    Expression eleft;
    Expression e;

    if (e1.op == TOKdotid)
    {
        DotIdExp die = cast(DotIdExp)e1;
        eleft = die.e1;
        e = searchUFCS(sc, die, die.ident);
    }
    else if (e1.op == TOKdotti)
    {
        DotTemplateInstanceExp dti;
        dti = cast(DotTemplateInstanceExp)e1;
        eleft = dti.e1;
        e = searchUFCS(sc, dti, dti.ti.name);
    }
    else
        return null;

    // Rewrite
    if (e2)
    {
        // run semantic without gagging
        e2 = e2.semantic(sc);

        /* f(e1) = e2
         */
        Expression ex = e.copy();
        auto a1 = new Expressions();
        a1.setDim(1);
        (*a1)[0] = eleft;
        ex = new CallExp(loc, ex, a1);
        ex = ex.trySemantic(sc);

        /* f(e1, e2)
         */
        auto a2 = new Expressions();
        a2.setDim(2);
        (*a2)[0] = eleft;
        (*a2)[1] = e2;
        e = new CallExp(loc, e, a2);
        if (ex)
        {
            // if fallback setter exists, gag errors
            e = e.trySemantic(sc);
            if (!e)
            {
                checkPropertyCall(ex, e1);
                ex = new AssignExp(loc, ex, e2);
                return ex.semantic(sc);
            }
        }
        else
        {
            // strict setter prints errors if fails
            e = e.semantic(sc);
        }
        checkPropertyCall(e, e1);
        return e;
    }
    else
    {
        /* f(e1)
         */
        auto arguments = new Expressions();
        arguments.setDim(1);
        (*arguments)[0] = eleft;
        e = new CallExp(loc, e, arguments);
        e = e.semantic(sc);
        checkPropertyCall(e, e1);
        return e.semantic(sc);
    }
}

/******************************
 * Perform semantic() on an array of Expressions.
 */
extern (C++) bool arrayExpressionSemantic(Expressions* exps, Scope* sc, bool preserveErrors = false)
{
    bool err = false;
    if (exps)
    {
        for (size_t i = 0; i < exps.dim; i++)
        {
            Expression e = (*exps)[i];
            if (e)
            {
                e = e.semantic(sc);
                if (e.op == TOKerror)
                    err = true;
                if (preserveErrors || e.op != TOKerror)
                    (*exps)[i] = e;
            }
        }
    }
    return err;
}

/****************************************
 * Expand tuples.
 * Input:
 *      exps    aray of Expressions
 * Output:
 *      exps    rewritten in place
 */
extern (C++) void expandTuples(Expressions* exps)
{
    //printf("expandTuples()\n");
    if (exps)
    {
        for (size_t i = 0; i < exps.dim; i++)
        {
            Expression arg = (*exps)[i];
            if (!arg)
                continue;

            // Look for tuple with 0 members
            if (arg.op == TOKtype)
            {
                TypeExp e = cast(TypeExp)arg;
                if (e.type.toBasetype().ty == Ttuple)
                {
                    TypeTuple tt = cast(TypeTuple)e.type.toBasetype();
                    if (!tt.arguments || tt.arguments.dim == 0)
                    {
                        exps.remove(i);
                        if (i == exps.dim)
                            return;
                        i--;
                        continue;
                    }
                }
            }

            // Inline expand all the tuples
            while (arg.op == TOKtuple)
            {
                TupleExp te = cast(TupleExp)arg;
                exps.remove(i); // remove arg
                exps.insert(i, te.exps); // replace with tuple contents
                if (i == exps.dim)
                    return; // empty tuple, no more arguments
                (*exps)[i] = Expression.combine(te.e0, (*exps)[i]);
                arg = (*exps)[i];
            }
        }
    }
}

/****************************************
 * Expand alias this tuples.
 */
extern (C++) TupleDeclaration isAliasThisTuple(Expression e)
{
    if (!e.type)
        return null;

    Type t = e.type.toBasetype();
Lagain:
    if (Dsymbol s = t.toDsymbol(null))
    {
        AggregateDeclaration ad = s.isAggregateDeclaration();
        if (ad)
        {
            s = ad.aliasthis;
            if (s && s.isVarDeclaration())
            {
                TupleDeclaration td = s.isVarDeclaration().toAlias().isTupleDeclaration();
                if (td && td.isexp)
                    return td;
            }
            if (Type att = t.aliasthisOf())
            {
                t = att;
                goto Lagain;
            }
        }
    }
    return null;
}

extern (C++) int expandAliasThisTuples(Expressions* exps, size_t starti = 0)
{
    if (!exps || exps.dim == 0)
        return -1;

    for (size_t u = starti; u < exps.dim; u++)
    {
        Expression exp = (*exps)[u];
        TupleDeclaration td = isAliasThisTuple(exp);
        if (td)
        {
            exps.remove(u);
            for (size_t i = 0; i < td.objects.dim; ++i)
            {
                Expression e = isExpression((*td.objects)[i]);
                assert(e);
                assert(e.op == TOKdsymbol);
                DsymbolExp se = cast(DsymbolExp)e;
                Declaration d = se.s.isDeclaration();
                assert(d);
                e = new DotVarExp(exp.loc, exp, d);
                assert(d.type);
                e.type = d.type;
                exps.insert(u + i, e);
            }
            version (none)
            {
                printf("expansion ->\n");
                for (size_t i = 0; i < exps.dim; ++i)
                {
                    Expression e = (*exps)[i];
                    printf("\texps[%d] e = %s %s\n", i, Token.tochars[e.op], e.toChars());
                }
            }
            return cast(int)u;
        }
    }
    return -1;
}

/****************************************
 * The common type is determined by applying ?: to each pair.
 * Output:
 *      exps[]  properties resolved, implicitly cast to common type, rewritten in place
 *      *pt     if pt is not NULL, set to the common type
 * Returns:
 *      true    a semantic error was detected
 */
extern (C++) bool arrayExpressionToCommonType(Scope* sc, Expressions* exps, Type* pt)
{
    /* Still have a problem with:
     *  ubyte[][] = [ cast(ubyte[])"hello", [1]];
     * which works if the array literal is initialized top down with the ubyte[][]
     * type, but fails with this function doing bottom up typing.
     */

    //printf("arrayExpressionToCommonType()\n");
    scope IntegerExp integerexp = new IntegerExp(0);
    scope CondExp condexp = new CondExp(Loc(), integerexp, null, null);

    Type t0 = null;
    Expression e0 = null;
    size_t j0 = ~0;

    for (size_t i = 0; i < exps.dim; i++)
    {
        Expression e = (*exps)[i];
        if (!e)
            continue;

        e = resolveProperties(sc, e);
        if (!e.type)
        {
            e.error("%s has no value", e.toChars());
            t0 = Type.terror;
            continue;
        }
        if (e.op == TOKtype)
        {
            e.checkValue(); // report an error "type T has no value"
            t0 = Type.terror;
            continue;
        }
        if (checkNonAssignmentArrayOp(e))
        {
            t0 = Type.terror;
            continue;
        }

        e = doCopyOrMove(sc, e);

        if (t0 && !t0.equals(e.type))
        {
            /* This applies ?: to merge the types. It's backwards;
             * ?: should call this function to merge types.
             */
            condexp.type = null;
            condexp.e1 = e0;
            condexp.e2 = e;
            condexp.loc = e.loc;
            Expression ex = condexp.semantic(sc);
            if (ex.op == TOKerror)
                e = ex;
            else
            {
                (*exps)[j0] = condexp.e1;
                e = condexp.e2;
            }
        }
        j0 = i;
        e0 = e;
        t0 = e.type;
        if (e.op != TOKerror)
            (*exps)[i] = e;
    }

    if (!t0)
        t0 = Type.tvoid; // [] is typed as void[]
    else if (t0.ty != Terror)
    {
        for (size_t i = 0; i < exps.dim; i++)
        {
            Expression e = (*exps)[i];
            if (!e)
                continue;

            e = e.implicitCastTo(sc, t0);
            //assert(e->op != TOKerror);
            if (e.op == TOKerror)
            {
                /* Bugzilla 13024: a workaround for the bug in typeMerge -
                 * it should paint e1 and e2 by deduced common type,
                 * but doesn't in this particular case.
                 */
                t0 = Type.terror;
                break;
            }
            (*exps)[i] = e;
        }
    }
    if (pt)
        *pt = t0;

    return (t0 == Type.terror);
}

/****************************************
 * Get TemplateDeclaration enclosing FuncDeclaration.
 */
extern (C++) TemplateDeclaration getFuncTemplateDecl(Dsymbol s)
{
    FuncDeclaration f = s.isFuncDeclaration();
    if (f && f.parent)
    {
        TemplateInstance ti = f.parent.isTemplateInstance();
        if (ti && !ti.isTemplateMixin() && ti.tempdecl && (cast(TemplateDeclaration)ti.tempdecl).onemember && ti.tempdecl.ident == f.ident)
        {
            return cast(TemplateDeclaration)ti.tempdecl;
        }
    }
    return null;
}

/****************************************
 * Preprocess arguments to function.
 * Output:
 *      exps[]  tuples expanded, properties resolved, rewritten in place
 * Returns:
 *      true    a semantic error occurred
 */
extern (C++) bool preFunctionParameters(Loc loc, Scope* sc, Expressions* exps)
{
    bool err = false;
    if (exps)
    {
        expandTuples(exps);

        for (size_t i = 0; i < exps.dim; i++)
        {
            Expression arg = (*exps)[i];
            arg = resolveProperties(sc, arg);
            if (arg.op == TOKtype)
            {
                arg.error("cannot pass type %s as a function argument", arg.toChars());
                arg = new ErrorExp();
                err = true;
            }
            else if (checkNonAssignmentArrayOp(arg))
            {
                arg = new ErrorExp();
                err = true;
            }
            (*exps)[i] = arg;
        }
    }
    return err;
}

/************************************************
 * If we want the value of this expression, but do not want to call
 * the destructor on it.
 */
extern (C++) Expression valueNoDtor(Expression e)
{
    if (e.op == TOKcall)
    {
        /* The struct value returned from the function is transferred
         * so do not call the destructor on it.
         * Recognize:
         *       ((S _ctmp = S.init), _ctmp).this(...)
         * and make sure the destructor is not called on _ctmp
         * BUG: if e is a CommaExp, we should go down the right side.
         */
        CallExp ce = cast(CallExp)e;
        if (ce.e1.op == TOKdotvar)
        {
            DotVarExp dve = cast(DotVarExp)ce.e1;
            if (dve.var.isCtorDeclaration())
            {
                // It's a constructor call
                if (dve.e1.op == TOKcomma)
                {
                    CommaExp comma = cast(CommaExp)dve.e1;
                    if (comma.e2.op == TOKvar)
                    {
                        VarExp ve = cast(VarExp)comma.e2;
                        VarDeclaration ctmp = ve.var.isVarDeclaration();
                        if (ctmp)
                        {
                            ctmp.storage_class |= STCnodtor;
                            assert(!ce.isLvalue());
                        }
                    }
                }
            }
        }
    }
    else if (e.op == TOKvar)
    {
        auto vtmp = (cast(VarExp)e).var.isVarDeclaration();
        if (vtmp && (vtmp.storage_class & STCrvalue))
        {
            vtmp.storage_class |= STCnodtor;
        }
    }
    return e;
}

/********************************************
 * Issue an error if default construction is disabled for type t.
 * Default construction is required for arrays and 'out' parameters.
 * Returns:
 *      true    an error was issued
 */
extern (C++) bool checkDefCtor(Loc loc, Type t)
{
    t = t.baseElemOf();
    if (t.ty == Tstruct)
    {
        StructDeclaration sd = (cast(TypeStruct)t).sym;
        if (sd.noDefaultCtor)
        {
            sd.error(loc, "default construction is disabled");
            return true;
        }
    }
    return false;
}

/*********************************************
 * If e is an instance of a struct, and that struct has a copy constructor,
 * rewrite e as:
 *    (tmp = e),tmp
 * Input:
 *      sc      just used to specify the scope of created temporary variable
 */
extern (C++) Expression callCpCtor(Scope* sc, Expression e)
{
    Type tv = e.type.baseElemOf();
    if (tv.ty == Tstruct)
    {
        StructDeclaration sd = (cast(TypeStruct)tv).sym;
        if (sd.postblit)
        {
            /* Create a variable tmp, and replace the argument e with:
             *      (tmp = e),tmp
             * and let AssignExp() handle the construction.
             * This is not the most efficent, ideally tmp would be constructed
             * directly onto the stack.
             */
            auto tmp = copyToTemp(STCrvalue, "__copytmp", e);
            tmp.storage_class |= STCnodtor;
            tmp.semantic(sc);
            Expression de = new DeclarationExp(e.loc, tmp);
            Expression ve = new VarExp(e.loc, tmp);
            de.type = Type.tvoid;
            ve.type = e.type;
            e = Expression.combine(de, ve);
        }
    }
    return e;
}

/************************************************
 * Handle the postblit call on lvalue, or the move of rvalue.
 */
extern (C++) Expression doCopyOrMove(Scope *sc, Expression e)
{
    if (e.op == TOKquestion)
    {
        auto ce = cast(CondExp)e;
        ce.e1 = doCopyOrMove(sc, ce.e1);
        ce.e2 = doCopyOrMove(sc, ce.e2);
    }
    else
    {
        e = e.isLvalue() ? callCpCtor(sc, e) : valueNoDtor(e);
    }
    return e;
}

/****************************************
 * Now that we know the exact type of the function we're calling,
 * the arguments[] need to be adjusted:
 *      1. implicitly convert argument to the corresponding parameter type
 *      2. add default arguments for any missing arguments
 *      3. do default promotions on arguments corresponding to ...
 *      4. add hidden _arguments[] argument
 *      5. call copy constructor for struct value arguments
 * Input:
 *      tf      type of the function
 *      fd      the function being called, NULL if called indirectly
 * Output:
 *      *prettype return type of function
 *      *peprefix expression to execute before arguments[] are evaluated, NULL if none
 * Returns:
 *      true    errors happened
 */
extern (C++) bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, Expressions* arguments, FuncDeclaration fd, Type* prettype, Expression* peprefix)
{
    //printf("functionParameters()\n");
    assert(arguments);
    assert(fd || tf.next);
    size_t nargs = arguments ? arguments.dim : 0;
    size_t nparams = Parameter.dim(tf.parameters);
    uint olderrors = global.errors;
    bool err = false;
    *prettype = Type.terror;
    Expression eprefix = null;
    *peprefix = null;

    if (nargs > nparams && tf.varargs == 0)
    {
        error(loc, "expected %llu arguments, not %llu for non-variadic function type %s", cast(ulong)nparams, cast(ulong)nargs, tf.toChars());
        return true;
    }

    // If inferring return type, and semantic3() needs to be run if not already run
    if (!tf.next && fd.inferRetType)
    {
        fd.functionSemantic();
    }
    else if (fd && fd.parent)
    {
        TemplateInstance ti = fd.parent.isTemplateInstance();
        if (ti && ti.tempdecl)
        {
            fd.functionSemantic3();
        }
    }
    bool isCtorCall = fd && fd.needThis() && fd.isCtorDeclaration();

    size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams)

    /* If the function return type has wildcards in it, we'll need to figure out the actual type
     * based on the actual argument types.
     */
    MOD wildmatch = 0;
    if (tthis && tf.isWild() && !isCtorCall)
    {
        Type t = tthis;
        if (t.isImmutable())
            wildmatch = MODimmutable;
        else if (t.isWildConst())
            wildmatch = MODwildconst;
        else if (t.isWild())
            wildmatch = MODwild;
        else if (t.isConst())
            wildmatch = MODconst;
        else
            wildmatch = MODmutable;
    }

    int done = 0;
    for (size_t i = 0; i < n; i++)
    {
        Expression arg;

        if (i < nargs)
            arg = (*arguments)[i];
        else
            arg = null;

        if (i < nparams)
        {
            Parameter p = Parameter.getNth(tf.parameters, i);

            if (!arg)
            {
                if (!p.defaultArg)
                {
                    if (tf.varargs == 2 && i + 1 == nparams)
                        goto L2;
                    error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs);
                    return true;
                }
                arg = p.defaultArg;
                arg = inlineCopy(arg, sc);
                // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__
                arg = arg.resolveLoc(loc, sc);
                arguments.push(arg);
                nargs++;
            }

            if (tf.varargs == 2 && i + 1 == nparams)
            {
                //printf("\t\tvarargs == 2, p->type = '%s'\n", p->type->toChars());
                {
                    MATCH m;
                    if ((m = arg.implicitConvTo(p.type)) > MATCHnomatch)
                    {
                        if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m)
                            goto L2;
                        else if (nargs != nparams)
                        {
                            error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs);
                            return true;
                        }
                        goto L1;
                    }
                }
            L2:
                Type tb = p.type.toBasetype();
                Type tret = p.isLazyArray();
                switch (tb.ty)
                {
                case Tsarray:
                case Tarray:
                    {
                        /* Create a static array variable v of type arg->type:
                         *  T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ];
                         *
                         * The array literal in the initializer of the hidden variable
                         * is now optimized. See Bugzilla 2356.
                         */
                        Type tbn = (cast(TypeArray)tb).next;
                        Type tsa = tbn.sarrayOf(nargs - i);

                        auto elements = new Expressions();
                        elements.setDim(nargs - i);
                        for (size_t u = 0; u < elements.dim; u++)
                        {
                            Expression a = (*arguments)[i + u];
                            if (tret && a.implicitConvTo(tret))
                            {
                                a = a.implicitCastTo(sc, tret);
                                a = a.optimize(WANTvalue);
                                a = toDelegate(a, a.type, sc);
                            }
                            else
                                a = a.implicitCastTo(sc, tbn);
                            (*elements)[u] = a;
                        }
                        // Bugzilla 14395: Convert to a static array literal, or its slice.
                        arg = new ArrayLiteralExp(loc, elements);
                        arg.type = tsa;
                        if (tb.ty == Tarray)
                        {
                            arg = new SliceExp(loc, arg, null, null);
                            arg.type = p.type;
                        }
                        break;
                    }
                case Tclass:
                    {
                        /* Set arg to be:
                         *      new Tclass(arg0, arg1, ..., argn)
                         */
                        auto args = new Expressions();
                        args.setDim(nargs - i);
                        for (size_t u = i; u < nargs; u++)
                            (*args)[u - i] = (*arguments)[u];
                        arg = new NewExp(loc, null, null, p.type, args);
                        break;
                    }
                default:
                    if (!arg)
                    {
                        error(loc, "not enough arguments");
                        return true;
                    }
                    break;
                }
                arg = arg.semantic(sc);
                //printf("\targ = '%s'\n", arg->toChars());
                arguments.setDim(i + 1);
                (*arguments)[i] = arg;
                nargs = i + 1;
                done = 1;
            }

        L1:
            if (!(p.storageClass & STClazy && p.type.ty == Tvoid))
            {
                bool isRef = (p.storageClass & (STCref | STCout)) != 0;
                if (ubyte wm = arg.type.deduceWild(p.type, isRef))
                {
                    if (wildmatch)
                        wildmatch = MODmerge(wildmatch, wm);
                    else
                        wildmatch = wm;
                    //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p->type->toChars(), arg->type->toChars(), wm, wildmatch);
                }
            }
        }
        if (done)
            break;
    }
    if ((wildmatch == MODmutable || wildmatch == MODimmutable) && tf.next.hasWild() && (tf.isref || !tf.next.implicitConvTo(tf.next.immutableOf())))
    {
        if (fd)
        {
            /* If the called function may return the reference to
             * outer inout data, it should be rejected.
             *
             * void foo(ref inout(int) x) {
             *   ref inout(int) bar(inout(int)) { return x; }
             *   struct S { ref inout(int) bar() inout { return x; } }
             *   bar(int.init) = 1;  // bad!
             *   S().bar() = 1;      // bad!
             * }
             */
            Dsymbol s = null;
            if (fd.isThis() || fd.isNested())
                s = fd.toParent2();
            for (; s; s = s.toParent2())
            {
                if (auto ad = s.isAggregateDeclaration())
                {
                    if (ad.isNested())
                        continue;
                    break;
                }
                if (auto ff = s.isFuncDeclaration())
                {
                    if ((cast(TypeFunction)ff.type).iswild)
                        goto Linouterr;

                    if (ff.isNested() || ff.isThis())
                        continue;
                }
                break;
            }
        }
        else if (tf.isWild())
        {
        Linouterr:
            const(char)* s = wildmatch == MODmutable ? "mutable" : MODtoChars(wildmatch);
            error(loc, "modify inout to %s is not allowed inside inout function", s);
            return true;
        }
    }

    assert(nargs >= nparams);
    for (size_t i = 0; i < nargs; i++)
    {
        Expression arg = (*arguments)[i];
        assert(arg);
        if (i < nparams)
        {
            Parameter p = Parameter.getNth(tf.parameters, i);
            if (!(p.storageClass & STClazy && p.type.ty == Tvoid))
            {
                Type tprm = p.type;
                if (p.type.hasWild())
                    tprm = p.type.substWildTo(wildmatch);
                if (!tprm.equals(arg.type))
                {
                    //printf("arg->type = %s, p->type = %s\n", arg->type->toChars(), p->type->toChars());
                    arg = arg.implicitCastTo(sc, tprm);
                    arg = arg.optimize(WANTvalue, (p.storageClass & (STCref | STCout)) != 0);
                }
            }
            if (p.storageClass & STCref)
            {
                arg = arg.toLvalue(sc, arg);

                // Look for mutable misaligned pointer, etc., in @safe mode
                err |= checkUnsafeAccess(sc, arg, false, true);
            }
            else if (p.storageClass & STCout)
            {
                Type t = arg.type;
                if (!t.isMutable() || !t.isAssignable()) // check blit assignable
                {
                    arg.error("cannot modify struct %s with immutable members", arg.toChars());
                    err = true;
                }
                else
                {
                    // Look for misaligned pointer, etc., in @safe mode
                    err |= checkUnsafeAccess(sc, arg, false, true);
                    err |= checkDefCtor(arg.loc, t); // t must be default constructible
                }
                arg = arg.toLvalue(sc, arg);
            }
            else if (p.storageClass & STClazy)
            {
                // Convert lazy argument to a delegate
                if (p.type.ty == Tvoid)
                    arg = toDelegate(arg, p.type, sc);
                else
                    arg = toDelegate(arg, arg.type, sc);
            }
            //printf("arg: %s\n", arg->toChars());
            //printf("type: %s\n", arg->type->toChars());

            /* Look for arguments that cannot 'escape' from the called
             * function.
             */
            if (!tf.parameterEscapes(p))
            {
                Expression a = arg;
                if (a.op == TOKcast)
                    a = (cast(CastExp)a).e1;
                if (a.op == TOKfunction)
                {
                    /* Function literals can only appear once, so if this
                     * appearance was scoped, there cannot be any others.
                     */
                    FuncExp fe = cast(FuncExp)a;
                    fe.fd.tookAddressOf = 0;
                }
                else if (a.op == TOKdelegate)
                {
                    /* For passing a delegate to a scoped parameter,
                     * this doesn't count as taking the address of it.
                     * We only worry about 'escaping' references to the function.
                     */
                    DelegateExp de = cast(DelegateExp)a;
                    if (de.e1.op == TOKvar)
                    {
                        VarExp ve = cast(VarExp)de.e1;
                        FuncDeclaration f = ve.var.isFuncDeclaration();
                        if (f)
                        {
                            f.tookAddressOf--;
                            //printf("--tookAddressOf = %d\n", f.tookAddressOf);
                        }
                    }
                }
            }
            arg = arg.optimize(WANTvalue, (p.storageClass & (STCref | STCout)) != 0);
        }
        else
        {
            // These will be the trailing ... arguments
            // If not D linkage, do promotions
            if (tf.linkage != LINKd)
            {
                // Promote bytes, words, etc., to ints
                arg = integralPromotions(arg, sc);

                // Promote floats to doubles
                switch (arg.type.ty)
                {
                case Tfloat32:
                    arg = arg.castTo(sc, Type.tfloat64);
                    break;

                case Timaginary32:
                    arg = arg.castTo(sc, Type.timaginary64);
                    break;

                default:
                    break;
                }
                if (tf.varargs == 1)
                {
                    const(char)* p = tf.linkage == LINKc ? "extern(C)" : "extern(C++)";
                    if (arg.type.ty == Tarray)
                    {
                        arg.error("cannot pass dynamic arrays to %s vararg functions", p);
                        err = true;
                    }
                    if (arg.type.ty == Tsarray)
                    {
                        arg.error("cannot pass static arrays to %s vararg functions", p);
                        err = true;
                    }
                }
            }

            // Do not allow types that need destructors
            if (arg.type.needsDestruction())
            {
                arg.error("cannot pass types that need destruction as variadic arguments");
                err = true;
            }

            // Convert static arrays to dynamic arrays
            // BUG: I don't think this is right for D2
            Type tb = arg.type.toBasetype();
            if (tb.ty == Tsarray)
            {
                TypeSArray ts = cast(TypeSArray)tb;
                Type ta = ts.next.arrayOf();
                if (ts.size(arg.loc) == 0)
                    arg = new NullExp(arg.loc, ta);
                else
                    arg = arg.castTo(sc, ta);
            }
            if (tb.ty == Tstruct)
            {
                //arg = callCpCtor(sc, arg);
            }
            // Give error for overloaded function addresses
            if (arg.op == TOKsymoff)
            {
                SymOffExp se = cast(SymOffExp)arg;
                if (se.hasOverloads && !se.var.isFuncDeclaration().isUnique())
                {
                    arg.error("function %s is overloaded", arg.toChars());
                    err = true;
                }
            }
            if (arg.checkValue())
                err = true;
            arg = arg.optimize(WANTvalue);
        }
        (*arguments)[i] = arg;
    }

    /* Remaining problems:
     * 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is
     *    implemented by calling a function) we'll defer this for now.
     * 2. value structs (or static arrays of them) that need to be copy constructed
     * 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the
     *    function gets called (functions normally destroy their parameters)
     * 2 and 3 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned
     * up properly. Pushing arguments on the stack then cannot fail.
     */
    if (1)
    {
        /* TODO: tackle problem 1)
         */
        const bool leftToRight = true; // TODO: something like !fd.isArrayOp
        if (!leftToRight)
            assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity

        const ptrdiff_t start = (leftToRight ? 0 : cast(ptrdiff_t)nargs - 1);
        const ptrdiff_t end = (leftToRight ? cast(ptrdiff_t)nargs : -1);
        const ptrdiff_t step = (leftToRight ? 1 : -1);

        /* Compute indices of last throwing argument and first arg needing destruction.
         * Used to not set up destructors unless an arg needs destruction on a throw
         * in a later argument.
         */
        ptrdiff_t lastthrow = -1;
        ptrdiff_t firstdtor = -1;
        for (ptrdiff_t i = start; i != end; i += step)
        {
            Expression arg = (*arguments)[i];
            if (canThrow(arg, sc.func, false))
                lastthrow = i;
            if (firstdtor == -1 && arg.type.needsDestruction())
            {
                Parameter p = (i >= nparams ? null : Parameter.getNth(tf.parameters, i));
                if (!(p && (p.storageClass & (STClazy | STCref | STCout))))
                    firstdtor = i;
            }
        }

        /* Does problem 3) apply to this call?
         */
        const bool needsPrefix = (firstdtor >= 0 && lastthrow >= 0
            && (lastthrow - firstdtor) * step > 0);

        /* If so, initialize 'eprefix' by declaring the gate
         */
        VarDeclaration gate = null;
        if (needsPrefix)
        {
            // eprefix => bool __gate [= false]
            Identifier idtmp = Identifier.generateId("__gate");
            gate = new VarDeclaration(loc, Type.tbool, idtmp, null);
            gate.storage_class |= STCtemp | STCctfe | STCvolatile;
            gate.semantic(sc);

            auto ae = new DeclarationExp(loc, gate);
            eprefix = ae.semantic(sc);
        }

        for (ptrdiff_t i = start; i != end; i += step)
        {
            Expression arg = (*arguments)[i];

            Parameter parameter = (i >= nparams ? null : Parameter.getNth(tf.parameters, i));
            const bool isRef = (parameter && (parameter.storageClass & (STCref | STCout)));
            const bool isLazy = (parameter && (parameter.storageClass & STClazy));

            /* Skip lazy parameters
             */
            if (isLazy)
                continue;

            /* Do we have a gate? Then we have a prefix and we're not yet past the last throwing arg.
             * Declare a temporary variable for this arg and append that declaration to 'eprefix',
             * which will implicitly take care of potential problem 2) for this arg.
             * 'eprefix' will therefore finally contain all args up to and including the last
             * potentially throwing arg, excluding all lazy parameters.
             */
            if (gate)
            {
                const bool needsDtor = (!isRef && arg.type.needsDestruction() && i != lastthrow);

                /* Declare temporary 'auto __pfx = arg' (needsDtor) or 'auto __pfy = arg' (!needsDtor)
                 */
                auto tmp = copyToTemp(0,
                    needsDtor ? "__pfx" : "__pfy",
                    !isRef ? arg : arg.addressOf());
                tmp.semantic(sc);

                /* Modify the destructor so it only runs if gate==false, i.e.,
                 * only if there was a throw while constructing the args
                 */
                if (!needsDtor)
                {
                    if (tmp.edtor)
                    {
                        assert(i == lastthrow);
                        tmp.edtor = null;
                    }
                }
                else
                {
                    // edtor => (__gate || edtor)
                    assert(tmp.edtor);
                    Expression e = tmp.edtor;
                    e = new OrOrExp(e.loc, new VarExp(e.loc, gate), e);
                    tmp.edtor = e.semantic(sc);
                    //printf("edtor: %s\n", tmp.edtor.toChars());
                }

                // eprefix => (eprefix, auto __pfx/y = arg)
                auto ae = new DeclarationExp(loc, tmp);
                eprefix = Expression.combine(eprefix, ae.semantic(sc));

                // arg => __pfx/y
                arg = new VarExp(loc, tmp);
                arg = arg.semantic(sc);
                if (isRef)
                {
                    arg = new PtrExp(loc, arg);
                    arg = arg.semantic(sc);
                }

                /* Last throwing arg? Then finalize eprefix => (eprefix, gate = true),
                 * i.e., disable the dtors right after constructing the last throwing arg.
                 * From now on, the callee will take care of destructing the args because
                 * the args are implicitly moved into function parameters.
                 *
                 * Set gate to null to let the next iterations know they don't need to
                 * append to eprefix anymore.
                 */
                if (i == lastthrow)
                {
                    auto e = new AssignExp(gate.loc, new VarExp(gate.loc, gate), new IntegerExp(gate.loc, 1, Type.tbool));
                    eprefix = Expression.combine(eprefix, e.semantic(sc));
                    gate = null;
                }
            }
            else
            {
                /* No gate, no prefix to append to.
                 * Handle problem 2) by calling the copy constructor for value structs
                 * (or static arrays of them) if appropriate.
                 */
                Type tv = arg.type.baseElemOf();
                if (!isRef && tv.ty == Tstruct)
                    arg = doCopyOrMove(sc, arg);
            }

            (*arguments)[i] = arg;
        }
    }
    //if (eprefix) printf("eprefix: %s\n", eprefix->toChars());

    // If D linkage and variadic, add _arguments[] as first argument
    if (tf.linkage == LINKd && tf.varargs == 1)
    {
        assert(arguments.dim >= nparams);

        auto args = new Parameters();
        args.setDim(arguments.dim - nparams);
        for (size_t i = 0; i < arguments.dim - nparams; i++)
        {
            auto arg = new Parameter(STCin, (*arguments)[nparams + i].type, null, null);
            (*args)[i] = arg;
        }
        auto tup = new TypeTuple(args);
        Expression e = new TypeidExp(loc, tup);
        e = e.semantic(sc);
        arguments.insert(0, e);
    }

    Type tret = tf.next;
    if (isCtorCall)
    {
        //printf("[%s] fd = %s %s, %d %d %d\n", loc.toChars(), fd->toChars(), fd->type->toChars(),
        //    wildmatch, tf->isWild(), fd->isolateReturn());
        if (!tthis)
        {
            assert(sc.intypeof || global.errors);
            tthis = fd.isThis().type.addMod(fd.type.mod);
        }
        if (tf.isWild() && !fd.isolateReturn())
        {
            if (wildmatch)
                tret = tret.substWildTo(wildmatch);
            int offset;
            if (!tret.implicitConvTo(tthis) && !(MODimplicitConv(tret.mod, tthis.mod) && tret.isBaseOf(tthis, &offset) && offset == 0))
            {
                const(char)* s1 = tret.isNaked() ? " mutable" : tret.modToChars();
                const(char)* s2 = tthis.isNaked() ? " mutable" : tthis.modToChars();
                .error(loc, "inout constructor %s creates%s object, not%s", fd.toPrettyChars(), s1, s2);
                err = true;
            }
        }
        tret = tthis;
    }
    else if (wildmatch && tret)
    {
        /* Adjust function return type based on wildmatch
         */
        //printf("wildmatch = x%x, tret = %s\n", wildmatch, tret.toChars());
        tret = tret.substWildTo(wildmatch);
    }
    *prettype = tret;
    *peprefix = eprefix;
    return (err || olderrors != global.errors);
}

/****************************************************************/
/* A type meant as a union of all the Expression types,
 * to serve essentially as a Variant that will sit on the stack
 * during CTFE to reduce memory consumption.
 */
struct UnionExp
{
    // yes, default constructor does nothing
    extern (D) this(Expression e)
    {
        memcpy(&this, cast(void*)e, e.size);
    }

    /* Extract pointer to Expression
     */
    extern (C++) Expression exp()
    {
        return cast(Expression)&u;
    }

    /* Convert to an allocated Expression
     */
    extern (C++) Expression copy()
    {
        Expression e = exp();
        //if (e->size > sizeof(u)) printf("%s\n", Token::toChars(e->op));
        assert(e.size <= u.sizeof);
        if (e.op == TOKcantexp)
            return CTFEExp.cantexp;
        if (e.op == TOKvoidexp)
            return CTFEExp.voidexp;
        if (e.op == TOKbreak)
            return CTFEExp.breakexp;
        if (e.op == TOKcontinue)
            return CTFEExp.continueexp;
        if (e.op == TOKgoto)
            return CTFEExp.gotoexp;
        return e.copy();
    }

private:
    union __AnonStruct__u
    {
        char[__traits(classInstanceSize, Expression)] exp;
        char[__traits(classInstanceSize, IntegerExp)] integerexp;
        char[__traits(classInstanceSize, ErrorExp)] errorexp;
        char[__traits(classInstanceSize, RealExp)] realexp;
        char[__traits(classInstanceSize, ComplexExp)] complexexp;
        char[__traits(classInstanceSize, SymOffExp)] symoffexp;
        char[__traits(classInstanceSize, StringExp)] stringexp;
        char[__traits(classInstanceSize, ArrayLiteralExp)] arrayliteralexp;
        char[__traits(classInstanceSize, AssocArrayLiteralExp)] assocarrayliteralexp;
        char[__traits(classInstanceSize, StructLiteralExp)] structliteralexp;
        char[__traits(classInstanceSize, NullExp)] nullexp;
        char[__traits(classInstanceSize, DotVarExp)] dotvarexp;
        char[__traits(classInstanceSize, AddrExp)] addrexp;
        char[__traits(classInstanceSize, IndexExp)] indexexp;
        char[__traits(classInstanceSize, SliceExp)] sliceexp;
        // Ensure that the union is suitably aligned.
        real_t for_alignment_only;
    }

    __AnonStruct__u u;
}

/********************************
 * Test to see if two reals are the same.
 * Regard NaN's as equivalent.
 * Regard +0 and -0 as different.
 */
extern (C++) int RealEquals(real_t x1, real_t x2)
{
    return (CTFloat.isNaN(x1) && CTFloat.isNaN(x2)) || CTFloat.isIdentical(x1, x2);
}

/************************ TypeDotIdExp ************************************/
/* Things like:
 *      int.size
 *      foo.size
 *      (foo).size
 *      cast(foo).size
 */
extern (C++) DotIdExp typeDotIdExp(Loc loc, Type type, Identifier ident)
{
    return new DotIdExp(loc, new TypeExp(loc, type), ident);
}

/***********************************************
 * Mark variable v as modified if it is inside a constructor that var
 * is a field in.
 */
extern (C++) int modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1)
{
    //printf("modifyFieldVar(var = %s)\n", var->toChars());
    Dsymbol s = sc.func;
    while (1)
    {
        FuncDeclaration fd = null;
        if (s)
            fd = s.isFuncDeclaration();
        if (fd &&
            ((fd.isCtorDeclaration() && var.isField()) ||
             (fd.isStaticCtorDeclaration() && !var.isField())) &&
            fd.toParent2() == var.toParent2() &&
            (!e1 || e1.op == TOKthis))
        {
            bool result = true;

            var.ctorinit = true;
            //printf("setting ctorinit\n");

            if (var.isField() && sc.fieldinit && !sc.intypeof)
            {
                assert(e1);
                auto mustInit = ((var.storage_class & STCnodefaultctor) != 0 ||
                                 var.type.needsNested());

                auto dim = sc.fieldinit_dim;
                auto ad = fd.isMember2();
                assert(ad);
                size_t i;
                for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ?
                {
                    if (ad.fields[i] == var)
                        break;
                }
                assert(i < dim);
                uint fi = sc.fieldinit[i];

                if (fi & CSXthis_ctor)
                {
                    if (var.type.isMutable() && e1.type.isMutable())
                        result = false;
                    else
                    {
                        const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod);
                        .error(loc, "%s field '%s' initialized multiple times", modStr, var.toChars());
                    }
                }
                else if (sc.noctor || (fi & CSXlabel))
                {
                    if (!mustInit && var.type.isMutable() && e1.type.isMutable())
                        result = false;
                    else
                    {
                        const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod);
                        .error(loc, "%s field '%s' initialization is not allowed in loops or after labels", modStr, var.toChars());
                    }
                }

                sc.fieldinit[i] |= CSXthis_ctor;
                if (var.overlapped) // Bugzilla 15258
                {
                    foreach (j, v; ad.fields)
                    {
                        if (v is var || !var.isOverlappedWith(v))
                            continue;
                        v.ctorinit = true;
                        sc.fieldinit[j] = CSXthis_ctor;
                    }
                }
            }
            else if (fd != sc.func)
            {
                if (var.type.isMutable())
                    result = false;
                else if (sc.func.fes)
                {
                    const(char)* p = var.isField() ? "field" : var.kind();
                    .error(loc, "%s %s '%s' initialization is not allowed in foreach loop",
                        MODtoChars(var.type.mod), p, var.toChars());
                }
                else
                {
                    const(char)* p = var.isField() ? "field" : var.kind();
                    .error(loc, "%s %s '%s' initialization is not allowed in nested function '%s'",
                        MODtoChars(var.type.mod), p, var.toChars(), sc.func.toChars());
                }
            }
            return result;
        }
        else
        {
            if (s)
            {
                s = s.toParent2();
                continue;
            }
        }
        break;
    }
    return false;
}

extern (C++) Expression opAssignToOp(Loc loc, TOK op, Expression e1, Expression e2)
{
    Expression e;
    switch (op)
    {
    case TOKaddass:
        e = new AddExp(loc, e1, e2);
        break;

    case TOKminass:
        e = new MinExp(loc, e1, e2);
        break;

    case TOKmulass:
        e = new MulExp(loc, e1, e2);
        break;

    case TOKdivass:
        e = new DivExp(loc, e1, e2);
        break;

    case TOKmodass:
        e = new ModExp(loc, e1, e2);
        break;

    case TOKandass:
        e = new AndExp(loc, e1, e2);
        break;

    case TOKorass:
        e = new OrExp(loc, e1, e2);
        break;

    case TOKxorass:
        e = new XorExp(loc, e1, e2);
        break;

    case TOKshlass:
        e = new ShlExp(loc, e1, e2);
        break;

    case TOKshrass:
        e = new ShrExp(loc, e1, e2);
        break;

    case TOKushrass:
        e = new UshrExp(loc, e1, e2);
        break;

    default:
        assert(0);
    }
    return e;
}

/****************************************************************/

extern (C++) Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue)
{
    Expression e0;
    Expression e1 = Expression.extractLast(ue.e1, &e0);
    // Bugzilla 12585: Extract the side effect part if ue->e1 is comma.

    if (!isTrivialExp(e1))
    {
        /* Even if opDollar is needed, 'e1' should be evaluate only once. So
         * Rewrite:
         *      e1.opIndex( ... use of $ ... )
         *      e1.opSlice( ... use of $ ... )
         * as:
         *      (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...)
         *      (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...)
         */
        e1 = extractSideEffect(sc, "__dop", e0, e1, false);
        assert(e1.op == TOKvar);
        VarExp ve = cast(VarExp)e1;
        ve.var.storage_class |= STCexptemp;     // lifetime limited to expression
    }
    ue.e1 = e1;
    return e0;
}

/**************************************
 * Runs semantic on ae->arguments. Declares temporary variables
 * if '$' was used.
 */
extern (C++) Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0)
{
    assert(!ae.lengthVar);
    *pe0 = null;
    AggregateDeclaration ad = isAggregate(ae.e1.type);
    Dsymbol slice = search_function(ad, Id.slice);
    //printf("slice = %s %s\n", slice->kind(), slice->toChars());
    for (size_t i = 0; i < ae.arguments.dim; i++)
    {
        if (i == 0)
            *pe0 = extractOpDollarSideEffect(sc, ae);

        Expression e = (*ae.arguments)[i];
        if (e.op == TOKinterval && !(slice && slice.isTemplateDeclaration()))
        {
        Lfallback:
            if (ae.arguments.dim == 1)
                return null;
            ae.error("multi-dimensional slicing requires template opSlice");
            return new ErrorExp();
        }
        //printf("[%d] e = %s\n", i, e->toChars());

        // Create scope for '$' variable for this dimension
        auto sym = new ArrayScopeSymbol(sc, ae);
        sym.loc = ae.loc;
        sym.parent = sc.scopesym;
        sc = sc.push(sym);
        ae.lengthVar = null; // Create it only if required
        ae.currentDimension = i; // Dimension for $, if required

        e = e.semantic(sc);
        e = resolveProperties(sc, e);

        if (ae.lengthVar && sc.func)
        {
            // If $ was used, declare it now
            Expression de = new DeclarationExp(ae.loc, ae.lengthVar);
            de = de.semantic(sc);
            *pe0 = Expression.combine(*pe0, de);
        }
        sc = sc.pop();

        if (e.op == TOKinterval)
        {
            IntervalExp ie = cast(IntervalExp)e;

            auto tiargs = new Objects();
            Expression edim = new IntegerExp(ae.loc, i, Type.tsize_t);
            edim = edim.semantic(sc);
            tiargs.push(edim);

            auto fargs = new Expressions();
            fargs.push(ie.lwr);
            fargs.push(ie.upr);

            uint xerrors = global.startGagging();
            sc = sc.push();
            FuncDeclaration fslice = resolveFuncCall(ae.loc, sc, slice, tiargs, ae.e1.type, fargs, 1);
            sc = sc.pop();
            global.endGagging(xerrors);
            if (!fslice)
                goto Lfallback;

            e = new DotTemplateInstanceExp(ae.loc, ae.e1, slice.ident, tiargs);
            e = new CallExp(ae.loc, e, fargs);
            e = e.semantic(sc);
        }

        if (!e.type)
        {
            ae.error("%s has no value", e.toChars());
            e = new ErrorExp();
        }
        if (e.op == TOKerror)
            return e;

        (*ae.arguments)[i] = e;
    }
    return ae;
}

/**************************************
 * Runs semantic on se->lwr and se->upr. Declares a temporary variable
 * if '$' was used.
 */
extern (C++) Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, Expression* pe0)
{
    //assert(!ae->lengthVar);
    if (!ie)
        return ae;

    VarDeclaration lengthVar = ae.lengthVar;

    // create scope for '$'
    auto sym = new ArrayScopeSymbol(sc, ae);
    sym.loc = ae.loc;
    sym.parent = sc.scopesym;
    sc = sc.push(sym);

    for (size_t i = 0; i < 2; ++i)
    {
        Expression e = i == 0 ? ie.lwr : ie.upr;
        e = e.semantic(sc);
        e = resolveProperties(sc, e);
        if (!e.type)
        {
            ae.error("%s has no value", e.toChars());
            return new ErrorExp();
        }
        (i == 0 ? ie.lwr : ie.upr) = e;
    }

    if (lengthVar != ae.lengthVar && sc.func)
    {
        // If $ was used, declare it now
        Expression de = new DeclarationExp(ae.loc, ae.lengthVar);
        de = de.semantic(sc);
        *pe0 = Expression.combine(*pe0, de);
    }

    sc = sc.pop();

    return ae;
}

/***********************************************************
 * Resolve `exp` as a compile-time known string.
 * Params:
 *  sc  = scope
 *  exp = Expression which expected as a string
 *  s   = What the string is expected for, will be used in error diagnostic.
 * Returns:
 *  String literal, or `null` if error happens.
 */
StringExp semanticString(Scope *sc, Expression exp, const char* s)
{
    sc = sc.startCTFE();
    exp = exp.semantic(sc);
    exp = resolveProperties(sc, exp);
    sc = sc.endCTFE();

    if (exp.op == TOKerror)
        return null;

    auto e = exp;
    if (exp.type.isString())
    {
        e = e.ctfeInterpret();
        if (e.op == TOKerror)
            return null;
    }

    auto se = e.toStringExp();
    if (!se)
    {
        exp.error("string expected for %s, not (%s) of type %s",
            s, exp.toChars(), exp.type.toChars());
        return null;
    }
    return se;
}

enum OwnedBy : int
{
    OWNEDcode,          // normal code expression in AST
    OWNEDctfe,          // value expression for CTFE
    OWNEDcache,         // constant value cached for CTFE
}

alias OWNEDcode = OwnedBy.OWNEDcode;
alias OWNEDctfe = OwnedBy.OWNEDctfe;
alias OWNEDcache = OwnedBy.OWNEDcache;

enum WANTvalue  = 0;    // default
enum WANTexpand = 1;    // expand const/immutable variables if possible

/***********************************************************
 */
extern (C++) abstract class Expression : RootObject
{
    Loc loc;        // file location
    Type type;      // !=null means that semantic() has been run
    TOK op;         // to minimize use of dynamic_cast
    ubyte size;     // # of bytes in Expression so we can copy() it
    ubyte parens;   // if this is a parenthesized expression

    final extern (D) this(Loc loc, TOK op, int size)
    {
        //printf("Expression::Expression(op = %d) this = %p\n", op, this);
        this.loc = loc;
        this.op = op;
        this.size = cast(ubyte)size;
    }

    static void _init()
    {
        CTFEExp.cantexp = new CTFEExp(TOKcantexp);
        CTFEExp.voidexp = new CTFEExp(TOKvoidexp);
        CTFEExp.breakexp = new CTFEExp(TOKbreak);
        CTFEExp.continueexp = new CTFEExp(TOKcontinue);
        CTFEExp.gotoexp = new CTFEExp(TOKgoto);
    }

    /*********************************
     * Does *not* do a deep copy.
     */
    final Expression copy()
    {
        Expression e;
        if (!size)
        {
            debug
            {
                fprintf(stderr, "No expression copy for: %s\n", toChars());
                printf("op = %d\n", op);
                print();
            }
            assert(0);
        }
        e = cast(Expression)mem.xmalloc(size);
        //printf("Expression::copy(op = %d) e = %p\n", op, e);
        return cast(Expression)memcpy(cast(void*)e, cast(void*)this, size);
    }

    Expression syntaxCopy()
    {
        //printf("Expression::syntaxCopy()\n");
        //print();
        return copy();
    }

    /**************************
     * Semantically analyze Expression.
     * Determine types, fold constants, etc.
     */
    Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("Expression::semantic() %s\n", toChars());
        }
        if (type)
            type = type.semantic(loc, sc);
        else
            type = Type.tvoid;
        return this;
    }

    /**********************************
     * Try to run semantic routines.
     * If they fail, return NULL.
     */
    final Expression trySemantic(Scope* sc)
    {
        //printf("+trySemantic(%s)\n", toChars());
        uint errors = global.startGagging();
        Expression e = semantic(sc);
        if (global.endGagging(errors))
        {
            e = null;
        }
        //printf("-trySemantic(%s)\n", toChars());
        return e;
    }

    // kludge for template.isExpression()
    override final int dyncast()
    {
        return DYNCAST_EXPRESSION;
    }

    override final void print()
    {
        fprintf(stderr, "%s\n", toChars());
        fflush(stderr);
    }

    override const(char)* toChars()
    {
        OutBuffer buf;
        HdrGenState hgs;
        toCBuffer(this, &buf, &hgs);
        return buf.extractString();
    }

    /********************
     * Print AST data structure in a nice format.
     * Params:
     *  indent = indentation level
     */
    void printAST(int indent = 0)
    {
        foreach (i; 0 .. indent)
            printf(" ");
        printf("%s %s\n", Token.toChars(op), type ? type.toChars() : "");
    }

    final void error(const(char)* format, ...) const
    {
        if (type != Type.terror)
        {
            va_list ap;
            va_start(ap, format);
            .verror(loc, format, ap);
            va_end(ap);
        }
    }

    final void warning(const(char)* format, ...) const
    {
        if (type != Type.terror)
        {
            va_list ap;
            va_start(ap, format);
            .vwarning(loc, format, ap);
            va_end(ap);
        }
    }

    final void deprecation(const(char)* format, ...) const
    {
        if (type != Type.terror)
        {
            va_list ap;
            va_start(ap, format);
            .vdeprecation(loc, format, ap);
            va_end(ap);
        }
    }

    /**********************************
     * Combine e1 and e2 by CommaExp if both are not NULL.
     */
    static Expression combine(Expression e1, Expression e2)
    {
        if (e1)
        {
            if (e2)
            {
                e1 = new CommaExp(e1.loc, e1, e2);
                e1.type = e2.type;
            }
        }
        else
            e1 = e2;
        return e1;
    }

    /**********************************
     * If 'e' is a tree of commas, returns the leftmost expression
     * by stripping off it from the tree. The remained part of the tree
     * is returned via *pe0.
     * Otherwise 'e' is directly returned and *pe0 is set to NULL.
     */
    static Expression extractLast(Expression e, Expression* pe0)
    {
        if (e.op != TOKcomma)
        {
            *pe0 = null;
            return e;
        }

        CommaExp ce = cast(CommaExp)e;
        if (ce.e2.op != TOKcomma)
        {
            *pe0 = ce.e1;
            return ce.e2;
        }
        else
        {
            *pe0 = e;

            Expression* pce = &ce.e2;
            while ((cast(CommaExp)(*pce)).e2.op == TOKcomma)
            {
                pce = &(cast(CommaExp)(*pce)).e2;
            }
            assert((*pce).op == TOKcomma);
            ce = cast(CommaExp)(*pce);
            *pce = ce.e1;

            return ce.e2;
        }
    }

    static Expressions* arraySyntaxCopy(Expressions* exps)
    {
        Expressions* a = null;
        if (exps)
        {
            a = new Expressions();
            a.setDim(exps.dim);
            for (size_t i = 0; i < a.dim; i++)
            {
                Expression e = (*exps)[i];
                (*a)[i] = e ? e.syntaxCopy() : null;
            }
        }
        return a;
    }

    dinteger_t toInteger()
    {
        //printf("Expression %s\n", Token::toChars(op));
        error("integer constant expression expected instead of %s", toChars());
        return 0;
    }

    uinteger_t toUInteger()
    {
        //printf("Expression %s\n", Token::toChars(op));
        return cast(uinteger_t)toInteger();
    }

    real_t toReal()
    {
        error("floating point constant expression expected instead of %s", toChars());
        return CTFloat.zero;
    }

    real_t toImaginary()
    {
        error("floating point constant expression expected instead of %s", toChars());
        return CTFloat.zero;
    }

    complex_t toComplex()
    {
        error("floating point constant expression expected instead of %s", toChars());
        return complex_t(CTFloat.zero);
    }

    StringExp toStringExp()
    {
        return null;
    }

    /***************************************
     * Return !=0 if expression is an lvalue.
     */
    bool isLvalue()
    {
        return false;
    }

    /*******************************
     * Give error if we're not an lvalue.
     * If we can, convert expression to be an lvalue.
     */
    Expression toLvalue(Scope* sc, Expression e)
    {
        if (!e)
            e = this;
        else if (!loc.filename)
            loc = e.loc;

        if (e.op == TOKtype)
            error("%s '%s' is a type, not an lvalue", e.type.kind(), e.type.toChars());
        else
            error("%s is not an lvalue", e.toChars());

        return new ErrorExp();
    }

    Expression modifiableLvalue(Scope* sc, Expression e)
    {
        //printf("Expression::modifiableLvalue() %s, type = %s\n", toChars(), type->toChars());
        // See if this expression is a modifiable lvalue (i.e. not const)
        if (checkModifiable(sc) == 1)
        {
            assert(type);
            if (!type.isMutable())
            {
                error("cannot modify %s expression %s", MODtoChars(type.mod), toChars());
                return new ErrorExp();
            }
            else if (!type.isAssignable())
            {
                error("cannot modify struct %s %s with immutable members", toChars(), type.toChars());
                return new ErrorExp();
            }
        }
        return toLvalue(sc, e);
    }

    final Expression implicitCastTo(Scope* sc, Type t)
    {
        return .implicitCastTo(this, sc, t);
    }

    final MATCH implicitConvTo(Type t)
    {
        return .implicitConvTo(this, t);
    }

    final Expression castTo(Scope* sc, Type t)
    {
        return .castTo(this, sc, t);
    }

    /****************************************
     * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__ to loc.
     */
    Expression resolveLoc(Loc loc, Scope* sc)
    {
        return this;
    }

    /****************************************
     * Check that the expression has a valid type.
     * If not, generates an error "... has no type".
     * Returns:
     *      true if the expression is not valid.
     * Note:
     *      When this function returns true, `checkValue()` should also return true.
     */
    bool checkType()
    {
        return false;
    }

    /****************************************
     * Check that the expression has a valid value.
     * If not, generates an error "... has no value".
     * Returns:
     *      true if the expression is not valid or has void type.
     */
    bool checkValue()
    {
        if (type && type.toBasetype().ty == Tvoid)
        {
            error("expression %s is void and has no value", toChars());
            //print(); assert(0);
            if (!global.gag)
                type = Type.terror;
            return true;
        }
        return false;
    }

    final bool checkScalar()
    {
        if (op == TOKerror)
            return true;
        if (type.toBasetype().ty == Terror)
            return true;
        if (!type.isscalar())
        {
            error("'%s' is not a scalar, it is a %s", toChars(), type.toChars());
            return true;
        }
        return checkValue();
    }

    final bool checkNoBool()
    {
        if (op == TOKerror)
            return true;
        if (type.toBasetype().ty == Terror)
            return true;
        if (type.toBasetype().ty == Tbool)
        {
            error("operation not allowed on bool '%s'", toChars());
            return true;
        }
        return false;
    }

    final bool checkIntegral()
    {
        if (op == TOKerror)
            return true;
        if (type.toBasetype().ty == Terror)
            return true;
        if (!type.isintegral())
        {
            error("'%s' is not of integral type, it is a %s", toChars(), type.toChars());
            return true;
        }
        return checkValue();
    }

    final bool checkArithmetic()
    {
        if (op == TOKerror)
            return true;
        if (type.toBasetype().ty == Terror)
            return true;
        if (!type.isintegral() && !type.isfloating())
        {
            error("'%s' is not of arithmetic type, it is a %s", toChars(), type.toChars());
            return true;
        }
        return checkValue();
    }

    final void checkDeprecated(Scope* sc, Dsymbol s)
    {
        s.checkDeprecated(loc, sc);
    }

    /*********************************************
     * Calling function f.
     * Check the purity, i.e. if we're in a pure function
     * we can only call other pure functions.
     * Returns true if error occurs.
     */
    final bool checkPurity(Scope* sc, FuncDeclaration f)
    {
        if (!sc.func)
            return false;
        if (sc.func == f)
            return false;
        if (sc.intypeof == 1)
            return false;
        if (sc.flags & (SCOPEctfe | SCOPEdebug))
            return false;

        /* Given:
         * void f() {
         *   pure void g() {
         *     /+pure+/ void h() {
         *       /+pure+/ void i() { }
         *     }
         *   }
         * }
         * g() can call h() but not f()
         * i() can call h() and g() but not f()
         */

        // Find the closest pure parent of the calling function
        FuncDeclaration outerfunc = sc.func;
        FuncDeclaration calledparent = f;

        if (outerfunc.isInstantiated())
        {
            // The attributes of outerfunc should be inferred from the call of f.
        }
        else if (f.isInstantiated())
        {
            // The attributes of f are inferred from its body.
        }
        else if (f.isFuncLiteralDeclaration())
        {
            // The attributes of f are always inferred in its declared place.
        }
        else
        {
            /* Today, static local functions are impure by default, but they cannot
             * violate purity of enclosing functions.
             *
             *  auto foo() pure {      // non instantiated funciton
             *    static auto bar() {  // static, without pure attribute
             *      impureFunc();      // impure call
             *      // Although impureFunc is called inside bar, f(= impureFunc)
             *      // is not callable inside pure outerfunc(= foo <- bar).
             *    }
             *
             *    bar();
             *    // Although bar is called inside foo, f(= bar) is callable
             *    // bacause calledparent(= foo) is same with outerfunc(= foo).
             *  }
             */

            while (outerfunc.toParent2() && outerfunc.isPureBypassingInference() == PUREimpure && outerfunc.toParent2().isFuncDeclaration())
            {
                outerfunc = outerfunc.toParent2().isFuncDeclaration();
                if (outerfunc.type.ty == Terror)
                    return true;
            }
            while (calledparent.toParent2() && calledparent.isPureBypassingInference() == PUREimpure && calledparent.toParent2().isFuncDeclaration())
            {
                calledparent = calledparent.toParent2().isFuncDeclaration();
                if (calledparent.type.ty == Terror)
                    return true;
            }
        }

        // If the caller has a pure parent, then either the called func must be pure,
        // OR, they must have the same pure parent.
        if (!f.isPure() && calledparent != outerfunc)
        {
            FuncDeclaration ff = outerfunc;
            if (sc.flags & SCOPEcompile ? ff.isPureBypassingInference() >= PUREweak : ff.setImpure())
            {
                error("pure %s '%s' cannot call impure %s '%s'",
                    ff.kind(), ff.toPrettyChars(), f.kind(), f.toPrettyChars());
                return true;
            }
        }
        return false;
    }

    /*******************************************
     * Accessing variable v.
     * Check for purity and safety violations.
     * Returns true if error occurs.
     */
    final bool checkPurity(Scope* sc, VarDeclaration v)
    {
        //printf("v = %s %s\n", v->type->toChars(), v->toChars());
        /* Look for purity and safety violations when accessing variable v
         * from current function.
         */
        if (!sc.func)
            return false;
        if (sc.intypeof == 1)
            return false; // allow violations inside typeof(expression)
        if (sc.flags & (SCOPEctfe | SCOPEdebug))
            return false; // allow violations inside compile-time evaluated expressions and debug conditionals
        if (v.ident == Id.ctfe)
            return false; // magic variable never violates pure and safe
        if (v.isImmutable())
            return false; // always safe and pure to access immutables...
        if (v.isConst() && !v.isRef() && (v.isDataseg() || v.isParameter()) && v.type.implicitConvTo(v.type.immutableOf()))
            return false; // or const global/parameter values which have no mutable indirections
        if (v.storage_class & STCmanifest)
            return false; // ...or manifest constants

        bool err = false;
        if (v.isDataseg())
        {
            // Bugzilla 7533: Accessing implicit generated __gate is pure.
            if (v.ident == Id.gate)
                return false;

            /* Accessing global mutable state.
             * Therefore, this function and all its immediately enclosing
             * functions must be pure.
             */
            /* Today, static local functions are impure by default, but they cannot
             * violate purity of enclosing functions.
             *
             *  auto foo() pure {      // non instantiated funciton
             *    static auto bar() {  // static, without pure attribute
             *      globalData++;      // impure access
             *      // Although globalData is accessed inside bar,
             *      // it is not accessible inside pure foo.
             *    }
             *  }
             */
            for (Dsymbol s = sc.func; s; s = s.toParent2())
            {
                FuncDeclaration ff = s.isFuncDeclaration();
                if (!ff)
                    break;
                if (sc.flags & SCOPEcompile ? ff.isPureBypassingInference() >= PUREweak : ff.setImpure())
                {
                    error("pure %s '%s' cannot access mutable static data '%s'",
                        ff.kind(), ff.toPrettyChars(), v.toChars());
                    err = true;
                    break;
                }

                /* If the enclosing is an instantiated function or a lambda, its
                 * attribute inference result is preferred.
                 */
                if (ff.isInstantiated())
                    break;
                if (ff.isFuncLiteralDeclaration())
                    break;
            }
        }
        else
        {
            /* Given:
             * void f() {
             *   int fx;
             *   pure void g() {
             *     int gx;
             *     /+pure+/ void h() {
             *       int hx;
             *       /+pure+/ void i() { }
             *     }
             *   }
             * }
             * i() can modify hx and gx but not fx
             */

            Dsymbol vparent = v.toParent2();
            for (Dsymbol s = sc.func; !err && s; s = s.toParent2())
            {
                if (s == vparent)
                    break;

                if (AggregateDeclaration ad = s.isAggregateDeclaration())
                {
                    if (ad.isNested())
                        continue;
                    break;
                }
                FuncDeclaration ff = s.isFuncDeclaration();
                if (!ff)
                    break;
                if (ff.isNested() || ff.isThis())
                {
                    if (ff.type.isImmutable() ||
                        ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod))
                    {
                        OutBuffer ffbuf;
                        OutBuffer vbuf;
                        MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod);
                        MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod);
                        error("%s%s '%s' cannot access %sdata '%s'",
                            ffbuf.peekString(), ff.kind(), ff.toPrettyChars(), vbuf.peekString(), v.toChars());
                        err = true;
                        break;
                    }
                    continue;
                }
                break;
            }
        }

        /* Do not allow safe functions to access __gshared data
         */
        if (v.storage_class & STCgshared)
        {
            if (sc.func.setUnsafe())
            {
                error("safe %s '%s' cannot access __gshared data '%s'",
                    sc.func.kind(), sc.func.toChars(), v.toChars());
                err = true;
            }
        }

        return err;
    }

    /*********************************************
     * Calling function f.
     * Check the safety, i.e. if we're in a @safe function
     * we can only call @safe or @trusted functions.
     * Returns true if error occurs.
     */
    final bool checkSafety(Scope* sc, FuncDeclaration f)
    {
        if (!sc.func)
            return false;
        if (sc.func == f)
            return false;
        if (sc.intypeof == 1)
            return false;
        if (sc.flags & SCOPEctfe)
            return false;

        if (!f.isSafe() && !f.isTrusted())
        {
            if (sc.flags & SCOPEcompile ? sc.func.isSafeBypassingInference() : sc.func.setUnsafe())
            {
                if (loc.linnum == 0) // e.g. implicitly generated dtor
                    loc = sc.func.loc;
                error("@safe %s '%s' cannot call @system %s '%s'",
                    sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
                return true;
            }
        }
        return false;
    }

    /*********************************************
     * Calling function f.
     * Check the @nogc-ness, i.e. if we're in a @nogc function
     * we can only call other @nogc functions.
     * Returns true if error occurs.
     */
    final bool checkNogc(Scope* sc, FuncDeclaration f)
    {
        if (!sc.func)
            return false;
        if (sc.func == f)
            return false;
        if (sc.intypeof == 1)
            return false;
        if (sc.flags & SCOPEctfe)
            return false;

        if (!f.isNogc())
        {
            if (sc.flags & SCOPEcompile ? sc.func.isNogcBypassingInference() : sc.func.setGC())
            {
                if (loc.linnum == 0) // e.g. implicitly generated dtor
                    loc = sc.func.loc;
                error("@nogc %s '%s' cannot call non-@nogc %s '%s'",
                    sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
                return true;
            }
        }
        return false;
    }

    /********************************************
     * Check that the postblit is callable if t is an array of structs.
     * Returns true if error happens.
     */
    final bool checkPostblit(Scope* sc, Type t)
    {
        t = t.baseElemOf();
        if (t.ty == Tstruct)
        {
            // Bugzilla 11395: Require TypeInfo generation for array concatenation
            semanticTypeInfo(sc, t);

            StructDeclaration sd = (cast(TypeStruct)t).sym;
            if (sd.postblit)
            {
                if (sd.postblit.storage_class & STCdisable)
                {
                    sd.error(loc, "is not copyable because it is annotated with @disable");
                    return true;
                }
                //checkDeprecated(sc, sd->postblit);        // necessary?
                checkPurity(sc, sd.postblit);
                checkSafety(sc, sd.postblit);
                checkNogc(sc, sd.postblit);
                //checkAccess(sd, loc, sc, sd->postblit);   // necessary?
                return false;
            }
        }
        return false;
    }

    final bool checkRightThis(Scope* sc)
    {
        if (op == TOKerror)
            return true;
        if (op == TOKvar && type.ty != Terror)
        {
            VarExp ve = cast(VarExp)this;
            if (isNeedThisScope(sc, ve.var))
            {
                //printf("checkRightThis sc->intypeof = %d, ad = %p, func = %p, fdthis = %p\n",
                //        sc->intypeof, sc->getStructClassScope(), func, fdthis);
                error("need 'this' for '%s' of type '%s'", ve.var.toChars(), ve.var.type.toChars());
                return true;
            }
        }
        return false;
    }

    /*******************************
     * Check whether the expression allows RMW operations, error with rmw operator diagnostic if not.
     * ex is the RHS expression, or NULL if ++/-- is used (for diagnostics)
     * Returns true if error occurs.
     */
    final bool checkReadModifyWrite(TOK rmwOp, Expression ex = null)
    {
        //printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex->toChars() : "");
        if (!type || !type.isShared())
            return false;

        // atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal.
        switch (rmwOp)
        {
        case TOKplusplus:
        case TOKpreplusplus:
            rmwOp = TOKaddass;
            break;
        case TOKminusminus:
        case TOKpreminusminus:
            rmwOp = TOKminass;
            break;
        default:
            break;
        }

        deprecation("read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!\"%s\"(%s, %s) instead.", Token.toChars(rmwOp), toChars(), ex ? ex.toChars() : "1");
        return false;

        // note: enable when deprecation becomes an error.
        // return true;
    }

    /***************************************
     * Parameters:
     *      sc:     scope
     *      flag:   1: do not issue error message for invalid modification
     * Returns:
     *      0:      is not modifiable
     *      1:      is modifiable in default == being related to type->isMutable()
     *      2:      is modifiable, because this is a part of initializing.
     */
    int checkModifiable(Scope* sc, int flag = 0)
    {
        return type ? 1 : 0; // default modifiable
    }

    /*****************************
     * If expression can be tested for true or false,
     * returns the modified expression.
     * Otherwise returns ErrorExp.
     */
    Expression toBoolean(Scope* sc)
    {
        // Default is 'yes' - do nothing
        debug
        {
            if (!type)
                print();
            assert(type);
        }
        Expression e = this;
        Type t = type;
        Type tb = type.toBasetype();
        Type att = null;
    Lagain:
        // Structs can be converted to bool using opCast(bool)()
        if (tb.ty == Tstruct)
        {
            AggregateDeclaration ad = (cast(TypeStruct)tb).sym;
            /* Don't really need to check for opCast first, but by doing so we
             * get better error messages if it isn't there.
             */
            Dsymbol fd = search_function(ad, Id._cast);
            if (fd)
            {
                e = new CastExp(loc, e, Type.tbool);
                e = e.semantic(sc);
                return e;
            }

            // Forward to aliasthis.
            if (ad.aliasthis && tb != att)
            {
                if (!att && tb.checkAliasThisRec())
                    att = tb;
                e = resolveAliasThis(sc, e);
                t = e.type;
                tb = e.type.toBasetype();
                goto Lagain;
            }
        }

        if (!t.isBoolean())
        {
            if (tb != Type.terror)
                error("expression %s of type %s does not have a boolean value", toChars(), t.toChars());
            return new ErrorExp();
        }
        return e;
    }

    /************************************************
     * Destructors are attached to VarDeclarations.
     * Hence, if expression returns a temp that needs a destructor,
     * make sure and create a VarDeclaration for that temp.
     */
    Expression addDtorHook(Scope* sc)
    {
        return this;
    }

    /******************************
     * Take address of expression.
     */
    final Expression addressOf()
    {
        //printf("Expression::addressOf()\n");
        debug
        {
            assert(op == TOKerror || isLvalue());
        }
        Expression e = new AddrExp(loc, this);
        e.type = type.pointerTo();
        return e;
    }

    /******************************
     * If this is a reference, dereference it.
     */
    final Expression deref()
    {
        //printf("Expression::deref()\n");
        // type could be null if forward referencing an 'auto' variable
        if (type && type.ty == Treference)
        {
            Expression e = new PtrExp(loc, this);
            e.type = (cast(TypeReference)type).next;
            return e;
        }
        return this;
    }

    final Expression optimize(int result, bool keepLvalue = false)
    {
        return Expression_optimize(this, result, keepLvalue);
    }

    // Entry point for CTFE.
    // A compile-time result is required. Give an error if not possible
    final Expression ctfeInterpret()
    {
        return .ctfeInterpret(this);
    }

    final int isConst()
    {
        return .isConst(this);
    }

    /********************************
     * Does this expression statically evaluate to a boolean 'result' (true or false)?
     */
    bool isBool(bool result)
    {
        return false;
    }

    final Expression op_overload(Scope* sc)
    {
        return .op_overload(this, sc);
    }

    void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class IntegerExp : Expression
{
    dinteger_t value;

    extern (D) this(Loc loc, dinteger_t value, Type type)
    {
        super(loc, TOKint64, __traits(classInstanceSize, IntegerExp));
        //printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type->toChars() : "");
        assert(type);
        if (!type.isscalar())
        {
            //printf("%s, loc = %d\n", toChars(), loc.linnum);
            if (type.ty != Terror)
                error("integral constant must be scalar type, not %s", type.toChars());
            type = Type.terror;
        }
        this.type = type;
        setInteger(value);
    }

    extern (D) this(dinteger_t value)
    {
        super(Loc(), TOKint64, __traits(classInstanceSize, IntegerExp));
        this.type = Type.tint32;
        this.value = cast(d_int32)value;
    }

    override bool equals(RootObject o)
    {
        if (this == o)
            return true;
        if ((cast(Expression)o).op == TOKint64)
        {
            IntegerExp ne = cast(IntegerExp)o;
            if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && value == ne.value)
            {
                return true;
            }
        }
        return false;
    }

    override Expression semantic(Scope* sc)
    {
        assert(type);
        if (type.ty == Terror)
            return new ErrorExp();
        assert(type.deco);
        normalize();
        return this;
    }

    override dinteger_t toInteger()
    {
        normalize(); // necessary until we fix all the paints of 'type'
        return value;
    }

    override real_t toReal()
    {
        normalize(); // necessary until we fix all the paints of 'type'
        Type t = type.toBasetype();
        if (t.ty == Tuns64)
            return real_t(cast(d_uns64)value);
        else
            return real_t(cast(d_int64)value);
    }

    override real_t toImaginary()
    {
        return CTFloat.zero;
    }

    override complex_t toComplex()
    {
        return complex_t(toReal());
    }

    override bool isBool(bool result)
    {
        bool r = toInteger() != 0;
        return result ? r : !r;
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        if (!e)
            e = this;
        else if (!loc.filename)
            loc = e.loc;
        e.error("constant %s is not an lvalue", e.toChars());
        return new ErrorExp();
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }

    dinteger_t getInteger()
    {
        return value;
    }

    void setInteger(dinteger_t value)
    {
        this.value = value;
        normalize();
    }

private:
    void normalize()
    {
        /* 'Normalize' the value of the integer to be in range of the type
         */
        switch (type.toBasetype().ty)
        {
        case Tbool:
            value = (value != 0);
            break;

        case Tint8:
            value = cast(d_int8)value;
            break;

        case Tchar:
        case Tuns8:
            value = cast(d_uns8)value;
            break;

        case Tint16:
            value = cast(d_int16)value;
            break;

        case Twchar:
        case Tuns16:
            value = cast(d_uns16)value;
            break;

        case Tint32:
            value = cast(d_int32)value;
            break;

        case Tdchar:
        case Tuns32:
            value = cast(d_uns32)value;
            break;

        case Tint64:
            value = cast(d_int64)value;
            break;

        case Tuns64:
            value = cast(d_uns64)value;
            break;

        case Tpointer:
            if (Target.ptrsize == 4)
                value = cast(d_uns32)value;
            else if (Target.ptrsize == 8)
                value = cast(d_uns64)value;
            else
                assert(0);
            break;

        default:
            break;
        }
    }
}

/***********************************************************
 * Use this expression for error recovery.
 * It should behave as a 'sink' to prevent further cascaded error messages.
 */
extern (C++) final class ErrorExp : Expression
{
    extern (D) this()
    {
        super(Loc(), TOKerror, __traits(classInstanceSize, ErrorExp));
        type = Type.terror;
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }

    extern (C++) static __gshared ErrorExp errorexp; // handy shared value
}

/***********************************************************
 */
extern (C++) final class RealExp : Expression
{
    real_t value;

    extern (D) this(Loc loc, real_t value, Type type)
    {
        super(loc, TOKfloat64, __traits(classInstanceSize, RealExp));
        //printf("RealExp::RealExp(%Lg)\n", value);
        this.value = value;
        this.type = type;
    }

    override bool equals(RootObject o)
    {
        if (this == o)
            return true;
        if ((cast(Expression)o).op == TOKfloat64)
        {
            RealExp ne = cast(RealExp)o;
            if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && RealEquals(value, ne.value))
            {
                return true;
            }
        }
        return false;
    }

    override Expression semantic(Scope* sc)
    {
        if (!type)
            type = Type.tfloat64;
        else
            type = type.semantic(loc, sc);
        return this;
    }

    override dinteger_t toInteger()
    {
        return cast(sinteger_t)toReal();
    }

    override uinteger_t toUInteger()
    {
        return cast(uinteger_t)toReal();
    }

    override real_t toReal()
    {
        return type.isreal() ? value : CTFloat.zero;
    }

    override real_t toImaginary()
    {
        return type.isreal() ? CTFloat.zero : value;
    }

    override complex_t toComplex()
    {
        return complex_t(toReal(), toImaginary());
    }

    override bool isBool(bool result)
    {
        return result ? cast(bool)value : !cast(bool)value;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class ComplexExp : Expression
{
    complex_t value;

    extern (D) this(Loc loc, complex_t value, Type type)
    {
        super(loc, TOKcomplex80, __traits(classInstanceSize, ComplexExp));
        this.value = value;
        this.type = type;
        //printf("ComplexExp::ComplexExp(%s)\n", toChars());
    }

    override bool equals(RootObject o)
    {
        if (this == o)
            return true;
        if ((cast(Expression)o).op == TOKcomplex80)
        {
            ComplexExp ne = cast(ComplexExp)o;
            if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && RealEquals(creall(value), creall(ne.value)) && RealEquals(cimagl(value), cimagl(ne.value)))
            {
                return true;
            }
        }
        return false;
    }

    override Expression semantic(Scope* sc)
    {
        if (!type)
            type = Type.tcomplex80;
        else
            type = type.semantic(loc, sc);
        return this;
    }

    override dinteger_t toInteger()
    {
        return cast(sinteger_t)toReal();
    }

    override uinteger_t toUInteger()
    {
        return cast(uinteger_t)toReal();
    }

    override real_t toReal()
    {
        return creall(value);
    }

    override real_t toImaginary()
    {
        return cimagl(value);
    }

    override complex_t toComplex()
    {
        return value;
    }

    override bool isBool(bool result)
    {
        if (result)
            return cast(bool)value;
        else
            return !value;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) class IdentifierExp : Expression
{
    Identifier ident;

    final extern (D) this(Loc loc, Identifier ident)
    {
        super(loc, TOKidentifier, __traits(classInstanceSize, IdentifierExp));
        this.ident = ident;
    }

    static IdentifierExp create(Loc loc, Identifier ident)
    {
        return new IdentifierExp(loc, ident);
    }

    override final Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("IdentifierExp::semantic('%s')\n", ident.toChars());
        }
        if (type) // This is used as the dummy expression
            return this;

        Dsymbol scopesym;
        Dsymbol s = sc.search(loc, ident, &scopesym);
        if (s)
        {
            if (s.errors)
                return new ErrorExp();

            Expression e;

            /* See if the symbol was a member of an enclosing 'with'
             */
            WithScopeSymbol withsym = scopesym.isWithScopeSymbol();
            if (withsym && withsym.withstate.wthis)
            {
                /* Disallow shadowing
                 */
                // First find the scope of the with
                Scope* scwith = sc;
                while (scwith.scopesym != scopesym)
                {
                    scwith = scwith.enclosing;
                    assert(scwith);
                }
                // Look at enclosing scopes for symbols with the same name,
                // in the same function
                for (Scope* scx = scwith; scx && scx.func == scwith.func; scx = scx.enclosing)
                {
                    Dsymbol s2;
                    if (scx.scopesym && scx.scopesym.symtab && (s2 = scx.scopesym.symtab.lookup(s.ident)) !is null && s != s2)
                    {
                        error("with symbol %s is shadowing local symbol %s", s.toPrettyChars(), s2.toPrettyChars());
                        return new ErrorExp();
                    }
                }
                s = s.toAlias();

                // Same as wthis.ident
                //  TODO: DotIdExp.semantic will find 'ident' from 'wthis' again.
                //  The redudancy should be removed.
                e = new VarExp(loc, withsym.withstate.wthis);
                e = new DotIdExp(loc, e, ident);
                e = e.semantic(sc);
            }
            else
            {
                if (withsym)
                {
                    Declaration d = s.isDeclaration();
                    if (d)
                        checkAccess(loc, sc, null, d);
                }

                /* If f is really a function template,
                 * then replace f with the function template declaration.
                 */
                FuncDeclaration f = s.isFuncDeclaration();
                if (f)
                {
                    TemplateDeclaration td = getFuncTemplateDecl(f);
                    if (td)
                    {
                        if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
                            td = td.overroot; // then get the start
                        e = new TemplateExp(loc, td, f);
                        e = e.semantic(sc);
                        return e;
                    }
                }
                // Haven't done overload resolution yet, so pass 1
                e = DsymbolExp.resolve(loc, sc, s, true);
            }
            return e;
        }

        if (hasThis(sc))
        {
            AggregateDeclaration ad = sc.getStructClassScope();
            if (ad && ad.aliasthis)
            {
                Expression e;
                e = new IdentifierExp(loc, Id.This);
                e = new DotIdExp(loc, e, ad.aliasthis.ident);
                e = new DotIdExp(loc, e, ident);
                e = e.trySemantic(sc);
                if (e)
                    return e;
            }
        }

        if (ident == Id.ctfe)
        {
            if (sc.flags & SCOPEctfe)
            {
                error("variable __ctfe cannot be read at compile time");
                return new ErrorExp();
            }

            // Create the magic __ctfe bool variable
            auto vd = new VarDeclaration(loc, Type.tbool, Id.ctfe, null);
            vd.storage_class |= STCtemp;
            Expression e = new VarExp(loc, vd);
            e = e.semantic(sc);
            return e;
        }

        const(char)* n = importHint(ident.toChars());
        if (n)
            error("'%s' is not defined, perhaps you need to import %s; ?", ident.toChars(), n);
        else
        {
            s = sc.search_correct(ident);
            if (s)
                error("undefined identifier '%s', did you mean %s '%s'?", ident.toChars(), s.kind(), s.toChars());
            else
                error("undefined identifier '%s'", ident.toChars());
        }
        return new ErrorExp();
    }

    override final bool isLvalue()
    {
        return true;
    }

    override final Expression toLvalue(Scope* sc, Expression e)
    {
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class DollarExp : IdentifierExp
{
    extern (D) this(Loc loc)
    {
        super(loc, Id.dollar);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * Won't be generated by parser.
 * A placeholder expression to call DsymbolExp.resolve on specific symbol.
 */
extern (C++) final class DsymbolExp : Expression
{
    Dsymbol s;
    bool hasOverloads;

    extern (D) this(Loc loc, Dsymbol s, bool hasOverloads = true)
    {
        super(loc, TOKdsymbol, __traits(classInstanceSize, DsymbolExp));
        this.s = s;
        this.hasOverloads = hasOverloads;
    }

    override Expression semantic(Scope* sc)
    {
        return resolve(loc ,sc, s, hasOverloads);
    }

    /****************************************
     * Resolve a symbol `s` and wraps it in an expression object.
     * Params:
     *      hasOverloads = works if the aliased symbol is a function.
     *          true:  it's overloaded and will be resolved later.
     *          false: it's exact function symbol.
     */
    static Expression resolve(Loc loc, Scope *sc, Dsymbol s, bool hasOverloads)
    {
        static if (LOGSEMANTIC)
        {
            printf("DsymbolExp::resolve(%s %s)\n", s.kind(), s.toChars());
        }

    Lagain:
        Expression e;

        //printf("DsymbolExp:: %p '%s' is a symbol\n", this, toChars());
        //printf("s = '%s', s.kind = '%s'\n", s.toChars(), s.kind());
        Dsymbol olds = s;
        Declaration d = s.isDeclaration();
        if (d && (d.storage_class & STCtemplateparameter))
        {
            s = s.toAlias();
        }
        else
        {
            if (!s.isFuncDeclaration()) // functions are checked after overloading
                s.checkDeprecated(loc, sc);

            // Bugzilla 12023: if 's' is a tuple variable, the tuple is returned.
            s = s.toAlias();

            //printf("s = '%s', s.kind = '%s', s.needThis() = %p\n", s.toChars(), s.kind(), s.needThis());
            if (s != olds && !s.isFuncDeclaration())
                s.checkDeprecated(loc, sc);
        }

        if (auto em = s.isEnumMember())
        {
            return em.getVarExp(loc, sc);
        }
        if (auto v = s.isVarDeclaration())
        {
            //printf("Identifier '%s' is a variable, type '%s'\n", s.toChars(), v.type.toChars());
            if (!v.type ||                  // during variable type inference
                !v.type.deco && v.inuse)    // during variable type semantic
            {
                if (v.inuse)    // variable type depends on the variable itself
                    .error(loc, "circular reference to %s '%s'", v.kind(), v.toPrettyChars());
                else            // variable type cannot be determined
                    .error(loc, "forward reference to %s '%s'", v.kind(), v.toPrettyChars());
                return new ErrorExp();
            }
            if (v.type.ty == Terror)
                return new ErrorExp();

            if ((v.storage_class & STCmanifest) && v._init)
            {
                if (v.inuse)
                {
                    .error(loc, "circular initialization of %s '%s'", v.kind(), v.toPrettyChars());
                    return new ErrorExp();
                }
                e = v.expandInitializer(loc);
                v.inuse++;
                e = e.semantic(sc);
                v.inuse--;
                return e;
            }

            // Change the ancestor lambdas to delegate before hasThis(sc) call.
            if (v.checkNestedReference(sc, loc))
                return new ErrorExp();

            if (v.needThis() && hasThis(sc))
                e = new DotVarExp(loc, new ThisExp(loc), v);
            else
                e = new VarExp(loc, v);
            e = e.semantic(sc);
            return e;
        }
        if (auto fld = s.isFuncLiteralDeclaration())
        {
            //printf("'%s' is a function literal\n", fld.toChars());
            e = new FuncExp(loc, fld);
            return e.semantic(sc);
        }
        if (auto f = s.isFuncDeclaration())
        {
            f = f.toAliasFunc();
            if (!f.functionSemantic())
                return new ErrorExp();

            if (!hasOverloads && f.checkForwardRef(loc))
                return new ErrorExp();

            auto fd = s.isFuncDeclaration();
            fd.type = f.type;
            return new VarExp(loc, fd, hasOverloads);
        }
        if (OverDeclaration od = s.isOverDeclaration())
        {
            e = new VarExp(loc, od, true);
            e.type = Type.tvoid;
            return e;
        }
        if (OverloadSet o = s.isOverloadSet())
        {
            //printf("'%s' is an overload set\n", o.toChars());
            return new OverExp(loc, o);
        }

        if (Import imp = s.isImport())
        {
            if (!imp.pkg)
            {
                .error(loc, "forward reference of import %s", imp.toChars());
                return new ErrorExp();
            }
            auto ie = new ScopeExp(loc, imp.pkg);
            return ie.semantic(sc);
        }
        if (Package pkg = s.isPackage())
        {
            auto ie = new ScopeExp(loc, pkg);
            return ie.semantic(sc);
        }
        if (Module mod = s.isModule())
        {
            auto ie = new ScopeExp(loc, mod);
            return ie.semantic(sc);
        }
        if (Nspace ns = s.isNspace())
        {
            auto ie = new ScopeExp(loc, ns);
            return ie.semantic(sc);
        }

        if (Type t = s.getType())
        {
            return (new TypeExp(loc, t)).semantic(sc);
        }

        if (TupleDeclaration tup = s.isTupleDeclaration())
        {
            if (tup.needThis() && hasThis(sc))
                e = new DotVarExp(loc, new ThisExp(loc), tup);
            else
                e = new TupleExp(loc, tup);
            e = e.semantic(sc);
            return e;
        }

        if (TemplateInstance ti = s.isTemplateInstance())
        {
            ti.semantic(sc);
            if (!ti.inst || ti.errors)
                return new ErrorExp();
            s = ti.toAlias();
            if (!s.isTemplateInstance())
                goto Lagain;
            e = new ScopeExp(loc, ti);
            e = e.semantic(sc);
            return e;
        }
        if (TemplateDeclaration td = s.isTemplateDeclaration())
        {
            Dsymbol p = td.toParent2();
            FuncDeclaration fdthis = hasThis(sc);
            AggregateDeclaration ad = p ? p.isAggregateDeclaration() : null;
            if (fdthis && ad && isAggregate(fdthis.vthis.type) == ad && (td._scope.stc & STCstatic) == 0)
            {
                e = new DotTemplateExp(loc, new ThisExp(loc), td);
            }
            else
                e = new TemplateExp(loc, td);
            e = e.semantic(sc);
            return e;
        }

        .error(loc, "%s '%s' is not a variable", s.kind(), s.toChars());
        return new ErrorExp();
    }

    override bool isLvalue()
    {
        return true;
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) class ThisExp : Expression
{
    VarDeclaration var;

    final extern (D) this(Loc loc)
    {
        super(loc, TOKthis, __traits(classInstanceSize, ThisExp));
        //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("ThisExp::semantic()\n");
        }
        if (type)
            return this;

        FuncDeclaration fd = hasThis(sc); // fd is the uplevel function with the 'this' variable

        /* Special case for typeof(this) and typeof(super) since both
         * should work even if they are not inside a non-static member function
         */
        if (!fd && sc.intypeof == 1)
        {
            // Find enclosing struct or class
            for (Dsymbol s = sc.getStructClassScope(); 1; s = s.parent)
            {
                if (!s)
                {
                    error("%s is not in a class or struct scope", toChars());
                    goto Lerr;
                }
                ClassDeclaration cd = s.isClassDeclaration();
                if (cd)
                {
                    type = cd.type;
                    return this;
                }
                StructDeclaration sd = s.isStructDeclaration();
                if (sd)
                {
                    type = sd.type;
                    return this;
                }
            }
        }
        if (!fd)
            goto Lerr;

        assert(fd.vthis);
        var = fd.vthis;
        assert(var.parent);
        type = var.type;
        if (var.checkNestedReference(sc, loc))
            return new ErrorExp();
        if (!sc.intypeof)
            sc.callSuper |= CSXthis;
        return this;

    Lerr:
        error("'this' is only defined in non-static member functions, not %s", sc.parent.toChars());
        return new ErrorExp();
    }

    override final bool isBool(bool result)
    {
        return result ? true : false;
    }

    override final bool isLvalue()
    {
        // Class `this` should be an rvalue; struct `this` should be an lvalue.
        return type.toBasetype().ty != Tclass;
    }

    override final Expression toLvalue(Scope* sc, Expression e)
    {
        if (type.toBasetype().ty == Tclass)
        {
            // Class `this` is an rvalue; struct `this` is an lvalue.
            return Expression.toLvalue(sc, e);
        }
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class SuperExp : ThisExp
{
    extern (D) this(Loc loc)
    {
        super(loc);
        op = TOKsuper;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("SuperExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        FuncDeclaration fd = hasThis(sc);
        ClassDeclaration cd;
        Dsymbol s;

        /* Special case for typeof(this) and typeof(super) since both
         * should work even if they are not inside a non-static member function
         */
        if (!fd && sc.intypeof == 1)
        {
            // Find enclosing class
            for (s = sc.getStructClassScope(); 1; s = s.parent)
            {
                if (!s)
                {
                    error("%s is not in a class scope", toChars());
                    goto Lerr;
                }
                cd = s.isClassDeclaration();
                if (cd)
                {
                    cd = cd.baseClass;
                    if (!cd)
                    {
                        error("class %s has no 'super'", s.toChars());
                        goto Lerr;
                    }
                    type = cd.type;
                    return this;
                }
            }
        }
        if (!fd)
            goto Lerr;

        var = fd.vthis;
        assert(var && var.parent);

        s = fd.toParent();
        while (s && s.isTemplateInstance())
            s = s.toParent();
        if (s.isTemplateDeclaration()) // allow inside template constraint
            s = s.toParent();
        assert(s);
        cd = s.isClassDeclaration();
        //printf("parent is %s %s\n", fd->toParent()->kind(), fd->toParent()->toChars());
        if (!cd)
            goto Lerr;
        if (!cd.baseClass)
        {
            error("no base class for %s", cd.toChars());
            type = var.type;
        }
        else
        {
            type = cd.baseClass.type;
            type = type.castMod(var.type.mod);
        }

        if (var.checkNestedReference(sc, loc))
            return new ErrorExp();

        if (!sc.intypeof)
            sc.callSuper |= CSXsuper;
        return this;

    Lerr:
        error("'super' is only allowed in non-static class member functions");
        return new ErrorExp();
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class NullExp : Expression
{
    ubyte committed;    // !=0 if type is committed

    extern (D) this(Loc loc, Type type = null)
    {
        super(loc, TOKnull, __traits(classInstanceSize, NullExp));
        this.type = type;
    }

    override bool equals(RootObject o)
    {
        if (o && o.dyncast() == DYNCAST_EXPRESSION)
        {
            Expression e = cast(Expression)o;
            if (e.op == TOKnull && type.equals(e.type))
            {
                return true;
            }
        }
        return false;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("NullExp::semantic('%s')\n", toChars());
        }
        // NULL is the same as (void *)0
        if (type)
            return this;
        type = Type.tnull;
        return this;
    }

    override bool isBool(bool result)
    {
        return result ? false : true;
    }

    override StringExp toStringExp()
    {
        if (implicitConvTo(Type.tstring))
        {
            auto se = new StringExp(loc, cast(char*)mem.xcalloc(1, 1), 0);
            se.type = Type.tstring;
            return se;
        }
        return null;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class StringExp : Expression
{
    union
    {
        char* string;   // if sz == 1
        wchar* wstring; // if sz == 2
        dchar* dstring; // if sz == 4
    }                   // (const if ownedByCtfe == OWNEDcode)
    size_t len;         // number of code units
    ubyte sz = 1;       // 1: char, 2: wchar, 4: dchar
    ubyte committed;    // !=0 if type is committed
    char postfix = 0;   // 'c', 'w', 'd'
    OwnedBy ownedByCtfe = OWNEDcode;

    extern (D) this(Loc loc, char* string)
    {
        super(loc, TOKstring, __traits(classInstanceSize, StringExp));
        this.string = string;
        this.len = strlen(string);
        this.sz = 1;                    // work around LDC bug #1286
    }

    extern (D) this(Loc loc, void* string, size_t len)
    {
        super(loc, TOKstring, __traits(classInstanceSize, StringExp));
        this.string = cast(char*)string;
        this.len = len;
        this.sz = 1;                    // work around LDC bug #1286
    }

    extern (D) this(Loc loc, void* string, size_t len, char postfix)
    {
        super(loc, TOKstring, __traits(classInstanceSize, StringExp));
        this.string = cast(char*)string;
        this.len = len;
        this.postfix = postfix;
        this.sz = 1;                    // work around LDC bug #1286
    }

    static StringExp create(Loc loc, char* s)
    {
        return new StringExp(loc, s);
    }

    override bool equals(RootObject o)
    {
        //printf("StringExp::equals('%s') %s\n", o->toChars(), toChars());
        if (o && o.dyncast() == DYNCAST_EXPRESSION)
        {
            Expression e = cast(Expression)o;
            if (e.op == TOKstring)
            {
                return compare(o) == 0;
            }
        }
        return false;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("StringExp::semantic() %s\n", toChars());
        }
        if (type)
            return this;

        OutBuffer buffer;
        size_t newlen = 0;
        const(char)* p;
        size_t u;
        dchar c;

        switch (postfix)
        {
        case 'd':
            for (u = 0; u < len;)
            {
                p = utf_decodeChar(string, len, u, c);
                if (p)
                {
                    error("%s", p);
                    return new ErrorExp();
                }
                else
                {
                    buffer.write4(c);
                    newlen++;
                }
            }
            buffer.write4(0);
            dstring = cast(dchar*)buffer.extractData();
            len = newlen;
            sz = 4;
            type = new TypeDArray(Type.tdchar.immutableOf());
            committed = 1;
            break;

        case 'w':
            for (u = 0; u < len;)
            {
                p = utf_decodeChar(string, len, u, c);
                if (p)
                {
                    error("%s", p);
                    return new ErrorExp();
                }
                else
                {
                    buffer.writeUTF16(c);
                    newlen++;
                    if (c >= 0x10000)
                        newlen++;
                }
            }
            buffer.writeUTF16(0);
            wstring = cast(wchar*)buffer.extractData();
            len = newlen;
            sz = 2;
            type = new TypeDArray(Type.twchar.immutableOf());
            committed = 1;
            break;

        case 'c':
            committed = 1;
            goto default;

        default:
            type = new TypeDArray(Type.tchar.immutableOf());
            break;
        }
        type = type.semantic(loc, sc);
        //type = type->immutableOf();
        //printf("type = %s\n", type->toChars());

        return this;
    }

    /**********************************
     * Return the number of code units the string would be if it were re-encoded
     * as tynto.
     * Params:
     *      tynto = code unit type of the target encoding
     * Returns:
     *      number of code units
     */
    final size_t numberOfCodeUnits(int tynto = 0) const
    {
        int encSize;
        switch (tynto)
        {
            case 0:      return len;
            case Tchar:  encSize = 1; break;
            case Twchar: encSize = 2; break;
            case Tdchar: encSize = 4; break;
            default:
                assert(0);
        }
        if (sz == encSize)
            return len;

        size_t result = 0;
        dchar c;

        switch (sz)
        {
        case 1:
            for (size_t u = 0; u < len;)
            {
                if (const p = utf_decodeChar(string, len, u, c))
                {
                    error("%s", p);
                    return 0;
                }
                result += utf_codeLength(encSize, c);
            }
            break;

        case 2:
            for (size_t u = 0; u < len;)
            {
                if (const p = utf_decodeWchar(wstring, len, u, c))
                {
                    error("%s", p);
                    return 0;
                }
                result += utf_codeLength(encSize, c);
            }
            break;

        case 4:
            foreach (u; 0 .. len)
            {
                result += utf_codeLength(encSize, dstring[u]);
            }
            break;

        default:
            assert(0);
        }
        return result;
    }

    /**********************************************
     * Write the contents of the string to dest.
     * Use numberOfCodeUnits() to determine size of result.
     * Params:
     *  dest = destination
     *  tyto = encoding type of the result
     *  zero = add terminating 0
     */
    void writeTo(void* dest, bool zero, int tyto = 0) const
    {
        int encSize;
        switch (tyto)
        {
            case 0:      encSize = sz; break;
            case Tchar:  encSize = 1; break;
            case Twchar: encSize = 2; break;
            case Tdchar: encSize = 4; break;
            default:
                assert(0);
        }
        if (sz == encSize)
        {
            memcpy(dest, string, len * sz);
            if (zero)
                memset(dest + len * sz, 0, sz);
        }
        else
            assert(0);
    }

    /*********************************************
     * Get the code unit at index i
     * Params:
     *  i = index
     * Returns:
     *  code unit at index i
     */
    final dchar getCodeUnit(size_t i) const pure
    {
        assert(i < len);
        final switch (sz)
        {
        case 1:
            return string[i];
        case 2:
            return wstring[i];
        case 4:
            return dstring[i];
        }
    }

    /*********************************************
     * Set the code unit at index i to c
     * Params:
     *  i = index
     *  c = code unit to set it to
     */
    final void setCodeUnit(size_t i, dchar c)
    {
        assert(i < len);
        final switch (sz)
        {
        case 1:
            string[i] = cast(char)c;
            break;
        case 2:
            wstring[i] = cast(wchar)c;
            break;
        case 4:
            dstring[i] = c;
            break;
        }
    }

    /**************************************************
     * If the string data is UTF-8 and can be accessed directly,
     * return a pointer to it.
     * Do not assume a terminating 0.
     * Returns:
     *  pointer to string data if possible, null if not
     */
    char* toPtr()
    {
        return (sz == 1) ? string : null;
    }

    override StringExp toStringExp()
    {
        return this;
    }

    /****************************************
     * Convert string to char[].
     */
    StringExp toUTF8(Scope* sc)
    {
        if (sz != 1)
        {
            // Convert to UTF-8 string
            committed = 0;
            Expression e = castTo(sc, Type.tchar.arrayOf());
            e = e.optimize(WANTvalue);
            assert(e.op == TOKstring);
            StringExp se = cast(StringExp)e;
            assert(se.sz == 1);
            return se;
        }
        return this;
    }

    override int compare(RootObject obj)
    {
        //printf("StringExp::compare()\n");
        // Used to sort case statement expressions so we can do an efficient lookup
        StringExp se2 = cast(StringExp)obj;

        // This is a kludge so isExpression() in template.c will return 5
        // for StringExp's.
        if (!se2)
            return 5;

        assert(se2.op == TOKstring);

        size_t len1 = len;
        size_t len2 = se2.len;

        //printf("sz = %d, len1 = %d, len2 = %d\n", sz, (int)len1, (int)len2);
        if (len1 == len2)
        {
            switch (sz)
            {
            case 1:
                return memcmp(string, se2.string, len1);

            case 2:
                {
                    wchar* s1 = cast(wchar*)string;
                    wchar* s2 = cast(wchar*)se2.string;
                    for (size_t u = 0; u < len; u++)
                    {
                        if (s1[u] != s2[u])
                            return s1[u] - s2[u];
                    }
                }
                break;
            case 4:
                {
                    dchar* s1 = cast(dchar*)string;
                    dchar* s2 = cast(dchar*)se2.string;
                    for (size_t u = 0; u < len; u++)
                    {
                        if (s1[u] != s2[u])
                            return s1[u] - s2[u];
                    }
                }
                break;
            default:
                assert(0);
            }
        }
        return cast(int)(len1 - len2);
    }

    override bool isBool(bool result)
    {
        return result ? true : false;
    }

    override bool isLvalue()
    {
        /* string literal is rvalue in default, but
         * conversion to reference of static array is only allowed.
         */
        return (type && type.toBasetype().ty == Tsarray);
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        //printf("StringExp::toLvalue(%s) type = %s\n", toChars(), type ? type->toChars() : NULL);
        return (type && type.toBasetype().ty == Tsarray) ? this : Expression.toLvalue(sc, e);
    }

    override Expression modifiableLvalue(Scope* sc, Expression e)
    {
        error("cannot modify string literal %s", toChars());
        return new ErrorExp();
    }

    uint charAt(uinteger_t i) const
    {
        uint value;
        switch (sz)
        {
        case 1:
            value = (cast(char*)string)[cast(size_t)i];
            break;

        case 2:
            value = (cast(ushort*)string)[cast(size_t)i];
            break;

        case 4:
            value = (cast(uint*)string)[cast(size_t)i];
            break;

        default:
            assert(0);
        }
        return value;
    }

    /********************************
     * Convert string contents to a 0 terminated string,
     * allocated by mem.xmalloc().
     */
    extern (D) final const(char)[] toStringz() const
    {
        auto nbytes = len * sz;
        char* s = cast(char*)mem.xmalloc(nbytes + sz);
        writeTo(s, true);
        return s[0 .. nbytes];
    }

    extern (D) const(char)[] peekSlice() const
    {
        assert(sz == 1);
        return this.string[0 .. len];
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class TupleExp : Expression
{
    /* Tuple-field access may need to take out its side effect part.
     * For example:
     *      foo().tupleof
     * is rewritten as:
     *      (ref __tup = foo(); tuple(__tup.field0, __tup.field1, ...))
     * The declaration of temporary variable __tup will be stored in TupleExp.e0.
     */
    Expression e0;

    Expressions* exps;

    extern (D) this(Loc loc, Expression e0, Expressions* exps)
    {
        super(loc, TOKtuple, __traits(classInstanceSize, TupleExp));
        //printf("TupleExp(this = %p)\n", this);
        this.e0 = e0;
        this.exps = exps;
    }

    extern (D) this(Loc loc, Expressions* exps)
    {
        super(loc, TOKtuple, __traits(classInstanceSize, TupleExp));
        //printf("TupleExp(this = %p)\n", this);
        this.exps = exps;
    }

    extern (D) this(Loc loc, TupleDeclaration tup)
    {
        super(loc, TOKtuple, __traits(classInstanceSize, TupleExp));
        this.exps = new Expressions();

        this.exps.reserve(tup.objects.dim);
        for (size_t i = 0; i < tup.objects.dim; i++)
        {
            RootObject o = (*tup.objects)[i];
            if (Dsymbol s = getDsymbol(o))
            {
                /* If tuple element represents a symbol, translate to DsymbolExp
                 * to supply implicit 'this' if needed later.
                 */
                Expression e = new DsymbolExp(loc, s);
                this.exps.push(e);
            }
            else if (o.dyncast() == DYNCAST_EXPRESSION)
            {
                auto e = (cast(Expression)o).copy();
                e.loc = loc;    // Bugzilla 15669
                this.exps.push(e);
            }
            else if (o.dyncast() == DYNCAST_TYPE)
            {
                Type t = cast(Type)o;
                Expression e = new TypeExp(loc, t);
                this.exps.push(e);
            }
            else
            {
                error("%s is not an expression", o.toChars());
            }
        }
    }

    override Expression syntaxCopy()
    {
        return new TupleExp(loc, e0 ? e0.syntaxCopy() : null, arraySyntaxCopy(exps));
    }

    override bool equals(RootObject o)
    {
        if (this == o)
            return true;
        if ((cast(Expression)o).op == TOKtuple)
        {
            TupleExp te = cast(TupleExp)o;
            if (exps.dim != te.exps.dim)
                return false;
            if (e0 && !e0.equals(te.e0) || !e0 && te.e0)
                return false;
            for (size_t i = 0; i < exps.dim; i++)
            {
                Expression e1 = (*exps)[i];
                Expression e2 = (*te.exps)[i];
                if (!e1.equals(e2))
                    return false;
            }
            return true;
        }
        return false;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("+TupleExp::semantic(%s)\n", toChars());
        }
        if (type)
            return this;

        if (e0)
            e0 = e0.semantic(sc);

        // Run semantic() on each argument
        bool err = false;
        for (size_t i = 0; i < exps.dim; i++)
        {
            Expression e = (*exps)[i];
            e = e.semantic(sc);
            if (!e.type)
            {
                error("%s has no value", e.toChars());
                err = true;
            }
            else if (e.op == TOKerror)
                err = true;
            else
                (*exps)[i] = e;
        }
        if (err)
            return new ErrorExp();

        expandTuples(exps);

        type = new TypeTuple(exps);
        type = type.semantic(loc, sc);
        //printf("-TupleExp::semantic(%s)\n", toChars());
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * [ e1, e2, e3, ... ]
 */
extern (C++) final class ArrayLiteralExp : Expression
{
    /** If !is null, elements[] can be sparse and basis is used for the
     * "default" element value. In other words, non-null elements[i] overrides
     * this 'basis' value.
     */
    Expression basis;

    Expressions* elements;
    OwnedBy ownedByCtfe = OWNEDcode;

    extern (D) this(Loc loc, Expressions* elements)
    {
        super(loc, TOKarrayliteral, __traits(classInstanceSize, ArrayLiteralExp));
        this.elements = elements;
    }

    extern (D) this(Loc loc, Expression e)
    {
        super(loc, TOKarrayliteral, __traits(classInstanceSize, ArrayLiteralExp));
        elements = new Expressions();
        elements.push(e);
    }

    extern (D) this(Loc loc, Expression basis, Expressions* elements)
    {
        super(loc, TOKarrayliteral, __traits(classInstanceSize, ArrayLiteralExp));
        this.basis = basis;
        this.elements = elements;
    }

    override Expression syntaxCopy()
    {
        return new ArrayLiteralExp(loc,
            basis ? basis.syntaxCopy() : null,
            arraySyntaxCopy(elements));
    }

    override bool equals(RootObject o)
    {
        if (this == o)
            return true;
        if (o && o.dyncast() == DYNCAST_EXPRESSION && (cast(Expression)o).op == TOKarrayliteral)
        {
            ArrayLiteralExp ae = cast(ArrayLiteralExp)o;
            if (elements.dim != ae.elements.dim)
                return false;
            if (elements.dim == 0 && !type.equals(ae.type))
            {
                return false;
            }
            for (size_t i = 0; i < elements.dim; i++)
            {
                Expression e1 = (*elements)[i];
                Expression e2 = (*ae.elements)[i];
                if (!e1)
                    e1 = basis;
                if (!e2)
                    e2 = ae.basis;
                if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
                    return false;
            }
            return true;
        }
        return false;
    }

    final Expression getElement(size_t i)
    {
        auto el = (*elements)[i];
        if (!el)
            el = basis;
        return el;
    }

    /** Copy element `Expressions` in the parameters when they're `ArrayLiteralExp`s.
     * Params:
     *      e1  = If it's ArrayLiteralExp, its `elements` will be copied.
     *            Otherwise, `e1` itself will be pushed into the new `Expressions`.
     *      e2  = If it's not `null`, it will be pushed/appended to the new
     *            `Expressions` by the same way with `e1`.
     * Returns:
     *      Newly allocated `Expresions. Note that it points the original
     *      `Expression` values in e1 and e2.
     */
    static Expressions* copyElements(Expression e1, Expression e2 = null)
    {
        auto elems = new Expressions();

        void append(ArrayLiteralExp ale)
        {
            if (!ale.elements)
                return;
            auto d = elems.dim;
            elems.append(ale.elements);
            foreach (ref el; (*elems)[][d .. elems.dim])
            {
                if (!el)
                    el = ale.basis;
            }
        }

        if (e1.op == TOKarrayliteral)
            append(cast(ArrayLiteralExp)e1);
        else
            elems.push(e1);

        if (e2)
        {
            if (e2.op == TOKarrayliteral)
                append(cast(ArrayLiteralExp)e2);
            else
                elems.push(e2);
        }

        return elems;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("ArrayLiteralExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        /* Perhaps an empty array literal [ ] should be rewritten as null?
         */

        if (basis)
            basis = basis.semantic(sc);
        if (arrayExpressionSemantic(elements, sc) || (basis && basis.op == TOKerror))
            return new ErrorExp();

        expandTuples(elements);

        Type t0;
        if (basis)
            elements.push(basis);
        bool err = arrayExpressionToCommonType(sc, elements, &t0);
        if (basis)
            basis = elements.pop();
        if (err)
            return new ErrorExp();

        type = t0.arrayOf();
        type = type.semantic(loc, sc);

        /* Disallow array literals of type void being used.
         */
        if (elements.dim > 0 && t0.ty == Tvoid)
        {
            error("%s of type %s has no value", toChars(), type.toChars());
            return new ErrorExp();
        }

        semanticTypeInfo(sc, type);

        return this;
    }

    override bool isBool(bool result)
    {
        size_t dim = elements ? elements.dim : 0;
        return result ? (dim != 0) : (dim == 0);
    }

    override StringExp toStringExp()
    {
        TY telem = type.nextOf().toBasetype().ty;
        if (telem == Tchar || telem == Twchar || telem == Tdchar || (telem == Tvoid && (!elements || elements.dim == 0)))
        {
            ubyte sz = 1;
            if (telem == Twchar)
                sz = 2;
            else if (telem == Tdchar)
                sz = 4;

            OutBuffer buf;
            if (elements)
            {
                for (size_t i = 0; i < elements.dim; ++i)
                {
                    auto ch = getElement(i);
                    if (ch.op != TOKint64)
                        return null;
                    if (sz == 1)
                        buf.writeByte(cast(uint)ch.toInteger());
                    else if (sz == 2)
                        buf.writeword(cast(uint)ch.toInteger());
                    else
                        buf.write4(cast(uint)ch.toInteger());
                }
            }
            char prefix;
            if (sz == 1)
            {
                prefix = 'c';
                buf.writeByte(0);
            }
            else if (sz == 2)
            {
                prefix = 'w';
                buf.writeword(0);
            }
            else
            {
                prefix = 'd';
                buf.write4(0);
            }

            const(size_t) len = buf.offset / sz - 1;
            auto se = new StringExp(loc, buf.extractData(), len, prefix);
            se.sz = sz;
            se.type = type;
            return se;
        }
        return null;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * [ key0 : value0, key1 : value1, ... ]
 */
extern (C++) final class AssocArrayLiteralExp : Expression
{
    Expressions* keys;
    Expressions* values;
    OwnedBy ownedByCtfe = OWNEDcode;

    extern (D) this(Loc loc, Expressions* keys, Expressions* values)
    {
        super(loc, TOKassocarrayliteral, __traits(classInstanceSize, AssocArrayLiteralExp));
        assert(keys.dim == values.dim);
        this.keys = keys;
        this.values = values;
    }

    override bool equals(RootObject o)
    {
        if (this == o)
            return true;
        if (o && o.dyncast() == DYNCAST_EXPRESSION && (cast(Expression)o).op == TOKassocarrayliteral)
        {
            AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)o;
            if (keys.dim != ae.keys.dim)
                return false;
            size_t count = 0;
            for (size_t i = 0; i < keys.dim; i++)
            {
                for (size_t j = 0; j < ae.keys.dim; j++)
                {
                    if ((*keys)[i].equals((*ae.keys)[j]))
                    {
                        if (!(*values)[i].equals((*ae.values)[j]))
                            return false;
                        ++count;
                    }
                }
            }
            return count == keys.dim;
        }
        return false;
    }

    override Expression syntaxCopy()
    {
        return new AssocArrayLiteralExp(loc, arraySyntaxCopy(keys), arraySyntaxCopy(values));
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("AssocArrayLiteralExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        // Run semantic() on each element
        bool err_keys = arrayExpressionSemantic(keys, sc);
        bool err_vals = arrayExpressionSemantic(values, sc);
        if (err_keys || err_vals)
            return new ErrorExp();

        expandTuples(keys);
        expandTuples(values);
        if (keys.dim != values.dim)
        {
            error("number of keys is %u, must match number of values %u", keys.dim, values.dim);
            return new ErrorExp();
        }

        Type tkey = null;
        Type tvalue = null;
        err_keys = arrayExpressionToCommonType(sc, keys, &tkey);
        err_vals = arrayExpressionToCommonType(sc, values, &tvalue);
        if (err_keys || err_vals)
            return new ErrorExp();

        if (tkey == Type.terror || tvalue == Type.terror)
            return new ErrorExp();

        type = new TypeAArray(tvalue, tkey);
        type = type.semantic(loc, sc);

        semanticTypeInfo(sc, type);

        return this;
    }

    override bool isBool(bool result)
    {
        size_t dim = keys.dim;
        return result ? (dim != 0) : (dim == 0);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

enum stageScrub             = 0x1;  /// scrubReturnValue is running
enum stageSearchPointers    = 0x2;  /// hasNonConstPointers is running
enum stageOptimize          = 0x4;  /// optimize is running
enum stageApply             = 0x8;  /// apply is running
enum stageInlineScan        = 0x10; /// inlineScan is running
enum stageToCBuffer         = 0x20; /// toCBuffer is running

/***********************************************************
 * sd( e1, e2, e3, ... )
 */
extern (C++) final class StructLiteralExp : Expression
{
    StructDeclaration sd;   /// which aggregate this is for
    Expressions* elements;  /// parallels sd.fields[] with null entries for fields to skip
    Type stype;             /// final type of result (can be different from sd's type)

    bool useStaticInit;     /// if this is true, use the StructDeclaration's init symbol
    Symbol* sym;            /// back end symbol to initialize with literal

    OwnedBy ownedByCtfe = OWNEDcode;

    /** pointer to the origin instance of the expression.
     * once a new expression is created, origin is set to 'this'.
     * anytime when an expression copy is created, 'origin' pointer is set to
     * 'origin' pointer value of the original expression.
     */
    StructLiteralExp origin;

    /// those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer.
    StructLiteralExp inlinecopy;

    /** anytime when recursive function is calling, 'stageflags' marks with bit flag of
     * current stage and unmarks before return from this function.
     * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline'
     * (with infinite recursion) of this expression.
     */
    int stageflags;

    extern (D) this(Loc loc, StructDeclaration sd, Expressions* elements, Type stype = null)
    {
        super(loc, TOKstructliteral, __traits(classInstanceSize, StructLiteralExp));
        this.sd = sd;
        if (!elements)
            elements = new Expressions();
        this.elements = elements;
        this.stype = stype;
        this.origin = this;
        //printf("StructLiteralExp::StructLiteralExp(%s)\n", toChars());
    }

    static StructLiteralExp create(Loc loc, StructDeclaration sd, void* elements, Type stype = null)
    {
        return new StructLiteralExp(loc, sd, cast(Expressions*)elements, stype);
    }

    override bool equals(RootObject o)
    {
        if (this == o)
            return true;
        if (o && o.dyncast() == DYNCAST_EXPRESSION && (cast(Expression)o).op == TOKstructliteral)
        {
            StructLiteralExp se = cast(StructLiteralExp)o;
            if (!type.equals(se.type))
                return false;
            if (elements.dim != se.elements.dim)
                return false;
            for (size_t i = 0; i < elements.dim; i++)
            {
                Expression e1 = (*elements)[i];
                Expression e2 = (*se.elements)[i];
                if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
                    return false;
            }
            return true;
        }
        return false;
    }

    override Expression syntaxCopy()
    {
        auto exp = new StructLiteralExp(loc, sd, arraySyntaxCopy(elements), type ? type : stype);
        exp.origin = this;
        return exp;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("StructLiteralExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        sd.size(loc);
        if (sd.sizeok != SIZEOKdone)
            return new ErrorExp();

        if (arrayExpressionSemantic(elements, sc)) // run semantic() on each element
            return new ErrorExp();

        expandTuples(elements);

        /* Fit elements[] to the corresponding type of field[].
         */
        if (!sd.fit(loc, sc, elements, stype))
            return new ErrorExp();

        /* Fill out remainder of elements[] with default initializers for fields[]
         */
        if (!sd.fill(loc, elements, false))
        {
            /* An error in the initializer needs to be recorded as an error
             * in the enclosing function or template, since the initializer
             * will be part of the stuct declaration.
             */
            global.increaseErrorCount();
            return new ErrorExp();
        }

        if (checkFrameAccess(loc, sc, sd, elements.dim))
            return new ErrorExp();

        type = stype ? stype : sd.type;
        return this;
    }

    /**************************************
     * Gets expression at offset of type.
     * Returns NULL if not found.
     */
    Expression getField(Type type, uint offset)
    {
        //printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n",
        //  /*toChars()*/"", type->toChars(), offset);
        Expression e = null;
        int i = getFieldIndex(type, offset);

        if (i != -1)
        {
            //printf("\ti = %d\n", i);
            if (i == sd.fields.dim - 1 && sd.isNested())
                return null;

            assert(i < elements.dim);
            e = (*elements)[i];
            if (e)
            {
                //printf("e = %s, e->type = %s\n", e->toChars(), e->type->toChars());

                /* If type is a static array, and e is an initializer for that array,
                 * then the field initializer should be an array literal of e.
                 */
                if (e.type.castMod(0) != type.castMod(0) && type.ty == Tsarray)
                {
                    TypeSArray tsa = cast(TypeSArray)type;
                    size_t length = cast(size_t)tsa.dim.toInteger();
                    auto z = new Expressions();
                    z.setDim(length);
                    for (size_t q = 0; q < length; ++q)
                        (*z)[q] = e.copy();
                    e = new ArrayLiteralExp(loc, z);
                    e.type = type;
                }
                else
                {
                    e = e.copy();
                    e.type = type;
                }
                if (useStaticInit && e.op == TOKstructliteral && e.type.needsNested())
                {
                    StructLiteralExp se = cast(StructLiteralExp)e;
                    se.useStaticInit = true;
                }
            }
        }
        return e;
    }

    /************************************
     * Get index of field.
     * Returns -1 if not found.
     */
    int getFieldIndex(Type type, uint offset)
    {
        /* Find which field offset is by looking at the field offsets
         */
        if (elements.dim)
        {
            for (size_t i = 0; i < sd.fields.dim; i++)
            {
                VarDeclaration v = sd.fields[i];
                if (offset == v.offset && type.size() == v.type.size())
                {
                    /* context field might not be filled. */
                    if (i == sd.fields.dim - 1 && sd.isNested())
                        return cast(int)i;
                    Expression e = (*elements)[i];
                    if (e)
                    {
                        return cast(int)i;
                    }
                    break;
                }
            }
        }
        return -1;
    }

    override Expression addDtorHook(Scope* sc)
    {
        /* If struct requires a destructor, rewrite as:
         *    (S tmp = S()),tmp
         * so that the destructor can be hung on tmp.
         */
        if (sd.dtor && sc.func)
        {
            /* Make an identifier for the temporary of the form:
             *   __sl%s%d, where %s is the struct name
             */
            const(size_t) len = 10;
            char[len + 1] buf;
            buf[len] = 0;
            strcpy(buf.ptr, "__sl");
            strncat(buf.ptr, sd.ident.toChars(), len - 4 - 1);
            assert(buf[len] == 0);

            auto tmp = copyToTemp(0, buf.ptr, this);
            Expression ae = new DeclarationExp(loc, tmp);
            Expression e = new CommaExp(loc, ae, new VarExp(loc, tmp));
            e = e.semantic(sc);
            return e;
        }
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * Mainly just a placeholder
 */
extern (C++) final class TypeExp : Expression
{
    extern (D) this(Loc loc, Type type)
    {
        super(loc, TOKtype, __traits(classInstanceSize, TypeExp));
        //printf("TypeExp::TypeExp(%s)\n", type->toChars());
        this.type = type;
    }

    override Expression syntaxCopy()
    {
        return new TypeExp(loc, type.syntaxCopy());
    }

    override Expression semantic(Scope* sc)
    {
        if (type.ty == Terror)
            return new ErrorExp();

        //printf("TypeExp::semantic(%s)\n", type->toChars());
        Expression e;
        Type t;
        Dsymbol s;

        type.resolve(loc, sc, &e, &t, &s, true);
        if (e)
        {
            //printf("e = %s %s\n", Token::toChars(e->op), e->toChars());
            e = e.semantic(sc);
        }
        else if (t)
        {
            //printf("t = %d %s\n", t->ty, t->toChars());
            type = t.semantic(loc, sc);
            e = this;
        }
        else if (s)
        {
            //printf("s = %s %s\n", s->kind(), s->toChars());
            e = DsymbolExp.resolve(loc, sc, s, true);
        }
        else
            assert(0);

        if (global.params.vcomplex)
            type.checkComplexTransition(loc);

        return e;
    }

    override bool checkType()
    {
        error("type %s is not an expression", toChars());
        return true;
    }

    override bool checkValue()
    {
        error("type %s has no value", toChars());
        return true;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * Mainly just a placeholder of
 *  Package, Module, Nspace, and TemplateInstance (including TemplateMixin)
 *
 * A template instance that requires IFTI:
 *      foo!tiargs(fargs)       // foo!tiargs
 * is left until CallExp::semantic() or resolveProperties()
 */
extern (C++) final class ScopeExp : Expression
{
    ScopeDsymbol sds;

    extern (D) this(Loc loc, ScopeDsymbol sds)
    {
        super(loc, TOKscope, __traits(classInstanceSize, ScopeExp));
        //printf("ScopeExp::ScopeExp(sds = '%s')\n", sds.toChars());
        //static int count; if (++count == 38) *(char*)0=0;
        this.sds = sds;
        assert(!sds.isTemplateDeclaration());   // instead, you should use TemplateExp
    }

    override Expression syntaxCopy()
    {
        return new ScopeExp(loc, cast(ScopeDsymbol)sds.syntaxCopy(null));
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("+ScopeExp::semantic(%p '%s')\n", this, toChars());
        }
        if (type)
            return this;

        ScopeDsymbol sds2 = sds;
        TemplateInstance ti = sds2.isTemplateInstance();
        while (ti)
        {
            WithScopeSymbol withsym;
            if (!ti.findTempDecl(sc, &withsym) || !ti.semanticTiargs(sc))
            {
                return new ErrorExp();
            }
            if (withsym && withsym.withstate.wthis)
            {
                Expression e = new VarExp(loc, withsym.withstate.wthis);
                e = new DotTemplateInstanceExp(loc, e, ti);
                return e.semantic(sc);
            }
            if (ti.needsTypeInference(sc))
            {
                if (TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration())
                {
                    Dsymbol p = td.toParent2();
                    FuncDeclaration fdthis = hasThis(sc);
                    AggregateDeclaration ad = p ? p.isAggregateDeclaration() : null;
                    if (fdthis && ad && isAggregate(fdthis.vthis.type) == ad && (td._scope.stc & STCstatic) == 0)
                    {
                        Expression e = new DotTemplateInstanceExp(loc, new ThisExp(loc), ti.name, ti.tiargs);
                        return e.semantic(sc);
                    }
                }
                else if (OverloadSet os = ti.tempdecl.isOverloadSet())
                {
                    FuncDeclaration fdthis = hasThis(sc);
                    AggregateDeclaration ad = os.parent.isAggregateDeclaration();
                    if (fdthis && ad && isAggregate(fdthis.vthis.type) == ad)
                    {
                        Expression e = new DotTemplateInstanceExp(loc, new ThisExp(loc), ti.name, ti.tiargs);
                        return e.semantic(sc);
                    }
                }
                // ti is an instance which requires IFTI.
                sds = ti;
                type = Type.tvoid;
                return this;
            }
            ti.semantic(sc);
            if (!ti.inst || ti.errors)
                return new ErrorExp();

            Dsymbol s = ti.toAlias();
            if (s == ti)
            {
                sds = ti;
                type = Type.tvoid;
                return this;
            }
            sds2 = s.isScopeDsymbol();
            if (sds2)
            {
                ti = sds2.isTemplateInstance();
                //printf("+ sds2 = %s, '%s'\n", sds2.kind(), sds2.toChars());
                continue;
            }

            if (auto v = s.isVarDeclaration())
            {
                if (!v.type)
                {
                    error("forward reference of %s %s", v.kind(), v.toChars());
                    return new ErrorExp();
                }
                if ((v.storage_class & STCmanifest) && v._init)
                {
                    /* When an instance that will be converted to a constant exists,
                     * the instance representation "foo!tiargs" is treated like a
                     * variable name, and its recursive appearance check (note that
                     * it's equivalent with a recursive instantiation of foo) is done
                     * separately from the circular initialization check for the
                     * eponymous enum variable declaration.
                     *
                     *  template foo(T) {
                     *    enum bool foo = foo;    // recursive definition check (v.inuse)
                     *  }
                     *  template bar(T) {
                     *    enum bool bar = bar!T;  // recursive instantiation check (ti.inuse)
                     *  }
                     */
                    if (ti.inuse)
                    {
                        error("recursive expansion of %s '%s'", ti.kind(), ti.toPrettyChars());
                        return new ErrorExp();
                    }
                    auto e = v.expandInitializer(loc);
                    ti.inuse++;
                    e = e.semantic(sc);
                    ti.inuse--;
                    return e;
                }
            }

            //printf("s = %s, '%s'\n", s.kind(), s.toChars());
            auto e = DsymbolExp.resolve(loc, sc, s, true);
            //printf("-1ScopeExp::semantic()\n");
            return e;
        }

        //printf("sds2 = %s, '%s'\n", sds2.kind(), sds2.toChars());
        //printf("\tparent = '%s'\n", sds2.parent.toChars());
        sds2.semantic(sc);

        if (auto t = sds2.getType())    // (Aggregate|Enum)Declaration
            return (new TypeExp(loc, t)).semantic(sc);

        if (auto td = sds2.isTemplateDeclaration())
            return (new TemplateExp(loc, td)).semantic(sc);

        sds = sds2;
        type = Type.tvoid;
        //printf("-2ScopeExp::semantic() %s\n", toChars());
        return this;
    }

    override bool checkType()
    {
        if (sds.isPackage())
        {
            error("%s %s has no type", sds.kind(), sds.toChars());
            return true;
        }
        if (auto ti = sds.isTemplateInstance())
        {
            //assert(ti.needsTypeInference(sc));
            if (ti.tempdecl &&
                ti.semantictiargsdone &&
                ti.semanticRun == PASSinit)
            {
                error("partial %s %s has no type", sds.kind(), toChars());
                return true;
            }
        }
        return false;
    }

    override bool checkValue()
    {
        error("%s %s has no value", sds.kind(), sds.toChars());
        return true;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * Mainly just a placeholder
 */
extern (C++) final class TemplateExp : Expression
{
    TemplateDeclaration td;
    FuncDeclaration fd;

    extern (D) this(Loc loc, TemplateDeclaration td, FuncDeclaration fd = null)
    {
        super(loc, TOKtemplate, __traits(classInstanceSize, TemplateExp));
        //printf("TemplateExp(): %s\n", td->toChars());
        this.td = td;
        this.fd = fd;
    }

    override bool isLvalue()
    {
        return fd !is null;
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        if (!fd)
            return Expression.toLvalue(sc, e);

        assert(sc);
        return DsymbolExp.resolve(loc, sc, fd, true);
    }

    override bool checkType()
    {
        error("%s %s has no type", td.kind(), toChars());
        return true;
    }

    override bool checkValue()
    {
        error("%s %s has no value", td.kind(), toChars());
        return true;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * thisexp.new(newargs) newtype(arguments)
 */
extern (C++) final class NewExp : Expression
{
    Expression thisexp;         // if !=null, 'this' for class being allocated
    Expressions* newargs;       // Array of Expression's to call new operator
    Type newtype;
    Expressions* arguments;     // Array of Expression's
    Expression argprefix;       // expression to be evaluated just before arguments[]
    CtorDeclaration member;     // constructor function
    NewDeclaration allocator;   // allocator function
    int onstack;                // allocate on stack

    extern (D) this(Loc loc, Expression thisexp, Expressions* newargs, Type newtype, Expressions* arguments)
    {
        super(loc, TOKnew, __traits(classInstanceSize, NewExp));
        this.thisexp = thisexp;
        this.newargs = newargs;
        this.newtype = newtype;
        this.arguments = arguments;
    }

    override Expression syntaxCopy()
    {
        return new NewExp(loc,
            thisexp ? thisexp.syntaxCopy() : null,
            arraySyntaxCopy(newargs),
            newtype.syntaxCopy(),
            arraySyntaxCopy(arguments));
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("NewExp::semantic() %s\n", toChars());
            if (thisexp)
                printf("\tthisexp = %s\n", thisexp.toChars());
            printf("\tnewtype: %s\n", newtype.toChars());
        }
        if (type) // if semantic() already run
            return this;

        // Bugzilla 11581: With the syntax `new T[edim]` or `thisexp.new T[edim]`,
        // T should be analyzed first and edim should go into arguments iff it's
        // not a tuple.
        Expression edim = null;
        if (!arguments && newtype.ty == Tsarray)
        {
            edim = (cast(TypeSArray)newtype).dim;
            newtype = (cast(TypeNext)newtype).next;
        }

        ClassDeclaration cdthis = null;
        if (thisexp)
        {
            thisexp = thisexp.semantic(sc);
            if (thisexp.op == TOKerror)
                return new ErrorExp();
            cdthis = thisexp.type.isClassHandle();
            if (!cdthis)
            {
                error("'this' for nested class must be a class type, not %s", thisexp.type.toChars());
                return new ErrorExp();
            }

            sc = sc.push(cdthis);
            type = newtype.semantic(loc, sc);
            sc = sc.pop();
        }
        else
        {
            type = newtype.semantic(loc, sc);
        }
        if (type.ty == Terror)
            return new ErrorExp();

        if (edim)
        {
            if (type.toBasetype().ty == Ttuple)
            {
                // --> new T[edim]
                type = new TypeSArray(type, edim);
                type = type.semantic(loc, sc);
                if (type.ty == Terror)
                    return new ErrorExp();
            }
            else
            {
                // --> new T[](edim)
                arguments = new Expressions();
                arguments.push(edim);
                type = type.arrayOf();
            }
        }

        newtype = type; // in case type gets cast to something else
        Type tb = type.toBasetype();
        //printf("tb: %s, deco = %s\n", tb.toChars(), tb.deco);

        if (arrayExpressionSemantic(newargs, sc) ||
            preFunctionParameters(loc, sc, newargs))
        {
            return new ErrorExp();
        }
        if (arrayExpressionSemantic(arguments, sc) ||
            preFunctionParameters(loc, sc, arguments))
        {
            return new ErrorExp();
        }

        if (thisexp && tb.ty != Tclass)
        {
            error("e.new is only for allocating nested classes, not %s", tb.toChars());
            return new ErrorExp();
        }

        size_t nargs = arguments ? arguments.dim : 0;
        Expression newprefix = null;

        if (tb.ty == Tclass)
        {
            auto cd = (cast(TypeClass)tb).sym;
            cd.size(loc);
            if (cd.sizeok != SIZEOKdone)
                return new ErrorExp();
            if (!cd.ctor)
                cd.ctor = cd.searchCtor();
            if (cd.noDefaultCtor && !nargs && !cd.defaultCtor)
            {
                error("default construction is disabled for type %s", cd.type.toChars());
                return new ErrorExp();
            }

            if (cd.isInterfaceDeclaration())
            {
                error("cannot create instance of interface %s", cd.toChars());
                return new ErrorExp();
            }
            if (cd.isAbstract())
            {
                error("cannot create instance of abstract class %s", cd.toChars());
                for (size_t i = 0; i < cd.vtbl.dim; i++)
                {
                    FuncDeclaration fd = cd.vtbl[i].isFuncDeclaration();
                    if (fd && fd.isAbstract())
                    {
                        errorSupplemental(loc, "function '%s' is not implemented",
                            fd.toFullSignature());
                    }
                }
                return new ErrorExp();
            }
            // checkDeprecated() is already done in newtype->semantic().

            if (cd.isNested())
            {
                /* We need a 'this' pointer for the nested class.
                 * Ensure we have the right one.
                 */
                Dsymbol s = cd.toParent2();

                //printf("cd isNested, parent = %s '%s'\n", s.kind(), s.toPrettyChars());
                if (auto cdn = s.isClassDeclaration())
                {
                    if (!cdthis)
                    {
                        // Supply an implicit 'this' and try again
                        thisexp = new ThisExp(loc);
                        for (Dsymbol sp = sc.parent; 1; sp = sp.parent)
                        {
                            if (!sp)
                            {
                                error("outer class %s 'this' needed to 'new' nested class %s",
                                    cdn.toChars(), cd.toChars());
                                return new ErrorExp();
                            }
                            ClassDeclaration cdp = sp.isClassDeclaration();
                            if (!cdp)
                                continue;
                            if (cdp == cdn || cdn.isBaseOf(cdp, null))
                                break;
                            // Add a '.outer' and try again
                            thisexp = new DotIdExp(loc, thisexp, Id.outer);
                        }

                        thisexp = thisexp.semantic(sc);
                        if (thisexp.op == TOKerror)
                            return new ErrorExp();
                        cdthis = thisexp.type.isClassHandle();
                    }
                    if (cdthis != cdn && !cdn.isBaseOf(cdthis, null))
                    {
                        //printf("cdthis = %s\n", cdthis.toChars());
                        error("'this' for nested class must be of type %s, not %s",
                            cdn.toChars(), thisexp.type.toChars());
                        return new ErrorExp();
                    }
                    if (!MODimplicitConv(thisexp.type.mod, newtype.mod))
                    {
                        error("nested type %s should have the same or weaker constancy as enclosing type %s",
                            newtype.toChars(), thisexp.type.toChars());
                        return new ErrorExp();
                    }
                }
                else if (thisexp)
                {
                    error("e.new is only for allocating nested classes");
                    return new ErrorExp();
                }
                else if (auto fdn = s.isFuncDeclaration())
                {
                    // make sure the parent context fdn of cd is reachable from sc
                    if (checkNestedRef(sc.parent, fdn))
                    {
                        error("outer function context of %s is needed to 'new' nested class %s",
                            fdn.toPrettyChars(), cd.toPrettyChars());
                        return new ErrorExp();
                    }
                }
                else
                    assert(0);
            }
            else if (thisexp)
            {
                error("e.new is only for allocating nested classes");
                return new ErrorExp();
            }

            if (cd.aggNew)
            {
                // Prepend the size argument to newargs[]
                Expression e = new IntegerExp(loc, cd.size(loc), Type.tsize_t);
                if (!newargs)
                    newargs = new Expressions();
                newargs.shift(e);

                FuncDeclaration f = resolveFuncCall(loc, sc, cd.aggNew, null, tb, newargs);
                if (!f || f.errors)
                    return new ErrorExp();
                checkDeprecated(sc, f);
                checkPurity(sc, f);
                checkSafety(sc, f);
                checkNogc(sc, f);
                checkAccess(cd, loc, sc, f);

                TypeFunction tf = cast(TypeFunction)f.type;
                Type rettype;
                if (functionParameters(loc, sc, tf, null, newargs, f, &rettype, &newprefix))
                    return new ErrorExp();

                allocator = f.isNewDeclaration();
                assert(allocator);
            }
            else
            {
                if (newargs && newargs.dim)
                {
                    error("no allocator for %s", cd.toChars());
                    return new ErrorExp();
                }
            }

            if (cd.ctor)
            {
                FuncDeclaration f = resolveFuncCall(loc, sc, cd.ctor, null, tb, arguments, 0);
                if (!f || f.errors)
                    return new ErrorExp();
                checkDeprecated(sc, f);
                checkPurity(sc, f);
                checkSafety(sc, f);
                checkNogc(sc, f);
                checkAccess(cd, loc, sc, f);

                TypeFunction tf = cast(TypeFunction)f.type;
                if (!arguments)
                    arguments = new Expressions();
                if (functionParameters(loc, sc, tf, type, arguments, f, &type, &argprefix))
                    return new ErrorExp();

                member = f.isCtorDeclaration();
                assert(member);
            }
            else
            {
                if (nargs)
                {
                    error("no constructor for %s", cd.toChars());
                    return new ErrorExp();
                }
            }
        }
        else if (tb.ty == Tstruct)
        {
            auto sd = (cast(TypeStruct)tb).sym;
            sd.size(loc);
            if (sd.sizeok != SIZEOKdone)
                return new ErrorExp();
            if (!sd.ctor)
                sd.ctor = sd.searchCtor();
            if (sd.noDefaultCtor && !nargs)
            {
                error("default construction is disabled for type %s", sd.type.toChars());
                return new ErrorExp();
            }
            // checkDeprecated() is already done in newtype->semantic().

            if (sd.aggNew)
            {
                // Prepend the uint size argument to newargs[]
                Expression e = new IntegerExp(loc, sd.size(loc), Type.tsize_t);
                if (!newargs)
                    newargs = new Expressions();
                newargs.shift(e);

                FuncDeclaration f = resolveFuncCall(loc, sc, sd.aggNew, null, tb, newargs);
                if (!f || f.errors)
                    return new ErrorExp();
                checkDeprecated(sc, f);
                checkPurity(sc, f);
                checkSafety(sc, f);
                checkNogc(sc, f);
                checkAccess(sd, loc, sc, f);

                TypeFunction tf = cast(TypeFunction)f.type;
                Type rettype;
                if (functionParameters(loc, sc, tf, null, newargs, f, &rettype, &newprefix))
                    return new ErrorExp();

                allocator = f.isNewDeclaration();
                assert(allocator);
            }
            else
            {
                if (newargs && newargs.dim)
                {
                    error("no allocator for %s", sd.toChars());
                    return new ErrorExp();
                }
            }

            if (sd.ctor && nargs)
            {
                FuncDeclaration f = resolveFuncCall(loc, sc, sd.ctor, null, tb, arguments, 0);
                if (!f || f.errors)
                    return new ErrorExp();
                checkDeprecated(sc, f);
                checkPurity(sc, f);
                checkSafety(sc, f);
                checkNogc(sc, f);
                checkAccess(sd, loc, sc, f);

                TypeFunction tf = cast(TypeFunction)f.type;
                if (!arguments)
                    arguments = new Expressions();
                if (functionParameters(loc, sc, tf, type, arguments, f, &type, &argprefix))
                    return new ErrorExp();

                member = f.isCtorDeclaration();
                assert(member);

                if (checkFrameAccess(loc, sc, sd, sd.fields.dim))
                    return new ErrorExp();
            }
            else
            {
                if (!arguments)
                    arguments = new Expressions();

                if (!sd.fit(loc, sc, arguments, tb))
                    return new ErrorExp();

                if (!sd.fill(loc, arguments, false))
                    return new ErrorExp();

                if (checkFrameAccess(loc, sc, sd, arguments ? arguments.dim : 0))
                    return new ErrorExp();
            }

            type = type.pointerTo();
        }
        else if (tb.ty == Tarray && nargs)
        {
            Type tn = tb.nextOf().baseElemOf();
            Dsymbol s = tn.toDsymbol(sc);
            AggregateDeclaration ad = s ? s.isAggregateDeclaration() : null;
            if (ad && ad.noDefaultCtor)
            {
                error("default construction is disabled for type %s", tb.nextOf().toChars());
                return new ErrorExp();
            }
            for (size_t i = 0; i < nargs; i++)
            {
                if (tb.ty != Tarray)
                {
                    error("too many arguments for array");
                    return new ErrorExp();
                }

                Expression arg = (*arguments)[i];
                arg = resolveProperties(sc, arg);
                arg = arg.implicitCastTo(sc, Type.tsize_t);
                arg = arg.optimize(WANTvalue);
                if (arg.op == TOKint64 && cast(sinteger_t)arg.toInteger() < 0)
                {
                    error("negative array index %s", arg.toChars());
                    return new ErrorExp();
                }
                (*arguments)[i] = arg;
                tb = (cast(TypeDArray)tb).next.toBasetype();
            }
        }
        else if (tb.isscalar())
        {
            if (!nargs)
            {
            }
            else if (nargs == 1)
            {
                Expression e = (*arguments)[0];
                e = e.implicitCastTo(sc, tb);
                (*arguments)[0] = e;
            }
            else
            {
                error("more than one argument for construction of %s", type.toChars());
                return new ErrorExp();
            }

            type = type.pointerTo();
        }
        else
        {
            error("new can only create structs, dynamic arrays or class objects, not %s's", type.toChars());
            return new ErrorExp();
        }

        //printf("NewExp: '%s'\n", toChars());
        //printf("NewExp:type '%s'\n", type->toChars());
        semanticTypeInfo(sc, type);

        if (newprefix)
            return combine(newprefix, this);
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * thisexp.new(newargs) class baseclasses { } (arguments)
 */
extern (C++) final class NewAnonClassExp : Expression
{
    Expression thisexp;     // if !=null, 'this' for class being allocated
    Expressions* newargs;   // Array of Expression's to call new operator
    ClassDeclaration cd;    // class being instantiated
    Expressions* arguments; // Array of Expression's to call class constructor

    extern (D) this(Loc loc, Expression thisexp, Expressions* newargs, ClassDeclaration cd, Expressions* arguments)
    {
        super(loc, TOKnewanonclass, __traits(classInstanceSize, NewAnonClassExp));
        this.thisexp = thisexp;
        this.newargs = newargs;
        this.cd = cd;
        this.arguments = arguments;
    }

    override Expression syntaxCopy()
    {
        return new NewAnonClassExp(loc, thisexp ? thisexp.syntaxCopy() : null, arraySyntaxCopy(newargs), cast(ClassDeclaration)cd.syntaxCopy(null), arraySyntaxCopy(arguments));
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("NewAnonClassExp::semantic() %s\n", toChars());
            //printf("thisexp = %p\n", thisexp);
            //printf("type: %s\n", type->toChars());
        }

        Expression d = new DeclarationExp(loc, cd);
        sc = sc.push(); // just create new scope
        sc.flags &= ~SCOPEctfe; // temporary stop CTFE
        d = d.semantic(sc);
        sc = sc.pop();

        if (!cd.errors && sc.intypeof && !sc.parent.inNonRoot())
        {
            ScopeDsymbol sds = sc.tinst ? cast(ScopeDsymbol)sc.tinst : sc._module;
            sds.members.push(cd);
        }

        Expression n = new NewExp(loc, thisexp, newargs, cd.type, arguments);

        Expression c = new CommaExp(loc, d, n);
        return c.semantic(sc);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) class SymbolExp : Expression
{
    Declaration var;
    bool hasOverloads;

    final extern (D) this(Loc loc, TOK op, int size, Declaration var, bool hasOverloads)
    {
        super(loc, op, size);
        assert(var);
        this.var = var;
        this.hasOverloads = hasOverloads;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }

    override void printAST(int indent)
    {
        Expression.printAST(indent);
        foreach (i; 0 .. indent + 2)
            printf(" ");
        printf(".var: %s\n", var ? var.toChars() : "");
    }
}

/***********************************************************
 * Offset from symbol
 */
extern (C++) final class SymOffExp : SymbolExp
{
    dinteger_t offset;

    extern (D) this(Loc loc, Declaration var, dinteger_t offset, bool hasOverloads = true)
    {
        if (auto v = var.isVarDeclaration())
        {
            // FIXME: This error report will never be handled anyone.
            // It should be done before the SymOffExp construction.
            if (v.needThis())
                .error(loc, "need 'this' for address of %s", v.toChars());
            hasOverloads = false;
        }
        super(loc, TOKsymoff, __traits(classInstanceSize, SymOffExp), var, hasOverloads);
        this.offset = offset;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("SymOffExp::semantic('%s')\n", toChars());
        }
        //var->semantic(sc);
        if (!type)
            type = var.type.pointerTo();

        if (auto v = var.isVarDeclaration())
        {
            if (v.checkNestedReference(sc, loc))
                return new ErrorExp();
        }
        else if (auto f = var.isFuncDeclaration())
        {
            if (f.checkNestedReference(sc, loc))
                return new ErrorExp();
        }

        return this;
    }

    override bool isBool(bool result)
    {
        return result ? true : false;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * Variable
 */
extern (C++) final class VarExp : SymbolExp
{
    extern (D) this(Loc loc, Declaration var, bool hasOverloads = true)
    {
        if (var.isVarDeclaration())
            hasOverloads = false;

        super(loc, TOKvar, __traits(classInstanceSize, VarExp), var, hasOverloads);
        //printf("VarExp(this = %p, '%s', loc = %s)\n", this, var->toChars(), loc.toChars());
        //if (strcmp(var->ident->toChars(), "func") == 0) assert(0);
        this.type = var.type;
    }

    static VarExp create(Loc loc, Declaration var, bool hasOverloads = true)
    {
        return new VarExp(loc, var, hasOverloads);
    }

    override bool equals(RootObject o)
    {
        if (this == o)
            return true;
        if ((cast(Expression)o).op == TOKvar)
        {
            VarExp ne = cast(VarExp)o;
            if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && var == ne.var)
            {
                return true;
            }
        }
        return false;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("VarExp::semantic(%s)\n", toChars());
        }
        if (auto fd = var.isFuncDeclaration())
        {
            //printf("L%d fd = %s\n", __LINE__, f->toChars());
            if (!fd.functionSemantic())
                return new ErrorExp();
        }

        if (!type)
            type = var.type;
        if (type && !type.deco)
            type = type.semantic(loc, sc);

        /* Fix for 1161 doesn't work because it causes protection
         * problems when instantiating imported templates passing private
         * variables as alias template parameters.
         */
        //checkAccess(loc, sc, NULL, var);

        if (auto vd = var.isVarDeclaration())
        {
            if (vd.checkNestedReference(sc, loc))
                return new ErrorExp();

            // Bugzilla 12025: If the variable is not actually used in runtime code,
            // the purity violation error is redundant.
            //checkPurity(sc, vd);
        }
        else if (auto fd = var.isFuncDeclaration())
        {
            // TODO: If fd isn't yet resolved its overload, the checkNestedReference
            // call would cause incorrect validation.
            // Maybe here should be moved in CallExp, or AddrExp for functions.
            if (fd.checkNestedReference(sc, loc))
                return new ErrorExp();
        }
        else if (auto od = var.isOverDeclaration())
        {
            type = Type.tvoid; // ambiguous type?
        }

        return this;
    }

    override int checkModifiable(Scope* sc, int flag)
    {
        //printf("VarExp::checkModifiable %s", toChars());
        assert(type);
        return var.checkModify(loc, sc, type, null, flag);
    }

    bool checkReadModifyWrite();

    override bool isLvalue()
    {
        if (var.storage_class & (STClazy | STCrvalue | STCmanifest))
            return false;
        return true;
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        if (var.storage_class & STCmanifest)
        {
            error("manifest constant '%s' is not lvalue", var.toChars());
            return new ErrorExp();
        }
        if (var.storage_class & STClazy)
        {
            error("lazy variables cannot be lvalues");
            return new ErrorExp();
        }
        if (var.ident == Id.ctfe)
        {
            error("compiler-generated variable __ctfe is not an lvalue");
            return new ErrorExp();
        }
        if (var.ident == Id.dollar) // Bugzilla 13574
        {
            error("'$' is not an lvalue");
            return new ErrorExp();
        }
        return this;
    }

    override Expression modifiableLvalue(Scope* sc, Expression e)
    {
        //printf("VarExp::modifiableLvalue('%s')\n", var->toChars());
        if (var.storage_class & STCmanifest)
        {
            error("cannot modify manifest constant '%s'", toChars());
            return new ErrorExp();
        }
        // See if this expression is a modifiable lvalue (i.e. not const)
        return Expression.modifiableLvalue(sc, e);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * Overload Set
 */
extern (C++) final class OverExp : Expression
{
    OverloadSet vars;

    extern (D) this(Loc loc, OverloadSet s)
    {
        super(loc, TOKoverloadset, __traits(classInstanceSize, OverExp));
        //printf("OverExp(this = %p, '%s')\n", this, var->toChars());
        vars = s;
        type = Type.tvoid;
    }

    override bool isLvalue()
    {
        return true;
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * Function/Delegate literal
 */
extern (C++) final class FuncExp : Expression
{
    FuncLiteralDeclaration fd;
    TemplateDeclaration td;
    TOK tok;

    extern (D) this(Loc loc, Dsymbol s)
    {
        super(loc, TOKfunction, __traits(classInstanceSize, FuncExp));
        this.td = s.isTemplateDeclaration();
        this.fd = s.isFuncLiteralDeclaration();
        if (td)
        {
            assert(td.literal);
            assert(td.members && td.members.dim == 1);
            fd = (*td.members)[0].isFuncLiteralDeclaration();
        }
        tok = fd.tok; // save original kind of function/delegate/(infer)
        assert(fd.fbody);
    }

    override bool equals(RootObject o)
    {
        if (this == o)
            return true;
        if (o.dyncast() != DYNCAST_EXPRESSION)
            return false;
        if ((cast(Expression)o).op == TOKfunction)
        {
            FuncExp fe = cast(FuncExp)o;
            return fd == fe.fd;
        }
        return false;
    }

    void genIdent(Scope* sc)
    {
        if (fd.ident == Id.empty)
        {
            const(char)* s;
            if (fd.fes)
                s = "__foreachbody";
            else if (fd.tok == TOKreserved)
                s = "__lambda";
            else if (fd.tok == TOKdelegate)
                s = "__dgliteral";
            else
                s = "__funcliteral";

            DsymbolTable symtab;
            if (FuncDeclaration func = sc.parent.isFuncDeclaration())
            {
                if (func.localsymtab is null)
                {
                    // Inside template constraint, symtab is not set yet.
                    // Initialize it lazily.
                    func.localsymtab = new DsymbolTable();
                }
                symtab = func.localsymtab;
            }
            else
            {
                ScopeDsymbol sds = sc.parent.isScopeDsymbol();
                if (!sds.symtab)
                {
                    // Inside template constraint, symtab may not be set yet.
                    // Initialize it lazily.
                    assert(sds.isTemplateInstance());
                    sds.symtab = new DsymbolTable();
                }
                symtab = sds.symtab;
            }
            assert(symtab);
            Identifier id = Identifier.generateId(s, symtab.len() + 1);
            fd.ident = id;
            if (td)
                td.ident = id;
            symtab.insert(td ? cast(Dsymbol)td : cast(Dsymbol)fd);
        }
    }

    override Expression syntaxCopy()
    {
        if (td)
            return new FuncExp(loc, td.syntaxCopy(null));
        else if (fd.semanticRun == PASSinit)
            return new FuncExp(loc, fd.syntaxCopy(null));
        else // Bugzilla 13481: Prevent multiple semantic analysis of lambda body.
            return new FuncExp(loc, fd);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("FuncExp::semantic(%s)\n", toChars());
            if (fd.treq)
                printf("  treq = %s\n", fd.treq.toChars());
        }
        Expression e = this;

        sc = sc.push(); // just create new scope
        sc.flags &= ~SCOPEctfe; // temporary stop CTFE
        sc.protection = Prot(PROTpublic); // Bugzilla 12506

        if (!type || type == Type.tvoid)
        {
            /* fd->treq might be incomplete type,
             * so should not semantic it.
             * void foo(T)(T delegate(int) dg){}
             * foo(a=>a); // in IFTI, treq == T delegate(int)
             */
            //if (fd->treq)
            //    fd->treq = fd->treq->semantic(loc, sc);

            genIdent(sc);

            // Set target of return type inference
            if (fd.treq && !fd.type.nextOf())
            {
                TypeFunction tfv = null;
                if (fd.treq.ty == Tdelegate || (fd.treq.ty == Tpointer && fd.treq.nextOf().ty == Tfunction))
                    tfv = cast(TypeFunction)fd.treq.nextOf();
                if (tfv)
                {
                    TypeFunction tfl = cast(TypeFunction)fd.type;
                    tfl.next = tfv.nextOf();
                }
            }

            //printf("td = %p, treq = %p\n", td, fd->treq);
            if (td)
            {
                assert(td.parameters && td.parameters.dim);
                td.semantic(sc);
                type = Type.tvoid; // temporary type

                if (fd.treq) // defer type determination
                {
                    FuncExp fe;
                    if (matchType(fd.treq, sc, &fe) > MATCHnomatch)
                        e = fe;
                    else
                        e = new ErrorExp();
                }
                goto Ldone;
            }

            uint olderrors = global.errors;
            fd.semantic(sc);
            if (olderrors == global.errors)
            {
                fd.semantic2(sc);
                if (olderrors == global.errors)
                    fd.semantic3(sc);
            }
            if (olderrors != global.errors)
            {
                if (fd.type && fd.type.ty == Tfunction && !fd.type.nextOf())
                    (cast(TypeFunction)fd.type).next = Type.terror;
                e = new ErrorExp();
                goto Ldone;
            }

            // Type is a "delegate to" or "pointer to" the function literal
            if ((fd.isNested() && fd.tok == TOKdelegate) || (tok == TOKreserved && fd.treq && fd.treq.ty == Tdelegate))
            {
                type = new TypeDelegate(fd.type);
                type = type.semantic(loc, sc);

                fd.tok = TOKdelegate;
            }
            else
            {
                type = new TypePointer(fd.type);
                type = type.semantic(loc, sc);
                //type = fd->type->pointerTo();

                /* A lambda expression deduced to function pointer might become
                 * to a delegate literal implicitly.
                 *
                 *   auto foo(void function() fp) { return 1; }
                 *   assert(foo({}) == 1);
                 *
                 * So, should keep fd->tok == TOKreserve if fd->treq == NULL.
                 */
                if (fd.treq && fd.treq.ty == Tpointer)
                {
                    // change to non-nested
                    fd.tok = TOKfunction;
                    fd.vthis = null;
                }
            }
            fd.tookAddressOf++;
        }

    Ldone:
        sc = sc.pop();
        return e;
    }

    // used from CallExp::semantic()
    Expression semantic(Scope* sc, Expressions* arguments)
    {
        if ((!type || type == Type.tvoid) && td && arguments && arguments.dim)
        {
            for (size_t k = 0; k < arguments.dim; k++)
            {
                Expression checkarg = (*arguments)[k];
                if (checkarg.op == TOKerror)
                    return checkarg;
            }

            genIdent(sc);

            assert(td.parameters && td.parameters.dim);
            td.semantic(sc);

            TypeFunction tfl = cast(TypeFunction)fd.type;
            size_t dim = Parameter.dim(tfl.parameters);
            if (arguments.dim < dim)
            {
                // Default arguments are always typed, so they don't need inference.
                Parameter p = Parameter.getNth(tfl.parameters, arguments.dim);
                if (p.defaultArg)
                    dim = arguments.dim;
            }

            if ((!tfl.varargs && arguments.dim == dim) || (tfl.varargs && arguments.dim >= dim))
            {
                auto tiargs = new Objects();
                tiargs.reserve(td.parameters.dim);

                for (size_t i = 0; i < td.parameters.dim; i++)
                {
                    TemplateParameter tp = (*td.parameters)[i];
                    for (size_t u = 0; u < dim; u++)
                    {
                        Parameter p = Parameter.getNth(tfl.parameters, u);
                        if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident)
                        {
                            Expression e = (*arguments)[u];
                            tiargs.push(e.type);
                            u = dim; // break inner loop
                        }
                    }
                }

                auto ti = new TemplateInstance(loc, td, tiargs);
                return (new ScopeExp(loc, ti)).semantic(sc);
            }
            error("cannot infer function literal type");
            return new ErrorExp();
        }
        return semantic(sc);
    }

    MATCH matchType(Type to, Scope* sc, FuncExp* presult, int flag = 0)
    {
        //printf("FuncExp::matchType('%s'), to=%s\n", type ? type->toChars() : "null", to->toChars());
        if (presult)
            *presult = null;

        TypeFunction tof = null;
        if (to.ty == Tdelegate)
        {
            if (tok == TOKfunction)
            {
                if (!flag)
                    error("cannot match function literal to delegate type '%s'", to.toChars());
                return MATCHnomatch;
            }
            tof = cast(TypeFunction)to.nextOf();
        }
        else if (to.ty == Tpointer && to.nextOf().ty == Tfunction)
        {
            if (tok == TOKdelegate)
            {
                if (!flag)
                    error("cannot match delegate literal to function pointer type '%s'", to.toChars());
                return MATCHnomatch;
            }
            tof = cast(TypeFunction)to.nextOf();
        }

        if (td)
        {
            if (!tof)
            {
            L1:
                if (!flag)
                    error("cannot infer parameter types from %s", to.toChars());
                return MATCHnomatch;
            }

            // Parameter types inference from 'tof'
            assert(td._scope);
            TypeFunction tf = cast(TypeFunction)fd.type;
            //printf("\ttof = %s\n", tof->toChars());
            //printf("\ttf  = %s\n", tf->toChars());
            size_t dim = Parameter.dim(tf.parameters);

            if (Parameter.dim(tof.parameters) != dim || tof.varargs != tf.varargs)
                goto L1;

            auto tiargs = new Objects();
            tiargs.reserve(td.parameters.dim);

            for (size_t i = 0; i < td.parameters.dim; i++)
            {
                TemplateParameter tp = (*td.parameters)[i];
                size_t u = 0;
                for (; u < dim; u++)
                {
                    Parameter p = Parameter.getNth(tf.parameters, u);
                    if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident)
                    {
                        break;
                    }
                }
                assert(u < dim);
                Parameter pto = Parameter.getNth(tof.parameters, u);
                Type t = pto.type;
                if (t.ty == Terror)
                    goto L1;
                tiargs.push(t);
            }

            // Set target of return type inference
            if (!tf.next && tof.next)
                fd.treq = to;

            auto ti = new TemplateInstance(loc, td, tiargs);
            Expression ex = (new ScopeExp(loc, ti)).semantic(td._scope);

            // Reset inference target for the later re-semantic
            fd.treq = null;

            if (ex.op == TOKerror)
                return MATCHnomatch;
            if (ex.op != TOKfunction)
                goto L1;
            return (cast(FuncExp)ex).matchType(to, sc, presult, flag);
        }

        if (!tof || !tof.next)
            return MATCHnomatch;

        assert(type && type != Type.tvoid);
        TypeFunction tfx = cast(TypeFunction)fd.type;
        bool convertMatch = (type.ty != to.ty);

        if (fd.inferRetType && tfx.next.implicitConvTo(tof.next) == MATCHconvert)
        {
            /* If return type is inferred and covariant return,
             * tweak return statements to required return type.
             *
             * interface I {}
             * class C : Object, I{}
             *
             * I delegate() dg = delegate() { return new class C(); }
             */
            convertMatch = true;

            auto tfy = new TypeFunction(tfx.parameters, tof.next, tfx.varargs, tfx.linkage, STCundefined);
            tfy.mod = tfx.mod;
            tfy.isnothrow = tfx.isnothrow;
            tfy.isnogc = tfx.isnogc;
            tfy.purity = tfx.purity;
            tfy.isproperty = tfx.isproperty;
            tfy.isref = tfx.isref;
            tfy.iswild = tfx.iswild;
            tfy.deco = tfy.merge().deco;

            tfx = tfy;
        }
        Type tx;
        if (tok == TOKdelegate || tok == TOKreserved && (type.ty == Tdelegate || type.ty == Tpointer && to.ty == Tdelegate))
        {
            // Allow conversion from implicit function pointer to delegate
            tx = new TypeDelegate(tfx);
            tx.deco = tx.merge().deco;
        }
        else
        {
            assert(tok == TOKfunction || tok == TOKreserved && type.ty == Tpointer);
            tx = tfx.pointerTo();
        }
        //printf("\ttx = %s, to = %s\n", tx->toChars(), to->toChars());

        MATCH m = tx.implicitConvTo(to);
        if (m > MATCHnomatch)
        {
            // MATCHexact:      exact type match
            // MATCHconst:      covairiant type match (eg. attributes difference)
            // MATCHconvert:    context conversion
            m = convertMatch ? MATCHconvert : tx.equals(to) ? MATCHexact : MATCHconst;

            if (presult)
            {
                (*presult) = cast(FuncExp)copy();
                (*presult).type = to;

                // Bugzilla 12508: Tweak function body for covariant returns.
                (*presult).fd.modifyReturns(sc, tof.next);
            }
        }
        else if (!flag)
        {
            error("cannot implicitly convert expression (%s) of type %s to %s", toChars(), tx.toChars(), to.toChars());
        }
        return m;
    }

    override const(char)* toChars()
    {
        return fd.toChars();
    }

    override bool checkType()
    {
        if (td)
        {
            error("template lambda has no type");
            return true;
        }
        return false;
    }

    override bool checkValue()
    {
        if (td)
        {
            error("template lambda has no value");
            return true;
        }
        return false;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * Declaration of a symbol
 *
 * D grammar allows declarations only as statements. However in AST representation
 * it can be part of any expression. This is used, for example, during internal
 * syntax re-writes to inject hidden symbols.
 */
extern (C++) final class DeclarationExp : Expression
{
    Dsymbol declaration;

    extern (D) this(Loc loc, Dsymbol declaration)
    {
        super(loc, TOKdeclaration, __traits(classInstanceSize, DeclarationExp));
        this.declaration = declaration;
    }

    override Expression syntaxCopy()
    {
        return new DeclarationExp(loc, declaration.syntaxCopy(null));
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;
        static if (LOGSEMANTIC)
        {
            printf("DeclarationExp::semantic() %s\n", toChars());
        }

        uint olderrors = global.errors;

        /* This is here to support extern(linkage) declaration,
         * where the extern(linkage) winds up being an AttribDeclaration
         * wrapper.
         */
        Dsymbol s = declaration;

        while (1)
        {
            AttribDeclaration ad = s.isAttribDeclaration();
            if (ad)
            {
                if (ad.decl && ad.decl.dim == 1)
                {
                    s = (*ad.decl)[0];
                    continue;
                }
            }
            break;
        }

        VarDeclaration v = s.isVarDeclaration();
        if (v)
        {
            // Do semantic() on initializer first, so:
            //      int a = a;
            // will be illegal.
            declaration.semantic(sc);
            s.parent = sc.parent;
        }

        //printf("inserting '%s' %p into sc = %p\n", s->toChars(), s, sc);
        // Insert into both local scope and function scope.
        // Must be unique in both.
        if (s.ident)
        {
            if (!sc.insert(s))
            {
                error("declaration %s is already defined", s.toPrettyChars());
                return new ErrorExp();
            }
            else if (sc.func)
            {
                // Bugzilla 11720 - include Dataseg variables
                if ((s.isFuncDeclaration() ||
                     s.isAggregateDeclaration() ||
                     s.isEnumDeclaration() ||
                     v && v.isDataseg()) && !sc.func.localsymtab.insert(s))
                {
                    error("declaration %s is already defined in another scope in %s", s.toPrettyChars(), sc.func.toChars());
                    return new ErrorExp();
                }
                else
                {
                    // Disallow shadowing
                    for (Scope* scx = sc.enclosing; scx && scx.func == sc.func; scx = scx.enclosing)
                    {
                        Dsymbol s2;
                        if (scx.scopesym && scx.scopesym.symtab && (s2 = scx.scopesym.symtab.lookup(s.ident)) !is null && s != s2)
                        {
                            error("%s %s is shadowing %s %s", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars());
                            return new ErrorExp();
                        }
                    }
                }
            }
        }
        if (!s.isVarDeclaration())
        {
            Scope* sc2 = sc;
            if (sc2.stc & (STCpure | STCnothrow | STCnogc))
                sc2 = sc.push();
            sc2.stc &= ~(STCpure | STCnothrow | STCnogc);
            declaration.semantic(sc2);
            if (sc2 != sc)
                sc2.pop();
            s.parent = sc.parent;
        }
        if (global.errors == olderrors)
        {
            declaration.semantic2(sc);
            if (global.errors == olderrors)
            {
                declaration.semantic3(sc);
            }
        }
        // todo: error in declaration should be propagated.

        type = Type.tvoid;
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * typeid(int)
 */
extern (C++) final class TypeidExp : Expression
{
    RootObject obj;

    extern (D) this(Loc loc, RootObject o)
    {
        super(loc, TOKtypeid, __traits(classInstanceSize, TypeidExp));
        this.obj = o;
    }

    override Expression syntaxCopy()
    {
        return new TypeidExp(loc, objectSyntaxCopy(obj));
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("TypeidExp::semantic() %s\n", toChars());
        }
        Type ta = isType(obj);
        Expression ea = isExpression(obj);
        Dsymbol sa = isDsymbol(obj);
        //printf("ta %p ea %p sa %p\n", ta, ea, sa);

        if (ta)
        {
            ta.resolve(loc, sc, &ea, &ta, &sa, true);
        }

        if (ea)
        {
            if (auto sym = getDsymbol(ea))
                ea = DsymbolExp.resolve(loc, sc, sym, false);
            else
                ea = ea.semantic(sc);
            ea = resolveProperties(sc, ea);
            ta = ea.type;
            if (ea.op == TOKtype)
                ea = null;
        }

        if (!ta)
        {
            //printf("ta %p ea %p sa %p\n", ta, ea, sa);
            error("no type for typeid(%s)", ea ? ea.toChars() : (sa ? sa.toChars() : ""));
            return new ErrorExp();
        }

        if (global.params.vcomplex)
            ta.checkComplexTransition(loc);

        Expression e;
        if (ea && ta.toBasetype().ty == Tclass)
        {
            /* Get the dynamic type, which is .classinfo
             */
            ea = ea.semantic(sc);
            e = new TypeidExp(ea.loc, ea);
            e.type = Type.typeinfoclass.type;
        }
        else if (ta.ty == Terror)
        {
            e = new ErrorExp();
        }
        else
        {
            // Handle this in the glue layer
            e = new TypeidExp(loc, ta);
            e.type = getTypeInfoType(ta, sc);

            semanticTypeInfo(sc, ta);

            if (ea)
            {
                e = new CommaExp(loc, ea, e); // execute ea
                e = e.semantic(sc);
            }
        }
        return e;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * __traits(identifier, args...)
 */
extern (C++) final class TraitsExp : Expression
{
    Identifier ident;
    Objects* args;

    extern (D) this(Loc loc, Identifier ident, Objects* args)
    {
        super(loc, TOKtraits, __traits(classInstanceSize, TraitsExp));
        this.ident = ident;
        this.args = args;
    }

    override Expression syntaxCopy()
    {
        return new TraitsExp(loc, ident, TemplateInstance.arraySyntaxCopy(args));
    }

    override Expression semantic(Scope* sc)
    {
        return semanticTraits(this, sc);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class HaltExp : Expression
{
    extern (D) this(Loc loc)
    {
        super(loc, TOKhalt, __traits(classInstanceSize, HaltExp));
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("HaltExp::semantic()\n");
        }
        type = Type.tvoid;
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * is(targ id tok tspec)
 * is(targ id == tok2)
 */
extern (C++) final class IsExp : Expression
{
    Type targ;
    Identifier id;      // can be null
    TOK tok;            // ':' or '=='
    Type tspec;         // can be null
    TOK tok2;           // 'struct', 'union', etc.
    TemplateParameters* parameters;

    extern (D) this(Loc loc, Type targ, Identifier id, TOK tok, Type tspec, TOK tok2, TemplateParameters* parameters)
    {
        super(loc, TOKis, __traits(classInstanceSize, IsExp));
        this.targ = targ;
        this.id = id;
        this.tok = tok;
        this.tspec = tspec;
        this.tok2 = tok2;
        this.parameters = parameters;
    }

    override Expression syntaxCopy()
    {
        // This section is identical to that in TemplateDeclaration::syntaxCopy()
        TemplateParameters* p = null;
        if (parameters)
        {
            p = new TemplateParameters();
            p.setDim(parameters.dim);
            for (size_t i = 0; i < p.dim; i++)
                (*p)[i] = (*parameters)[i].syntaxCopy();
        }
        return new IsExp(loc, targ.syntaxCopy(), id, tok, tspec ? tspec.syntaxCopy() : null, tok2, p);
    }

    override Expression semantic(Scope* sc)
    {
        /* is(targ id tok tspec)
         * is(targ id :  tok2)
         * is(targ id == tok2)
         */

        //printf("IsExp::semantic(%s)\n", toChars());
        if (id && !(sc.flags & SCOPEcondition))
        {
            error("can only declare type aliases within static if conditionals or static asserts");
            return new ErrorExp();
        }

        Type tded = null;
        Scope* sc2 = sc.copy(); // keep sc->flags
        sc2.tinst = null;
        sc2.minst = null;
        sc2.flags |= SCOPEfullinst;
        Type t = targ.trySemantic(loc, sc2);
        sc2.pop();
        if (!t)
            goto Lno;
        // errors, so condition is false
        targ = t;
        if (tok2 != TOKreserved)
        {
            switch (tok2)
            {
            case TOKstruct:
                if (targ.ty != Tstruct)
                    goto Lno;
                if ((cast(TypeStruct)targ).sym.isUnionDeclaration())
                    goto Lno;
                tded = targ;
                break;

            case TOKunion:
                if (targ.ty != Tstruct)
                    goto Lno;
                if (!(cast(TypeStruct)targ).sym.isUnionDeclaration())
                    goto Lno;
                tded = targ;
                break;

            case TOKclass:
                if (targ.ty != Tclass)
                    goto Lno;
                if ((cast(TypeClass)targ).sym.isInterfaceDeclaration())
                    goto Lno;
                tded = targ;
                break;

            case TOKinterface:
                if (targ.ty != Tclass)
                    goto Lno;
                if (!(cast(TypeClass)targ).sym.isInterfaceDeclaration())
                    goto Lno;
                tded = targ;
                break;

            case TOKconst:
                if (!targ.isConst())
                    goto Lno;
                tded = targ;
                break;

            case TOKimmutable:
                if (!targ.isImmutable())
                    goto Lno;
                tded = targ;
                break;

            case TOKshared:
                if (!targ.isShared())
                    goto Lno;
                tded = targ;
                break;

            case TOKwild:
                if (!targ.isWild())
                    goto Lno;
                tded = targ;
                break;

            case TOKsuper:
                // If class or interface, get the base class and interfaces
                if (targ.ty != Tclass)
                    goto Lno;
                else
                {
                    ClassDeclaration cd = (cast(TypeClass)targ).sym;
                    auto args = new Parameters();
                    args.reserve(cd.baseclasses.dim);
                    if (cd._scope && !cd.symtab)
                        cd.semantic(cd._scope);
                    for (size_t i = 0; i < cd.baseclasses.dim; i++)
                    {
                        BaseClass* b = (*cd.baseclasses)[i];
                        args.push(new Parameter(STCin, b.type, null, null));
                    }
                    tded = new TypeTuple(args);
                }
                break;

            case TOKenum:
                if (targ.ty != Tenum)
                    goto Lno;
                if (id)
                    tded = (cast(TypeEnum)targ).sym.getMemtype(loc);
                else
                    tded = targ;
                if (tded.ty == Terror)
                    return new ErrorExp();
                break;

            case TOKdelegate:
                if (targ.ty != Tdelegate)
                    goto Lno;
                tded = (cast(TypeDelegate)targ).next; // the underlying function type
                break;

            case TOKfunction:
            case TOKparameters:
                {
                    if (targ.ty != Tfunction)
                        goto Lno;
                    tded = targ;

                    /* Generate tuple from function parameter types.
                     */
                    assert(tded.ty == Tfunction);
                    Parameters* params = (cast(TypeFunction)tded).parameters;
                    size_t dim = Parameter.dim(params);
                    auto args = new Parameters();
                    args.reserve(dim);
                    for (size_t i = 0; i < dim; i++)
                    {
                        Parameter arg = Parameter.getNth(params, i);
                        assert(arg && arg.type);
                        /* If one of the default arguments was an error,
                           don't return an invalid tuple
                         */
                        if (tok2 == TOKparameters && arg.defaultArg && arg.defaultArg.op == TOKerror)
                            return new ErrorExp();
                        args.push(new Parameter(arg.storageClass, arg.type, (tok2 == TOKparameters) ? arg.ident : null, (tok2 == TOKparameters) ? arg.defaultArg : null));
                    }
                    tded = new TypeTuple(args);
                    break;
                }
            case TOKreturn:
                /* Get the 'return type' for the function,
                 * delegate, or pointer to function.
                 */
                if (targ.ty == Tfunction)
                    tded = (cast(TypeFunction)targ).next;
                else if (targ.ty == Tdelegate)
                {
                    tded = (cast(TypeDelegate)targ).next;
                    tded = (cast(TypeFunction)tded).next;
                }
                else if (targ.ty == Tpointer && (cast(TypePointer)targ).next.ty == Tfunction)
                {
                    tded = (cast(TypePointer)targ).next;
                    tded = (cast(TypeFunction)tded).next;
                }
                else
                    goto Lno;
                break;

            case TOKargTypes:
                /* Generate a type tuple of the equivalent types used to determine if a
                 * function argument of this type can be passed in registers.
                 * The results of this are highly platform dependent, and intended
                 * primarly for use in implementing va_arg().
                 */
                tded = toArgTypes(targ);
                if (!tded)
                    goto Lno;
                // not valid for a parameter
                break;

            default:
                assert(0);
            }
            goto Lyes;
        }
        else if (tspec && !id && !(parameters && parameters.dim))
        {
            /* Evaluate to true if targ matches tspec
             * is(targ == tspec)
             * is(targ : tspec)
             */
            tspec = tspec.semantic(loc, sc);
            //printf("targ  = %s, %s\n", targ->toChars(), targ->deco);
            //printf("tspec = %s, %s\n", tspec->toChars(), tspec->deco);

            if (tok == TOKcolon)
            {
                if (targ.implicitConvTo(tspec))
                    goto Lyes;
                else
                    goto Lno;
            }
            else /* == */
            {
                if (targ.equals(tspec))
                    goto Lyes;
                else
                    goto Lno;
            }
        }
        else if (tspec)
        {
            /* Evaluate to true if targ matches tspec.
             * If true, declare id as an alias for the specialized type.
             * is(targ == tspec, tpl)
             * is(targ : tspec, tpl)
             * is(targ id == tspec)
             * is(targ id : tspec)
             * is(targ id == tspec, tpl)
             * is(targ id : tspec, tpl)
             */
            Identifier tid = id ? id : Identifier.generateId("__isexp_id");
            parameters.insert(0, new TemplateTypeParameter(loc, tid, null, null));

            Objects dedtypes;
            dedtypes.setDim(parameters.dim);
            dedtypes.zero();

            MATCH m = deduceType(targ, sc, tspec, parameters, &dedtypes);
            //printf("targ: %s\n", targ->toChars());
            //printf("tspec: %s\n", tspec->toChars());
            if (m <= MATCHnomatch || (m != MATCHexact && tok == TOKequal))
            {
                goto Lno;
            }
            else
            {
                tded = cast(Type)dedtypes[0];
                if (!tded)
                    tded = targ;
                Objects tiargs;
                tiargs.setDim(1);
                tiargs[0] = targ;

                /* Declare trailing parameters
                 */
                for (size_t i = 1; i < parameters.dim; i++)
                {
                    TemplateParameter tp = (*parameters)[i];
                    Declaration s = null;

                    m = tp.matchArg(loc, sc, &tiargs, i, parameters, &dedtypes, &s);
                    if (m <= MATCHnomatch)
                        goto Lno;
                    s.semantic(sc);
                    if (sc.sds)
                        s.addMember(sc, sc.sds);
                    else if (!sc.insert(s))
                        error("declaration %s is already defined", s.toChars());

                    unSpeculative(sc, s);
                }
                goto Lyes;
            }
        }
        else if (id)
        {
            /* Declare id as an alias for type targ. Evaluate to true
             * is(targ id)
             */
            tded = targ;
            goto Lyes;
        }

    Lyes:
        if (id)
        {
            Dsymbol s;
            Tuple tup = isTuple(tded);
            if (tup)
                s = new TupleDeclaration(loc, id, &tup.objects);
            else
                s = new AliasDeclaration(loc, id, tded);
            s.semantic(sc);

            /* The reason for the !tup is unclear. It fails Phobos unittests if it is not there.
             * More investigation is needed.
             */
            if (!tup && !sc.insert(s))
                error("declaration %s is already defined", s.toChars());
            if (sc.sds)
                s.addMember(sc, sc.sds);

            unSpeculative(sc, s);
        }
        //printf("Lyes\n");
        return new IntegerExp(loc, 1, Type.tbool);

    Lno:
        //printf("Lno\n");
        return new IntegerExp(loc, 0, Type.tbool);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) class UnaExp : Expression
{
    Expression e1;
    Type att1;      // Save alias this type to detect recursion

    final extern (D) this(Loc loc, TOK op, int size, Expression e1)
    {
        super(loc, op, size);
        this.e1 = e1;
    }

    override Expression syntaxCopy()
    {
        UnaExp e = cast(UnaExp)copy();
        e.type = null;
        e.e1 = e.e1.syntaxCopy();
        return e;
    }

    override abstract Expression semantic(Scope* sc);

    /**************************
     * Helper function for easy error propagation.
     * If error occurs, returns ErrorExp. Otherwise returns NULL.
     */
    final Expression unaSemantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("UnaExp::semantic('%s')\n", toChars());
        }
        Expression e1x = e1.semantic(sc);
        if (e1x.op == TOKerror)
            return e1x;
        e1 = e1x;
        return null;
    }

    /*********************
     * Mark the operand as will never be dereferenced,
     * which is useful info for @safe checks.
     * Do before semantic() on operands rewrites them.
     */
    final void setNoderefOperand()
    {
        if (e1.op == TOKdotid)
            (cast(DotIdExp)e1).noderef = true;

    }

    override final Expression resolveLoc(Loc loc, Scope* sc)
    {
        e1 = e1.resolveLoc(loc, sc);
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }

    override void printAST(int indent)
    {
        Expression.printAST(indent);
        e1.printAST(indent + 2);
    }
}

extern (C++) alias fp_t = UnionExp function(Loc loc, Type, Expression, Expression);
extern (C++) alias fp2_t = int function(Loc loc, TOK, Expression, Expression);

/***********************************************************
 */
extern (C++) abstract class BinExp : Expression
{
    Expression e1;
    Expression e2;
    Type att1;      // Save alias this type to detect recursion
    Type att2;      // Save alias this type to detect recursion

    final extern (D) this(Loc loc, TOK op, int size, Expression e1, Expression e2)
    {
        super(loc, op, size);
        this.e1 = e1;
        this.e2 = e2;
    }

    override Expression syntaxCopy()
    {
        BinExp e = cast(BinExp)copy();
        e.type = null;
        e.e1 = e.e1.syntaxCopy();
        e.e2 = e.e2.syntaxCopy();
        return e;
    }

    override abstract Expression semantic(Scope* sc);

    /**************************
     * Helper function for easy error propagation.
     * If error occurs, returns ErrorExp. Otherwise returns NULL.
     */
    final Expression binSemantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("BinExp::semantic('%s')\n", toChars());
        }
        Expression e1x = e1.semantic(sc);
        Expression e2x = e2.semantic(sc);
        if (e1x.op == TOKerror)
            return e1x;
        if (e2x.op == TOKerror)
            return e2x;
        e1 = e1x;
        e2 = e2x;
        return null;
    }

    final Expression binSemanticProp(Scope* sc)
    {
        if (Expression ex = binSemantic(sc))
            return ex;
        Expression e1x = resolveProperties(sc, e1);
        Expression e2x = resolveProperties(sc, e2);
        if (e1x.op == TOKerror)
            return e1x;
        if (e2x.op == TOKerror)
            return e2x;
        e1 = e1x;
        e2 = e2x;
        return null;
    }

    final Expression incompatibleTypes()
    {
        if (e1.type.toBasetype() != Type.terror && e2.type.toBasetype() != Type.terror)
        {
            // CondExp uses 'a ? b : c' but we're comparing 'b : c'
            TOK thisOp = (op == TOKquestion) ? TOKcolon : op;
            if (e1.op == TOKtype || e2.op == TOKtype)
            {
                error("incompatible types for ((%s) %s (%s)): cannot use '%s' with types", e1.toChars(), Token.toChars(thisOp), e2.toChars(), Token.toChars(op));
            }
            else
            {
                error("incompatible types for ((%s) %s (%s)): '%s' and '%s'", e1.toChars(), Token.toChars(thisOp), e2.toChars(), e1.type.toChars(), e2.type.toChars());
            }
            return new ErrorExp();
        }
        return this;
    }

    final Expression checkOpAssignTypes(Scope* sc)
    {
        // At that point t1 and t2 are the merged types. type is the original type of the lhs.
        Type t1 = e1.type;
        Type t2 = e2.type;

        // T opAssign floating yields a floating. Prevent truncating conversions (float to int).
        // See issue 3841.
        // Should we also prevent double to float (type->isfloating() && type->size() < t2 ->size()) ?
        if (op == TOKaddass || op == TOKminass ||
            op == TOKmulass || op == TOKdivass || op == TOKmodass ||
            op == TOKpowass)
        {
            if ((type.isintegral() && t2.isfloating()))
            {
                warning("%s %s %s is performing truncating conversion", type.toChars(), Token.toChars(op), t2.toChars());
            }
        }

        // generate an error if this is a nonsensical *=,/=, or %=, eg real *= imaginary
        if (op == TOKmulass || op == TOKdivass || op == TOKmodass)
        {
            // Any multiplication by an imaginary or complex number yields a complex result.
            // r *= c, i*=c, r*=i, i*=i are all forbidden operations.
            const(char)* opstr = Token.toChars(op);
            if (t1.isreal() && t2.iscomplex())
            {
                error("%s %s %s is undefined. Did you mean %s %s %s.re ?", t1.toChars(), opstr, t2.toChars(), t1.toChars(), opstr, t2.toChars());
                return new ErrorExp();
            }
            else if (t1.isimaginary() && t2.iscomplex())
            {
                error("%s %s %s is undefined. Did you mean %s %s %s.im ?", t1.toChars(), opstr, t2.toChars(), t1.toChars(), opstr, t2.toChars());
                return new ErrorExp();
            }
            else if ((t1.isreal() || t1.isimaginary()) && t2.isimaginary())
            {
                error("%s %s %s is an undefined operation", t1.toChars(), opstr, t2.toChars());
                return new ErrorExp();
            }
        }

        // generate an error if this is a nonsensical += or -=, eg real += imaginary
        if (op == TOKaddass || op == TOKminass)
        {
            // Addition or subtraction of a real and an imaginary is a complex result.
            // Thus, r+=i, r+=c, i+=r, i+=c are all forbidden operations.
            if ((t1.isreal() && (t2.isimaginary() || t2.iscomplex())) || (t1.isimaginary() && (t2.isreal() || t2.iscomplex())))
            {
                error("%s %s %s is undefined (result is complex)", t1.toChars(), Token.toChars(op), t2.toChars());
                return new ErrorExp();
            }
            if (type.isreal() || type.isimaginary())
            {
                assert(global.errors || t2.isfloating());
                e2 = e2.castTo(sc, t1);
            }
        }
        if (op == TOKmulass)
        {
            if (t2.isfloating())
            {
                if (t1.isreal())
                {
                    if (t2.isimaginary() || t2.iscomplex())
                    {
                        e2 = e2.castTo(sc, t1);
                    }
                }
                else if (t1.isimaginary())
                {
                    if (t2.isimaginary() || t2.iscomplex())
                    {
                        switch (t1.ty)
                        {
                        case Timaginary32:
                            t2 = Type.tfloat32;
                            break;

                        case Timaginary64:
                            t2 = Type.tfloat64;
                            break;

                        case Timaginary80:
                            t2 = Type.tfloat80;
                            break;

                        default:
                            assert(0);
                        }
                        e2 = e2.castTo(sc, t2);
                    }
                }
            }
        }
        else if (op == TOKdivass)
        {
            if (t2.isimaginary())
            {
                if (t1.isreal())
                {
                    // x/iv = i(-x/v)
                    // Therefore, the result is 0
                    e2 = new CommaExp(loc, e2, new RealExp(loc, CTFloat.zero, t1));
                    e2.type = t1;
                    Expression e = new AssignExp(loc, e1, e2);
                    e.type = t1;
                    return e;
                }
                else if (t1.isimaginary())
                {
                    Type t3;
                    switch (t1.ty)
                    {
                    case Timaginary32:
                        t3 = Type.tfloat32;
                        break;

                    case Timaginary64:
                        t3 = Type.tfloat64;
                        break;

                    case Timaginary80:
                        t3 = Type.tfloat80;
                        break;

                    default:
                        assert(0);
                    }
                    e2 = e2.castTo(sc, t3);
                    Expression e = new AssignExp(loc, e1, e2);
                    e.type = t1;
                    return e;
                }
            }
        }
        else if (op == TOKmodass)
        {
            if (t2.iscomplex())
            {
                error("cannot perform modulo complex arithmetic");
                return new ErrorExp();
            }
        }
        return this;
    }

    final bool checkIntegralBin()
    {
        bool r1 = e1.checkIntegral();
        bool r2 = e2.checkIntegral();
        return (r1 || r2);
    }

    final bool checkArithmeticBin()
    {
        bool r1 = e1.checkArithmetic();
        bool r2 = e2.checkArithmetic();
        return (r1 || r2);
    }

    /*********************
     * Mark the operands as will never be dereferenced,
     * which is useful info for @safe checks.
     * Do before semantic() on operands rewrites them.
     */
    final void setNoderefOperands()
    {
        if (e1.op == TOKdotid)
            (cast(DotIdExp)e1).noderef = true;
        if (e2.op == TOKdotid)
            (cast(DotIdExp)e2).noderef = true;

    }

    final Expression reorderSettingAAElem(Scope* sc)
    {
        BinExp be = this;

        if (be.e1.op != TOKindex)
            return be;
        auto ie = cast(IndexExp)be.e1;
        if (ie.e1.type.toBasetype().ty != Taarray)
            return be;

        /* Fix evaluation order of setting AA element. (Bugzilla 3825)
         * Rewrite:
         *     aa[k1][k2][k3] op= val;
         * as:
         *     auto ref __aatmp = aa;
         *     auto ref __aakey3 = k1, __aakey2 = k2, __aakey1 = k3;
         *     auto ref __aaval = val;
         *     __aatmp[__aakey3][__aakey2][__aakey1] op= __aaval;  // assignment
         */

        Expression e0;
        while (1)
        {
            Expression de;
            ie.e2 = extractSideEffect(sc, "__aakey", de, ie.e2);
            e0 = Expression.combine(de, e0);

            Expression ie1 = ie.e1;
            if (ie1.op != TOKindex ||
                (cast(IndexExp)ie1).e1.type.toBasetype().ty != Taarray)
            {
                break;
            }
            ie = cast(IndexExp)ie1;
        }
        assert(ie.e1.type.toBasetype().ty == Taarray);

        Expression de;
        ie.e1 = extractSideEffect(sc, "__aatmp", de, ie.e1);
        e0 = Expression.combine(de, e0);

        be.e2 = extractSideEffect(sc, "__aaval", e0, be.e2, true);

        //printf("-e0 = %s, be = %s\n", e0.toChars(), be.toChars());
        return Expression.combine(e0, be);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }

    override void printAST(int indent)
    {
        Expression.printAST(indent);
        e1.printAST(indent + 2);
        e2.printAST(indent + 2);
    }
}

/***********************************************************
 */
extern (C++) class BinAssignExp : BinExp
{
    final extern (D) this(Loc loc, TOK op, int size, Expression e1, Expression e2)
    {
        super(loc, op, size, e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        Expression e = op_overload(sc);
        if (e)
            return e;

        if (e1.checkReadModifyWrite(op, e2))
            return new ErrorExp();

        if (e1.op == TOKarraylength)
        {
            // arr.length op= e2;
            e = ArrayLengthExp.rewriteOpAssign(this);
            e = e.semantic(sc);
            return e;
        }
        if (e1.op == TOKslice || e1.type.ty == Tarray || e1.type.ty == Tsarray)
        {
            // T[] op= ...
            if (e2.implicitConvTo(e1.type.nextOf()))
            {
                // T[] op= T
                e2 = e2.castTo(sc, e1.type.nextOf());
            }
            else if (Expression ex = typeCombine(this, sc))
                return ex;
            type = e1.type;
            return arrayOp(this, sc);
        }

        e1 = e1.semantic(sc);
        e1 = e1.optimize(WANTvalue);
        e1 = e1.modifiableLvalue(sc, e1);
        type = e1.type;
        if (checkScalar())
            return new ErrorExp();

        int arith = (op == TOKaddass || op == TOKminass || op == TOKmulass || op == TOKdivass || op == TOKmodass || op == TOKpowass);
        int bitwise = (op == TOKandass || op == TOKorass || op == TOKxorass);
        int shift = (op == TOKshlass || op == TOKshrass || op == TOKushrass);

        if (bitwise && type.toBasetype().ty == Tbool)
            e2 = e2.implicitCastTo(sc, type);
        else if (checkNoBool())
            return new ErrorExp();

        if ((op == TOKaddass || op == TOKminass) && e1.type.toBasetype().ty == Tpointer && e2.type.toBasetype().isintegral())
            return scaleFactor(this, sc);

        if (Expression ex = typeCombine(this, sc))
            return ex;

        if (arith && checkArithmeticBin())
            return new ErrorExp();
        if ((bitwise || shift) && checkIntegralBin())
            return new ErrorExp();
        if (shift)
        {
            e2 = e2.castTo(sc, Type.tshiftcnt);
        }

        // vectors
        if (shift && (e1.type.toBasetype().ty == Tvector || e2.type.toBasetype().ty == Tvector))
            return incompatibleTypes();

        int isvector = type.toBasetype().ty == Tvector;

        if (op == TOKmulass && isvector && !e2.type.isfloating() && (cast(TypeVector)type.toBasetype()).elementType().size(loc) != 2)
            return incompatibleTypes(); // Only short[8] and ushort[8] work with multiply

        if (op == TOKdivass && isvector && !e1.type.isfloating())
            return incompatibleTypes();

        if (op == TOKmodass && isvector)
            return incompatibleTypes();

        if (e1.op == TOKerror || e2.op == TOKerror)
            return new ErrorExp();

        e = checkOpAssignTypes(sc);
        if (e.op == TOKerror)
            return e;

        assert(e.op == TOKassign || e == this);
        return (cast(BinExp)e).reorderSettingAAElem(sc);
    }

    override final bool isLvalue()
    {
        return true;
    }

    override final Expression toLvalue(Scope* sc, Expression ex)
    {
        // Lvalue-ness will be handled in glue layer.
        return this;
    }

    override final Expression modifiableLvalue(Scope* sc, Expression e)
    {
        // should check e1->checkModifiable() ?
        return toLvalue(sc, this);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class CompileExp : UnaExp
{
    extern (D) this(Loc loc, Expression e)
    {
        super(loc, TOKmixin, __traits(classInstanceSize, CompileExp), e);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("CompileExp::semantic('%s')\n", toChars());
        }

        auto se = semanticString(sc, e1, "argument to mixin");
        if (!se)
            return new ErrorExp();
        se = se.toUTF8(sc);

        uint errors = global.errors;
        scope Parser p = new Parser(loc, sc._module, se.toStringz(), false);
        p.nextToken();
        //printf("p.loc.linnum = %d\n", p.loc.linnum);

        Expression e = p.parseExpression();
        if (p.errors)
        {
            assert(global.errors != errors); // should have caught all these cases
            return new ErrorExp();
        }
        if (p.token.value != TOKeof)
        {
            error("incomplete mixin expression (%s)", se.toChars());
            return new ErrorExp();
        }

        return e.semantic(sc);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class ImportExp : UnaExp
{
    extern (D) this(Loc loc, Expression e)
    {
        super(loc, TOKimport, __traits(classInstanceSize, ImportExp), e);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("ImportExp::semantic('%s')\n", toChars());
        }

        auto se = semanticString(sc, e1, "file name argument");
        if (!se)
            return new ErrorExp();
        se = se.toUTF8(sc);

        auto namez = se.toStringz().ptr;
        if (!global.params.fileImppath)
        {
            error("need -Jpath switch to import text file %s", namez);
            return new ErrorExp();
        }

        /* Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory
         * ('Path Traversal') attacks.
         * http://cwe.mitre.org/data/definitions/22.html
         */

        auto name = FileName.safeSearchPath(global.filePath, namez);
        if (!name)
        {
            error("file %s cannot be found or not in a path specified with -J", se.toChars());
            return new ErrorExp();
        }

        if (global.params.verbose)
            fprintf(global.stdmsg, "file      %.*s\t(%s)\n", cast(int)se.len, se.string, name);
        if (global.params.moduleDeps !is null)
        {
            OutBuffer* ob = global.params.moduleDeps;
            Module imod = sc.instantiatingModule();

            if (!global.params.moduleDepsFile)
                ob.writestring("depsFile ");
            ob.writestring(imod.toPrettyChars());
            ob.writestring(" (");
            escapePath(ob, imod.srcfile.toChars());
            ob.writestring(") : ");
            if (global.params.moduleDepsFile)
                ob.writestring("string : ");
            ob.write(se.string, se.len);
            ob.writestring(" (");
            escapePath(ob, name);
            ob.writestring(")");
            ob.writenl();
        }

        {
            auto f = File(name);
            if (f.read())
            {
                error("cannot read file %s", f.toChars());
                return new ErrorExp();
            }
            else
            {
                f._ref = 1;
                se = new StringExp(loc, f.buffer, f.len);
            }
        }
        return se.semantic(sc);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class AssertExp : UnaExp
{
    Expression msg;

    extern (D) this(Loc loc, Expression e, Expression msg = null)
    {
        super(loc, TOKassert, __traits(classInstanceSize, AssertExp), e);
        this.msg = msg;
    }

    override Expression syntaxCopy()
    {
        return new AssertExp(loc, e1.syntaxCopy(), msg ? msg.syntaxCopy() : null);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("AssertExp::semantic('%s')\n", toChars());
        }

        if (Expression ex = unaSemantic(sc))
            return ex;
        e1 = resolveProperties(sc, e1);
        // BUG: see if we can do compile time elimination of the Assert
        e1 = e1.optimize(WANTvalue);
        e1 = e1.toBoolean(sc);

        if (msg)
        {
            msg = msg.semantic(sc);
            msg = resolveProperties(sc, msg);
            msg = msg.implicitCastTo(sc, Type.tchar.constOf().arrayOf());
            msg = msg.optimize(WANTvalue);
        }

        if (e1.op == TOKerror)
            return e1;
        if (msg && msg.op == TOKerror)
            return msg;

        auto f1 = checkNonAssignmentArrayOp(e1);
        auto f2 = msg && checkNonAssignmentArrayOp(msg);
        if (f1 || f2)
            return new ErrorExp();

        if (e1.isBool(false))
        {
            FuncDeclaration fd = sc.parent.isFuncDeclaration();
            if (fd)
                fd.hasReturnExp |= 4;
            sc.callSuper |= CSXhalt;
            if (sc.fieldinit)
            {
                for (size_t i = 0; i < sc.fieldinit_dim; i++)
                    sc.fieldinit[i] |= CSXhalt;
            }

            if (!global.params.useAssert)
            {
                Expression e = new HaltExp(loc);
                e = e.semantic(sc);
                return e;
            }
        }
        type = Type.tvoid;
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class DotIdExp : UnaExp
{
    Identifier ident;
    bool noderef;       // true if the result of the expression will never be dereferenced

    extern (D) this(Loc loc, Expression e, Identifier ident)
    {
        super(loc, TOKdotid, __traits(classInstanceSize, DotIdExp), e);
        this.ident = ident;
    }

    static DotIdExp create(Loc loc, Expression e, Identifier ident)
    {
        return new DotIdExp(loc, e, ident);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("DotIdExp::semantic(this = %p, '%s')\n", this, toChars());
            //printf("e1->op = %d, '%s'\n", e1->op, Token::toChars(e1->op));
        }
        Expression e = semanticY(sc, 1);
        if (e && isDotOpDispatch(e))
        {
            uint errors = global.startGagging();
            e = resolvePropertiesX(sc, e);
            if (global.endGagging(errors))
                e = null; /* fall down to UFCS */
            else
                return e;
        }
        if (!e) // if failed to find the property
        {
            /* If ident is not a valid property, rewrite:
             *   e1.ident
             * as:
             *   .ident(e1)
             */
            e = resolveUFCSProperties(sc, this);
        }
        return e;
    }

    // Run semantic in e1
    Expression semanticX(Scope* sc)
    {
        //printf("DotIdExp::semanticX(this = %p, '%s')\n", this, toChars());
        if (Expression ex = unaSemantic(sc))
            return ex;

        if (ident == Id._mangleof)
        {
            // symbol.mangleof
            Dsymbol ds;
            switch (e1.op)
            {
            case TOKscope:
                ds = (cast(ScopeExp)e1).sds;
                goto L1;
            case TOKvar:
                ds = (cast(VarExp)e1).var;
                goto L1;
            case TOKdotvar:
                ds = (cast(DotVarExp)e1).var;
                goto L1;
            case TOKoverloadset:
                ds = (cast(OverExp)e1).vars;
                goto L1;
            case TOKtemplate:
                {
                    TemplateExp te = cast(TemplateExp)e1;
                    ds = te.fd ? cast(Dsymbol)te.fd : te.td;
                }
            L1:
                {
                    assert(ds);
                    if (auto f = ds.isFuncDeclaration())
                    {
                        if (f.checkForwardRef(loc))
                            return new ErrorExp();
                    }
                    OutBuffer buf;
                    mangleToBuffer(ds, &buf);
                    const s = buf.peekSlice();
                    Expression e = new StringExp(loc, buf.extractString(), s.length);
                    e = e.semantic(sc);
                    return e;
                }
            default:
                break;
            }
        }

        if (e1.op == TOKvar && e1.type.toBasetype().ty == Tsarray && ident == Id.length)
        {
            // bypass checkPurity
            return e1.type.dotExp(sc, e1, ident, noderef ? Type.DotExpFlag.noDeref : 0);
        }

        if (e1.op == TOKdot)
        {
        }
        else
        {
            e1 = resolvePropertiesX(sc, e1);
        }
        if (e1.op == TOKtuple && ident == Id.offsetof)
        {
            /* 'distribute' the .offsetof to each of the tuple elements.
             */
            TupleExp te = cast(TupleExp)e1;
            auto exps = new Expressions();
            exps.setDim(te.exps.dim);
            for (size_t i = 0; i < exps.dim; i++)
            {
                Expression e = (*te.exps)[i];
                e = e.semantic(sc);
                e = new DotIdExp(e.loc, e, Id.offsetof);
                (*exps)[i] = e;
            }
            // Don't evaluate te->e0 in runtime
            Expression e = new TupleExp(loc, null, exps);
            e = e.semantic(sc);
            return e;
        }
        if (e1.op == TOKtuple && ident == Id.length)
        {
            TupleExp te = cast(TupleExp)e1;
            // Don't evaluate te->e0 in runtime
            Expression e = new IntegerExp(loc, te.exps.dim, Type.tsize_t);
            return e;
        }

        // Bugzilla 14416: Template has no built-in properties except for 'stringof'.
        if ((e1.op == TOKdottd || e1.op == TOKtemplate) && ident != Id.stringof)
        {
            error("template %s does not have property '%s'", e1.toChars(), ident.toChars());
            return new ErrorExp();
        }
        if (!e1.type)
        {
            error("expression %s does not have property '%s'", e1.toChars(), ident.toChars());
            return new ErrorExp();
        }

        return this;
    }

    // Resolve e1.ident without seeing UFCS.
    // If flag == 1, stop "not a property" error and return NULL.
    Expression semanticY(Scope* sc, int flag)
    {
        //printf("DotIdExp::semanticY(this = %p, '%s')\n", this, toChars());

        //{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; }

        /* Special case: rewrite this.id and super.id
         * to be classtype.id and baseclasstype.id
         * if we have no this pointer.
         */
        if ((e1.op == TOKthis || e1.op == TOKsuper) && !hasThis(sc))
        {
            if (AggregateDeclaration ad = sc.getStructClassScope())
            {
                if (e1.op == TOKthis)
                {
                    e1 = new TypeExp(e1.loc, ad.type);
                }
                else
                {
                    ClassDeclaration cd = ad.isClassDeclaration();
                    if (cd && cd.baseClass)
                        e1 = new TypeExp(e1.loc, cd.baseClass.type);
                }
            }
        }

        Expression e = semanticX(sc);
        if (e != this)
            return e;

        Expression eleft;
        Expression eright;
        if (e1.op == TOKdot)
        {
            DotExp de = cast(DotExp)e1;
            eleft = de.e1;
            eright = de.e2;
        }
        else
        {
            eleft = null;
            eright = e1;
        }

        Type t1b = e1.type.toBasetype();

        if (eright.op == TOKscope) // also used for template alias's
        {
            ScopeExp ie = cast(ScopeExp)eright;

            int flags = SearchLocalsOnly;
            /* Disable access to another module's private imports.
             * The check for 'is sds our current module' is because
             * the current module should have access to its own imports.
             */
            if (ie.sds.isModule() && ie.sds != sc._module)
                flags |= IgnorePrivateImports;
            if (sc.flags & SCOPEignoresymbolvisibility)
                flags |= IgnoreSymbolVisibility;
            Dsymbol s = ie.sds.search(loc, ident, flags);
            /* Check for visibility before resolving aliases because public
             * aliases to private symbols are public.
             */
            if (s && !(sc.flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc._module, s))
            {
                if (s.isDeclaration())
                    .error(loc, "%s is not visible from module %s", s.toPrettyChars(), sc._module.toChars());
                else
                    .deprecation(loc, "%s is not visible from module %s", s.toPrettyChars(), sc._module.toChars());
                // s = null;
            }
            if (s)
            {
                if (auto p = s.isPackage())
                    checkAccess(loc, sc, p);

                // if 's' is a tuple variable, the tuple is returned.
                s = s.toAlias();

                checkDeprecated(sc, s);

                EnumMember em = s.isEnumMember();
                if (em)
                {
                    return em.getVarExp(loc, sc);
                }
                VarDeclaration v = s.isVarDeclaration();
                if (v)
                {
                    //printf("DotIdExp:: Identifier '%s' is a variable, type '%s'\n", toChars(), v->type->toChars());
                    if (!v.type ||
                        !v.type.deco && v.inuse)
                    {
                        if (v.inuse)
                            error("circular reference to %s '%s'", v.kind(), v.toPrettyChars());
                        else
                            error("forward reference to %s '%s'", v.kind(), v.toPrettyChars());
                        return new ErrorExp();
                    }
                    if (v.type.ty == Terror)
                        return new ErrorExp();

                    if ((v.storage_class & STCmanifest) && v._init)
                    {
                        if (v.inuse)
                        {
                            .error(loc, "circular initialization of %s '%s'", v.kind(), v.toPrettyChars());
                            return new ErrorExp();
                        }
                        e = v.expandInitializer(loc);
                        v.inuse++;
                        e = e.semantic(sc);
                        v.inuse--;
                        return e;
                    }

                    if (v.needThis())
                    {
                        if (!eleft)
                            eleft = new ThisExp(loc);
                        e = new DotVarExp(loc, eleft, v);
                        e = e.semantic(sc);
                    }
                    else
                    {
                        e = new VarExp(loc, v);
                        if (eleft)
                        {
                            e = new CommaExp(loc, eleft, e);
                            e.type = v.type;
                        }
                    }
                    e = e.deref();
                    return e.semantic(sc);
                }

                FuncDeclaration f = s.isFuncDeclaration();
                if (f)
                {
                    //printf("it's a function\n");
                    if (!f.functionSemantic())
                        return new ErrorExp();
                    if (f.needThis())
                    {
                        if (!eleft)
                            eleft = new ThisExp(loc);
                        e = new DotVarExp(loc, eleft, f, true);
                        e = e.semantic(sc);
                    }
                    else
                    {
                        e = new VarExp(loc, f, true);
                        if (eleft)
                        {
                            e = new CommaExp(loc, eleft, e);
                            e.type = f.type;
                        }
                    }
                    return e;
                }
                if (auto td = s.isTemplateDeclaration())
                {
                    if (eleft)
                        e = new DotTemplateExp(loc, eleft, td);
                    else
                        e = new TemplateExp(loc, td);
                    e = e.semantic(sc);
                    return e;
                }
                if (OverDeclaration od = s.isOverDeclaration())
                {
                    e = new VarExp(loc, od, true);
                    if (eleft)
                    {
                        e = new CommaExp(loc, eleft, e);
                        e.type = Type.tvoid; // ambiguous type?
                    }
                    return e;
                }
                OverloadSet o = s.isOverloadSet();
                if (o)
                {
                    //printf("'%s' is an overload set\n", o->toChars());
                    return new OverExp(loc, o);
                }

                if (auto t = s.getType())
                {
                    return (new TypeExp(loc, t)).semantic(sc);
                }

                TupleDeclaration tup = s.isTupleDeclaration();
                if (tup)
                {
                    if (eleft)
                    {
                        e = new DotVarExp(loc, eleft, tup);
                        e = e.semantic(sc);
                        return e;
                    }
                    e = new TupleExp(loc, tup);
                    e = e.semantic(sc);
                    return e;
                }

                ScopeDsymbol sds = s.isScopeDsymbol();
                if (sds)
                {
                    //printf("it's a ScopeDsymbol %s\n", ident->toChars());
                    e = new ScopeExp(loc, sds);
                    e = e.semantic(sc);
                    if (eleft)
                        e = new DotExp(loc, eleft, e);
                    return e;
                }

                Import imp = s.isImport();
                if (imp)
                {
                    ie = new ScopeExp(loc, imp.pkg);
                    return ie.semantic(sc);
                }
                // BUG: handle other cases like in IdentifierExp::semantic()
                debug
                {
                    printf("s = '%s', kind = '%s'\n", s.toChars(), s.kind());
                }
                assert(0);
            }
            else if (ident == Id.stringof)
            {
                const p = ie.toChars();
                e = new StringExp(loc, cast(char*)p, strlen(p));
                e = e.semantic(sc);
                return e;
            }
            if (ie.sds.isPackage() || ie.sds.isImport() || ie.sds.isModule())
            {
                flag = 0;
            }
            if (flag)
                return null;
            s = ie.sds.search_correct(ident);
            if (s)
                error("undefined identifier '%s' in %s '%s', did you mean %s '%s'?", ident.toChars(), ie.sds.kind(), ie.sds.toPrettyChars(), s.kind(), s.toChars());
            else
                error("undefined identifier '%s' in %s '%s'", ident.toChars(), ie.sds.kind(), ie.sds.toPrettyChars());
            return new ErrorExp();
        }
        else if (t1b.ty == Tpointer && e1.type.ty != Tenum && ident != Id._init && ident != Id.__sizeof && ident != Id.__xalignof && ident != Id.offsetof && ident != Id._mangleof && ident != Id.stringof)
        {
            Type t1bn = t1b.nextOf();
            if (flag)
            {
                AggregateDeclaration ad = isAggregate(t1bn);
                if (ad && !ad.members) // Bugzilla 11312
                    return null;
            }

            /* Rewrite:
             *   p.ident
             * as:
             *   (*p).ident
             */
            if (flag && t1bn.ty == Tvoid)
                return null;
            e = new PtrExp(loc, e1);
            e = e.semantic(sc);
            return e.type.dotExp(sc, e, ident, flag | (noderef ? Type.DotExpFlag.noDeref : 0));
        }
        else
        {
            if (e1.op == TOKtype || e1.op == TOKtemplate)
                flag = 0;
            e = e1.type.dotExp(sc, e1, ident, flag | (noderef ? Type.DotExpFlag.noDeref : 0));
            if (!flag || e)
                e = e.semantic(sc);
            return e;
        }
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * Mainly just a placeholder
 */
extern (C++) final class DotTemplateExp : UnaExp
{
    TemplateDeclaration td;

    extern (D) this(Loc loc, Expression e, TemplateDeclaration td)
    {
        super(loc, TOKdottd, __traits(classInstanceSize, DotTemplateExp), e);
        this.td = td;
    }

    override Expression semantic(Scope* sc)
    {
        if (Expression ex = unaSemantic(sc))
            return ex;
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class DotVarExp : UnaExp
{
    Declaration var;
    bool hasOverloads;

    extern (D) this(Loc loc, Expression e, Declaration var, bool hasOverloads = true)
    {
        if (var.isVarDeclaration())
            hasOverloads = false;

        super(loc, TOKdotvar, __traits(classInstanceSize, DotVarExp), e);
        //printf("DotVarExp()\n");
        this.var = var;
        this.hasOverloads = hasOverloads;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("DotVarExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        var = var.toAlias().isDeclaration();

        e1 = e1.semantic(sc);

        if (auto tup = var.isTupleDeclaration())
        {
            /* Replace:
             *  e1.tuple(a, b, c)
             * with:
             *  tuple(e1.a, e1.b, e1.c)
             */
            Expression e0;
            Expression ev = extractSideEffect(sc, "__tup", e0, e1);

            auto exps = new Expressions();
            exps.reserve(tup.objects.dim);
            for (size_t i = 0; i < tup.objects.dim; i++)
            {
                RootObject o = (*tup.objects)[i];
                Expression e;
                if (o.dyncast() == DYNCAST_EXPRESSION)
                {
                    e = cast(Expression)o;
                    if (e.op == TOKdsymbol)
                    {
                        Dsymbol s = (cast(DsymbolExp)e).s;
                        e = new DotVarExp(loc, ev, s.isDeclaration());
                    }
                }
                else if (o.dyncast() == DYNCAST_DSYMBOL)
                {
                    e = new DsymbolExp(loc, cast(Dsymbol)o);
                }
                else if (o.dyncast() == DYNCAST_TYPE)
                {
                    e = new TypeExp(loc, cast(Type)o);
                }
                else
                {
                    error("%s is not an expression", o.toChars());
                    return new ErrorExp();
                }
                exps.push(e);
            }

            Expression e = new TupleExp(loc, e0, exps);
            e = e.semantic(sc);
            return e;
        }

        e1 = e1.addDtorHook(sc);

        Type t1 = e1.type;

        if (FuncDeclaration fd = var.isFuncDeclaration())
        {
            // for functions, do checks after overload resolution
            if (!fd.functionSemantic())
                return new ErrorExp();

            /* Bugzilla 13843: If fd obviously has no overloads, we should
             * normalize AST, and it will give a chance to wrap fd with FuncExp.
             */
            if (fd.isNested() || fd.isFuncLiteralDeclaration())
            {
                // (e1, fd)
                auto e = DsymbolExp.resolve(loc, sc, fd, false);
                return Expression.combine(e1, e);
            }

            type = fd.type;
            assert(type);
        }
        else if (OverDeclaration od = var.isOverDeclaration())
        {
            type = Type.tvoid; // ambiguous type?
        }
        else
        {
            type = var.type;
            if (!type && global.errors)
            {
                // var is goofed up, just return 0
                return new ErrorExp();
            }
            assert(type);

            if (t1.ty == Tpointer)
                t1 = t1.nextOf();

            type = type.addMod(t1.mod);

            Dsymbol vparent = var.toParent();
            AggregateDeclaration ad = vparent ? vparent.isAggregateDeclaration() : null;
            if (Expression e1x = getRightThis(loc, sc, ad, e1, var, 1))
                e1 = e1x;
            else
            {
                /* Later checkRightThis will report correct error for invalid field variable access.
                 */
                Expression e = new VarExp(loc, var);
                e = e.semantic(sc);
                return e;
            }
            checkAccess(loc, sc, e1, var);

            VarDeclaration v = var.isVarDeclaration();
            if (v && (v.isDataseg() || (v.storage_class & STCmanifest)))
            {
                Expression e = expandVar(WANTvalue, v);
                if (e)
                    return e;
            }

            if (v && v.isDataseg()) // fix bugzilla 8238
            {
                // (e1, v)
                checkAccess(loc, sc, e1, v);
                Expression e = new VarExp(loc, v);
                e = new CommaExp(loc, e1, e);
                e = e.semantic(sc);
                return e;
            }
        }
        //printf("-DotVarExp::semantic('%s')\n", toChars());
        return this;
    }

    override int checkModifiable(Scope* sc, int flag)
    {
        //printf("DotVarExp::checkModifiable %s %s\n", toChars(), type.toChars());
        if (checkUnsafeAccess(sc, this, false, !flag))
            return 2;

        if (e1.op == TOKthis)
            return var.checkModify(loc, sc, type, e1, flag);

        //printf("\te1 = %s\n", e1.toChars());
        return e1.checkModifiable(sc, flag);
    }

    bool checkReadModifyWrite();

    override bool isLvalue()
    {
        return true;
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        //printf("DotVarExp::toLvalue(%s)\n", toChars());
        return this;
    }

    override Expression modifiableLvalue(Scope* sc, Expression e)
    {
        version (none)
        {
            printf("DotVarExp::modifiableLvalue(%s)\n", toChars());
            printf("e1->type = %s\n", e1.type.toChars());
            printf("var->type = %s\n", var.type.toChars());
        }

        return Expression.modifiableLvalue(sc, e);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * foo.bar!(args)
 */
extern (C++) final class DotTemplateInstanceExp : UnaExp
{
    TemplateInstance ti;

    extern (D) this(Loc loc, Expression e, Identifier name, Objects* tiargs)
    {
        super(loc, TOKdotti, __traits(classInstanceSize, DotTemplateInstanceExp), e);
        //printf("DotTemplateInstanceExp()\n");
        this.ti = new TemplateInstance(loc, name, tiargs);
    }

    extern (D) this(Loc loc, Expression e, TemplateInstance ti)
    {
        super(loc, TOKdotti, __traits(classInstanceSize, DotTemplateInstanceExp), e);
        this.ti = ti;
    }

    override Expression syntaxCopy()
    {
        return new DotTemplateInstanceExp(loc, e1.syntaxCopy(), ti.name, TemplateInstance.arraySyntaxCopy(ti.tiargs));
    }

    bool findTempDecl(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("DotTemplateInstanceExp::findTempDecl('%s')\n", toChars());
        }
        if (ti.tempdecl)
            return true;

        Expression e = new DotIdExp(loc, e1, ti.name);
        e = e.semantic(sc);
        if (e.op == TOKdot)
            e = (cast(DotExp)e).e2;

        Dsymbol s = null;
        switch (e.op)
        {
        case TOKoverloadset:
            s = (cast(OverExp)e).vars;
            break;

        case TOKdottd:
            s = (cast(DotTemplateExp)e).td;
            break;

        case TOKscope:
            s = (cast(ScopeExp)e).sds;
            break;

        case TOKdotvar:
            s = (cast(DotVarExp)e).var;
            break;

        case TOKvar:
            s = (cast(VarExp)e).var;
            break;

        default:
            return false;
        }
        return ti.updateTempDecl(sc, s);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("DotTemplateInstanceExp::semantic('%s')\n", toChars());
        }
        // Indicate we need to resolve by UFCS.
        Expression e = semanticY(sc, 1);
        if (!e)
            e = resolveUFCSProperties(sc, this);
        return e;
    }

    // Resolve e1.ident!tiargs without seeing UFCS.
    // If flag == 1, stop "not a property" error and return NULL.
    Expression semanticY(Scope* sc, int flag)
    {
        static if (LOGSEMANTIC)
        {
            printf("DotTemplateInstanceExpY::semantic('%s')\n", toChars());
        }

        auto die = new DotIdExp(loc, e1, ti.name);

        Expression e = die.semanticX(sc);
        if (e == die)
        {
            e1 = die.e1; // take back
            Type t1b = e1.type.toBasetype();
            if (t1b.ty == Tarray || t1b.ty == Tsarray || t1b.ty == Taarray || t1b.ty == Tnull || (t1b.isTypeBasic() && t1b.ty != Tvoid))
            {
                /* No built-in type has templatized properties, so do shortcut.
                 * It is necessary in: 1024.max!"a < b"
                 */
                if (flag)
                    return null;
            }
            e = die.semanticY(sc, flag);
            if (flag && e && isDotOpDispatch(e))
            {
                /* opDispatch!tiargs would be a function template that needs IFTI,
                 * so it's not a template
                 */
                e = null; /* fall down to UFCS */
            }
            if (flag && !e)
                return null;
        }
        assert(e);

    L1:
        if (e.op == TOKerror)
            return e;
        if (e.op == TOKdotvar)
        {
            DotVarExp dve = cast(DotVarExp)e;
            if (FuncDeclaration fd = dve.var.isFuncDeclaration())
            {
                TemplateDeclaration td = fd.findTemplateDeclRoot();
                if (td)
                {
                    e = new DotTemplateExp(dve.loc, dve.e1, td);
                    e = e.semantic(sc);
                }
            }
            else if (OverDeclaration od = dve.var.isOverDeclaration())
            {
                e1 = dve.e1; // pull semantic() result

                if (!findTempDecl(sc))
                    goto Lerr;
                if (ti.needsTypeInference(sc))
                    return this;
                ti.semantic(sc);
                if (!ti.inst || ti.errors) // if template failed to expand
                    return new ErrorExp();

                Dsymbol s = ti.toAlias();
                Declaration v = s.isDeclaration();
                if (v)
                {
                    if (v.type && !v.type.deco)
                        v.type = v.type.semantic(v.loc, sc);
                    e = new DotVarExp(loc, e1, v);
                    e = e.semantic(sc);
                    return e;
                }
                e = new ScopeExp(loc, ti);
                e = new DotExp(loc, e1, e);
                e = e.semantic(sc);
                return e;
            }
        }
        else if (e.op == TOKvar)
        {
            VarExp ve = cast(VarExp)e;
            if (FuncDeclaration fd = ve.var.isFuncDeclaration())
            {
                TemplateDeclaration td = fd.findTemplateDeclRoot();
                if (td)
                {
                    e = new TemplateExp(ve.loc, td);
                    e = e.semantic(sc);
                }
            }
            else if (OverDeclaration od = ve.var.isOverDeclaration())
            {
                ti.tempdecl = od;
                e = new ScopeExp(loc, ti);
                e = e.semantic(sc);
                return e;
            }
        }
        if (e.op == TOKdottd)
        {
            DotTemplateExp dte = cast(DotTemplateExp)e;
            e1 = dte.e1; // pull semantic() result

            ti.tempdecl = dte.td;
            if (!ti.semanticTiargs(sc))
                return new ErrorExp();
            if (ti.needsTypeInference(sc))
                return this;
            ti.semantic(sc);
            if (!ti.inst || ti.errors) // if template failed to expand
                return new ErrorExp();

            Dsymbol s = ti.toAlias();
            Declaration v = s.isDeclaration();
            if (v && (v.isFuncDeclaration() || v.isVarDeclaration()))
            {
                e = new DotVarExp(loc, e1, v);
                e = e.semantic(sc);
                return e;
            }
            e = new ScopeExp(loc, ti);
            e = new DotExp(loc, e1, e);
            e = e.semantic(sc);
            return e;
        }
        else if (e.op == TOKtemplate)
        {
            ti.tempdecl = (cast(TemplateExp)e).td;
            e = new ScopeExp(loc, ti);
            e = e.semantic(sc);
            return e;
        }
        else if (e.op == TOKdot)
        {
            DotExp de = cast(DotExp)e;

            if (de.e2.op == TOKoverloadset)
            {
                if (!findTempDecl(sc) || !ti.semanticTiargs(sc))
                {
                    return new ErrorExp();
                }
                if (ti.needsTypeInference(sc))
                    return this;
                ti.semantic(sc);
                if (!ti.inst || ti.errors) // if template failed to expand
                    return new ErrorExp();

                Dsymbol s = ti.toAlias();
                Declaration v = s.isDeclaration();
                if (v)
                {
                    if (v.type && !v.type.deco)
                        v.type = v.type.semantic(v.loc, sc);
                    e = new DotVarExp(loc, e1, v);
                    e = e.semantic(sc);
                    return e;
                }
                e = new ScopeExp(loc, ti);
                e = new DotExp(loc, e1, e);
                e = e.semantic(sc);
                return e;
            }
        }
        else if (e.op == TOKoverloadset)
        {
            OverExp oe = cast(OverExp)e;
            ti.tempdecl = oe.vars;
            e = new ScopeExp(loc, ti);
            e = e.semantic(sc);
            return e;
        }

    Lerr:
        error("%s isn't a template", e.toChars());
        return new ErrorExp();
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class DelegateExp : UnaExp
{
    FuncDeclaration func;
    bool hasOverloads;

    extern (D) this(Loc loc, Expression e, FuncDeclaration f, bool hasOverloads = true)
    {
        super(loc, TOKdelegate, __traits(classInstanceSize, DelegateExp), e);
        this.func = f;
        this.hasOverloads = hasOverloads;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("DelegateExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        e1 = e1.semantic(sc);

        type = new TypeDelegate(func.type);
        type = type.semantic(loc, sc);

        FuncDeclaration f = func.toAliasFunc();
        AggregateDeclaration ad = f.toParent().isAggregateDeclaration();
        if (f.needThis())
            e1 = getRightThis(loc, sc, ad, e1, f);
        if (f.type.ty == Tfunction)
        {
            TypeFunction tf = cast(TypeFunction)f.type;
            if (!MODmethodConv(e1.type.mod, f.type.mod))
            {
                OutBuffer thisBuf, funcBuf;
                MODMatchToBuffer(&thisBuf, e1.type.mod, tf.mod);
                MODMatchToBuffer(&funcBuf, tf.mod, e1.type.mod);
                error("%smethod %s is not callable using a %s%s",
                    funcBuf.peekString(), f.toPrettyChars(), thisBuf.peekString(), e1.toChars());
                return new ErrorExp();
            }
        }
        if (ad && ad.isClassDeclaration() && ad.type != e1.type)
        {
            // A downcast is required for interfaces, see Bugzilla 3706
            e1 = new CastExp(loc, e1, ad.type);
            e1 = e1.semantic(sc);
        }
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }

    override void printAST(int indent)
    {
        UnaExp.printAST(indent);
        foreach (i; 0 .. indent + 2)
            printf(" ");
        printf(".func: %s\n", func ? func.toChars() : "");
    }
}

/***********************************************************
 */
extern (C++) final class DotTypeExp : UnaExp
{
    Dsymbol sym;        // symbol that represents a type

    extern (D) this(Loc loc, Expression e, Dsymbol s)
    {
        super(loc, TOKdottype, __traits(classInstanceSize, DotTypeExp), e);
        this.sym = s;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("DotTypeExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        if (auto e = unaSemantic(sc))
            return e;

        type = sym.getType().addMod(e1.type.mod);
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class CallExp : UnaExp
{
    Expressions* arguments; // function arguments
    FuncDeclaration f;      // symbol to call
    bool directcall;        // true if a virtual call is devirtualized

    extern (D) this(Loc loc, Expression e, Expressions* exps)
    {
        super(loc, TOKcall, __traits(classInstanceSize, CallExp), e);
        this.arguments = exps;
    }

    extern (D) this(Loc loc, Expression e)
    {
        super(loc, TOKcall, __traits(classInstanceSize, CallExp), e);
    }

    extern (D) this(Loc loc, Expression e, Expression earg1)
    {
        super(loc, TOKcall, __traits(classInstanceSize, CallExp), e);
        auto arguments = new Expressions();
        if (earg1)
        {
            arguments.setDim(1);
            (*arguments)[0] = earg1;
        }
        this.arguments = arguments;
    }

    extern (D) this(Loc loc, Expression e, Expression earg1, Expression earg2)
    {
        super(loc, TOKcall, __traits(classInstanceSize, CallExp), e);
        auto arguments = new Expressions();
        arguments.setDim(2);
        (*arguments)[0] = earg1;
        (*arguments)[1] = earg2;
        this.arguments = arguments;
    }

    static CallExp create(Loc loc, Expression e, Expressions* exps)
    {
        return new CallExp(loc, e, exps);
    }

    static CallExp create(Loc loc, Expression e)
    {
        return new CallExp(loc, e);
    }

    static CallExp create(Loc loc, Expression e, Expression earg1)
    {
        return new CallExp(loc, e, earg1);
    }

    override Expression syntaxCopy()
    {
        return new CallExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments));
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("CallExp::semantic() %s\n", toChars());
        }
        if (type)
            return this; // semantic() already run
        version (none)
        {
            if (arguments && arguments.dim)
            {
                Expression earg = (*arguments)[0];
                earg.print();
                if (earg.type)
                    earg.type.print();
            }
        }

        Type t1;
        Objects* tiargs = null; // initial list of template arguments
        Expression ethis = null;
        Type tthis = null;
        Expression e1org = e1;

        if (e1.op == TOKcomma)
        {
            /* Rewrite (a,b)(args) as (a,(b(args)))
             */
            auto ce = cast(CommaExp)e1;
            e1 = ce.e2;
            ce.e2 = this;
            return ce.semantic(sc);
        }
        if (e1.op == TOKdelegate)
        {
            DelegateExp de = cast(DelegateExp)e1;
            e1 = new DotVarExp(de.loc, de.e1, de.func, de.hasOverloads);
            return semantic(sc);
        }
        if (e1.op == TOKfunction)
        {
            if (arrayExpressionSemantic(arguments, sc) || preFunctionParameters(loc, sc, arguments))
            {
                return new ErrorExp();
            }

            // Run e1 semantic even if arguments have any errors
            FuncExp fe = cast(FuncExp)e1;
            e1 = fe.semantic(sc, arguments);
            if (e1.op == TOKerror)
                return e1;
        }

        if (Expression ex = resolveUFCS(sc, this))
            return ex;

        /* This recognizes:
         *  foo!(tiargs)(funcargs)
         */
        if (e1.op == TOKscope)
        {
            ScopeExp se = cast(ScopeExp)e1;
            TemplateInstance ti = se.sds.isTemplateInstance();
            if (ti)
            {
                /* Attempt to instantiate ti. If that works, go with it.
                 * If not, go with partial explicit specialization.
                 */
                WithScopeSymbol withsym;
                if (!ti.findTempDecl(sc, &withsym) || !ti.semanticTiargs(sc))
                {
                    return new ErrorExp();
                }
                if (withsym && withsym.withstate.wthis)
                {
                    e1 = new VarExp(e1.loc, withsym.withstate.wthis);
                    e1 = new DotTemplateInstanceExp(e1.loc, e1, ti);
                    goto Ldotti;
                }
                if (ti.needsTypeInference(sc, 1))
                {
                    /* Go with partial explicit specialization
                     */
                    tiargs = ti.tiargs;
                    assert(ti.tempdecl);
                    if (TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration())
                        e1 = new TemplateExp(loc, td);
                    else if (OverDeclaration od = ti.tempdecl.isOverDeclaration())
                        e1 = new VarExp(loc, od);
                    else
                        e1 = new OverExp(loc, ti.tempdecl.isOverloadSet());
                }
                else
                {
                    Expression e1x = e1.semantic(sc);
                    if (e1x.op == TOKerror)
                        return e1x;
                    e1 = e1x;
                }
            }
        }

        /* This recognizes:
         *  expr.foo!(tiargs)(funcargs)
         */
    Ldotti:
        if (e1.op == TOKdotti && !e1.type)
        {
            DotTemplateInstanceExp se = cast(DotTemplateInstanceExp)e1;
            TemplateInstance ti = se.ti;
            {
                /* Attempt to instantiate ti. If that works, go with it.
                 * If not, go with partial explicit specialization.
                 */
                if (!se.findTempDecl(sc) || !ti.semanticTiargs(sc))
                {
                    return new ErrorExp();
                }
                if (ti.needsTypeInference(sc, 1))
                {
                    /* Go with partial explicit specialization
                     */
                    tiargs = ti.tiargs;
                    assert(ti.tempdecl);
                    if (TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration())
                        e1 = new DotTemplateExp(loc, se.e1, td);
                    else if (OverDeclaration od = ti.tempdecl.isOverDeclaration())
                    {
                        e1 = new DotVarExp(loc, se.e1, od, true);
                    }
                    else
                        e1 = new DotExp(loc, se.e1, new OverExp(loc, ti.tempdecl.isOverloadSet()));
                }
                else
                {
                    Expression e1x = e1.semantic(sc);
                    if (e1x.op == TOKerror)
                        return e1x;
                    e1 = e1x;
                }
            }
        }

    Lagain:
        //printf("Lagain: %s\n", toChars());
        f = null;
        if (e1.op == TOKthis || e1.op == TOKsuper)
        {
            // semantic() run later for these
        }
        else
        {
            if (e1.op == TOKdotid)
            {
                DotIdExp die = cast(DotIdExp)e1;
                e1 = die.semantic(sc);
                /* Look for e1 having been rewritten to expr.opDispatch!(string)
                 * We handle such earlier, so go back.
                 * Note that in the rewrite, we carefully did not run semantic() on e1
                 */
                if (e1.op == TOKdotti && !e1.type)
                {
                    goto Ldotti;
                }
            }
            else
            {
                static __gshared int nest;
                if (++nest > 500)
                {
                    error("recursive evaluation of %s", toChars());
                    --nest;
                    return new ErrorExp();
                }
                Expression ex = unaSemantic(sc);
                --nest;
                if (ex)
                    return ex;
            }

            /* Look for e1 being a lazy parameter
             */
            if (e1.op == TOKvar)
            {
                VarExp ve = cast(VarExp)e1;
                if (ve.var.storage_class & STClazy)
                {
                    // lazy paramaters can be called without violating purity and safety
                    Type tw = ve.var.type;
                    Type tc = ve.var.type.substWildTo(MODconst);
                    auto tf = new TypeFunction(null, tc, 0, LINKd, STCsafe | STCpure);
                    (tf = cast(TypeFunction)tf.semantic(loc, sc)).next = tw; // hack for bug7757
                    auto t = new TypeDelegate(tf);
                    ve.type = t.semantic(loc, sc);
                }
                VarDeclaration v = ve.var.isVarDeclaration();
                if (v && ve.checkPurity(sc, v))
                    return new ErrorExp();
            }

            if (e1.op == TOKsymoff && (cast(SymOffExp)e1).hasOverloads)
            {
                SymOffExp se = cast(SymOffExp)e1;
                e1 = new VarExp(se.loc, se.var, true);
                e1 = e1.semantic(sc);
            }
            else if (e1.op == TOKdot)
            {
                DotExp de = cast(DotExp)e1;

                if (de.e2.op == TOKoverloadset)
                {
                    ethis = de.e1;
                    tthis = de.e1.type;
                    e1 = de.e2;
                }
            }
            else if (e1.op == TOKstar && e1.type.ty == Tfunction)
            {
                // Rewrite (*fp)(arguments) to fp(arguments)
                e1 = (cast(PtrExp)e1).e1;
            }
        }

        t1 = e1.type ? e1.type.toBasetype() : null;

        if (e1.op == TOKerror)
            return e1;
        if (arrayExpressionSemantic(arguments, sc) || preFunctionParameters(loc, sc, arguments))
        {
            return new ErrorExp();
        }

        // Check for call operator overload
        if (t1)
        {
            if (t1.ty == Tstruct)
            {
                auto sd = (cast(TypeStruct)t1).sym;
                sd.size(loc); // Resolve forward references to construct object
                if (sd.sizeok != SIZEOKdone)
                    return new ErrorExp();
                if (!sd.ctor)
                    sd.ctor = sd.searchCtor();

                // First look for constructor
                if (e1.op == TOKtype && sd.ctor)
                {
                    if (!sd.noDefaultCtor && !(arguments && arguments.dim))
                        goto Lx;

                    auto sle = new StructLiteralExp(loc, sd, null, e1.type);
                    if (!sd.fill(loc, sle.elements, true))
                        return new ErrorExp();
                    if (checkFrameAccess(loc, sc, sd, sle.elements.dim))
                        return new ErrorExp();

                    // Bugzilla 14556: Set concrete type to avoid further redundant semantic().
                    sle.type = e1.type;

                    /* Constructor takes a mutable object, so don't use
                     * the immutable initializer symbol.
                     */
                    sle.useStaticInit = false;

                    Expression e = sle;
                    if (auto cf = sd.ctor.isCtorDeclaration())
                    {
                        e = new DotVarExp(loc, e, cf, true);
                    }
                    else if (auto td = sd.ctor.isTemplateDeclaration())
                    {
                        e = new DotTemplateExp(loc, e, td);
                    }
                    else if (auto os = sd.ctor.isOverloadSet())
                    {
                        e = new DotExp(loc, e, new OverExp(loc, os));
                    }
                    else
                        assert(0);
                    e = new CallExp(loc, e, arguments);
                    e = e.semantic(sc);
                    return e;
                }
                // No constructor, look for overload of opCall
                if (search_function(sd, Id.call))
                    goto L1;
                // overload of opCall, therefore it's a call
                if (e1.op != TOKtype)
                {
                    if (sd.aliasthis && e1.type != att1)
                    {
                        if (!att1 && e1.type.checkAliasThisRec())
                            att1 = e1.type;
                        e1 = resolveAliasThis(sc, e1);
                        goto Lagain;
                    }
                    error("%s %s does not overload ()", sd.kind(), sd.toChars());
                    return new ErrorExp();
                }

                /* It's a struct literal
                 */
            Lx:
                Expression e = new StructLiteralExp(loc, sd, arguments, e1.type);
                e = e.semantic(sc);
                return e;
            }
            else if (t1.ty == Tclass)
            {
            L1:
                // Rewrite as e1.call(arguments)
                Expression e = new DotIdExp(loc, e1, Id.call);
                e = new CallExp(loc, e, arguments);
                e = e.semantic(sc);
                return e;
            }
            else if (e1.op == TOKtype && t1.isscalar())
            {
                Expression e;
                if (!arguments || arguments.dim == 0)
                {
                    e = t1.defaultInitLiteral(loc);
                }
                else if (arguments.dim == 1)
                {
                    e = (*arguments)[0];
                    e = e.implicitCastTo(sc, t1);
                    e = new CastExp(loc, e, t1);
                }
                else
                {
                    error("more than one argument for construction of %s", t1.toChars());
                    e = new ErrorExp();
                }
                e = e.semantic(sc);
                return e;
            }
        }

        static FuncDeclaration resolveOverloadSet(Loc loc, Scope* sc,
            OverloadSet os, Objects* tiargs, Type tthis, Expressions* arguments)
        {
            FuncDeclaration f = null;
            foreach (s; os.a)
            {
                if (tiargs && s.isFuncDeclaration())
                    continue;
                if (auto f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, arguments, 1))
                {
                    if (f2.errors)
                        return null;
                    if (f)
                    {
                        /* Error if match in more than one overload set,
                         * even if one is a 'better' match than the other.
                         */
                        ScopeDsymbol.multiplyDefined(loc, f, f2);
                    }
                    else
                        f = f2;
                }
            }
            if (!f)
                .error(loc, "no overload matches for %s", os.toChars());
            else if (f.errors)
                f = null;
            return f;
        }

        if (e1.op == TOKdotvar && t1.ty == Tfunction || e1.op == TOKdottd)
        {
            UnaExp ue = cast(UnaExp)e1;

            Expression ue1 = ue.e1;
            Expression ue1old = ue1; // need for 'right this' check
            VarDeclaration v;
            if (ue1.op == TOKvar && (v = (cast(VarExp)ue1).var.isVarDeclaration()) !is null && v.needThis())
            {
                ue.e1 = new TypeExp(ue1.loc, ue1.type);
                ue1 = null;
            }

            DotVarExp dve;
            DotTemplateExp dte;
            Dsymbol s;
            if (e1.op == TOKdotvar)
            {
                dve = cast(DotVarExp)e1;
                dte = null;
                s = dve.var;
                tiargs = null;
            }
            else
            {
                dve = null;
                dte = cast(DotTemplateExp)e1;
                s = dte.td;
            }

            // Do overload resolution
            f = resolveFuncCall(loc, sc, s, tiargs, ue1 ? ue1.type : null, arguments);
            if (!f || f.errors || f.type.ty == Terror)
                return new ErrorExp();

            if (f.interfaceVirtual)
            {
                /* Cast 'this' to the type of the interface, and replace f with the interface's equivalent
                 */
                auto b = f.interfaceVirtual;
                auto ad2 = b.sym;
                ue.e1 = ue.e1.castTo(sc, ad2.type.addMod(ue.e1.type.mod));
                ue.e1 = ue.e1.semantic(sc);
                ue1 = ue.e1;
                auto vi = f.findVtblIndex(&ad2.vtbl, cast(int)ad2.vtbl.dim);
                assert(vi >= 0);
                f = ad2.vtbl[vi].isFuncDeclaration();
                assert(f);
            }
            if (f.needThis())
            {
                AggregateDeclaration ad = f.toParent2().isAggregateDeclaration();
                ue.e1 = getRightThis(loc, sc, ad, ue.e1, f);
                if (ue.e1.op == TOKerror)
                    return ue.e1;
                ethis = ue.e1;
                tthis = ue.e1.type;
            }

            /* Cannot call public functions from inside invariant
             * (because then the invariant would have infinite recursion)
             */
            if (sc.func && sc.func.isInvariantDeclaration() && ue.e1.op == TOKthis && f.addPostInvariant())
            {
                error("cannot call public/export function %s from invariant", f.toChars());
                return new ErrorExp();
            }

            checkDeprecated(sc, f);
            checkPurity(sc, f);
            checkSafety(sc, f);
            checkNogc(sc, f);
            checkAccess(loc, sc, ue.e1, f);
            if (!f.needThis())
            {
                e1 = Expression.combine(ue.e1, new VarExp(loc, f, false));
            }
            else
            {
                if (ue1old.checkRightThis(sc))
                    return new ErrorExp();
                if (e1.op == TOKdotvar)
                {
                    dve.var = f;
                    e1.type = f.type;
                }
                else
                {
                    e1 = new DotVarExp(loc, dte.e1, f, false);
                    e1 = e1.semantic(sc);
                    if (e1.op == TOKerror)
                        return new ErrorExp();
                    ue = cast(UnaExp)e1;
                }
                version (none)
                {
                    printf("ue->e1 = %s\n", ue.e1.toChars());
                    printf("f = %s\n", f.toChars());
                    printf("t = %s\n", t.toChars());
                    printf("e1 = %s\n", e1.toChars());
                    printf("e1->type = %s\n", e1.type.toChars());
                }

                // See if we need to adjust the 'this' pointer
                AggregateDeclaration ad = f.isThis();
                ClassDeclaration cd = ue.e1.type.isClassHandle();
                if (ad && cd && ad.isClassDeclaration())
                {
                    if (ue.e1.op == TOKdottype)
                    {
                        ue.e1 = (cast(DotTypeExp)ue.e1).e1;
                        directcall = true;
                    }
                    else if (ue.e1.op == TOKsuper)
                        directcall = true;
                    else if ((cd.storage_class & STCfinal) != 0) // Bugzilla 14211
                        directcall = true;

                    if (ad != cd)
                    {
                        ue.e1 = ue.e1.castTo(sc, ad.type.addMod(ue.e1.type.mod));
                        ue.e1 = ue.e1.semantic(sc);
                    }
                }
            }
            t1 = e1.type;
        }
        else if (e1.op == TOKsuper)
        {
            // Base class constructor call
            auto ad = sc.func ? sc.func.isThis() : null;
            auto cd = ad ? ad.isClassDeclaration() : null;
            if (!cd || !cd.baseClass || !sc.func.isCtorDeclaration())
            {
                error("super class constructor call must be in a constructor");
                return new ErrorExp();
            }
            if (!cd.baseClass.ctor)
            {
                error("no super class constructor for %s", cd.baseClass.toChars());
                return new ErrorExp();
            }

            if (!sc.intypeof && !(sc.callSuper & CSXhalt))
            {
                if (sc.noctor || sc.callSuper & CSXlabel)
                    error("constructor calls not allowed in loops or after labels");
                if (sc.callSuper & (CSXsuper_ctor | CSXthis_ctor))
                    error("multiple constructor calls");
                if ((sc.callSuper & CSXreturn) && !(sc.callSuper & CSXany_ctor))
                    error("an earlier return statement skips constructor");
                sc.callSuper |= CSXany_ctor | CSXsuper_ctor;
            }

            tthis = cd.type.addMod(sc.func.type.mod);
            if (auto os = cd.baseClass.ctor.isOverloadSet())
                f = resolveOverloadSet(loc, sc, os, null, tthis, arguments);
            else
                f = resolveFuncCall(loc, sc, cd.baseClass.ctor, null, tthis, arguments, 0);
            if (!f || f.errors)
                return new ErrorExp();
            checkDeprecated(sc, f);
            checkPurity(sc, f);
            checkSafety(sc, f);
            checkNogc(sc, f);
            checkAccess(loc, sc, null, f);

            e1 = new DotVarExp(e1.loc, e1, f, false);
            e1 = e1.semantic(sc);
            t1 = e1.type;
        }
        else if (e1.op == TOKthis)
        {
            // same class constructor call
            auto ad = sc.func ? sc.func.isThis() : null;
            if (!ad || !sc.func.isCtorDeclaration())
            {
                error("constructor call must be in a constructor");
                return new ErrorExp();
            }

            if (!sc.intypeof && !(sc.callSuper & CSXhalt))
            {
                if (sc.noctor || sc.callSuper & CSXlabel)
                    error("constructor calls not allowed in loops or after labels");
                if (sc.callSuper & (CSXsuper_ctor | CSXthis_ctor))
                    error("multiple constructor calls");
                if ((sc.callSuper & CSXreturn) && !(sc.callSuper & CSXany_ctor))
                    error("an earlier return statement skips constructor");
                sc.callSuper |= CSXany_ctor | CSXthis_ctor;
            }

            tthis = ad.type.addMod(sc.func.type.mod);
            if (auto os = ad.ctor.isOverloadSet())
                f = resolveOverloadSet(loc, sc, os, null, tthis, arguments);
            else
                f = resolveFuncCall(loc, sc, ad.ctor, null, tthis, arguments, 0);
            if (!f || f.errors)
                return new ErrorExp();
            checkDeprecated(sc, f);
            checkPurity(sc, f);
            checkSafety(sc, f);
            checkNogc(sc, f);
            //checkAccess(loc, sc, NULL, f);    // necessary?

            e1 = new DotVarExp(e1.loc, e1, f, false);
            e1 = e1.semantic(sc);
            t1 = e1.type;

            // BUG: this should really be done by checking the static
            // call graph
            if (f == sc.func)
            {
                error("cyclic constructor call");
                return new ErrorExp();
            }
        }
        else if (e1.op == TOKoverloadset)
        {
            auto os = (cast(OverExp)e1).vars;
            f = resolveOverloadSet(loc, sc, os, tiargs, tthis, arguments);
            if (!f)
                return new ErrorExp();
            if (ethis)
                e1 = new DotVarExp(loc, ethis, f, false);
            else
                e1 = new VarExp(loc, f, false);
            goto Lagain;
        }
        else if (!t1)
        {
            error("function expected before (), not '%s'", e1.toChars());
            return new ErrorExp();
        }
        else if (t1.ty == Terror)
        {
            return new ErrorExp();
        }
        else if (t1.ty != Tfunction)
        {
            TypeFunction tf;
            const(char)* p;
            Dsymbol s;
            f = null;
            if (e1.op == TOKfunction)
            {
                // function literal that direct called is always inferred.
                assert((cast(FuncExp)e1).fd);
                f = (cast(FuncExp)e1).fd;
                tf = cast(TypeFunction)f.type;
                p = "function literal";
            }
            else if (t1.ty == Tdelegate)
            {
                TypeDelegate td = cast(TypeDelegate)t1;
                assert(td.next.ty == Tfunction);
                tf = cast(TypeFunction)td.next;
                p = "delegate";
            }
            else if (t1.ty == Tpointer && (cast(TypePointer)t1).next.ty == Tfunction)
            {
                tf = cast(TypeFunction)(cast(TypePointer)t1).next;
                p = "function pointer";
            }
            else if (e1.op == TOKdotvar && (cast(DotVarExp)e1).var.isOverDeclaration())
            {
                DotVarExp dve = cast(DotVarExp)e1;
                f = resolveFuncCall(loc, sc, dve.var, tiargs, dve.e1.type, arguments, 2);
                if (!f)
                    return new ErrorExp();
                if (f.needThis())
                {
                    dve.var = f;
                    dve.type = f.type;
                    dve.hasOverloads = false;
                    goto Lagain;
                }
                e1 = new VarExp(dve.loc, f, false);
                Expression e = new CommaExp(loc, dve.e1, this);
                return e.semantic(sc);
            }
            else if (e1.op == TOKvar && (cast(VarExp)e1).var.isOverDeclaration())
            {
                s = (cast(VarExp)e1).var;
                goto L2;
            }
            else if (e1.op == TOKtemplate)
            {
                s = (cast(TemplateExp)e1).td;
            L2:
                f = resolveFuncCall(loc, sc, s, tiargs, null, arguments);
                if (!f || f.errors)
                    return new ErrorExp();
                if (f.needThis())
                {
                    if (hasThis(sc))
                    {
                        // Supply an implicit 'this', as in
                        //    this.ident
                        e1 = new DotVarExp(loc, (new ThisExp(loc)).semantic(sc), f, false);
                        goto Lagain;
                    }
                    else if (isNeedThisScope(sc, f))
                    {
                        error("need 'this' for '%s' of type '%s'", f.toChars(), f.type.toChars());
                        return new ErrorExp();
                    }
                }
                e1 = new VarExp(e1.loc, f, false);
                goto Lagain;
            }
            else
            {
                error("function expected before (), not %s of type %s", e1.toChars(), e1.type.toChars());
                return new ErrorExp();
            }

            if (!tf.callMatch(null, arguments))
            {
                OutBuffer buf;
                buf.writeByte('(');
                argExpTypesToCBuffer(&buf, arguments);
                buf.writeByte(')');
                if (tthis)
                    tthis.modToBuffer(&buf);

                //printf("tf = %s, args = %s\n", tf->deco, (*arguments)[0]->type->deco);
                .error(loc, "%s %s %s is not callable using argument types %s", p, e1.toChars(), parametersTypeToChars(tf.parameters, tf.varargs), buf.peekString());

                return new ErrorExp();
            }
            // Purity and safety check should run after testing arguments matching
            if (f)
            {
                checkPurity(sc, f);
                checkSafety(sc, f);
                checkNogc(sc, f);
                if (f.checkNestedReference(sc, loc))
                    return new ErrorExp();
            }
            else if (sc.func && sc.intypeof != 1 && !(sc.flags & SCOPEctfe))
            {
                bool err = false;
                if (!tf.purity && !(sc.flags & SCOPEdebug) && sc.func.setImpure())
                {
                    error("pure %s '%s' cannot call impure %s '%s'",
                        sc.func.kind(), sc.func.toPrettyChars(), p, e1.toChars());
                    err = true;
                }
                if (!tf.isnogc && sc.func.setGC())
                {
                    error("@nogc %s '%s' cannot call non-@nogc %s '%s'",
                        sc.func.kind(), sc.func.toPrettyChars(), p, e1.toChars());
                    err = true;
                }
                if (tf.trust <= TRUSTsystem && sc.func.setUnsafe())
                {
                    error("@safe %s '%s' cannot call @system %s '%s'",
                        sc.func.kind(), sc.func.toPrettyChars(), p, e1.toChars());
                    err = true;
                }
                if (err)
                    return new ErrorExp();
            }

            if (t1.ty == Tpointer)
            {
                Expression e = new PtrExp(loc, e1);
                e.type = tf;
                e1 = e;
            }
            t1 = tf;
        }
        else if (e1.op == TOKvar)
        {
            // Do overload resolution
            VarExp ve = cast(VarExp)e1;

            f = ve.var.isFuncDeclaration();
            assert(f);
            tiargs = null;

            if (ve.hasOverloads)
                f = resolveFuncCall(loc, sc, f, tiargs, null, arguments, 2);
            else
            {
                f = f.toAliasFunc();
                TypeFunction tf = cast(TypeFunction)f.type;
                if (!tf.callMatch(null, arguments))
                {
                    OutBuffer buf;
                    buf.writeByte('(');
                    argExpTypesToCBuffer(&buf, arguments);
                    buf.writeByte(')');

                    //printf("tf = %s, args = %s\n", tf->deco, (*arguments)[0]->type->deco);
                    .error(loc, "%s %s is not callable using argument types %s", e1.toChars(), parametersTypeToChars(tf.parameters, tf.varargs), buf.peekString());

                    f = null;
                }
            }
            if (!f || f.errors)
                return new ErrorExp();

            if (f.needThis())
            {
                // Change the ancestor lambdas to delegate before hasThis(sc) call.
                if (f.checkNestedReference(sc, loc))
                    return new ErrorExp();

                if (hasThis(sc))
                {
                    // Supply an implicit 'this', as in
                    //    this.ident
                    e1 = new DotVarExp(loc, (new ThisExp(loc)).semantic(sc), ve.var);
                    // Note: we cannot use f directly, because further overload resolution
                    // through the supplied 'this' may cause different result.
                    goto Lagain;
                }
                else if (isNeedThisScope(sc, f))
                {
                    error("need 'this' for '%s' of type '%s'", f.toChars(), f.type.toChars());
                    return new ErrorExp();
                }
            }

            checkDeprecated(sc, f);
            checkPurity(sc, f);
            checkSafety(sc, f);
            checkNogc(sc, f);
            checkAccess(loc, sc, null, f);
            if (f.checkNestedReference(sc, loc))
                return new ErrorExp();

            ethis = null;
            tthis = null;

            if (ve.hasOverloads)
            {
                e1 = new VarExp(ve.loc, f, false);
                e1.type = f.type;
            }
            t1 = f.type;
        }
        assert(t1.ty == Tfunction);

        Expression argprefix;
        if (!arguments)
            arguments = new Expressions();
        if (functionParameters(loc, sc, cast(TypeFunction)t1, tthis, arguments, f, &type, &argprefix))
            return new ErrorExp();

        if (!type)
        {
            e1 = e1org; // Bugzilla 10922, avoid recursive expression printing
            error("forward reference to inferred return type of function call '%s'", toChars());
            return new ErrorExp();
        }

        if (f && f.tintro)
        {
            Type t = type;
            int offset = 0;
            TypeFunction tf = cast(TypeFunction)f.tintro;
            if (tf.next.isBaseOf(t, &offset) && offset)
            {
                type = tf.next;
                return combine(argprefix, castTo(sc, t));
            }
        }

        // Handle the case of a direct lambda call
        if (f && f.isFuncLiteralDeclaration() && sc.func && !sc.intypeof)
        {
            f.tookAddressOf = 0;
        }

        return combine(argprefix, this);
    }

    override bool isLvalue()
    {
        Type tb = e1.type.toBasetype();
        if (tb.ty == Tdelegate || tb.ty == Tpointer)
            tb = tb.nextOf();
        if (tb.ty == Tfunction && (cast(TypeFunction)tb).isref)
        {
            if (e1.op == TOKdotvar)
                if ((cast(DotVarExp)e1).var.isCtorDeclaration())
                    return false;
            return true; // function returns a reference
        }
        return false;
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        if (isLvalue())
            return this;
        return Expression.toLvalue(sc, e);
    }

    override Expression addDtorHook(Scope* sc)
    {
        /* Only need to add dtor hook if it's a type that needs destruction.
         * Use same logic as VarDeclaration::callScopeDtor()
         */

        if (e1.type && e1.type.ty == Tfunction)
        {
            TypeFunction tf = cast(TypeFunction)e1.type;
            if (tf.isref)
                return this;
        }

        Type tv = type.baseElemOf();
        if (tv.ty == Tstruct)
        {
            TypeStruct ts = cast(TypeStruct)tv;
            StructDeclaration sd = ts.sym;
            if (sd.dtor)
            {
                /* Type needs destruction, so declare a tmp
                 * which the back end will recognize and call dtor on
                 */
                auto tmp = copyToTemp(0, "__tmpfordtor", this);
                auto de = new DeclarationExp(loc, tmp);
                auto ve = new VarExp(loc, tmp);
                Expression e = new CommaExp(loc, de, new VarExp(loc, tmp));
                e = e.semantic(sc);
                return e;
            }
        }
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

FuncDeclaration isFuncAddress(Expression e, bool* hasOverloads = null)
{
    if (e.op == TOKaddress)
    {
        auto ae1 = (cast(AddrExp)e).e1;
        if (ae1.op == TOKvar)
        {
            auto ve = cast(VarExp)ae1;
            if (hasOverloads)
                *hasOverloads = ve.hasOverloads;
            return ve.var.isFuncDeclaration();
        }
        if (ae1.op == TOKdotvar)
        {
            auto dve = cast(DotVarExp)ae1;
            if (hasOverloads)
                *hasOverloads = dve.hasOverloads;
            return dve.var.isFuncDeclaration();
        }
    }
    else
    {
        if (e.op == TOKsymoff)
        {
            auto soe = cast(SymOffExp)e;
            if (hasOverloads)
                *hasOverloads = soe.hasOverloads;
            return soe.var.isFuncDeclaration();
        }
        if (e.op == TOKdelegate)
        {
            auto dge = cast(DelegateExp)e;
            if (hasOverloads)
                *hasOverloads = dge.hasOverloads;
            return dge.func.isFuncDeclaration();
        }
    }
    return null;
}

/***********************************************************
 */
extern (C++) final class AddrExp : UnaExp
{
    extern (D) this(Loc loc, Expression e)
    {
        super(loc, TOKaddress, __traits(classInstanceSize, AddrExp), e);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("AddrExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        if (Expression ex = unaSemantic(sc))
            return ex;

        int wasCond = e1.op == TOKquestion;

        if (e1.op == TOKdotti)
        {
            DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)e1;
            TemplateInstance ti = dti.ti;
            {
                //assert(ti.needsTypeInference(sc));
                ti.semantic(sc);
                if (!ti.inst || ti.errors) // if template failed to expand
                    return new ErrorExp();
                Dsymbol s = ti.toAlias();
                FuncDeclaration f = s.isFuncDeclaration();
                if (f)
                {
                    e1 = new DotVarExp(e1.loc, dti.e1, f);
                    e1 = e1.semantic(sc);
                }
            }
        }
        else if (e1.op == TOKscope)
        {
            TemplateInstance ti = (cast(ScopeExp)e1).sds.isTemplateInstance();
            if (ti)
            {
                //assert(ti.needsTypeInference(sc));
                ti.semantic(sc);
                if (!ti.inst || ti.errors) // if template failed to expand
                    return new ErrorExp();
                Dsymbol s = ti.toAlias();
                FuncDeclaration f = s.isFuncDeclaration();
                if (f)
                {
                    e1 = new VarExp(e1.loc, f);
                    e1 = e1.semantic(sc);
                }
            }
        }
        e1 = e1.toLvalue(sc, null);
        if (e1.op == TOKerror)
            return e1;
        if (checkNonAssignmentArrayOp(e1))
            return new ErrorExp();

        if (!e1.type)
        {
            error("cannot take address of %s", e1.toChars());
            return new ErrorExp();
        }

        bool hasOverloads;
        if (auto f = isFuncAddress(this, &hasOverloads))
        {
            if (!hasOverloads && f.checkForwardRef(loc))
                return new ErrorExp();
        }
        else if (!e1.type.deco)
        {
            if (e1.op == TOKvar)
            {
                VarExp ve = cast(VarExp)e1;
                Declaration d = ve.var;
                error("forward reference to %s %s", d.kind(), d.toChars());
            }
            else
                error("forward reference to %s", e1.toChars());
            return new ErrorExp();
        }

        type = e1.type.pointerTo();

        // See if this should really be a delegate
        if (e1.op == TOKdotvar)
        {
            DotVarExp dve = cast(DotVarExp)e1;
            FuncDeclaration f = dve.var.isFuncDeclaration();
            if (f)
            {
                f = f.toAliasFunc(); // FIXME, should see overloads - Bugzilla 1983
                if (!dve.hasOverloads)
                    f.tookAddressOf++;

                Expression e;
                if (f.needThis())
                    e = new DelegateExp(loc, dve.e1, f, dve.hasOverloads);
                else // It is a function pointer. Convert &v.f() --> (v, &V.f())
                    e = new CommaExp(loc, dve.e1, new AddrExp(loc, new VarExp(loc, f, dve.hasOverloads)));
                e = e.semantic(sc);
                return e;
            }

            // Look for misaligned pointer in @safe mode
            if (checkUnsafeAccess(sc, dve, !type.isMutable(), true))
                return new ErrorExp();
        }
        else if (e1.op == TOKvar)
        {
            VarExp ve = cast(VarExp)e1;
            VarDeclaration v = ve.var.isVarDeclaration();
            if (v)
            {
                if (!v.canTakeAddressOf())
                {
                    error("cannot take address of %s", e1.toChars());
                    return new ErrorExp();
                }
                if (sc.func && !sc.intypeof && !v.isDataseg())
                {
                    if (sc.func.setUnsafe())
                    {
                        const(char)* p = v.isParameter() ? "parameter" : "local";
                        error("cannot take address of %s %s in @safe function %s", p, v.toChars(), sc.func.toChars());
                    }
                }

                ve.checkPurity(sc, v);
            }
            FuncDeclaration f = ve.var.isFuncDeclaration();
            if (f)
            {
                /* Because nested functions cannot be overloaded,
                 * mark here that we took its address because castTo()
                 * may not be called with an exact match.
                 */
                if (!ve.hasOverloads || f.isNested())
                    f.tookAddressOf++;
                if (f.isNested())
                {
                    if (f.isFuncLiteralDeclaration())
                    {
                        if (!f.FuncDeclaration.isNested())
                        {
                            /* Supply a 'null' for a this pointer if no this is available
                             */
                            Expression e = new DelegateExp(loc, new NullExp(loc, Type.tnull), f, ve.hasOverloads);
                            e = e.semantic(sc);
                            return e;
                        }
                    }
                    Expression e = new DelegateExp(loc, e1, f, ve.hasOverloads);
                    e = e.semantic(sc);
                    return e;
                }
                if (f.needThis())
                {
                    if (hasThis(sc))
                    {
                        /* Should probably supply 'this' after overload resolution,
                         * not before.
                         */
                        Expression ethis = new ThisExp(loc);
                        Expression e = new DelegateExp(loc, ethis, f, ve.hasOverloads);
                        e = e.semantic(sc);
                        return e;
                    }
                    if (sc.func && !sc.intypeof)
                    {
                        if (sc.func.setUnsafe())
                        {
                            error("'this' reference necessary to take address of member %s in @safe function %s", f.toChars(), sc.func.toChars());
                        }
                    }
                }
            }
        }
        else if (e1.op == TOKcall)
        {
            CallExp ce = cast(CallExp)e1;
            if (ce.e1.type.ty == Tfunction)
            {
                TypeFunction tf = cast(TypeFunction)ce.e1.type;
                if (tf.isref && sc.func && !sc.intypeof && sc.func.setUnsafe())
                {
                    error("cannot take address of ref return of %s() in @safe function %s",
                        ce.e1.toChars(), sc.func.toChars());
                }
            }
        }
        else if (wasCond)
        {
            /* a ? b : c was transformed to *(a ? &b : &c), but we still
             * need to do safety checks
             */
            assert(e1.op == TOKstar);
            PtrExp pe = cast(PtrExp)e1;
            assert(pe.e1.op == TOKquestion);
            CondExp ce = cast(CondExp)pe.e1;
            assert(ce.e1.op == TOKaddress);
            assert(ce.e2.op == TOKaddress);

            // Re-run semantic on the address expressions only
            ce.e1.type = null;
            ce.e1 = ce.e1.semantic(sc);
            ce.e2.type = null;
            ce.e2 = ce.e2.semantic(sc);
        }
        return optimize(WANTvalue);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class PtrExp : UnaExp
{
    extern (D) this(Loc loc, Expression e)
    {
        super(loc, TOKstar, __traits(classInstanceSize, PtrExp), e);
        //if (e->type)
        //  type = ((TypePointer *)e->type)->next;
    }

    extern (D) this(Loc loc, Expression e, Type t)
    {
        super(loc, TOKstar, __traits(classInstanceSize, PtrExp), e);
        type = t;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("PtrExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        Expression e = op_overload(sc);
        if (e)
            return e;

        Type tb = e1.type.toBasetype();
        switch (tb.ty)
        {
        case Tpointer:
            type = (cast(TypePointer)tb).next;
            break;

        case Tsarray:
        case Tarray:
            if (isNonAssignmentArrayOp(e1))
                goto default;
            error("using * on an array is no longer supported; use *(%s).ptr instead", e1.toChars());
            type = (cast(TypeArray)tb).next;
            e1 = e1.castTo(sc, type.pointerTo());
            break;

        default:
            error("can only * a pointer, not a '%s'", e1.type.toChars());
            goto case Terror;
        case Terror:
            return new ErrorExp();
        }
        if (checkValue())
            return new ErrorExp();

        return this;
    }

    override int checkModifiable(Scope* sc, int flag)
    {
        if (e1.op == TOKsymoff)
        {
            SymOffExp se = cast(SymOffExp)e1;
            return se.var.checkModify(loc, sc, type, null, flag);
        }
        else if (e1.op == TOKaddress)
        {
            AddrExp ae = cast(AddrExp)e1;
            return ae.e1.checkModifiable(sc, flag);
        }
        return 1;
    }

    override bool isLvalue()
    {
        return true;
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        return this;
    }

    override Expression modifiableLvalue(Scope* sc, Expression e)
    {
        //printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type->toChars());
        return Expression.modifiableLvalue(sc, e);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class NegExp : UnaExp
{
    extern (D) this(Loc loc, Expression e)
    {
        super(loc, TOKneg, __traits(classInstanceSize, NegExp), e);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("NegExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        Expression e = op_overload(sc);
        if (e)
            return e;

        type = e1.type;
        Type tb = type.toBasetype();
        if (tb.ty == Tarray || tb.ty == Tsarray)
        {
            if (!isArrayOpValid(e1))
            {
                error("invalid array operation %s (possible missing [])", toChars());
                return new ErrorExp();
            }
            return this;
        }

        if (e1.checkNoBool())
            return new ErrorExp();
        if (e1.checkArithmetic())
            return new ErrorExp();

        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class UAddExp : UnaExp
{
    extern (D) this(Loc loc, Expression e)
    {
        super(loc, TOKuadd, __traits(classInstanceSize, UAddExp), e);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("UAddExp::semantic('%s')\n", toChars());
        }
        assert(!type);

        Expression e = op_overload(sc);
        if (e)
            return e;

        if (e1.checkNoBool())
            return new ErrorExp();
        if (e1.checkArithmetic())
            return new ErrorExp();

        return e1;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class ComExp : UnaExp
{
    extern (D) this(Loc loc, Expression e)
    {
        super(loc, TOKtilde, __traits(classInstanceSize, ComExp), e);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        Expression e = op_overload(sc);
        if (e)
            return e;

        type = e1.type;
        Type tb = type.toBasetype();
        if (tb.ty == Tarray || tb.ty == Tsarray)
        {
            if (!isArrayOpValid(e1))
            {
                error("invalid array operation %s (possible missing [])", toChars());
                return new ErrorExp();
            }
            return this;
        }

        if (e1.checkNoBool())
            return new ErrorExp();
        if (e1.checkIntegral())
            return new ErrorExp();

        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class NotExp : UnaExp
{
    extern (D) this(Loc loc, Expression e)
    {
        super(loc, TOKnot, __traits(classInstanceSize, NotExp), e);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        setNoderefOperand();

        // Note there is no operator overload
        if (Expression ex = unaSemantic(sc))
            return ex;
        e1 = resolveProperties(sc, e1);
        e1 = e1.toBoolean(sc);
        if (e1.type == Type.terror)
            return e1;

        // Bugzilla 13910: Today NotExp can take an array as its operand.
        if (checkNonAssignmentArrayOp(e1))
            return new ErrorExp();

        type = Type.tbool;
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class DeleteExp : UnaExp
{
    extern (D) this(Loc loc, Expression e)
    {
        super(loc, TOKdelete, __traits(classInstanceSize, DeleteExp), e);
    }

    override Expression semantic(Scope* sc)
    {
        if (Expression ex = unaSemantic(sc))
            return ex;
        e1 = resolveProperties(sc, e1);
        e1 = e1.modifiableLvalue(sc, null);
        if (e1.op == TOKerror)
            return e1;
        type = Type.tvoid;

        AggregateDeclaration ad = null;
        Type tb = e1.type.toBasetype();
        switch (tb.ty)
        {
        case Tclass:
            {
                auto cd = (cast(TypeClass)tb).sym;
                if (cd.isCOMinterface())
                {
                    /* Because COM classes are deleted by IUnknown.Release()
                     */
                    error("cannot delete instance of COM interface %s", cd.toChars());
                    return new ErrorExp();
                }

                ad = cd;
                break;
            }
        case Tpointer:
            tb = (cast(TypePointer)tb).next.toBasetype();
            if (tb.ty == Tstruct)
            {
                ad = (cast(TypeStruct)tb).sym;
                auto f = ad.aggDelete;
                auto fd = ad.dtor;
                if (!f)
                {
                    semanticTypeInfo(sc, tb);
                    break;
                }

                /* Construct:
                 *      ea = copy e1 to a tmp to do side effects only once
                 *      eb = call destructor
                 *      ec = call deallocator
                 */
                Expression ea = null;
                Expression eb = null;
                Expression ec = null;
                VarDeclaration v = null;
                if (fd && f)
                {
                    v = copyToTemp(0, "__tmpea", e1);
                    v.semantic(sc);
                    ea = new DeclarationExp(loc, v);
                    ea.type = v.type;
                }
                if (fd)
                {
                    Expression e = ea ? new VarExp(loc, v) : e1;
                    e = new DotVarExp(Loc(), e, fd, false);
                    eb = new CallExp(loc, e);
                    eb = eb.semantic(sc);
                }
                if (f)
                {
                    Type tpv = Type.tvoid.pointerTo();
                    Expression e = ea ? new VarExp(loc, v) : e1.castTo(sc, tpv);
                    e = new CallExp(loc, new VarExp(loc, f, false), e);
                    ec = e.semantic(sc);
                }
                ea = combine(ea, eb);
                ea = combine(ea, ec);
                assert(ea);
                return ea;
            }
            break;

        case Tarray:
            {
                Type tv = tb.nextOf().baseElemOf();
                if (tv.ty == Tstruct)
                {
                    ad = (cast(TypeStruct)tv).sym;
                    if (ad.dtor)
                        semanticTypeInfo(sc, ad.type);
                }
                break;
            }
        default:
            error("cannot delete type %s", e1.type.toChars());
            return new ErrorExp();
        }

        bool err = false;
        if (ad)
        {
            if (ad.dtor)
            {
                err |= checkPurity(sc, ad.dtor);
                err |= checkSafety(sc, ad.dtor);
                err |= checkNogc(sc, ad.dtor);
            }
            if (ad.aggDelete && tb.ty != Tarray)
            {
                err |= checkPurity(sc, ad.aggDelete);
                err |= checkSafety(sc, ad.aggDelete);
                err |= checkNogc(sc, ad.aggDelete);
            }
            if (err)
                return new ErrorExp();
        }

        // unsafe
        if (!sc.intypeof && sc.func && sc.func.setUnsafe())
        {
            error("%s is not @safe but is used in @safe function %s", toChars(), sc.func.toChars());
            err = true;
        }
        if (err)
            return new ErrorExp();

        return this;
    }

    override Expression toBoolean(Scope* sc)
    {
        error("delete does not give a boolean result");
        return new ErrorExp();
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * Possible to cast to one type while painting to another type
 */
extern (C++) final class CastExp : UnaExp
{
    Type to;                    // type to cast to
    ubyte mod = cast(ubyte)~0;  // MODxxxxx

    extern (D) this(Loc loc, Expression e, Type t)
    {
        super(loc, TOKcast, __traits(classInstanceSize, CastExp), e);
        this.to = t;
    }

    /* For cast(const) and cast(immutable)
     */
    extern (D) this(Loc loc, Expression e, ubyte mod)
    {
        super(loc, TOKcast, __traits(classInstanceSize, CastExp), e);
        this.mod = mod;
    }

    override Expression syntaxCopy()
    {
        return to ? new CastExp(loc, e1.syntaxCopy(), to.syntaxCopy()) : new CastExp(loc, e1.syntaxCopy(), mod);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("CastExp::semantic('%s')\n", toChars());
        }
        //static int x; assert(++x < 10);
        if (type)
            return this;

        if (to)
        {
            to = to.semantic(loc, sc);
            if (to == Type.terror)
                return new ErrorExp();

            if (!to.hasPointers())
                setNoderefOperand();

            // When e1 is a template lambda, this cast may instantiate it with
            // the type 'to'.
            e1 = inferType(e1, to);
        }

        if (auto e = unaSemantic(sc))
            return e;
        auto e1x = resolveProperties(sc, e1);
        if (e1x.op == TOKerror)
            return e1x;
        if (e1x.checkType())
            return new ErrorExp();
        e1 = e1x;

        if (!e1.type)
        {
            error("cannot cast %s", e1.toChars());
            return new ErrorExp();
        }

        if (!to) // Handle cast(const) and cast(immutable), etc.
        {
            to = e1.type.castMod(mod);
            to = to.semantic(loc, sc);
            if (to == Type.terror)
                return new ErrorExp();
        }

        if (to.ty == Ttuple)
        {
            error("cannot cast %s to tuple type %s", e1.toChars(), to.toChars());
            return new ErrorExp();
        }

        // cast(void) is used to mark e1 as unused, so it is safe
        if (to.ty == Tvoid)
        {
            type = to;
            return this;
        }

        if (!to.equals(e1.type) && mod == cast(ubyte)~0)
        {
            if (Expression e = op_overload(sc))
                return e.implicitCastTo(sc, to);
        }

        Type t1b = e1.type.toBasetype();
        Type tob = to.toBasetype();

        if (tob.ty == Tstruct && !tob.equals(t1b))
        {
            /* Look to replace:
             *  cast(S)t
             * with:
             *  S(t)
             */

            // Rewrite as to.call(e1)
            Expression e = new TypeExp(loc, to);
            e = new CallExp(loc, e, e1);
            e = e.trySemantic(sc);
            if (e)
                return e;
        }

        if (!t1b.equals(tob) && (t1b.ty == Tarray || t1b.ty == Tsarray))
        {
            if (checkNonAssignmentArrayOp(e1))
                return new ErrorExp();
        }

        // Look for casting to a vector type
        if (tob.ty == Tvector && t1b.ty != Tvector)
        {
            return new VectorExp(loc, e1, to);
        }

        Expression ex = e1.castTo(sc, to);
        if (ex.op == TOKerror)
            return ex;

        // Check for unsafe casts
        if (sc.func && !sc.intypeof &&
            !isSafeCast(ex, t1b, tob) &&
            sc.func.setUnsafe())
        {
            error("cast from %s to %s not allowed in safe code", e1.type.toChars(), to.toChars());
            return new ErrorExp();
        }

        return ex;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class VectorExp : UnaExp
{
    TypeVector to;      // the target vector type before semantic()
    uint dim = ~0;      // number of elements in the vector

    extern (D) this(Loc loc, Expression e, Type t)
    {
        super(loc, TOKvector, __traits(classInstanceSize, VectorExp), e);
        assert(t.ty == Tvector);
        to = cast(TypeVector)t;
    }

    override Expression syntaxCopy()
    {
        return new VectorExp(loc, e1.syntaxCopy(), to.syntaxCopy());
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("VectorExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        e1 = e1.semantic(sc);
        type = to.semantic(loc, sc);
        if (e1.op == TOKerror || type.ty == Terror)
            return e1;

        Type tb = type.toBasetype();
        assert(tb.ty == Tvector);
        TypeVector tv = cast(TypeVector)tb;
        Type te = tv.elementType();
        dim = cast(int)(tv.size(loc) / te.size(loc));

        bool checkElem(Expression elem)
        {
            if (elem.isConst() == 1)
                return false;

             error("constant expression expected, not %s", elem.toChars());
             return true;
        }

        e1 = e1.optimize(WANTvalue);
        bool result;
        if (e1.op == TOKarrayliteral)
        {
            foreach (i; 0 .. dim)
            {
                // Do not stop on first error - check all AST nodes even if error found
                result |= checkElem((cast(ArrayLiteralExp)e1).getElement(i));
            }
        }
        else
            result = checkElem(e1);

        return result ? new ErrorExp() : this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class SliceExp : UnaExp
{
    Expression upr;             // null if implicit 0
    Expression lwr;             // null if implicit [length - 1]
    VarDeclaration lengthVar;
    bool upperIsInBounds;       // true if upr <= e1.length
    bool lowerIsLessThanUpper;  // true if lwr <= upr

    /************************************************************/
    extern (D) this(Loc loc, Expression e1, IntervalExp ie)
    {
        super(loc, TOKslice, __traits(classInstanceSize, SliceExp), e1);
        this.upr = ie ? ie.upr : null;
        this.lwr = ie ? ie.lwr : null;
    }

    extern (D) this(Loc loc, Expression e1, Expression lwr, Expression upr)
    {
        super(loc, TOKslice, __traits(classInstanceSize, SliceExp), e1);
        this.upr = upr;
        this.lwr = lwr;
    }

    override Expression syntaxCopy()
    {
        auto se = new SliceExp(loc, e1.syntaxCopy(), lwr ? lwr.syntaxCopy() : null, upr ? upr.syntaxCopy() : null);
        se.lengthVar = this.lengthVar; // bug7871
        return se;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("SliceExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        // operator overloading should be handled in ArrayExp already.
        if (Expression ex = unaSemantic(sc))
            return ex;
        e1 = resolveProperties(sc, e1);
        if (e1.op == TOKtype && e1.type.ty != Ttuple)
        {
            if (lwr || upr)
            {
                error("cannot slice type '%s'", e1.toChars());
                return new ErrorExp();
            }
            Expression e = new TypeExp(loc, e1.type.arrayOf());
            return e.semantic(sc);
        }
        if (!lwr && !upr)
        {
            if (e1.op == TOKarrayliteral)
            {
                // Convert [a,b,c][] to [a,b,c]
                Type t1b = e1.type.toBasetype();
                Expression e = e1;
                if (t1b.ty == Tsarray)
                {
                    e = e.copy();
                    e.type = t1b.nextOf().arrayOf();
                }
                return e;
            }
            if (e1.op == TOKslice)
            {
                // Convert e[][] to e[]
                SliceExp se = cast(SliceExp)e1;
                if (!se.lwr && !se.upr)
                    return se;
            }
            if (isArrayOpOperand(e1))
            {
                // Convert (a[]+b[])[] to a[]+b[]
                return e1;
            }
        }
        if (e1.op == TOKerror)
            return e1;
        if (e1.type.ty == Terror)
            return new ErrorExp();

        Type t1b = e1.type.toBasetype();
        if (t1b.ty == Tpointer)
        {
            if ((cast(TypePointer)t1b).next.ty == Tfunction)
            {
                error("cannot slice function pointer %s", e1.toChars());
                return new ErrorExp();
            }
            if (!lwr || !upr)
            {
                error("need upper and lower bound to slice pointer");
                return new ErrorExp();
            }
            if (sc.func && !sc.intypeof && sc.func.setUnsafe())
            {
                error("pointer slicing not allowed in safe functions");
                return new ErrorExp();
            }
        }
        else if (t1b.ty == Tarray)
        {
        }
        else if (t1b.ty == Tsarray)
        {
        }
        else if (t1b.ty == Ttuple)
        {
            if (!lwr && !upr)
                return e1;
            if (!lwr || !upr)
            {
                error("need upper and lower bound to slice tuple");
                return new ErrorExp();
            }
        }
        else
        {
            error("%s cannot be sliced with []", t1b.ty == Tvoid ? e1.toChars() : t1b.toChars());
            return new ErrorExp();
        }

        /* Run semantic on lwr and upr.
         */
        Scope* scx = sc;
        if (t1b.ty == Tsarray || t1b.ty == Tarray || t1b.ty == Ttuple)
        {
            // Create scope for 'length' variable
            ScopeDsymbol sym = new ArrayScopeSymbol(sc, this);
            sym.loc = loc;
            sym.parent = sc.scopesym;
            sc = sc.push(sym);
        }
        if (lwr)
        {
            if (t1b.ty == Ttuple)
                sc = sc.startCTFE();
            lwr = lwr.semantic(sc);
            lwr = resolveProperties(sc, lwr);
            if (t1b.ty == Ttuple)
                sc = sc.endCTFE();
            lwr = lwr.implicitCastTo(sc, Type.tsize_t);
        }
        if (upr)
        {
            if (t1b.ty == Ttuple)
                sc = sc.startCTFE();
            upr = upr.semantic(sc);
            upr = resolveProperties(sc, upr);
            if (t1b.ty == Ttuple)
                sc = sc.endCTFE();
            upr = upr.implicitCastTo(sc, Type.tsize_t);
        }
        if (sc != scx)
            sc = sc.pop();
        if (lwr && lwr.type == Type.terror || upr && upr.type == Type.terror)
        {
            return new ErrorExp();
        }

        if (t1b.ty == Ttuple)
        {
            lwr = lwr.ctfeInterpret();
            upr = upr.ctfeInterpret();
            uinteger_t i1 = lwr.toUInteger();
            uinteger_t i2 = upr.toUInteger();

            TupleExp te;
            TypeTuple tup;
            size_t length;
            if (e1.op == TOKtuple) // slicing an expression tuple
            {
                te = cast(TupleExp)e1;
                tup = null;
                length = te.exps.dim;
            }
            else if (e1.op == TOKtype) // slicing a type tuple
            {
                te = null;
                tup = cast(TypeTuple)t1b;
                length = Parameter.dim(tup.arguments);
            }
            else
                assert(0);

            if (i2 < i1 || length < i2)
            {
                error("string slice [%llu .. %llu] is out of bounds", i1, i2);
                return new ErrorExp();
            }

            size_t j1 = cast(size_t)i1;
            size_t j2 = cast(size_t)i2;
            Expression e;
            if (e1.op == TOKtuple)
            {
                auto exps = new Expressions();
                exps.setDim(j2 - j1);
                for (size_t i = 0; i < j2 - j1; i++)
                {
                    (*exps)[i] = (*te.exps)[j1 + i];
                }
                e = new TupleExp(loc, te.e0, exps);
            }
            else
            {
                auto args = new Parameters();
                args.reserve(j2 - j1);
                for (size_t i = j1; i < j2; i++)
                {
                    Parameter arg = Parameter.getNth(tup.arguments, i);
                    args.push(arg);
                }
                e = new TypeExp(e1.loc, new TypeTuple(args));
            }
            e = e.semantic(sc);
            return e;
        }

        type = t1b.nextOf().arrayOf();
        // Allow typedef[] -> typedef[]
        if (type.equals(t1b))
            type = e1.type;

        if (lwr && upr)
        {
            lwr = lwr.optimize(WANTvalue);
            upr = upr.optimize(WANTvalue);

            IntRange lwrRange = getIntRange(lwr);
            IntRange uprRange = getIntRange(upr);

            if (t1b.ty == Tsarray || t1b.ty == Tarray)
            {
                Expression el = new ArrayLengthExp(loc, e1);
                el = el.semantic(sc);
                el = el.optimize(WANTvalue);
                if (el.op == TOKint64)
                {
                    dinteger_t length = el.toInteger();
                    auto bounds = IntRange(SignExtendedNumber(0), SignExtendedNumber(length));
                    this.upperIsInBounds = bounds.contains(uprRange);
                }
            }
            else if (t1b.ty == Tpointer)
            {
                this.upperIsInBounds = true;
            }
            else
                assert(0);

            this.lowerIsLessThanUpper = (lwrRange.imax <= uprRange.imin);

            //printf("upperIsInBounds = %d lowerIsLessThanUpper = %d\n", upperIsInBounds, lowerIsLessThanUpper);
        }

        return this;
    }

    override int checkModifiable(Scope* sc, int flag)
    {
        //printf("SliceExp::checkModifiable %s\n", toChars());
        if (e1.type.ty == Tsarray || (e1.op == TOKindex && e1.type.ty != Tarray) || e1.op == TOKslice)
        {
            return e1.checkModifiable(sc, flag);
        }
        return 1;
    }

    override bool isLvalue()
    {
        /* slice expression is rvalue in default, but
         * conversion to reference of static array is only allowed.
         */
        return (type && type.toBasetype().ty == Tsarray);
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        //printf("SliceExp::toLvalue(%s) type = %s\n", toChars(), type ? type->toChars() : NULL);
        return (type && type.toBasetype().ty == Tsarray) ? this : Expression.toLvalue(sc, e);
    }

    override Expression modifiableLvalue(Scope* sc, Expression e)
    {
        error("slice expression %s is not a modifiable lvalue", toChars());
        return this;
    }

    override bool isBool(bool result)
    {
        return e1.isBool(result);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class ArrayLengthExp : UnaExp
{
    extern (D) this(Loc loc, Expression e1)
    {
        super(loc, TOKarraylength, __traits(classInstanceSize, ArrayLengthExp), e1);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("ArrayLengthExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        if (Expression ex = unaSemantic(sc))
            return ex;
        e1 = resolveProperties(sc, e1);

        type = Type.tsize_t;
        return this;
    }

    /*********************
     * Rewrite:
     *    array.length op= e2
     * as:
     *    array.length = array.length op e2
     * or:
     *    auto tmp = &array;
     *    (*tmp).length = (*tmp).length op e2
     */
    static Expression rewriteOpAssign(BinExp exp)
    {
        Expression e;

        assert(exp.e1.op == TOKarraylength);
        ArrayLengthExp ale = cast(ArrayLengthExp)exp.e1;
        if (ale.e1.op == TOKvar)
        {
            e = opAssignToOp(exp.loc, exp.op, ale, exp.e2);
            e = new AssignExp(exp.loc, ale.syntaxCopy(), e);
        }
        else
        {
            /*    auto tmp = &array;
             *    (*tmp).length = (*tmp).length op e2
             */
            auto tmp = copyToTemp(0, "__arraylength", new AddrExp(ale.loc, ale.e1));

            Expression e1 = new ArrayLengthExp(ale.loc, new PtrExp(ale.loc, new VarExp(ale.loc, tmp)));
            Expression elvalue = e1.syntaxCopy();
            e = opAssignToOp(exp.loc, exp.op, e1, exp.e2);
            e = new AssignExp(exp.loc, elvalue, e);
            e = new CommaExp(exp.loc, new DeclarationExp(ale.loc, tmp), e);
        }
        return e;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * e1 [ a0, a1, a2, a3 ,... ]
 */
extern (C++) final class ArrayExp : UnaExp
{
    Expressions* arguments;     // Array of Expression's
    size_t currentDimension;    // for opDollar
    VarDeclaration lengthVar;

    extern (D) this(Loc loc, Expression e1, Expression index = null)
    {
        super(loc, TOKarray, __traits(classInstanceSize, ArrayExp), e1);
        arguments = new Expressions();
        if (index)
            arguments.push(index);
    }

    extern (D) this(Loc loc, Expression e1, Expressions* args)
    {
        super(loc, TOKarray, __traits(classInstanceSize, ArrayExp), e1);
        arguments = args;
    }

    override Expression syntaxCopy()
    {
        auto ae = new ArrayExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments));
        ae.lengthVar = this.lengthVar; // bug7871
        return ae;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("ArrayExp::semantic('%s')\n", toChars());
        }
        assert(!type);
        Expression e = op_overload(sc);
        if (e)
            return e;

        if (isAggregate(e1.type))
            error("no [] operator overload for type %s", e1.type.toChars());
        else
            error("only one index allowed to index %s", e1.type.toChars());
        return new ErrorExp();
    }

    override bool isLvalue()
    {
        if (type && type.toBasetype().ty == Tvoid)
            return false;
        return true;
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        if (type && type.toBasetype().ty == Tvoid)
            error("voids have no value");
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class DotExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKdot, __traits(classInstanceSize, DotExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("DotExp::semantic('%s')\n", toChars());
            if (type)
                printf("\ttype = %s\n", type.toChars());
        }
        e1 = e1.semantic(sc);
        e2 = e2.semantic(sc);

        if (e1.op == TOKtype)
            return e2;
        if (e2.op == TOKtype)
            return e2;

        if (e2.op == TOKtemplate)
        {
            auto td = (cast(TemplateExp)e2).td;
            Expression e = new DotTemplateExp(loc, e1, td);
            return e.semantic(sc);
        }
        if (!type)
            type = e2.type;
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class CommaExp : BinExp
{
    /// This is needed because AssignExp rewrites CommaExp, hence it needs
    /// to trigger the deprecation.
    const bool isGenerated;

    /// Temporary variable to enable / disable deprecation of comma expression
    /// depending on the context.
    /// Since most constructor calls are rewritting, the only place where
    /// false will be passed will be from the parser.
    bool allowCommaExp;


    extern (D) this(Loc loc, Expression e1, Expression e2, bool generated = true)
    {
        super(loc, TOKcomma, __traits(classInstanceSize, CommaExp), e1, e2);
        allowCommaExp = isGenerated = generated;
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        // Allow `((a,b),(x,y))`
        if (allowCommaExp)
        {
            CommaExp.allow(e1);
            CommaExp.allow(e2);
        }

        if (Expression ex = binSemanticProp(sc))
            return ex;
        e1 = e1.addDtorHook(sc);

        if (checkNonAssignmentArrayOp(e1))
            return new ErrorExp();

        type = e2.type;
        if (type !is Type.tvoid && !allowCommaExp && !isGenerated)
            deprecation("Using the result of a comma expression is deprecated");
        return this;
    }

    override int checkModifiable(Scope* sc, int flag)
    {
        return e2.checkModifiable(sc, flag);
    }

    override bool isLvalue()
    {
        return e2.isLvalue();
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        e2 = e2.toLvalue(sc, null);
        return this;
    }

    override Expression modifiableLvalue(Scope* sc, Expression e)
    {
        e2 = e2.modifiableLvalue(sc, e);
        return this;
    }

    override bool isBool(bool result)
    {
        return e2.isBool(result);
    }

    override Expression toBoolean(Scope* sc)
    {
        auto ex2 = e2.toBoolean(sc);
        if (ex2.op == TOKerror)
            return ex2;
        e2 = ex2;
        return this;
    }

    override Expression addDtorHook(Scope* sc)
    {
        e2 = e2.addDtorHook(sc);
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }

    /**
     * If the argument is a CommaExp, set a flag to prevent deprecation messages
     *
     * It's impossible to know from CommaExp.semantic if the result will
     * be used, hence when there is a result (type != void), a deprecation
     * message is always emitted.
     * However, some construct can produce a result but won't use it
     * (ExpStatement and for loop increment).  Those should call this function
     * to prevent unwanted deprecations to be emitted.
     *
     * Params:
     *   exp = An expression that discards its result.
     *         If the argument is null or not a CommaExp, nothing happens.
     */
    static void allow(Expression exp)
    {
        if (exp && exp.op == TOK.TOKcomma)
            (cast(CommaExp)exp).allowCommaExp = true;
    }
}

/***********************************************************
 * Mainly just a placeholder
 */
extern (C++) final class IntervalExp : Expression
{
    Expression lwr;
    Expression upr;

    extern (D) this(Loc loc, Expression lwr, Expression upr)
    {
        super(loc, TOKinterval, __traits(classInstanceSize, IntervalExp));
        this.lwr = lwr;
        this.upr = upr;
    }

    override Expression syntaxCopy()
    {
        return new IntervalExp(loc, lwr.syntaxCopy(), upr.syntaxCopy());
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("IntervalExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        Expression le = lwr;
        le = le.semantic(sc);
        le = resolveProperties(sc, le);

        Expression ue = upr;
        ue = ue.semantic(sc);
        ue = resolveProperties(sc, ue);

        if (le.op == TOKerror)
            return le;
        if (ue.op == TOKerror)
            return ue;

        lwr = le;
        upr = ue;

        type = Type.tvoid;
        return this;
    }

    override Expression modifiableLvalue(Scope* sc, Expression e)
    {
        if (sc.func.setUnsafe())
        {
            error("cannot modify delegate pointer in @safe code %s", toChars());
            return new ErrorExp();
        }
        return Expression.modifiableLvalue(sc, e);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

extern (C++) final class DelegatePtrExp : UnaExp
{
    extern (D) this(Loc loc, Expression e1)
    {
        super(loc, TOKdelegateptr, __traits(classInstanceSize, DelegatePtrExp), e1);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("DelegatePtrExp::semantic('%s')\n", toChars());
        }
        if (!type)
        {
            unaSemantic(sc);
            e1 = resolveProperties(sc, e1);

            if (e1.op == TOKerror)
                return e1;
            type = Type.tvoidptr;
        }
        return this;
    }

    override bool isLvalue()
    {
        return e1.isLvalue();
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        e1 = e1.toLvalue(sc, e);
        return this;
    }

    override Expression modifiableLvalue(Scope* sc, Expression e)
    {
        if (sc.func.setUnsafe())
        {
            error("cannot modify delegate pointer in @safe code %s", toChars());
            return new ErrorExp();
        }
        return Expression.modifiableLvalue(sc, e);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class DelegateFuncptrExp : UnaExp
{
    extern (D) this(Loc loc, Expression e1)
    {
        super(loc, TOKdelegatefuncptr, __traits(classInstanceSize, DelegateFuncptrExp), e1);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("DelegateFuncptrExp::semantic('%s')\n", toChars());
        }
        if (!type)
        {
            unaSemantic(sc);
            e1 = resolveProperties(sc, e1);
            if (e1.op == TOKerror)
                return e1;
            type = e1.type.nextOf().pointerTo();
        }
        return this;
    }

    override bool isLvalue()
    {
        return e1.isLvalue();
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        e1 = e1.toLvalue(sc, e);
        return this;
    }

    override Expression modifiableLvalue(Scope* sc, Expression e)
    {
        if (sc.func.setUnsafe())
        {
            error("cannot modify delegate function pointer in @safe code %s", toChars());
            return new ErrorExp();
        }
        return Expression.modifiableLvalue(sc, e);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * e1 [ e2 ]
 */
extern (C++) final class IndexExp : BinExp
{
    VarDeclaration lengthVar;
    bool modifiable = false;    // assume it is an rvalue
    bool indexIsInBounds;       // true if 0 <= e2 && e2 <= e1.length - 1

    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKindex, __traits(classInstanceSize, IndexExp), e1, e2);
        //printf("IndexExp::IndexExp('%s')\n", toChars());
    }

    override Expression syntaxCopy()
    {
        auto ie = new IndexExp(loc, e1.syntaxCopy(), e2.syntaxCopy());
        ie.lengthVar = this.lengthVar; // bug7871
        return ie;
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("IndexExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        // operator overloading should be handled in ArrayExp already.
        if (!e1.type)
            e1 = e1.semantic(sc);
        assert(e1.type); // semantic() should already be run on it
        if (e1.op == TOKtype && e1.type.ty != Ttuple)
        {
            e2 = e2.semantic(sc);
            e2 = resolveProperties(sc, e2);
            Type nt;
            if (e2.op == TOKtype)
                nt = new TypeAArray(e1.type, e2.type);
            else
                nt = new TypeSArray(e1.type, e2);
            Expression e = new TypeExp(loc, nt);
            return e.semantic(sc);
        }
        if (e1.op == TOKerror)
            return e1;
        if (e1.type.ty == Terror)
            return new ErrorExp();

        // Note that unlike C we do not implement the int[ptr]

        Type t1b = e1.type.toBasetype();

        /* Run semantic on e2
         */
        Scope* scx = sc;
        if (t1b.ty == Tsarray || t1b.ty == Tarray || t1b.ty == Ttuple)
        {
            // Create scope for 'length' variable
            ScopeDsymbol sym = new ArrayScopeSymbol(sc, this);
            sym.loc = loc;
            sym.parent = sc.scopesym;
            sc = sc.push(sym);
        }
        if (t1b.ty == Ttuple)
            sc = sc.startCTFE();
        e2 = e2.semantic(sc);
        e2 = resolveProperties(sc, e2);
        if (t1b.ty == Ttuple)
            sc = sc.endCTFE();
        if (e2.op == TOKtuple)
        {
            TupleExp te = cast(TupleExp)e2;
            if (te.exps && te.exps.dim == 1)
                e2 = Expression.combine(te.e0, (*te.exps)[0]); // bug 4444 fix
        }
        if (sc != scx)
            sc = sc.pop();
        if (e2.type == Type.terror)
            return new ErrorExp();

        if (checkNonAssignmentArrayOp(e1))
            return new ErrorExp();

        switch (t1b.ty)
        {
        case Tpointer:
            if ((cast(TypePointer)t1b).next.ty == Tfunction)
            {
                error("cannot index function pointer %s", e1.toChars());
                return new ErrorExp();
            }
            e2 = e2.implicitCastTo(sc, Type.tsize_t);
            if (e2.type == Type.terror)
                return new ErrorExp();
            e2 = e2.optimize(WANTvalue);
            if (e2.op == TOKint64 && e2.toInteger() == 0)
            {
            }
            else if (sc.func && sc.func.setUnsafe())
            {
                error("safe function '%s' cannot index pointer '%s'", sc.func.toPrettyChars(), e1.toChars());
                return new ErrorExp();
            }
            type = (cast(TypeNext)t1b).next;
            break;

        case Tarray:
            e2 = e2.implicitCastTo(sc, Type.tsize_t);
            if (e2.type == Type.terror)
                return new ErrorExp();
            type = (cast(TypeNext)t1b).next;
            break;

        case Tsarray:
            {
                e2 = e2.implicitCastTo(sc, Type.tsize_t);
                if (e2.type == Type.terror)
                    return new ErrorExp();
                type = t1b.nextOf();
                break;
            }
        case Taarray:
            {
                TypeAArray taa = cast(TypeAArray)t1b;
                /* We can skip the implicit conversion if they differ only by
                 * constness (Bugzilla 2684, see also bug 2954b)
                 */
                if (!arrayTypeCompatibleWithoutCasting(e2.loc, e2.type, taa.index))
                {
                    e2 = e2.implicitCastTo(sc, taa.index); // type checking
                    if (e2.type == Type.terror)
                        return new ErrorExp();
                }

                semanticTypeInfo(sc, taa);

                type = taa.next;
                break;
            }
        case Ttuple:
            {
                e2 = e2.implicitCastTo(sc, Type.tsize_t);
                if (e2.type == Type.terror)
                    return new ErrorExp();
                e2 = e2.ctfeInterpret();
                uinteger_t index = e2.toUInteger();

                TupleExp te;
                TypeTuple tup;
                size_t length;
                if (e1.op == TOKtuple)
                {
                    te = cast(TupleExp)e1;
                    tup = null;
                    length = te.exps.dim;
                }
                else if (e1.op == TOKtype)
                {
                    te = null;
                    tup = cast(TypeTuple)t1b;
                    length = Parameter.dim(tup.arguments);
                }
                else
                    assert(0);

                if (length <= index)
                {
                    error("array index [%llu] is outside array bounds [0 .. %llu]", index, cast(ulong)length);
                    return new ErrorExp();
                }
                Expression e;
                if (e1.op == TOKtuple)
                {
                    e = (*te.exps)[cast(size_t)index];
                    e = combine(te.e0, e);
                }
                else
                    e = new TypeExp(e1.loc, Parameter.getNth(tup.arguments, cast(size_t)index).type);
                return e;
            }
        default:
            error("%s must be an array or pointer type, not %s", e1.toChars(), e1.type.toChars());
            return new ErrorExp();
        }

        if (t1b.ty == Tsarray || t1b.ty == Tarray)
        {
            Expression el = new ArrayLengthExp(loc, e1);
            el = el.semantic(sc);
            el = el.optimize(WANTvalue);
            if (el.op == TOKint64)
            {
                e2 = e2.optimize(WANTvalue);
                dinteger_t length = el.toInteger();
                if (length)
                {
                    auto bounds = IntRange(SignExtendedNumber(0), SignExtendedNumber(length - 1));
                    indexIsInBounds = bounds.contains(getIntRange(e2));
                }
            }
        }

        return this;
    }

    override int checkModifiable(Scope* sc, int flag)
    {
        if (e1.type.ty == Tsarray || e1.type.ty == Taarray || (e1.op == TOKindex && e1.type.ty != Tarray) || e1.op == TOKslice)
        {
            return e1.checkModifiable(sc, flag);
        }
        return 1;
    }

    override bool isLvalue()
    {
        return true;
    }

    override Expression toLvalue(Scope* sc, Expression e)
    {
        return this;
    }

    override Expression modifiableLvalue(Scope* sc, Expression e)
    {
        //printf("IndexExp::modifiableLvalue(%s)\n", toChars());
        Expression ex = markSettingAAElem();
        if (ex.op == TOKerror)
            return ex;

        return Expression.modifiableLvalue(sc, e);
    }

    Expression markSettingAAElem()
    {
        if (e1.type.toBasetype().ty == Taarray)
        {
            Type t2b = e2.type.toBasetype();
            if (t2b.ty == Tarray && t2b.nextOf().isMutable())
            {
                error("associative arrays can only be assigned values with immutable keys, not %s", e2.type.toChars());
                return new ErrorExp();
            }
            modifiable = true;

            if (e1.op == TOKindex)
            {
                Expression ex = (cast(IndexExp)e1).markSettingAAElem();
                if (ex.op == TOKerror)
                    return ex;
                assert(ex == e1);
            }
        }
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * For both i++ and i--
 */
extern (C++) final class PostExp : BinExp
{
    extern (D) this(TOK op, Loc loc, Expression e)
    {
        super(loc, op, __traits(classInstanceSize, PostExp), e, new IntegerExp(loc, 1, Type.tint32));
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("PostExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        if (Expression ex = binSemantic(sc))
            return ex;
        Expression e1x = resolveProperties(sc, e1);
        if (e1x.op == TOKerror)
            return e1x;
        e1 = e1x;

        Expression e = op_overload(sc);
        if (e)
            return e;

        if (e1.checkReadModifyWrite(op))
            return new ErrorExp();
        if (e1.op == TOKslice)
        {
            const(char)* s = op == TOKplusplus ? "increment" : "decrement";
            error("cannot post-%s array slice '%s', use pre-%s instead", s, e1.toChars(), s);
            return new ErrorExp();
        }

        e1 = e1.optimize(WANTvalue);

        Type t1 = e1.type.toBasetype();
        if (t1.ty == Tclass || t1.ty == Tstruct || e1.op == TOKarraylength)
        {
            /* Check for operator overloading,
             * but rewrite in terms of ++e instead of e++
             */

            /* If e1 is not trivial, take a reference to it
             */
            Expression de = null;
            if (e1.op != TOKvar && e1.op != TOKarraylength)
            {
                // ref v = e1;
                auto v = copyToTemp(STCref, "__postref", e1);
                de = new DeclarationExp(loc, v);
                e1 = new VarExp(e1.loc, v);
            }

            /* Rewrite as:
             * auto tmp = e1; ++e1; tmp
             */
            auto tmp = copyToTemp(0, "__pitmp", e1);
            Expression ea = new DeclarationExp(loc, tmp);

            Expression eb = e1.syntaxCopy();
            eb = new PreExp(op == TOKplusplus ? TOKpreplusplus : TOKpreminusminus, loc, eb);

            Expression ec = new VarExp(loc, tmp);

            // Combine de,ea,eb,ec
            if (de)
                ea = new CommaExp(loc, de, ea);
            e = new CommaExp(loc, ea, eb);
            e = new CommaExp(loc, e, ec);
            e = e.semantic(sc);
            return e;
        }

        e1 = e1.modifiableLvalue(sc, e1);

        e = this;
        if (e1.checkScalar())
            return new ErrorExp();
        if (e1.checkNoBool())
            return new ErrorExp();

        if (e1.type.ty == Tpointer)
            e = scaleFactor(this, sc);
        else
            e2 = e2.castTo(sc, e1.type);
        e.type = e1.type;
        return e;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * For both ++i and --i
 */
extern (C++) final class PreExp : UnaExp
{
    extern (D) this(TOK op, Loc loc, Expression e)
    {
        super(loc, op, __traits(classInstanceSize, PreExp), e);
    }

    override Expression semantic(Scope* sc)
    {
        Expression e = op_overload(sc);
        // printf("PreExp::semantic('%s')\n", toChars());
        if (e)
            return e;

        // Rewrite as e1+=1 or e1-=1
        if (op == TOKpreplusplus)
            e = new AddAssignExp(loc, e1, new IntegerExp(loc, 1, Type.tint32));
        else
            e = new MinAssignExp(loc, e1, new IntegerExp(loc, 1, Type.tint32));
        return e.semantic(sc);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

enum MemorySet
{
    blockAssign     = 1,    // setting the contents of an array
    referenceInit   = 2,    // setting the reference of STCref variable
}

/***********************************************************
 */
extern (C++) class AssignExp : BinExp
{
    int memset;         // combination of MemorySet flags

    /************************************************************/
    /* op can be TOKassign, TOKconstruct, or TOKblit */
    final extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKassign, __traits(classInstanceSize, AssignExp), e1, e2);
    }

    override final Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("AssignExp::semantic('%s')\n", toChars());
        }
        //printf("e1->op = %d, '%s'\n", e1->op, Token::toChars(e1->op));
        //printf("e2->op = %d, '%s'\n", e2->op, Token::toChars(e2->op));
        if (type)
            return this;

        Expression e1old = e1;

        if (e2.op == TOKcomma)
        {
            /* Rewrite to get rid of the comma from rvalue
             */
            if (!(cast(CommaExp)e2).isGenerated)
                deprecation("Using the result of a comma expression is deprecated");
            Expression e0;
            e2 = Expression.extractLast(e2, &e0);
            Expression e = Expression.combine(e0, this);
            return e.semantic(sc);
        }

        /* Look for operator overloading of a[arguments] = e2.
         * Do it before e1->semantic() otherwise the ArrayExp will have been
         * converted to unary operator overloading already.
         */
        if (e1.op == TOKarray)
        {
            Expression result;

            ArrayExp ae = cast(ArrayExp)e1;
            ae.e1 = ae.e1.semantic(sc);
            ae.e1 = resolveProperties(sc, ae.e1);
            Expression ae1old = ae.e1;

            const(bool) maybeSlice =
                (ae.arguments.dim == 0 ||
                 ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOKinterval);

            IntervalExp ie = null;
            if (maybeSlice && ae.arguments.dim)
            {
                assert((*ae.arguments)[0].op == TOKinterval);
                ie = cast(IntervalExp)(*ae.arguments)[0];
            }
            while (true)
            {
                if (ae.e1.op == TOKerror)
                    return ae.e1;
                Expression e0 = null;
                Expression ae1save = ae.e1;
                ae.lengthVar = null;

                Type t1b = ae.e1.type.toBasetype();
                AggregateDeclaration ad = isAggregate(t1b);
                if (!ad)
                    break;
                if (search_function(ad, Id.indexass))
                {
                    // Deal with $
                    result = resolveOpDollar(sc, ae, &e0);
                    if (!result) // a[i..j] = e2 might be: a.opSliceAssign(e2, i, j)
                        goto Lfallback;
                    if (result.op == TOKerror)
                        return result;

                    result = e2.semantic(sc);
                    if (result.op == TOKerror)
                        return result;
                    e2 = result;

                    /* Rewrite (a[arguments] = e2) as:
                     *      a.opIndexAssign(e2, arguments)
                     */
                    Expressions* a = ae.arguments.copy();
                    a.insert(0, e2);
                    result = new DotIdExp(loc, ae.e1, Id.indexass);
                    result = new CallExp(loc, result, a);
                    if (maybeSlice) // a[] = e2 might be: a.opSliceAssign(e2)
                        result = result.trySemantic(sc);
                    else
                        result = result.semantic(sc);
                    if (result)
                    {
                        result = Expression.combine(e0, result);
                        return result;
                    }
                }

            Lfallback:
                if (maybeSlice && search_function(ad, Id.sliceass))
                {
                    // Deal with $
                    result = resolveOpDollar(sc, ae, ie, &e0);
                    if (result.op == TOKerror)
                        return result;

                    result = e2.semantic(sc);
                    if (result.op == TOKerror)
                        return result;
                    e2 = result;

                    /* Rewrite (a[i..j] = e2) as:
                     *      a.opSliceAssign(e2, i, j)
                     */
                    auto a = new Expressions();
                    a.push(e2);
                    if (ie)
                    {
                        a.push(ie.lwr);
                        a.push(ie.upr);
                    }
                    result = new DotIdExp(loc, ae.e1, Id.sliceass);
                    result = new CallExp(loc, result, a);
                    result = result.semantic(sc);
                    result = Expression.combine(e0, result);
                    return result;
                }

                // No operator overloading member function found yet, but
                // there might be an alias this to try.
                if (ad.aliasthis && t1b != ae.att1)
                {
                    if (!ae.att1 && t1b.checkAliasThisRec())
                        ae.att1 = t1b;

                    /* Rewrite (a[arguments] op e2) as:
                     *      a.aliasthis[arguments] op e2
                     */
                    ae.e1 = resolveAliasThis(sc, ae1save, true);
                    if (ae.e1)
                        continue;
                }
                break;
            }
            ae.e1 = ae1old; // recovery
            ae.lengthVar = null;
        }

        /* Run this->e1 semantic.
         */
        {
            Expression e1x = e1;

            /* With UFCS, e.f = value
             * Could mean:
             *      .f(e, value)
             * or:
             *      .f(e) = value
             */
            if (e1x.op == TOKdotti)
            {
                DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)e1x;
                Expression e = dti.semanticY(sc, 1);
                if (!e)
                    return resolveUFCSProperties(sc, e1x, e2);
                e1x = e;
            }
            else if (e1x.op == TOKdotid)
            {
                DotIdExp die = cast(DotIdExp)e1x;
                Expression e = die.semanticY(sc, 1);
                if (e && isDotOpDispatch(e))
                {
                    uint errors = global.startGagging();
                    e = resolvePropertiesX(sc, e, e2);
                    if (global.endGagging(errors))
                        e = null; /* fall down to UFCS */
                    else
                        return e;
                }
                if (!e)
                    return resolveUFCSProperties(sc, e1x, e2);
                e1x = e;
            }
            else
                e1x = e1x.semantic(sc);

            /* We have f = value.
             * Could mean:
             *      f(value)
             * or:
             *      f() = value
             */
            if (Expression e = resolvePropertiesX(sc, e1x, e2))
                return e;
            if (e1x.checkRightThis(sc))
                return new ErrorExp();
            e1 = e1x;
            assert(e1.type);
        }
        Type t1 = e1.type.toBasetype();

        /* Run this->e2 semantic.
         * Different from other binary expressions, the analysis of e2
         * depends on the result of e1 in assignments.
         */
        {
            Expression e2x = inferType(e2, t1.baseElemOf());
            e2x = e2x.semantic(sc);
            e2x = resolveProperties(sc, e2x);
            if (e2x.op == TOKerror)
                return e2x;
            if (e2x.checkValue())
                return new ErrorExp();
            e2 = e2x;
        }

        /* Rewrite tuple assignment as a tuple of assignments.
         */
        {
            Expression e2x = e2;

        Ltupleassign:
            if (e1.op == TOKtuple && e2x.op == TOKtuple)
            {
                TupleExp tup1 = cast(TupleExp)e1;
                TupleExp tup2 = cast(TupleExp)e2x;
                size_t dim = tup1.exps.dim;
                Expression e = null;
                if (dim != tup2.exps.dim)
                {
                    error("mismatched tuple lengths, %d and %d", cast(int)dim, cast(int)tup2.exps.dim);
                    return new ErrorExp();
                }
                if (dim == 0)
                {
                    e = new IntegerExp(loc, 0, Type.tint32);
                    e = new CastExp(loc, e, Type.tvoid); // avoid "has no effect" error
                    e = combine(combine(tup1.e0, tup2.e0), e);
                }
                else
                {
                    auto exps = new Expressions();
                    exps.setDim(dim);
                    for (size_t i = 0; i < dim; i++)
                    {
                        Expression ex1 = (*tup1.exps)[i];
                        Expression ex2 = (*tup2.exps)[i];
                        (*exps)[i] = new AssignExp(loc, ex1, ex2);
                    }
                    e = new TupleExp(loc, combine(tup1.e0, tup2.e0), exps);
                }
                return e.semantic(sc);
            }

            /* Look for form: e1 = e2->aliasthis.
             */
            if (e1.op == TOKtuple)
            {
                TupleDeclaration td = isAliasThisTuple(e2x);
                if (!td)
                    goto Lnomatch;

                assert(e1.type.ty == Ttuple);
                TypeTuple tt = cast(TypeTuple)e1.type;

                Expression e0;
                Expression ev = extractSideEffect(sc, "__tup", e0, e2x);

                auto iexps = new Expressions();
                iexps.push(ev);
                for (size_t u = 0; u < iexps.dim; u++)
                {
                Lexpand:
                    Expression e = (*iexps)[u];

                    Parameter arg = Parameter.getNth(tt.arguments, u);
                    //printf("[%d] iexps->dim = %d, ", u, iexps->dim);
                    //printf("e = (%s %s, %s), ", Token::tochars[e->op], e->toChars(), e->type->toChars());
                    //printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars());

                    if (!arg || !e.type.implicitConvTo(arg.type))
                    {
                        // expand initializer to tuple
                        if (expandAliasThisTuples(iexps, u) != -1)
                        {
                            if (iexps.dim <= u)
                                break;
                            goto Lexpand;
                        }
                        goto Lnomatch;
                    }
                }
                e2x = new TupleExp(e2x.loc, e0, iexps);
                e2x = e2x.semantic(sc);
                if (e2x.op == TOKerror)
                    return e2x;
                // Do not need to overwrite this->e2
                goto Ltupleassign;
            }
        Lnomatch:
        }

        /* Inside constructor, if this is the first assignment of object field,
         * rewrite this to initializing the field.
         */
        if (op == TOKassign && e1.checkModifiable(sc) == 2)
        {
            //printf("[%s] change to init - %s\n", loc.toChars(), toChars());
            op = TOKconstruct;

            // Bugzilla 13515: set Index::modifiable flag for complex AA element initialization
            if (e1.op == TOKindex)
            {
                Expression e1x = (cast(IndexExp)e1).markSettingAAElem();
                if (e1x.op == TOKerror)
                    return e1x;
            }
        }
        else if (op == TOKconstruct && e1.op == TOKvar &&
                 (cast(VarExp)e1).var.storage_class & (STCout | STCref))
        {
            memset |= MemorySet.referenceInit;
        }

        /* If it is an assignment from a 'foreign' type,
         * check for operator overloading.
         */
        if (memset & MemorySet.referenceInit)
        {
            // If this is an initialization of a reference,
            // do nothing
        }
        else if (t1.ty == Tstruct)
        {
            auto e1x = e1;
            auto e2x = e2;
            auto sd = (cast(TypeStruct)t1).sym;

            if (op == TOKconstruct)
            {
                Type t2 = e2x.type.toBasetype();
                if (t2.ty == Tstruct && sd == (cast(TypeStruct)t2).sym)
                {
                    sd.size(loc);
                    if (sd.sizeok != SIZEOKdone)
                        return new ErrorExp();
                    if (!sd.ctor)
                        sd.ctor = sd.searchCtor();

                    // Bugzilla 15661: Look for the form from last of comma chain.
                    auto e2y = e2x;
                    while (e2y.op == TOKcomma)
                        e2y = (cast(CommaExp)e2y).e2;

                    CallExp ce = (e2y.op == TOKcall) ? cast(CallExp)e2y : null;
                    DotVarExp dve = (ce && ce.e1.op == TOKdotvar)
                        ? cast(DotVarExp)ce.e1 : null;
                    if (sd.ctor && ce && dve && dve.var.isCtorDeclaration() &&
                        e2y.type.implicitConvTo(t1))
                    {
                        /* Look for form of constructor call which is:
                         *    __ctmp.ctor(arguments...)
                         */

                        /* Before calling the constructor, initialize
                         * variable with a bit copy of the default
                         * initializer
                         */
                        Expression einit;
                        if (sd.zeroInit == 1 && !sd.isNested())
                        {
                            // Bugzilla 14606: Always use BlitExp for the special expression: (struct = 0)
                            einit = new IntegerExp(loc, 0, Type.tint32);
                        }
                        else if (sd.isNested())
                        {
                            auto sle = new StructLiteralExp(loc, sd, null, t1);
                            if (!sd.fill(loc, sle.elements, true))
                                return new ErrorExp();
                            if (checkFrameAccess(loc, sc, sd, sle.elements.dim))
                                return new ErrorExp();
                            sle.type = t1;

                            einit = sle;
                        }
                        else
                        {
                            einit = t1.defaultInit(loc);
                        }
                        auto ae = new BlitExp(loc, e1, einit);
                        ae.type = e1x.type;

                        /* Replace __ctmp being constructed with e1.
                         * We need to copy constructor call expression,
                         * because it may be used in other place.
                         */
                        auto dvx = cast(DotVarExp)dve.copy();
                        dvx.e1 = e1x;
                        auto cx = cast(CallExp)ce.copy();
                        cx.e1 = dvx;

                        Expression e0;
                        extractLast(e2x, &e0);

                        auto e = combine(ae, cx);
                        e = combine(e0, e);
                        e = e.semantic(sc);
                        return e;
                    }
                    if (sd.postblit)
                    {
                        /* We have a copy constructor for this
                         */
                        if (e2x.op == TOKquestion)
                        {
                            /* Rewrite as:
                             *  a ? e1 = b : e1 = c;
                             */
                            CondExp econd = cast(CondExp)e2x;
                            Expression ea1 = new ConstructExp(econd.e1.loc, e1x, econd.e1);
                            Expression ea2 = new ConstructExp(econd.e1.loc, e1x, econd.e2);
                            Expression e = new CondExp(loc, econd.econd, ea1, ea2);
                            return e.semantic(sc);
                        }

                        if (e2x.isLvalue())
                        {
                            if (!e2x.type.implicitConvTo(e1x.type))
                            {
                                error("conversion error from %s to %s",
                                    e2x.type.toChars(), e1x.type.toChars());
                                return new ErrorExp();
                            }

                            /* Rewrite as:
                             *  (e1 = e2).postblit();
                             *
                             * Blit assignment e1 = e2 returns a reference to the original e1,
                             * then call the postblit on it.
                             */
                            Expression e = e1x.copy();
                            e.type = e.type.mutableOf();
                            e = new BlitExp(loc, e, e2x);
                            e = new DotVarExp(loc, e, sd.postblit, false);
                            e = new CallExp(loc, e);
                            return e.semantic(sc);
                        }
                        else
                        {
                            /* The struct value returned from the function is transferred
                             * so should not call the destructor on it.
                             */
                            e2x = valueNoDtor(e2x);
                        }
                    }
                }
                else if (!e2x.implicitConvTo(t1))
                {
                    sd.size(loc);
                    if (sd.sizeok != SIZEOKdone)
                        return new ErrorExp();
                    if (!sd.ctor)
                        sd.ctor = sd.searchCtor();

                    if (sd.ctor)
                    {
                        /* Look for implicit constructor call
                         * Rewrite as:
                         *  e1 = init, e1.ctor(e2)
                         */
                        Expression einit;
                        einit = new BlitExp(loc, e1x, e1x.type.defaultInit(loc));
                        einit.type = e1x.type;

                        Expression e;
                        e = new DotIdExp(loc, e1x, Id.ctor);
                        e = new CallExp(loc, e, e2x);
                        e = new CommaExp(loc, einit, e);
                        e = e.semantic(sc);
                        return e;
                    }
                    if (search_function(sd, Id.call))
                    {
                        /* Look for static opCall
                         * (See bugzilla 2702 for more discussion)
                         * Rewrite as:
                         *  e1 = typeof(e1).opCall(arguments)
                         */
                        e2x = typeDotIdExp(e2x.loc, e1x.type, Id.call);
                        e2x = new CallExp(loc, e2x, this.e2);

                        e2x = e2x.semantic(sc);
                        e2x = resolveProperties(sc, e2x);
                        if (e2x.op == TOKerror)
                            return e2x;
                        if (e2x.checkValue())
                            return new ErrorExp();
                    }
                }
                else // Bugzilla 11355
                {
                    AggregateDeclaration ad2 = isAggregate(e2x.type);
                    if (ad2 && ad2.aliasthis && !(att2 && e2x.type == att2))
                    {
                        if (!att2 && e2.type.checkAliasThisRec())
                            att2 = e2.type;
                        /* Rewrite (e1 op e2) as:
                         *      (e1 op e2.aliasthis)
                         */
                        e2 = new DotIdExp(e2.loc, e2, ad2.aliasthis.ident);
                        return semantic(sc);
                    }
                }
            }
            else if (op == TOKassign)
            {
                if (e1x.op == TOKindex && (cast(IndexExp)e1x).e1.type.toBasetype().ty == Taarray)
                {
                    /*
                     * Rewrite:
                     *      aa[key] = e2;
                     * as:
                     *      ref __aatmp = aa;
                     *      ref __aakey = key;
                     *      ref __aaval = e2;
                     *      (__aakey in __aatmp
                     *          ? __aatmp[__aakey].opAssign(__aaval)
                     *          : ConstructExp(__aatmp[__aakey], __aaval));
                     */
                    IndexExp ie = cast(IndexExp)e1x;
                    Type t2 = e2x.type.toBasetype();

                    Expression e0 = null;
                    Expression ea = extractSideEffect(sc, "__aatmp", e0, ie.e1);
                    Expression ek = extractSideEffect(sc, "__aakey", e0, ie.e2);
                    Expression ev = extractSideEffect(sc, "__aaval", e0, e2x);

                    AssignExp ae = cast(AssignExp)copy();
                    ae.e1 = new IndexExp(loc, ea, ek);
                    ae.e1 = ae.e1.semantic(sc);
                    ae.e1 = ae.e1.optimize(WANTvalue);
                    ae.e2 = ev;
                    Expression e = ae.op_overload(sc);
                    if (e)
                    {
                        Expression ey = null;
                        if (t2.ty == Tstruct && sd == t2.toDsymbol(sc))
                        {
                            ey = ev;
                        }
                        else if (!ev.implicitConvTo(ie.type) && sd.ctor)
                        {
                            // Look for implicit constructor call
                            // Rewrite as S().ctor(e2)
                            ey = new StructLiteralExp(loc, sd, null);
                            ey = new DotIdExp(loc, ey, Id.ctor);
                            ey = new CallExp(loc, ey, ev);
                            ey = ey.trySemantic(sc);
                        }
                        if (ey)
                        {
                            Expression ex;
                            ex = new IndexExp(loc, ea, ek);
                            ex = ex.semantic(sc);
                            ex = ex.optimize(WANTvalue);
                            ex = ex.modifiableLvalue(sc, ex); // allocate new slot

                            ey = new ConstructExp(loc, ex, ey);
                            ey = ey.semantic(sc);
                            if (ey.op == TOKerror)
                                return ey;
                            ex = e;

                            // Bugzilla 14144: The whole expression should have the common type
                            // of opAssign() return and assigned AA entry.
                            // Even if there's no common type, expression should be typed as void.
                            Type t = null;
                            if (!typeMerge(sc, TOKquestion, &t, &ex, &ey))
                            {
                                ex = new CastExp(ex.loc, ex, Type.tvoid);
                                ey = new CastExp(ey.loc, ey, Type.tvoid);
                            }
                            e = new CondExp(loc, new InExp(loc, ek, ea), ex, ey);
                        }
                        e = combine(e0, e);
                        e = e.semantic(sc);
                        return e;
                    }
                }
                else
                {
                    Expression e = op_overload(sc);
                    if (e)
                        return e;
                }
            }
            else
                assert(op == TOKblit);

            e1 = e1x;
            e2 = e2x;
        }
        else if (t1.ty == Tclass)
        {
            // Disallow assignment operator overloads for same type
            if (op == TOKassign && !e2.implicitConvTo(e1.type))
            {
                Expression e = op_overload(sc);
                if (e)
                    return e;
            }
        }
        else if (t1.ty == Tsarray)
        {
            // SliceExp cannot have static array type without context inference.
            assert(e1.op != TOKslice);
            Expression e1x = e1;
            Expression e2x = e2;

            if (e2x.implicitConvTo(e1x.type))
            {
                if (op != TOKblit && (e2x.op == TOKslice && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op == TOKcast && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op != TOKslice && e2x.isLvalue()))
                {
                    if (e1x.checkPostblit(sc, t1))
                        return new ErrorExp();
                }

                // e2 matches to t1 because of the implicit length match, so
                if (isUnaArrayOp(e2x.op) || isBinArrayOp(e2x.op))
                {
                    // convert e1 to e1[]
                    // e.g. e1[] = a[] + b[];
                    e1x = new SliceExp(e1x.loc, e1x, null, null);
                    e1x = e1x.semantic(sc);
                }
                else
                {
                    // convert e2 to t1 later
                    // e.g. e1 = [1, 2, 3];
                }
            }
            else
            {
                if (e2x.implicitConvTo(t1.nextOf().arrayOf()) > MATCHnomatch)
                {
                    uinteger_t dim1 = (cast(TypeSArray)t1).dim.toInteger();
                    uinteger_t dim2 = dim1;
                    if (e2x.op == TOKarrayliteral)
                    {
                        ArrayLiteralExp ale = cast(ArrayLiteralExp)e2x;
                        dim2 = ale.elements ? ale.elements.dim : 0;
                    }
                    else if (e2x.op == TOKslice)
                    {
                        Type tx = toStaticArrayType(cast(SliceExp)e2x);
                        if (tx)
                            dim2 = (cast(TypeSArray)tx).dim.toInteger();
                    }
                    if (dim1 != dim2)
                    {
                        error("mismatched array lengths, %d and %d", cast(int)dim1, cast(int)dim2);
                        return new ErrorExp();
                    }
                }

                // May be block or element-wise assignment, so
                // convert e1 to e1[]
                if (op != TOKassign)
                {
                    // If multidimensional static array, treat as one large array
                    dinteger_t dim = (cast(TypeSArray)t1).dim.toInteger();
                    Type t = t1;
                    while (1)
                    {
                        t = t.nextOf().toBasetype();
                        if (t.ty != Tsarray)
                            break;
                        dim *= (cast(TypeSArray)t).dim.toInteger();
                        e1x.type = t.nextOf().sarrayOf(dim);
                    }
                }
                e1x = new SliceExp(e1x.loc, e1x, null, null);
                e1x = e1x.semantic(sc);
            }
            if (e1x.op == TOKerror)
                return e1x;
            if (e2x.op == TOKerror)
                return e2x;

            e1 = e1x;
            e2 = e2x;
            t1 = e1x.type.toBasetype();
        }
        /* Check the mutability of e1.
         */
        if (e1.op == TOKarraylength)
        {
            // e1 is not an lvalue, but we let code generator handle it
            ArrayLengthExp ale = cast(ArrayLengthExp)e1;

            Expression ale1x = ale.e1;
            ale1x = ale1x.modifiableLvalue(sc, e1);
            if (ale1x.op == TOKerror)
                return ale1x;
            ale.e1 = ale1x;

            Type tn = ale.e1.type.toBasetype().nextOf();
            checkDefCtor(ale.loc, tn);
            semanticTypeInfo(sc, tn);
        }
        else if (e1.op == TOKslice)
        {
            Type tn = e1.type.nextOf();
            if (op == TOKassign && !tn.isMutable())
            {
                error("slice %s is not mutable", e1.toChars());
                return new ErrorExp();
            }

            // For conditional operator, both branches need conversion.
            SliceExp se = cast(SliceExp)e1;
            while (se.e1.op == TOKslice)
                se = cast(SliceExp)se.e1;
            if (se.e1.op == TOKquestion && se.e1.type.toBasetype().ty == Tsarray)
            {
                se.e1 = se.e1.modifiableLvalue(sc, e1);
                if (se.e1.op == TOKerror)
                    return se.e1;
            }
        }
        else
        {
            Expression e1x = e1;

            // Try to do a decent error message with the expression
            // before it got constant folded

            if (e1x.op != TOKvar)
                e1x = e1x.optimize(WANTvalue);

            if (op == TOKassign)
                e1x = e1x.modifiableLvalue(sc, e1old);

            if (e1x.op == TOKerror)
                return e1x;
            e1 = e1x;
        }

        /* Tweak e2 based on the type of e1.
         */
        Expression e2x = e2;
        Type t2 = e2x.type.toBasetype();

        // If it is a array, get the element type. Note that it may be
        // multi-dimensional.
        Type telem = t1;
        while (telem.ty == Tarray)
            telem = telem.nextOf();

        if (e1.op == TOKslice && t1.nextOf() &&
            (telem.ty != Tvoid || e2x.op == TOKnull) &&
            e2x.implicitConvTo(t1.nextOf()))
        {
            // Check for block assignment. If it is of type void[], void[][], etc,
            // '= null' is the only allowable block assignment (Bug 7493)
            memset |= MemorySet.blockAssign;    // make it easy for back end to tell what this is
            e2x = e2x.implicitCastTo(sc, t1.nextOf());
            if (op != TOKblit && e2x.isLvalue() && e1.checkPostblit(sc, t1.nextOf()))
            {
                return new ErrorExp();
            }
        }
        else if (e1.op == TOKslice &&
                 (t2.ty == Tarray || t2.ty == Tsarray) &&
                 t2.nextOf().implicitConvTo(t1.nextOf()))
        {
            // Check element-wise assignment.

            /* If assigned elements number is known at compile time,
             * check the mismatch.
             */
            SliceExp se1 = cast(SliceExp)e1;
            TypeSArray tsa1 = cast(TypeSArray)toStaticArrayType(se1);
            TypeSArray tsa2 = null;
            if (e2x.op == TOKarrayliteral)
                tsa2 = cast(TypeSArray)t2.nextOf().sarrayOf((cast(ArrayLiteralExp)e2x).elements.dim);
            else if (e2x.op == TOKslice)
                tsa2 = cast(TypeSArray)toStaticArrayType(cast(SliceExp)e2x);
            else if (t2.ty == Tsarray)
                tsa2 = cast(TypeSArray)t2;
            if (tsa1 && tsa2)
            {
                uinteger_t dim1 = tsa1.dim.toInteger();
                uinteger_t dim2 = tsa2.dim.toInteger();
                if (dim1 != dim2)
                {
                    error("mismatched array lengths, %d and %d", cast(int)dim1, cast(int)dim2);
                    return new ErrorExp();
                }
            }

            if (op != TOKblit &&
                (e2x.op == TOKslice && (cast(UnaExp)e2x).e1.isLvalue() ||
                 e2x.op == TOKcast && (cast(UnaExp)e2x).e1.isLvalue() ||
                 e2x.op != TOKslice && e2x.isLvalue()))
            {
                if (e1.checkPostblit(sc, t1.nextOf()))
                    return new ErrorExp();
            }

            if (0 && global.params.warnings && !global.gag && op == TOKassign &&
                e2x.op != TOKslice && e2x.op != TOKassign &&
                e2x.op != TOKarrayliteral && e2x.op != TOKstring &&
                !(e2x.op == TOKadd || e2x.op == TOKmin ||
                  e2x.op == TOKmul || e2x.op == TOKdiv ||
                  e2x.op == TOKmod || e2x.op == TOKxor ||
                  e2x.op == TOKand || e2x.op == TOKor ||
                  e2x.op == TOKpow ||
                  e2x.op == TOKtilde || e2x.op == TOKneg))
            {
                const(char)* e1str = e1.toChars();
                const(char)* e2str = e2x.toChars();
                warning("explicit element-wise assignment %s = (%s)[] is better than %s = %s", e1str, e2str, e1str, e2str);
            }

            Type t2n = t2.nextOf();
            Type t1n = t1.nextOf();
            int offset;
            if (t2n.equivalent(t1n) ||
                t1n.isBaseOf(t2n, &offset) && offset == 0)
            {
                /* Allow copy of distinct qualifier elements.
                 * eg.
                 *  char[] dst;  const(char)[] src;
                 *  dst[] = src;
                 *
                 *  class C {}   class D : C {}
                 *  C[2] ca;  D[] da;
                 *  ca[] = da;
                 */
                if (isArrayOpValid(e2x))
                {
                    // Don't add CastExp to keep AST for array operations
                    e2x = e2x.copy();
                    e2x.type = e1.type.constOf();
                }
                else
                    e2x = e2x.castTo(sc, e1.type.constOf());
            }
            else
            {
                /* Bugzilla 15778: A string literal has an array type of immutable
                 * elements by default, and normally it cannot be convertible to
                 * array type of mutable elements. But for element-wise assignment,
                 * elements need to be const at best. So we should give a chance
                 * to change code unit size for polysemous string literal.
                 */
                if (e2x.op == TOKstring)
                    e2x = e2x.implicitCastTo(sc, e1.type.constOf());
                else
                    e2x = e2x.implicitCastTo(sc, e1.type);
            }
            if (t1n.toBasetype.ty == Tvoid && t2n.toBasetype.ty == Tvoid)
            {
                if (!sc.intypeof && sc.func && sc.func.setUnsafe())
                {
                    error("cannot copy void[] to void[] in @safe code");
                    return new ErrorExp();
                }
            }
        }
        else
        {
            if (0 && global.params.warnings && !global.gag && op == TOKassign &&
                t1.ty == Tarray && t2.ty == Tsarray &&
                e2x.op != TOKslice &&
                t2.implicitConvTo(t1))
            {
                // Disallow ar[] = sa (Converted to ar[] = sa[])
                // Disallow da   = sa (Converted to da   = sa[])
                const(char)* e1str = e1.toChars();
                const(char)* e2str = e2x.toChars();
                const(char)* atypestr = e1.op == TOKslice ? "element-wise" : "slice";
                warning("explicit %s assignment %s = (%s)[] is better than %s = %s", atypestr, e1str, e2str, e1str, e2str);
            }
            if (op == TOKblit)
                e2x = e2x.castTo(sc, e1.type);
            else
                e2x = e2x.implicitCastTo(sc, e1.type);
        }
        if (e2x.op == TOKerror)
            return e2x;
        e2 = e2x;
        t2 = e2.type.toBasetype();

        /* Look for array operations
         */
        if ((t2.ty == Tarray || t2.ty == Tsarray) && isArrayOpValid(e2))
        {
            // Look for valid array operations
            if (!(memset & MemorySet.blockAssign) &&
                e1.op == TOKslice &&
                (isUnaArrayOp(e2.op) || isBinArrayOp(e2.op)))
            {
                type = e1.type;
                if (op == TOKconstruct) // Bugzilla 10282: tweak mutability of e1 element
                    e1.type = e1.type.nextOf().mutableOf().arrayOf();
                return arrayOp(this, sc);
            }

            // Drop invalid array operations in e2
            //  d = a[] + b[], d = (a[] + b[])[0..2], etc
            if (checkNonAssignmentArrayOp(e2, !(memset & MemorySet.blockAssign) && op == TOKassign))
                return new ErrorExp();

            // Remains valid array assignments
            //  d = d[], d = [1,2,3], etc
        }

        /* Don't allow assignment to classes that were allocated on the stack with:
         *      scope Class c = new Class();
         */
        if (e1.op == TOKvar && op == TOKassign)
        {
            VarExp ve = cast(VarExp)e1;
            VarDeclaration vd = ve.var.isVarDeclaration();
            if (vd && (vd.onstack || vd.mynew))
            {
                assert(t1.ty == Tclass);
                error("cannot rebind scope variables");
            }
        }

        if (e1.op == TOKvar && (cast(VarExp)e1).var.ident == Id.ctfe)
        {
            error("cannot modify compiler-generated variable __ctfe");
        }

        type = e1.type;
        assert(type);
        return op == TOKassign ? reorderSettingAAElem(sc) : this;
    }

    override final bool isLvalue()
    {
        // Array-op 'x[] = y[]' should make an rvalue.
        // Setting array length 'x.length = v' should make an rvalue.
        if (e1.op == TOKslice || e1.op == TOKarraylength)
        {
            return false;
        }
        return true;
    }

    override final Expression toLvalue(Scope* sc, Expression ex)
    {
        if (e1.op == TOKslice || e1.op == TOKarraylength)
        {
            return Expression.toLvalue(sc, ex);
        }

        /* In front-end level, AssignExp should make an lvalue of e1.
         * Taking the address of e1 will be handled in low level layer,
         * so this function does nothing.
         */
        return this;
    }

    override final Expression toBoolean(Scope* sc)
    {
        // Things like:
        //  if (a = b) ...
        // are usually mistakes.

        error("assignment cannot be used as a condition, perhaps == was meant?");
        return new ErrorExp();
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class ConstructExp : AssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, e1, e2);
        op = TOKconstruct;
    }

    // Internal use only. If `v` is a reference variable, the assinment
    // will become a reference initialization automatically.
    extern (D) this(Loc loc, VarDeclaration v, Expression e2)
    {
        auto ve = new VarExp(loc, v);
        assert(v.type && ve.type);

        super(loc, ve, e2);
        op = TOKconstruct;

        if (v.storage_class & (STCref | STCout))
            memset |= MemorySet.referenceInit;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class BlitExp : AssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, e1, e2);
        op = TOKblit;
    }

    // Internal use only. If `v` is a reference variable, the assinment
    // will become a reference rebinding automatically.
    extern (D) this(Loc loc, VarDeclaration v, Expression e2)
    {
        auto ve = new VarExp(loc, v);
        assert(v.type && ve.type);

        super(loc, ve, e2);
        op = TOKblit;

        if (v.storage_class & (STCref | STCout))
            memset |= MemorySet.referenceInit;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class AddAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKaddass, __traits(classInstanceSize, AddAssignExp), e1, e2);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class MinAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKminass, __traits(classInstanceSize, MinAssignExp), e1, e2);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class MulAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKmulass, __traits(classInstanceSize, MulAssignExp), e1, e2);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class DivAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKdivass, __traits(classInstanceSize, DivAssignExp), e1, e2);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class ModAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKmodass, __traits(classInstanceSize, ModAssignExp), e1, e2);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class AndAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKandass, __traits(classInstanceSize, AndAssignExp), e1, e2);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class OrAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKorass, __traits(classInstanceSize, OrAssignExp), e1, e2);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class XorAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKxorass, __traits(classInstanceSize, XorAssignExp), e1, e2);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class PowAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKpowass, __traits(classInstanceSize, PowAssignExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        Expression e = op_overload(sc);
        if (e)
            return e;

        if (e1.checkReadModifyWrite(op, e2))
            return new ErrorExp();

        assert(e1.type && e2.type);
        if (e1.op == TOKslice || e1.type.ty == Tarray || e1.type.ty == Tsarray)
        {
            // T[] ^^= ...
            if (e2.implicitConvTo(e1.type.nextOf()))
            {
                // T[] ^^= T
                e2 = e2.castTo(sc, e1.type.nextOf());
            }
            else if (Expression ex = typeCombine(this, sc))
                return ex;

            // Check element types are arithmetic
            Type tb1 = e1.type.nextOf().toBasetype();
            Type tb2 = e2.type.toBasetype();
            if (tb2.ty == Tarray || tb2.ty == Tsarray)
                tb2 = tb2.nextOf().toBasetype();
            if ((tb1.isintegral() || tb1.isfloating()) && (tb2.isintegral() || tb2.isfloating()))
            {
                type = e1.type;
                return arrayOp(this, sc);
            }
        }
        else
        {
            e1 = e1.modifiableLvalue(sc, e1);
        }

        if ((e1.type.isintegral() || e1.type.isfloating()) && (e2.type.isintegral() || e2.type.isfloating()))
        {
            Expression e0 = null;
            e = reorderSettingAAElem(sc);
            e = extractLast(e, &e0);
            assert(e == this);

            if (e1.op == TOKvar)
            {
                // Rewrite: e1 = e1 ^^ e2
                e = new PowExp(loc, e1.syntaxCopy(), e2);
                e = new AssignExp(loc, e1, e);
            }
            else
            {
                // Rewrite: ref tmp = e1; tmp = tmp ^^ e2
                auto v = copyToTemp(STCref, "__powtmp", e1);
                auto de = new DeclarationExp(e1.loc, v);
                auto ve = new VarExp(e1.loc, v);
                e = new PowExp(loc, ve, e2);
                e = new AssignExp(loc, new VarExp(e1.loc, v), e);
                e = new CommaExp(loc, de, e);
            }
            e = Expression.combine(e0, e);
            e = e.semantic(sc);
            if (e.type.toBasetype().ty == Tvector)
                return incompatibleTypes();
            return e;
        }
        return incompatibleTypes();
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class ShlAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKshlass, __traits(classInstanceSize, ShlAssignExp), e1, e2);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class ShrAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKshrass, __traits(classInstanceSize, ShrAssignExp), e1, e2);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class UshrAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKushrass, __traits(classInstanceSize, UshrAssignExp), e1, e2);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class CatAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKcatass, __traits(classInstanceSize, CatAssignExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        //printf("CatAssignExp::semantic() %s\n", toChars());
        Expression e = op_overload(sc);
        if (e)
            return e;

        if (e1.op == TOKslice)
        {
            SliceExp se = cast(SliceExp)e1;
            if (se.e1.type.toBasetype().ty == Tsarray)
            {
                error("cannot append to static array %s", se.e1.type.toChars());
                return new ErrorExp();
            }
        }

        e1 = e1.modifiableLvalue(sc, e1);
        if (e1.op == TOKerror)
            return e1;
        if (e2.op == TOKerror)
            return e2;

        if (checkNonAssignmentArrayOp(e2))
            return new ErrorExp();

        Type tb1 = e1.type.toBasetype();
        Type tb1next = tb1.nextOf();
        Type tb2 = e2.type.toBasetype();

        if ((tb1.ty == Tarray) &&
            (tb2.ty == Tarray || tb2.ty == Tsarray) &&
            (e2.implicitConvTo(e1.type) ||
             (tb2.nextOf().implicitConvTo(tb1next) &&
              (tb2.nextOf().size(Loc()) == tb1next.size(Loc())))))
        {
            // Append array
            if (e1.checkPostblit(sc, tb1next))
                return new ErrorExp();
            e2 = e2.castTo(sc, e1.type);
        }
        else if ((tb1.ty == Tarray) && e2.implicitConvTo(tb1next))
        {
            // Append element
            if (e2.checkPostblit(sc, tb2))
                return new ErrorExp();
            e2 = e2.castTo(sc, tb1next);
            e2 = doCopyOrMove(sc, e2);
        }
        else if (tb1.ty == Tarray &&
                 (tb1next.ty == Tchar || tb1next.ty == Twchar) &&
                 e2.type.ty != tb1next.ty &&
                 e2.implicitConvTo(Type.tdchar))
        {
            // Append dchar to char[] or wchar[]
            e2 = e2.castTo(sc, Type.tdchar);

            /* Do not allow appending wchar to char[] because if wchar happens
             * to be a surrogate pair, nothing good can result.
             */
        }
        else
        {
            error("cannot append type %s to type %s", tb2.toChars(), tb1.toChars());
            return new ErrorExp();
        }
        if (e2.checkValue())
            return new ErrorExp();

        type = e1.type;
        return reorderSettingAAElem(sc);
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class AddExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKadd, __traits(classInstanceSize, AddExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("AddExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        Type tb1 = e1.type.toBasetype();
        Type tb2 = e2.type.toBasetype();

        bool err = false;
        if (tb1.ty == Tdelegate || tb1.ty == Tpointer && tb1.nextOf().ty == Tfunction)
        {
            err |= e1.checkArithmetic();
        }
        if (tb2.ty == Tdelegate || tb2.ty == Tpointer && tb2.nextOf().ty == Tfunction)
        {
            err |= e2.checkArithmetic();
        }
        if (err)
            return new ErrorExp();

        if (tb1.ty == Tpointer && e2.type.isintegral() || tb2.ty == Tpointer && e1.type.isintegral())
        {
            return scaleFactor(this, sc);
        }

        if (tb1.ty == Tpointer && tb2.ty == Tpointer)
        {
            return incompatibleTypes();
        }

        if (Expression ex = typeCombine(this, sc))
            return ex;

        Type tb = type.toBasetype();
        if (tb.ty == Tarray || tb.ty == Tsarray)
        {
            if (!isArrayOpValid(this))
            {
                error("invalid array operation %s (possible missing [])", toChars());
                return new ErrorExp();
            }
            return this;
        }

        tb1 = e1.type.toBasetype();
        if (tb1.ty == Tvector && !tb1.isscalar())
        {
            return incompatibleTypes();
        }
        if ((tb1.isreal() && e2.type.isimaginary()) || (tb1.isimaginary() && e2.type.isreal()))
        {
            switch (type.toBasetype().ty)
            {
            case Tfloat32:
            case Timaginary32:
                type = Type.tcomplex32;
                break;

            case Tfloat64:
            case Timaginary64:
                type = Type.tcomplex64;
                break;

            case Tfloat80:
            case Timaginary80:
                type = Type.tcomplex80;
                break;

            default:
                assert(0);
            }
        }
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class MinExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKmin, __traits(classInstanceSize, MinExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("MinExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        Type t1 = e1.type.toBasetype();
        Type t2 = e2.type.toBasetype();

        bool err = false;
        if (t1.ty == Tdelegate || t1.ty == Tpointer && t1.nextOf().ty == Tfunction)
        {
            err |= e1.checkArithmetic();
        }
        if (t2.ty == Tdelegate || t2.ty == Tpointer && t2.nextOf().ty == Tfunction)
        {
            err |= e2.checkArithmetic();
        }
        if (err)
            return new ErrorExp();

        if (t1.ty == Tpointer)
        {
            if (t2.ty == Tpointer)
            {
                // Need to divide the result by the stride
                // Replace (ptr - ptr) with (ptr - ptr) / stride
                d_int64 stride;

                // make sure pointer types are compatible
                if (Expression ex = typeCombine(this, sc))
                    return ex;

                type = Type.tptrdiff_t;
                stride = t2.nextOf().size();
                if (stride == 0)
                {
                    e = new IntegerExp(loc, 0, Type.tptrdiff_t);
                }
                else
                {
                    e = new DivExp(loc, this, new IntegerExp(Loc(), stride, Type.tptrdiff_t));
                    e.type = Type.tptrdiff_t;
                }
            }
            else if (t2.isintegral())
                e = scaleFactor(this, sc);
            else
            {
                error("can't subtract %s from pointer", t2.toChars());
                e = new ErrorExp();
            }
            return e;
        }
        if (t2.ty == Tpointer)
        {
            type = e2.type;
            error("can't subtract pointer from %s", e1.type.toChars());
            return new ErrorExp();
        }

        if (Expression ex = typeCombine(this, sc))
            return ex;

        Type tb = type.toBasetype();
        if (tb.ty == Tarray || tb.ty == Tsarray)
        {
            if (!isArrayOpValid(this))
            {
                error("invalid array operation %s (possible missing [])", toChars());
                return new ErrorExp();
            }
            return this;
        }

        t1 = e1.type.toBasetype();
        t2 = e2.type.toBasetype();
        if (t1.ty == Tvector && !t1.isscalar())
        {
            return incompatibleTypes();
        }
        if ((t1.isreal() && t2.isimaginary()) || (t1.isimaginary() && t2.isreal()))
        {
            switch (type.ty)
            {
            case Tfloat32:
            case Timaginary32:
                type = Type.tcomplex32;
                break;

            case Tfloat64:
            case Timaginary64:
                type = Type.tcomplex64;
                break;

            case Tfloat80:
            case Timaginary80:
                type = Type.tcomplex80;
                break;

            default:
                assert(0);
            }
        }
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class CatExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKcat, __traits(classInstanceSize, CatExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        //printf("CatExp::semantic() %s\n", toChars());
        if (type)
            return this;

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        Type tb1 = e1.type.toBasetype();
        Type tb2 = e2.type.toBasetype();

        auto f1 = checkNonAssignmentArrayOp(e1);
        auto f2 = checkNonAssignmentArrayOp(e2);
        if (f1 || f2)
            return new ErrorExp();

        /* BUG: Should handle things like:
         *      char c;
         *      c ~ ' '
         *      ' ' ~ c;
         */

        version (none)
        {
            e1.type.print();
            e2.type.print();
        }
        Type tb1next = tb1.nextOf();
        Type tb2next = tb2.nextOf();

        // Check for: array ~ array
        if (tb1next && tb2next && (tb1next.implicitConvTo(tb2next) >= MATCHconst || tb2next.implicitConvTo(tb1next) >= MATCHconst || e1.op == TOKarrayliteral && e1.implicitConvTo(tb2) || e2.op == TOKarrayliteral && e2.implicitConvTo(tb1)))
        {
            /* Bugzilla 9248: Here to avoid the case of:
             *    void*[] a = [cast(void*)1];
             *    void*[] b = [cast(void*)2];
             *    a ~ b;
             * becoming:
             *    a ~ [cast(void*)b];
             */

            /* Bugzilla 14682: Also to avoid the case of:
             *    int[][] a;
             *    a ~ [];
             * becoming:
             *    a ~ cast(int[])[];
             */
            goto Lpeer;
        }

        // Check for: array ~ element
        if ((tb1.ty == Tsarray || tb1.ty == Tarray) && tb2.ty != Tvoid)
        {
            if (e1.op == TOKarrayliteral)
            {
                e2 = doCopyOrMove(sc, e2);
                // Bugzilla 14686: Postblit call appears in AST, and this is
                // finally translated  to an ArrayLiteralExp in below optimize().
            }
            else if (e1.op == TOKstring)
            {
                // No postblit call exists on character (integer) value.
            }
            else
            {
                if (e2.checkPostblit(sc, tb2))
                    return new ErrorExp();
                // Postblit call will be done in runtime helper function
            }

            if (e1.op == TOKarrayliteral && e1.implicitConvTo(tb2.arrayOf()))
            {
                e1 = e1.implicitCastTo(sc, tb2.arrayOf());
                type = tb2.arrayOf();
                goto L2elem;
            }
            if (e2.implicitConvTo(tb1next) >= MATCHconvert)
            {
                e2 = e2.implicitCastTo(sc, tb1next);
                type = tb1next.arrayOf();
            L2elem:
                if (tb2.ty == Tarray || tb2.ty == Tsarray)
                {
                    // Make e2 into [e2]
                    e2 = new ArrayLiteralExp(e2.loc, e2);
                    e2.type = type;
                }
                return optimize(WANTvalue);
            }
        }
        // Check for: element ~ array
        if ((tb2.ty == Tsarray || tb2.ty == Tarray) && tb1.ty != Tvoid)
        {
            if (e2.op == TOKarrayliteral)
            {
                e1 = doCopyOrMove(sc, e1);
            }
            else if (e2.op == TOKstring)
            {
            }
            else
            {
                if (e1.checkPostblit(sc, tb1))
                    return new ErrorExp();
            }

            if (e2.op == TOKarrayliteral && e2.implicitConvTo(tb1.arrayOf()))
            {
                e2 = e2.implicitCastTo(sc, tb1.arrayOf());
                type = tb1.arrayOf();
                goto L1elem;
            }
            if (e1.implicitConvTo(tb2next) >= MATCHconvert)
            {
                e1 = e1.implicitCastTo(sc, tb2next);
                type = tb2next.arrayOf();
            L1elem:
                if (tb1.ty == Tarray || tb1.ty == Tsarray)
                {
                    // Make e1 into [e1]
                    e1 = new ArrayLiteralExp(e1.loc, e1);
                    e1.type = type;
                }
                return optimize(WANTvalue);
            }
        }

    Lpeer:
        if ((tb1.ty == Tsarray || tb1.ty == Tarray) && (tb2.ty == Tsarray || tb2.ty == Tarray) && (tb1next.mod || tb2next.mod) && (tb1next.mod != tb2next.mod))
        {
            Type t1 = tb1next.mutableOf().constOf().arrayOf();
            Type t2 = tb2next.mutableOf().constOf().arrayOf();
            if (e1.op == TOKstring && !(cast(StringExp)e1).committed)
                e1.type = t1;
            else
                e1 = e1.castTo(sc, t1);
            if (e2.op == TOKstring && !(cast(StringExp)e2).committed)
                e2.type = t2;
            else
                e2 = e2.castTo(sc, t2);
        }

        if (Expression ex = typeCombine(this, sc))
            return ex;
        type = type.toHeadMutable();

        Type tb = type.toBasetype();
        if (tb.ty == Tsarray)
            type = tb.nextOf().arrayOf();
        if (type.ty == Tarray && tb1next && tb2next && tb1next.mod != tb2next.mod)
        {
            type = type.nextOf().toHeadMutable().arrayOf();
        }
        if (Type tbn = tb.nextOf())
        {
            if (checkPostblit(sc, tbn))
                return new ErrorExp();
        }
        version (none)
        {
            e1.type.print();
            e2.type.print();
            type.print();
            print();
        }
        Type t1 = e1.type.toBasetype();
        Type t2 = e2.type.toBasetype();
        if ((t1.ty == Tarray || t1.ty == Tsarray) &&
            (t2.ty == Tarray || t2.ty == Tsarray))
        {
            // Normalize to ArrayLiteralExp or StringExp as far as possible
            e = optimize(WANTvalue);
        }
        else
        {
            //printf("(%s) ~ (%s)\n", e1->toChars(), e2->toChars());
            return incompatibleTypes();
        }

        return e;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class MulExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKmul, __traits(classInstanceSize, MulExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        version (none)
        {
            printf("MulExp::semantic() %s\n", toChars());
        }
        if (type)
            return this;

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        if (Expression ex = typeCombine(this, sc))
            return ex;

        Type tb = type.toBasetype();
        if (tb.ty == Tarray || tb.ty == Tsarray)
        {
            if (!isArrayOpValid(this))
            {
                error("invalid array operation %s (possible missing [])", toChars());
                return new ErrorExp();
            }
            return this;
        }

        if (checkArithmeticBin())
            return new ErrorExp();

        if (type.isfloating())
        {
            Type t1 = e1.type;
            Type t2 = e2.type;

            if (t1.isreal())
            {
                type = t2;
            }
            else if (t2.isreal())
            {
                type = t1;
            }
            else if (t1.isimaginary())
            {
                if (t2.isimaginary())
                {
                    switch (t1.toBasetype().ty)
                    {
                    case Timaginary32:
                        type = Type.tfloat32;
                        break;

                    case Timaginary64:
                        type = Type.tfloat64;
                        break;

                    case Timaginary80:
                        type = Type.tfloat80;
                        break;

                    default:
                        assert(0);
                    }

                    // iy * iv = -yv
                    e1.type = type;
                    e2.type = type;
                    e = new NegExp(loc, this);
                    e = e.semantic(sc);
                    return e;
                }
                else
                    type = t2; // t2 is complex
            }
            else if (t2.isimaginary())
            {
                type = t1; // t1 is complex
            }
        }
        else if (tb.ty == Tvector && (cast(TypeVector)tb).elementType().size(loc) != 2)
        {
            // Only short[8] and ushort[8] work with multiply
            return incompatibleTypes();
        }
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class DivExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKdiv, __traits(classInstanceSize, DivExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        if (Expression ex = typeCombine(this, sc))
            return ex;

        Type tb = type.toBasetype();
        if (tb.ty == Tarray || tb.ty == Tsarray)
        {
            if (!isArrayOpValid(this))
            {
                error("invalid array operation %s (possible missing [])", toChars());
                return new ErrorExp();
            }
            return this;
        }

        if (checkArithmeticBin())
            return new ErrorExp();

        if (type.isfloating())
        {
            Type t1 = e1.type;
            Type t2 = e2.type;

            if (t1.isreal())
            {
                type = t2;
                if (t2.isimaginary())
                {
                    // x/iv = i(-x/v)
                    e2.type = t1;
                    e = new NegExp(loc, this);
                    e = e.semantic(sc);
                    return e;
                }
            }
            else if (t2.isreal())
            {
                type = t1;
            }
            else if (t1.isimaginary())
            {
                if (t2.isimaginary())
                {
                    switch (t1.toBasetype().ty)
                    {
                    case Timaginary32:
                        type = Type.tfloat32;
                        break;

                    case Timaginary64:
                        type = Type.tfloat64;
                        break;

                    case Timaginary80:
                        type = Type.tfloat80;
                        break;

                    default:
                        assert(0);
                    }
                }
                else
                    type = t2; // t2 is complex
            }
            else if (t2.isimaginary())
            {
                type = t1; // t1 is complex
            }
        }
        else if (tb.ty == Tvector)
        {
            return incompatibleTypes();
        }
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class ModExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKmod, __traits(classInstanceSize, ModExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        if (Expression ex = typeCombine(this, sc))
            return ex;

        Type tb = type.toBasetype();
        if (tb.ty == Tarray || tb.ty == Tsarray)
        {
            if (!isArrayOpValid(this))
            {
                error("invalid array operation %s (possible missing [])", toChars());
                return new ErrorExp();
            }
            return this;
        }
        if (tb.ty == Tvector)
        {
            return incompatibleTypes();
        }

        if (checkArithmeticBin())
            return new ErrorExp();

        if (type.isfloating())
        {
            type = e1.type;
            if (e2.type.iscomplex())
            {
                error("cannot perform modulo complex arithmetic");
                return new ErrorExp();
            }
        }
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class PowExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKpow, __traits(classInstanceSize, PowExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        //printf("PowExp::semantic() %s\n", toChars());
        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        if (Expression ex = typeCombine(this, sc))
            return ex;

        Type tb = type.toBasetype();
        if (tb.ty == Tarray || tb.ty == Tsarray)
        {
            if (!isArrayOpValid(this))
            {
                error("invalid array operation %s (possible missing [])", toChars());
                return new ErrorExp();
            }
            return this;
        }

        if (checkArithmeticBin())
            return new ErrorExp();

        // For built-in numeric types, there are several cases.
        // TODO: backend support, especially for  e1 ^^ 2.

        // First, attempt to fold the expression.
        e = optimize(WANTvalue);
        if (e.op != TOKpow)
        {
            e = e.semantic(sc);
            return e;
        }

        // Determine if we're raising to an integer power.
        sinteger_t intpow = 0;
        if (e2.op == TOKint64 && (cast(sinteger_t)e2.toInteger() == 2 || cast(sinteger_t)e2.toInteger() == 3))
            intpow = e2.toInteger();
        else if (e2.op == TOKfloat64 && (e2.toReal() == real_t(cast(sinteger_t)e2.toReal())))
            intpow = cast(sinteger_t)e2.toReal();

        // Deal with x^^2, x^^3 immediately, since they are of practical importance.
        if (intpow == 2 || intpow == 3)
        {
            // Replace x^^2 with (tmp = x, tmp*tmp)
            // Replace x^^3 with (tmp = x, tmp*tmp*tmp)
            auto tmp = copyToTemp(0, "__powtmp", e1);
            Expression de = new DeclarationExp(loc, tmp);
            Expression ve = new VarExp(loc, tmp);

            /* Note that we're reusing ve. This should be ok.
             */
            Expression me = new MulExp(loc, ve, ve);
            if (intpow == 3)
                me = new MulExp(loc, me, ve);
            e = new CommaExp(loc, de, me);
            e = e.semantic(sc);
            return e;
        }

        Module mmath = loadStdMath();
        if (!mmath)
        {
            //error("requires std.math for ^^ operators");
            //fatal();
            // Leave handling of PowExp to the backend, or throw
            // an error gracefully if no backend support exists.
            if (Expression ex = typeCombine(this, sc))
                return ex;
            return this;
        }
        e = new ScopeExp(loc, mmath);

        if (e2.op == TOKfloat64 && e2.toReal() == CTFloat.half)
        {
            // Replace e1 ^^ 0.5 with .std.math.sqrt(x)
            e = new CallExp(loc, new DotIdExp(loc, e, Id._sqrt), e1);
        }
        else
        {
            // Replace e1 ^^ e2 with .std.math.pow(e1, e2)
            e = new CallExp(loc, new DotIdExp(loc, e, Id._pow), e1, e2);
        }
        e = e.semantic(sc);
        return e;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

extern (C++) Module loadStdMath()
{
    static __gshared Import impStdMath = null;
    if (!impStdMath)
    {
        auto a = new Identifiers();
        a.push(Id.std);
        auto s = new Import(Loc(), a, Id.math, null, false);
        s.load(null);
        if (s.mod)
        {
            s.mod.importAll(null);
            s.mod.semantic(null);
        }
        impStdMath = s;
    }
    return impStdMath.mod;
}

/***********************************************************
 */
extern (C++) final class ShlExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKshl, __traits(classInstanceSize, ShlExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        //printf("ShlExp::semantic(), type = %p\n", type);
        if (type)
            return this;

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        if (checkIntegralBin())
            return new ErrorExp();
        if (e1.type.toBasetype().ty == Tvector || e2.type.toBasetype().ty == Tvector)
        {
            return incompatibleTypes();
        }
        e1 = integralPromotions(e1, sc);
        e2 = e2.castTo(sc, Type.tshiftcnt);

        type = e1.type;
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class ShrExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKshr, __traits(classInstanceSize, ShrExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        if (checkIntegralBin())
            return new ErrorExp();
        if (e1.type.toBasetype().ty == Tvector || e2.type.toBasetype().ty == Tvector)
        {
            return incompatibleTypes();
        }
        e1 = integralPromotions(e1, sc);
        e2 = e2.castTo(sc, Type.tshiftcnt);

        type = e1.type;
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class UshrExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKushr, __traits(classInstanceSize, UshrExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        if (checkIntegralBin())
            return new ErrorExp();
        if (e1.type.toBasetype().ty == Tvector || e2.type.toBasetype().ty == Tvector)
        {
            return incompatibleTypes();
        }

        e1 = integralPromotions(e1, sc);
        e2 = e2.castTo(sc, Type.tshiftcnt);

        type = e1.type;
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class AndExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKand, __traits(classInstanceSize, AndExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        if (e1.type.toBasetype().ty == Tbool && e2.type.toBasetype().ty == Tbool)
        {
            type = e1.type;
            return this;
        }

        if (Expression ex = typeCombine(this, sc))
            return ex;

        Type tb = type.toBasetype();
        if (tb.ty == Tarray || tb.ty == Tsarray)
        {
            if (!isArrayOpValid(this))
            {
                error("invalid array operation %s (possible missing [])", toChars());
                return new ErrorExp();
            }
            return this;
        }

        if (checkIntegralBin())
            return new ErrorExp();

        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class OrExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKor, __traits(classInstanceSize, OrExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        if (e1.type.toBasetype().ty == Tbool && e2.type.toBasetype().ty == Tbool)
        {
            type = e1.type;
            return this;
        }

        if (Expression ex = typeCombine(this, sc))
            return ex;

        Type tb = type.toBasetype();
        if (tb.ty == Tarray || tb.ty == Tsarray)
        {
            if (!isArrayOpValid(this))
            {
                error("invalid array operation %s (possible missing [])", toChars());
                return new ErrorExp();
            }
            return this;
        }

        if (checkIntegralBin())
            return new ErrorExp();

        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class XorExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKxor, __traits(classInstanceSize, XorExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        if (e1.type.toBasetype().ty == Tbool && e2.type.toBasetype().ty == Tbool)
        {
            type = e1.type;
            return this;
        }

        if (Expression ex = typeCombine(this, sc))
            return ex;

        Type tb = type.toBasetype();
        if (tb.ty == Tarray || tb.ty == Tsarray)
        {
            if (!isArrayOpValid(this))
            {
                error("invalid array operation %s (possible missing [])", toChars());
                return new ErrorExp();
            }
            return this;
        }

        if (checkIntegralBin())
            return new ErrorExp();

        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class OrOrExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKoror, __traits(classInstanceSize, OrOrExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        setNoderefOperands();

        // same as for AndAnd
        e1 = e1.semantic(sc);
        e1 = resolveProperties(sc, e1);
        e1 = e1.toBoolean(sc);
        uint cs1 = sc.callSuper;

        if (sc.flags & SCOPEcondition)
        {
            /* If in static if, don't evaluate e2 if we don't have to.
             */
            e1 = e1.optimize(WANTvalue);
            if (e1.isBool(true))
            {
                return new IntegerExp(loc, 1, Type.tbool);
            }
        }

        e2 = e2.semantic(sc);
        sc.mergeCallSuper(loc, cs1);
        e2 = resolveProperties(sc, e2);

        auto f1 = checkNonAssignmentArrayOp(e1);
        auto f2 = checkNonAssignmentArrayOp(e2);
        if (f1 || f2)
            return new ErrorExp();

        if (e2.type.ty == Tvoid)
            type = Type.tvoid;
        else
        {
            e2 = e2.toBoolean(sc);
            type = Type.tbool;
        }
        if (e2.op == TOKtype || e2.op == TOKscope)
        {
            error("%s is not an expression", e2.toChars());
            return new ErrorExp();
        }
        if (e1.op == TOKerror)
            return e1;
        if (e2.op == TOKerror)
            return e2;

        return this;
    }

    override Expression toBoolean(Scope* sc)
    {
        auto ex2 = e2.toBoolean(sc);
        if (ex2.op == TOKerror)
            return ex2;
        e2 = ex2;
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class AndAndExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKandand, __traits(classInstanceSize, AndAndExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        setNoderefOperands();

        // same as for OrOr
        e1 = e1.semantic(sc);
        e1 = resolveProperties(sc, e1);
        e1 = e1.toBoolean(sc);
        uint cs1 = sc.callSuper;

        if (sc.flags & SCOPEcondition)
        {
            /* If in static if, don't evaluate e2 if we don't have to.
             */
            e1 = e1.optimize(WANTvalue);
            if (e1.isBool(false))
            {
                return new IntegerExp(loc, 0, Type.tbool);
            }
        }

        e2 = e2.semantic(sc);
        sc.mergeCallSuper(loc, cs1);
        e2 = resolveProperties(sc, e2);

        auto f1 = checkNonAssignmentArrayOp(e1);
        auto f2 = checkNonAssignmentArrayOp(e2);
        if (f1 || f2)
            return new ErrorExp();

        if (e2.type.ty == Tvoid)
            type = Type.tvoid;
        else
        {
            e2 = e2.toBoolean(sc);
            type = Type.tbool;
        }
        if (e2.op == TOKtype || e2.op == TOKscope)
        {
            error("%s is not an expression", e2.toChars());
            return new ErrorExp();
        }
        if (e1.op == TOKerror)
            return e1;
        if (e2.op == TOKerror)
            return e2;

        return this;
    }

    override Expression toBoolean(Scope* sc)
    {
        auto ex2 = e2.toBoolean(sc);
        if (ex2.op == TOKerror)
            return ex2;
        e2 = ex2;
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class CmpExp : BinExp
{
    extern (D) this(TOK op, Loc loc, Expression e1, Expression e2)
    {
        super(loc, op, __traits(classInstanceSize, CmpExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("CmpExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        setNoderefOperands();

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Type t1 = e1.type.toBasetype();
        Type t2 = e2.type.toBasetype();
        if (t1.ty == Tclass && e2.op == TOKnull || t2.ty == Tclass && e1.op == TOKnull)
        {
            error("do not use null when comparing class types");
            return new ErrorExp();
        }

        Expression e = op_overload(sc);
        if (e)
        {
            if (!e.type.isscalar() && e.type.equals(e1.type))
            {
                error("recursive opCmp expansion");
                return new ErrorExp();
            }
            if (e.op == TOKcall)
            {
                e = new CmpExp(op, loc, e, new IntegerExp(loc, 0, Type.tint32));
                e = e.semantic(sc);
            }
            return e;
        }

        if (Expression ex = typeCombine(this, sc))
            return ex;

        auto f1 = checkNonAssignmentArrayOp(e1);
        auto f2 = checkNonAssignmentArrayOp(e2);
        if (f1 || f2)
            return new ErrorExp();

        type = Type.tbool;

        // Special handling for array comparisons
        t1 = e1.type.toBasetype();
        t2 = e2.type.toBasetype();
        if ((t1.ty == Tarray || t1.ty == Tsarray || t1.ty == Tpointer) && (t2.ty == Tarray || t2.ty == Tsarray || t2.ty == Tpointer))
        {
            Type t1next = t1.nextOf();
            Type t2next = t2.nextOf();
            if (t1next.implicitConvTo(t2next) < MATCHconst && t2next.implicitConvTo(t1next) < MATCHconst && (t1next.ty != Tvoid && t2next.ty != Tvoid))
            {
                error("array comparison type mismatch, %s vs %s", t1next.toChars(), t2next.toChars());
                return new ErrorExp();
            }
            if ((t1.ty == Tarray || t1.ty == Tsarray) && (t2.ty == Tarray || t2.ty == Tsarray))
            {
                semanticTypeInfo(sc, t1.nextOf());
            }
        }
        else if (t1.ty == Tstruct || t2.ty == Tstruct || (t1.ty == Tclass && t2.ty == Tclass))
        {
            if (t2.ty == Tstruct)
                error("need member function opCmp() for %s %s to compare", t2.toDsymbol(sc).kind(), t2.toChars());
            else
                error("need member function opCmp() for %s %s to compare", t1.toDsymbol(sc).kind(), t1.toChars());
            return new ErrorExp();
        }
        else if (t1.iscomplex() || t2.iscomplex())
        {
            error("compare not defined for complex operands");
            return new ErrorExp();
        }
        else if (t1.ty == Taarray || t2.ty == Taarray)
        {
            error("%s is not defined for associative arrays", Token.toChars(op));
            return new ErrorExp();
        }
        else if (t1.ty == Tvector)
        {
            return incompatibleTypes();
        }
        else
        {
            bool r1 = e1.checkValue();
            bool r2 = e2.checkValue();
            if (r1 || r2)
                return new ErrorExp();
        }

        TOK altop;
        switch (op)
        {
            // Refer rel_integral[] table
        case TOKunord:
            altop = TOKerror;
            break;

        case TOKlg:
            altop = TOKnotequal;
            break;

        case TOKleg:
            altop = TOKerror;
            break;

        case TOKule:
            altop = TOKle;
            break;

        case TOKul:
            altop = TOKlt;
            break;

        case TOKuge:
            altop = TOKge;
            break;

        case TOKug:
            altop = TOKgt;
            break;

        case TOKue:
            altop = TOKequal;
            break;

        default:
            altop = TOKreserved;
            break;
        }
        if (altop == TOKerror && (t1.ty == Tarray || t1.ty == Tsarray || t2.ty == Tarray || t2.ty == Tsarray))
        {
            error("'%s' is not defined for array comparisons", Token.toChars(op));
            return new ErrorExp();
        }
        if (altop != TOKreserved)
        {
            if (!t1.isfloating())
            {
                if (altop == TOKerror)
                {
                    const(char)* s = op == TOKunord ? "false" : "true";
                    error("floating point operator '%s' always returns %s for non-floating comparisons", Token.toChars(op), s);
                }
                else
                {
                    error("use '%s' for non-floating comparisons rather than floating point operator '%s'", Token.toChars(altop), Token.toChars(op));
                }
            }
            else
            {
                error("use std.math.isNaN to deal with NaN operands rather than floating point operator '%s'", Token.toChars(op));
            }
            return new ErrorExp();
        }

        //printf("CmpExp: %s, type = %s\n", e->toChars(), e->type->toChars());
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class InExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKin, __traits(classInstanceSize, InExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        if (Expression ex = binSemanticProp(sc))
            return ex;
        Expression e = op_overload(sc);
        if (e)
            return e;

        Type t2b = e2.type.toBasetype();
        switch (t2b.ty)
        {
        case Taarray:
            {
                TypeAArray ta = cast(TypeAArray)t2b;

                // Special handling for array keys
                if (!arrayTypeCompatible(e1.loc, e1.type, ta.index))
                {
                    // Convert key to type of key
                    e1 = e1.implicitCastTo(sc, ta.index);
                }

                semanticTypeInfo(sc, ta.index);

                // Return type is pointer to value
                type = ta.nextOf().pointerTo();
                break;
            }
        default:
            error("rvalue of in expression must be an associative array, not %s", e2.type.toChars());
            goto case Terror;
        case Terror:
            return new ErrorExp();
        }
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * This deletes the key e1 from the associative array e2
 */
extern (C++) final class RemoveExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2)
    {
        super(loc, TOKremove, __traits(classInstanceSize, RemoveExp), e1, e2);
        type = Type.tbool;
    }

    override Expression semantic(Scope* sc)
    {
        if (Expression ex = binSemantic(sc))
            return ex;
        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * == and !=
 */
extern (C++) final class EqualExp : BinExp
{
    extern (D) this(TOK op, Loc loc, Expression e1, Expression e2)
    {
        super(loc, op, __traits(classInstanceSize, EqualExp), e1, e2);
        assert(op == TOKequal || op == TOKnotequal);
    }

    override Expression semantic(Scope* sc)
    {
        //printf("EqualExp::semantic('%s')\n", toChars());
        if (type)
            return this;

        setNoderefOperands();

        if (auto e = binSemanticProp(sc))
            return e;
        if (e1.op == TOKtype || e2.op == TOKtype)
            return incompatibleTypes();

        /* Before checking for operator overloading, check to see if we're
         * comparing the addresses of two statics. If so, we can just see
         * if they are the same symbol.
         */
        if (e1.op == TOKaddress && e2.op == TOKaddress)
        {
            AddrExp ae1 = cast(AddrExp)e1;
            AddrExp ae2 = cast(AddrExp)e2;
            if (ae1.e1.op == TOKvar && ae2.e1.op == TOKvar)
            {
                VarExp ve1 = cast(VarExp)ae1.e1;
                VarExp ve2 = cast(VarExp)ae2.e1;
                if (ve1.var == ve2.var)
                {
                    // They are the same, result is 'true' for ==, 'false' for !=
                    return new IntegerExp(loc, (op == TOKequal), Type.tbool);
                }
            }
        }

        if (auto e = op_overload(sc))
            return e;

        if (auto e = typeCombine(this, sc))
            return e;

        auto f1 = checkNonAssignmentArrayOp(e1);
        auto f2 = checkNonAssignmentArrayOp(e2);
        if (f1 || f2)
            return new ErrorExp();

        type = Type.tbool;

        // Special handling for array comparisons
        if (!arrayTypeCompatible(loc, e1.type, e2.type))
        {
            if (e1.type != e2.type && e1.type.isfloating() && e2.type.isfloating())
            {
                // Cast both to complex
                e1 = e1.castTo(sc, Type.tcomplex80);
                e2 = e2.castTo(sc, Type.tcomplex80);
            }
        }
        if (e1.type.toBasetype().ty == Taarray)
            semanticTypeInfo(sc, e1.type.toBasetype());

        if (e1.type.toBasetype().ty == Tvector)
            return incompatibleTypes();

        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 * is and !is
 */
extern (C++) final class IdentityExp : BinExp
{
    extern (D) this(TOK op, Loc loc, Expression e1, Expression e2)
    {
        super(loc, op, __traits(classInstanceSize, IdentityExp), e1, e2);
    }

    override Expression semantic(Scope* sc)
    {
        if (type)
            return this;

        setNoderefOperands();

        if (auto e = binSemanticProp(sc))
            return e;

        if (auto e = typeCombine(this, sc))
            return e;

        auto f1 = checkNonAssignmentArrayOp(e1);
        auto f2 = checkNonAssignmentArrayOp(e2);
        if (f1 || f2)
            return new ErrorExp();

        type = Type.tbool;

        if (e1.type != e2.type && e1.type.isfloating() && e2.type.isfloating())
        {
            // Cast both to complex
            e1 = e1.castTo(sc, Type.tcomplex80);
            e2 = e2.castTo(sc, Type.tcomplex80);
        }

        if (e1.type.toBasetype().ty == Tvector)
            return incompatibleTypes();

        if (e1.type.toBasetype().ty == Tsarray ||
            e2.type.toBasetype().ty == Tsarray)
            deprecation("identity comparison of static arrays "
                "implicitly coerces them to slices, "
                "which are compared by reference");

        return this;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class CondExp : BinExp
{
    Expression econd;

    extern (D) this(Loc loc, Expression econd, Expression e1, Expression e2)
    {
        super(loc, TOKquestion, __traits(classInstanceSize, CondExp), e1, e2);
        this.econd = econd;
    }

    override Expression syntaxCopy()
    {
        return new CondExp(loc, econd.syntaxCopy(), e1.syntaxCopy(), e2.syntaxCopy());
    }

    override Expression semantic(Scope* sc)
    {
        static if (LOGSEMANTIC)
        {
            printf("CondExp::semantic('%s')\n", toChars());
        }
        if (type)
            return this;

        if (econd.op == TOKdotid)
            (cast(DotIdExp)econd).noderef = true;

        Expression ec = econd.semantic(sc);
        ec = resolveProperties(sc, ec);
        ec = ec.toBoolean(sc);

        uint cs0 = sc.callSuper;
        uint* fi0 = sc.saveFieldInit();
        Expression e1x = e1.semantic(sc);
        e1x = resolveProperties(sc, e1x);

        uint cs1 = sc.callSuper;
        uint* fi1 = sc.fieldinit;
        sc.callSuper = cs0;
        sc.fieldinit = fi0;
        Expression e2x = e2.semantic(sc);
        e2x = resolveProperties(sc, e2x);

        sc.mergeCallSuper(loc, cs1);
        sc.mergeFieldInit(loc, fi1);

        if (ec.op == TOKerror)
            return ec;
        if (ec.type == Type.terror)
            return new ErrorExp();
        econd = ec;

        if (e1x.op == TOKerror)
            return e1x;
        if (e1x.type == Type.terror)
            return new ErrorExp();
        e1 = e1x;

        if (e2x.op == TOKerror)
            return e2x;
        if (e2x.type == Type.terror)
            return new ErrorExp();
        e2 = e2x;

        auto f0 = checkNonAssignmentArrayOp(econd);
        auto f1 = checkNonAssignmentArrayOp(e1);
        auto f2 = checkNonAssignmentArrayOp(e2);
        if (f0 || f1 || f2)
            return new ErrorExp();

        // If either operand is void, the result is void
        Type t1 = e1.type;
        Type t2 = e2.type;
        if (t1.ty == Tvoid || t2.ty == Tvoid)
            type = Type.tvoid;
        else if (t1 == t2)
            type = t1;
        else
        {
            if (Expression ex = typeCombine(this, sc))
                return ex;

            switch (e1.type.toBasetype().ty)
            {
            case Tcomplex32:
            case Tcomplex64:
            case Tcomplex80:
                e2 = e2.castTo(sc, e1.type);
                break;
            default:
                break;
            }
            switch (e2.type.toBasetype().ty)
            {
            case Tcomplex32:
            case Tcomplex64:
            case Tcomplex80:
                e1 = e1.castTo(sc, e2.type);
                break;
            default:
                break;
            }
            if (type.toBasetype().ty == Tarray)
            {
                e1 = e1.castTo(sc, type);
                e2 = e2.castTo(sc, type);
            }
        }
        type = type.merge2();
        version (none)
        {
            printf("res: %s\n", type.toChars());
            printf("e1 : %s\n", e1.type.toChars());
            printf("e2 : %s\n", e2.type.toChars());
        }

        /* Bugzilla 14696: If either e1 or e2 contain temporaries which need dtor,
         * make them conditional.
         * Rewrite:
         *      cond ? (__tmp1 = ..., __tmp1) : (__tmp2 = ..., __tmp2)
         * to:
         *      (auto __cond = cond) ? (... __tmp1) : (... __tmp2)
         * and replace edtors of __tmp1 and __tmp2 with:
         *      __tmp1->edtor --> __cond && __tmp1.dtor()
         *      __tmp2->edtor --> __cond || __tmp2.dtor()
         */
        hookDtors(sc);

        return this;
    }

    override int checkModifiable(Scope* sc, int flag)
    {
        return e1.checkModifiable(sc, flag) && e2.checkModifiable(sc, flag);
    }

    override bool isLvalue()
    {
        return e1.isLvalue() && e2.isLvalue();
    }

    override Expression toLvalue(Scope* sc, Expression ex)
    {
        // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2)
        CondExp e = cast(CondExp)copy();
        e.e1 = e1.toLvalue(sc, null).addressOf();
        e.e2 = e2.toLvalue(sc, null).addressOf();
        e.type = type.pointerTo();
        return new PtrExp(loc, e, type);
    }

    override Expression modifiableLvalue(Scope* sc, Expression e)
    {
        //error("conditional expression %s is not a modifiable lvalue", toChars());
        e1 = e1.modifiableLvalue(sc, e1);
        e2 = e2.modifiableLvalue(sc, e2);
        return toLvalue(sc, this);
    }

    override Expression toBoolean(Scope* sc)
    {
        auto ex1 = e1.toBoolean(sc);
        auto ex2 = e2.toBoolean(sc);
        if (ex1.op == TOKerror)
            return ex1;
        if (ex2.op == TOKerror)
            return ex2;
        e1 = ex1;
        e2 = ex2;
        return this;
    }

    void hookDtors(Scope* sc)
    {
        extern (C++) final class DtorVisitor : StoppableVisitor
        {
            alias visit = super.visit;
        public:
            Scope* sc;
            CondExp ce;
            VarDeclaration vcond;
            bool isThen;

            extern (D) this(Scope* sc, CondExp ce)
            {
                this.sc = sc;
                this.ce = ce;
            }

            override void visit(Expression e)
            {
                //printf("(e = %s)\n", e->toChars());
            }

            override void visit(DeclarationExp e)
            {
                auto v = e.declaration.isVarDeclaration();
                if (v && !v.isDataseg())
                {
                    if (v._init)
                    {
                        if (auto ei = v._init.isExpInitializer())
                            ei.exp.accept(this);
                    }

                    if (v.needsScopeDtor())
                    {
                        if (!vcond)
                        {
                            vcond = copyToTemp(STCvolatile, "__cond", ce.econd);
                            vcond.semantic(sc);

                            Expression de = new DeclarationExp(ce.econd.loc, vcond);
                            de = de.semantic(sc);

                            Expression ve = new VarExp(ce.econd.loc, vcond);
                            ce.econd = Expression.combine(de, ve);
                        }

                        //printf("\t++v = %s, v->edtor = %s\n", v->toChars(), v->edtor->toChars());
                        Expression ve = new VarExp(vcond.loc, vcond);
                        if (isThen)
                            v.edtor = new AndAndExp(v.edtor.loc, ve, v.edtor);
                        else
                            v.edtor = new OrOrExp(v.edtor.loc, ve, v.edtor);
                        v.edtor = v.edtor.semantic(sc);
                        //printf("\t--v = %s, v->edtor = %s\n", v->toChars(), v->edtor->toChars());
                    }
                }
            }
        }

        scope DtorVisitor v = new DtorVisitor(sc, this);
        //printf("+%s\n", toChars());
        v.isThen = true;
        walkPostorder(e1, v);
        v.isThen = false;
        walkPostorder(e2, v);
        //printf("-%s\n", toChars());
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) class DefaultInitExp : Expression
{
    TOK subop;      // which of the derived classes this is

    final extern (D) this(Loc loc, TOK subop, int size)
    {
        super(loc, TOKdefault, size);
        this.subop = subop;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class FileInitExp : DefaultInitExp
{
    extern (D) this(Loc loc, TOK tok)
    {
        super(loc, tok, __traits(classInstanceSize, FileInitExp));
    }

    override Expression semantic(Scope* sc)
    {
        //printf("FileInitExp::semantic()\n");
        type = Type.tstring;
        return this;
    }

    override Expression resolveLoc(Loc loc, Scope* sc)
    {
        //printf("FileInitExp::resolve() %s\n", toChars());
        const(char)* s = loc.filename ? loc.filename : sc._module.ident.toChars();
        Expression e = new StringExp(loc, cast(char*)s);
        e = e.semantic(sc);
        e = e.castTo(sc, type);
        return e;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class LineInitExp : DefaultInitExp
{
    extern (D) this(Loc loc)
    {
        super(loc, TOKline, __traits(classInstanceSize, LineInitExp));
    }

    override Expression semantic(Scope* sc)
    {
        type = Type.tint32;
        return this;
    }

    override Expression resolveLoc(Loc loc, Scope* sc)
    {
        Expression e = new IntegerExp(loc, loc.linnum, Type.tint32);
        e = e.castTo(sc, type);
        return e;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class ModuleInitExp : DefaultInitExp
{
    extern (D) this(Loc loc)
    {
        super(loc, TOKmodulestring, __traits(classInstanceSize, ModuleInitExp));
    }

    override Expression semantic(Scope* sc)
    {
        //printf("ModuleInitExp::semantic()\n");
        type = Type.tstring;
        return this;
    }

    override Expression resolveLoc(Loc loc, Scope* sc)
    {
        const(char)* s;
        if (sc.callsc)
            s = sc.callsc._module.toPrettyChars();
        else
            s = sc._module.toPrettyChars();
        Expression e = new StringExp(loc, cast(char*)s);
        e = e.semantic(sc);
        e = e.castTo(sc, type);
        return e;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class FuncInitExp : DefaultInitExp
{
    extern (D) this(Loc loc)
    {
        super(loc, TOKfuncstring, __traits(classInstanceSize, FuncInitExp));
    }

    override Expression semantic(Scope* sc)
    {
        //printf("FuncInitExp::semantic()\n");
        type = Type.tstring;
        if (sc.func)
            return this.resolveLoc(Loc(), sc);
        return this;
    }

    override Expression resolveLoc(Loc loc, Scope* sc)
    {
        const(char)* s;
        if (sc.callsc && sc.callsc.func)
            s = sc.callsc.func.Dsymbol.toPrettyChars();
        else if (sc.func)
            s = sc.func.Dsymbol.toPrettyChars();
        else
            s = "";
        Expression e = new StringExp(loc, cast(char*)s);
        e = e.semantic(sc);
        e = e.castTo(sc, type);
        return e;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}

/***********************************************************
 */
extern (C++) final class PrettyFuncInitExp : DefaultInitExp
{
    extern (D) this(Loc loc)
    {
        super(loc, TOKprettyfunc, __traits(classInstanceSize, PrettyFuncInitExp));
    }

    override Expression semantic(Scope* sc)
    {
        //printf("PrettyFuncInitExp::semantic()\n");
        type = Type.tstring;
        if (sc.func)
            return this.resolveLoc(Loc(), sc);
        return this;
    }

    override Expression resolveLoc(Loc loc, Scope* sc)
    {
        FuncDeclaration fd;
        if (sc.callsc && sc.callsc.func)
            fd = sc.callsc.func;
        else
            fd = sc.func;

        const(char)* s;
        if (fd)
        {
            const(char)* funcStr = fd.Dsymbol.toPrettyChars();
            OutBuffer buf;
            functionToBufferWithIdent(cast(TypeFunction)fd.type, &buf, funcStr);
            s = buf.extractString();
        }
        else
        {
            s = "";
        }

        Expression e = new StringExp(loc, cast(char*)s);
        e = e.semantic(sc);
        e = e.castTo(sc, type);
        return e;
    }

    override void accept(Visitor v)
    {
        v.visit(this);
    }
}
