/**
 * Defines the bulk of the classes which represent the AST at the expression level.
 *
 * Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions)
 *
 * Copyright:   Copyright (C) 1999-2026 by The D Language Foundation, All Rights Reserved
 * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
 * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/expression.d, _expression.d)
 * Documentation:  https://dlang.org/phobos/dmd_expression.html
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/expression.d
 */

module dmd.expression;

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

import dmd.arraytypes;
import dmd.astenums;
import dmd.ast_node;
import dmd.dclass;
import dmd.declaration;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dtemplate;
import dmd.errors;
import dmd.func;
import dmd.globals : dinteger_t;
import dmd.hdrgen : toChars;
import dmd.id;
import dmd.identifier;
import dmd.init;
import dmd.location;
import dmd.mtype;
import dmd.root.complex;
import dmd.root.ctfloat;
import dmd.root.rmem;
import dmd.rootobject;
import dmd.root.string;
import dmd.root.utf;
import dmd.tokens;
import dmd.visitor;

enum LOGSEMANTIC = false;

/****************************************
 * Find the last non-comma expression.
 * Params:
 *      e = Expressions connected by commas
 * Returns:
 *      right-most non-comma expression
 */

inout(Expression) lastComma(inout Expression e)
{
    Expression ex = cast()e;
    while (ex.op == EXP.comma)
        ex = (cast(CommaExp)ex).e2;
    return cast(inout)ex;

}

/****************************************
 * If `s` is a function template, i.e. the only member of a template
 * and that member is a function, return that template.
 * Params:
 *      s = symbol that might be a function template
 * Returns:
 *      template for that function, otherwise null
 */
TemplateDeclaration getFuncTemplateDecl(Dsymbol s) @safe
{
    FuncDeclaration f = s.isFuncDeclaration();
    if (f && f.parent)
    {
        if (auto ti = f.parent.isTemplateInstance())
        {
            if (!ti.isTemplateMixin() && ti.tempdecl)
            {
                auto td = ti.tempdecl.isTemplateDeclaration();
                if (td.onemember && td.ident == f.ident)
                {
                    return td;
                }
            }
        }
    }
    return null;
}

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

enum OwnedBy : ubyte
{
    code,          // normal code expression in AST
    ctfe,          // value expression for CTFE
    cache,         // constant value cached for CTFE
}

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

/***********************************************************
 * https://dlang.org/spec/expression.html#expression
 */
extern (C++) abstract class Expression : ASTNode
{
    /// Usually, this starts out as `null` and gets set to the final expression type by
    /// `expressionSemantic`. However, for some expressions (such as `TypeExp`,`RealExp`,
    /// `VarExp`), the field can get set to an assigned type before running semantic.
    /// See `expressionSemanticDone`
    Type type;

    Loc loc;        // file location
    const EXP op;   // to minimize use of dynamic_cast

    static struct BitFields
    {
    bool parens;    // if this is a parenthesized expression
    bool rvalue;    // true if this is considered to be an rvalue, even if it is an lvalue
    bool gcPassDone; // `checkGC` has been run on this expression
    }
    import dmd.common.bitfields;
    mixin(generateBitFields!(BitFields, ubyte));

    extern (D) this(Loc loc, EXP op) scope @safe
    {
        //printf("Expression::Expression(op = %d) this = %p\n", op, this);
        this.loc = loc;
        this.op = op;
    }

    /// Returns: class instance size of this expression (implemented manually because `extern(C++)`)
    final size_t size() nothrow @nogc pure @safe const { return expSize[op]; }

    static void _init()
    {
        CTFEExp.cantexp = new CTFEExp(EXP.cantExpression);
        CTFEExp.voidexp = new CTFEExp(EXP.voidExpression);
        CTFEExp.breakexp = new CTFEExp(EXP.break_);
        CTFEExp.continueexp = new CTFEExp(EXP.continue_);
        CTFEExp.gotoexp = new CTFEExp(EXP.goto_);
        CTFEExp.showcontext = new CTFEExp(EXP.showCtfeContext);
    }

    /**
     * Deinitializes the global state of the compiler.
     *
     * This can be used to restore the state set by `_init` to its original
     * state.
     */
    static void deinitialize()
    {
        CTFEExp.cantexp = CTFEExp.cantexp.init;
        CTFEExp.voidexp = CTFEExp.voidexp.init;
        CTFEExp.breakexp = CTFEExp.breakexp.init;
        CTFEExp.continueexp = CTFEExp.continueexp.init;
        CTFEExp.gotoexp = CTFEExp.gotoexp.init;
        CTFEExp.showcontext = CTFEExp.showcontext.init;
    }

    /*********************************
     * Does *not* do a deep copy.
     */
    extern (D) final Expression copy()
    {
        Expression e;
        if (!size)
        {
            debug
            {
                fprintf(stderr, "No expression copy for: %s\n", toChars());
                printf("op = %d\n", op);
            }
            assert(0);
        }

        // memory never freed, so can use the faster bump-pointer-allocation
        e = cast(Expression)allocmemoryNoFree(size, expAlign[op]);
        //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();
    }

    // kludge for template.isExpression()
    override final DYNCAST dyncast() const
    {
        return DYNCAST.expression;
    }

    final override const(char)* toChars() const
    {
        // FIXME: mangling (see runnable/mangle.d) relies on toChars outputting __lambdaXXX here
        if (auto fe = isFuncExp())
            return fe.fd.toChars();

        return .toChars(this);
    }

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

    extern (D) static Expression combine(Expression e1, Expression e2, Expression e3) @safe
    {
        return combine(combine(e1, e2), e3);
    }

    extern (D) static Expression combine(Expression e1, Expression e2, Expression e3, Expression e4) @safe
    {
        return combine(combine(e1, e2), combine(e3, e4));
    }

    /**********************************
     * If 'e' is a tree of commas, returns the rightmost expression
     * by stripping off it from the tree. The remained part of the tree
     * is returned via e0.
     * Otherwise 'e' is directly returned and e0 is set to NULL.
     */
    extern (D) static Expression extractLast(Expression e, out Expression e0) @trusted
    {
        if (e.op != EXP.comma)
        {
            return e;
        }

        CommaExp ce = cast(CommaExp)e;
        if (ce.e2.op != EXP.comma)
        {
            e0 = ce.e1;
            return ce.e2;
        }
        else
        {
            e0 = e;

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

            return ce.e2;
        }
    }

    extern (D) static Expressions* arraySyntaxCopy(Expressions* exps)
    {
        Expressions* a = null;
        if (exps)
        {
            a = new Expressions(exps.length);
            foreach (i, e; *exps)
            {
                (*a)[i] = e ? e.syntaxCopy() : null;
            }
        }
        return a;
    }

    /******************************
     * 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)
            if (auto tr = type.isTypeReference())
            {
                Expression e = new PtrExp(loc, this, tr.next);
                return e;
            }
        return this;
    }

    final int isConst()
    {
        //printf("Expression::isConst(): %s\n", e.toChars());
        switch (op)
        {
        case EXP.int64:
        case EXP.float64:
        case EXP.complex80:
            return 1;
        case EXP.null_:
            return 0;
        case EXP.symbolOffset:
            return 2;
        default:
            return 0;
        }
        assert(0);
    }

    bool hasCode()
    {
        return true;
    }

    final pure inout nothrow @nogc @trusted
    {
        inout(IntegerExp)   isIntegerExp() { return op == EXP.int64 ? cast(typeof(return))this : null; }
        inout(ErrorExp)     isErrorExp() { return op == EXP.error ? cast(typeof(return))this : null; }
        inout(VoidInitExp)  isVoidInitExp() { return op == EXP.void_ ? cast(typeof(return))this : null; }
        inout(RealExp)      isRealExp() { return op == EXP.float64 ? cast(typeof(return))this : null; }
        inout(ComplexExp)   isComplexExp() { return op == EXP.complex80 ? cast(typeof(return))this : null; }
        inout(IdentifierExp) isIdentifierExp() { return op == EXP.identifier ? cast(typeof(return))this : null; }
        inout(DollarExp)    isDollarExp() { return op == EXP.dollar ? cast(typeof(return))this : null; }
        inout(DsymbolExp)   isDsymbolExp() { return op == EXP.dSymbol ? cast(typeof(return))this : null; }
        inout(ThisExp)      isThisExp() { return op == EXP.this_ ? cast(typeof(return))this : null; }
        inout(SuperExp)     isSuperExp() { return op == EXP.super_ ? cast(typeof(return))this : null; }
        inout(NullExp)      isNullExp() { return op == EXP.null_ ? cast(typeof(return))this : null; }
        inout(StringExp)    isStringExp() { return op == EXP.string_ ? cast(typeof(return))this : null; }
        inout(InterpExp)    isInterpExp() { return op == EXP.interpolated ? cast(typeof(return))this : null; }
        inout(TupleExp)     isTupleExp() { return op == EXP.tuple ? cast(typeof(return))this : null; }
        inout(ArrayLiteralExp) isArrayLiteralExp() { return op == EXP.arrayLiteral ? cast(typeof(return))this : null; }
        inout(AssocArrayLiteralExp) isAssocArrayLiteralExp() { return op == EXP.assocArrayLiteral ? cast(typeof(return))this : null; }
        inout(StructLiteralExp) isStructLiteralExp() { return op == EXP.structLiteral ? cast(typeof(return))this : null; }
        inout(CompoundLiteralExp) isCompoundLiteralExp() { return op == EXP.compoundLiteral ? cast(typeof(return))this : null; }
        inout(TypeExp)      isTypeExp() { return op == EXP.type ? cast(typeof(return))this : null; }
        inout(ScopeExp)     isScopeExp() { return op == EXP.scope_ ? cast(typeof(return))this : null; }
        inout(TemplateExp)  isTemplateExp() { return op == EXP.template_ ? cast(typeof(return))this : null; }
        inout(NewExp) isNewExp() { return op == EXP.new_ ? cast(typeof(return))this : null; }
        inout(NewAnonClassExp) isNewAnonClassExp() { return op == EXP.newAnonymousClass ? cast(typeof(return))this : null; }
        inout(SymOffExp)    isSymOffExp() { return op == EXP.symbolOffset ? cast(typeof(return))this : null; }
        inout(VarExp)       isVarExp() { return op == EXP.variable ? cast(typeof(return))this : null; }
        inout(OverExp)      isOverExp() { return op == EXP.overloadSet ? cast(typeof(return))this : null; }
        inout(FuncExp)      isFuncExp() { return op == EXP.function_ ? cast(typeof(return))this : null; }
        inout(DeclarationExp) isDeclarationExp() { return op == EXP.declaration ? cast(typeof(return))this : null; }
        inout(TypeidExp)    isTypeidExp() { return op == EXP.typeid_ ? cast(typeof(return))this : null; }
        inout(TraitsExp)    isTraitsExp() { return op == EXP.traits ? cast(typeof(return))this : null; }
        inout(HaltExp)      isHaltExp() { return op == EXP.halt ? cast(typeof(return))this : null; }
        inout(IsExp)        isIsExp() { return op == EXP.is_ ? cast(typeof(return))this : null; }
        inout(MixinExp)     isMixinExp() { return op == EXP.mixin_ ? cast(typeof(return))this : null; }
        inout(ImportExp)    isImportExp() { return op == EXP.import_ ? cast(typeof(return))this : null; }
        inout(AssertExp)    isAssertExp() { return op == EXP.assert_ ? cast(typeof(return))this : null; }
        inout(ThrowExp)     isThrowExp() { return op == EXP.throw_ ? cast(typeof(return))this : null; }
        inout(DotIdExp)     isDotIdExp() { return op == EXP.dotIdentifier ? cast(typeof(return))this : null; }
        inout(DotTemplateExp) isDotTemplateExp() { return op == EXP.dotTemplateDeclaration ? cast(typeof(return))this : null; }
        inout(DotVarExp)    isDotVarExp() { return op == EXP.dotVariable ? cast(typeof(return))this : null; }
        inout(DotTemplateInstanceExp) isDotTemplateInstanceExp() { return op == EXP.dotTemplateInstance ? cast(typeof(return))this : null; }
        inout(DelegateExp)  isDelegateExp() { return op == EXP.delegate_ ? cast(typeof(return))this : null; }
        inout(DotTypeExp)   isDotTypeExp() { return op == EXP.dotType ? cast(typeof(return))this : null; }
        inout(CallExp)      isCallExp() { return op == EXP.call ? cast(typeof(return))this : null; }
        inout(AddrExp)      isAddrExp() { return op == EXP.address ? cast(typeof(return))this : null; }
        inout(PtrExp)       isPtrExp() { return op == EXP.star ? cast(typeof(return))this : null; }
        inout(NegExp)       isNegExp() { return op == EXP.negate ? cast(typeof(return))this : null; }
        inout(UAddExp)      isUAddExp() { return op == EXP.uadd ? cast(typeof(return))this : null; }
        inout(ComExp)       isComExp() { return op == EXP.tilde ? cast(typeof(return))this : null; }
        inout(NotExp)       isNotExp() { return op == EXP.not ? cast(typeof(return))this : null; }
        inout(DeleteExp)    isDeleteExp() { return op == EXP.delete_ ? cast(typeof(return))this : null; }
        inout(CastExp)      isCastExp() { return op == EXP.cast_ ? cast(typeof(return))this : null; }
        inout(VectorExp)    isVectorExp() { return op == EXP.vector ? cast(typeof(return))this : null; }
        inout(VectorArrayExp) isVectorArrayExp() { return op == EXP.vectorArray ? cast(typeof(return))this : null; }
        inout(SliceExp)     isSliceExp() { return op == EXP.slice ? cast(typeof(return))this : null; }
        inout(ArrayLengthExp) isArrayLengthExp() { return op == EXP.arrayLength ? cast(typeof(return))this : null; }
        inout(ArrayExp)     isArrayExp() { return op == EXP.array ? cast(typeof(return))this : null; }
        inout(DotExp)       isDotExp() { return op == EXP.dot ? cast(typeof(return))this : null; }
        inout(CommaExp)     isCommaExp() { return op == EXP.comma ? cast(typeof(return))this : null; }
        inout(IntervalExp)  isIntervalExp() { return op == EXP.interval ? cast(typeof(return))this : null; }
        inout(DelegatePtrExp)     isDelegatePtrExp() { return op == EXP.delegatePointer ? cast(typeof(return))this : null; }
        inout(DelegateFuncptrExp) isDelegateFuncptrExp() { return op == EXP.delegateFunctionPointer ? cast(typeof(return))this : null; }
        inout(IndexExp)     isIndexExp() { return op == EXP.index ? cast(typeof(return))this : null; }
        inout(PostExp)      isPostExp()  { return (op == EXP.plusPlus || op == EXP.minusMinus) ? cast(typeof(return))this : null; }
        inout(PreExp)       isPreExp()   { return (op == EXP.prePlusPlus || op == EXP.preMinusMinus) ? cast(typeof(return))this : null; }
        inout(AssignExp)    isAssignExp()    { return op == EXP.assign ? cast(typeof(return))this : null; }
        inout(LoweredAssignExp)    isLoweredAssignExp()    { return op == EXP.loweredAssignExp ? cast(typeof(return))this : null; }
        inout(ConstructExp) isConstructExp() { return op == EXP.construct ? cast(typeof(return))this : null; }
        inout(BlitExp)      isBlitExp()      { return op == EXP.blit ? cast(typeof(return))this : null; }
        inout(AddAssignExp) isAddAssignExp() { return op == EXP.addAssign ? cast(typeof(return))this : null; }
        inout(MinAssignExp) isMinAssignExp() { return op == EXP.minAssign ? cast(typeof(return))this : null; }
        inout(MulAssignExp) isMulAssignExp() { return op == EXP.mulAssign ? cast(typeof(return))this : null; }

        inout(DivAssignExp) isDivAssignExp() { return op == EXP.divAssign ? cast(typeof(return))this : null; }
        inout(ModAssignExp) isModAssignExp() { return op == EXP.modAssign ? cast(typeof(return))this : null; }
        inout(AndAssignExp) isAndAssignExp() { return op == EXP.andAssign ? cast(typeof(return))this : null; }
        inout(OrAssignExp)  isOrAssignExp()  { return op == EXP.orAssign ? cast(typeof(return))this : null; }
        inout(XorAssignExp) isXorAssignExp() { return op == EXP.xorAssign ? cast(typeof(return))this : null; }
        inout(PowAssignExp) isPowAssignExp() { return op == EXP.powAssign ? cast(typeof(return))this : null; }

        inout(ShlAssignExp)  isShlAssignExp()  { return op == EXP.leftShiftAssign ? cast(typeof(return))this : null; }
        inout(ShrAssignExp)  isShrAssignExp()  { return op == EXP.rightShiftAssign ? cast(typeof(return))this : null; }
        inout(UshrAssignExp) isUshrAssignExp() { return op == EXP.unsignedRightShiftAssign ? cast(typeof(return))this : null; }

        inout(CatAssignExp) isCatAssignExp() { return op == EXP.concatenateAssign
                                                ? cast(typeof(return))this
                                                : null; }

        inout(CatElemAssignExp) isCatElemAssignExp() { return op == EXP.concatenateElemAssign
                                                ? cast(typeof(return))this
                                                : null; }

        inout(CatDcharAssignExp) isCatDcharAssignExp() { return op == EXP.concatenateDcharAssign
                                                ? cast(typeof(return))this
                                                : null; }

        inout(AddExp)      isAddExp() { return op == EXP.add ? cast(typeof(return))this : null; }
        inout(MinExp)      isMinExp() { return op == EXP.min ? cast(typeof(return))this : null; }
        inout(CatExp)      isCatExp() { return op == EXP.concatenate ? cast(typeof(return))this : null; }
        inout(MulExp)      isMulExp() { return op == EXP.mul ? cast(typeof(return))this : null; }
        inout(DivExp)      isDivExp() { return op == EXP.div ? cast(typeof(return))this : null; }
        inout(ModExp)      isModExp() { return op == EXP.mod ? cast(typeof(return))this : null; }
        inout(PowExp)      isPowExp() { return op == EXP.pow ? cast(typeof(return))this : null; }
        inout(ShlExp)      isShlExp() { return op == EXP.leftShift ? cast(typeof(return))this : null; }
        inout(ShrExp)      isShrExp() { return op == EXP.rightShift ? cast(typeof(return))this : null; }
        inout(UshrExp)     isUshrExp() { return op == EXP.unsignedRightShift ? cast(typeof(return))this : null; }
        inout(AndExp)      isAndExp() { return op == EXP.and ? cast(typeof(return))this : null; }
        inout(OrExp)       isOrExp() { return op == EXP.or ? cast(typeof(return))this : null; }
        inout(XorExp)      isXorExp() { return op == EXP.xor ? cast(typeof(return))this : null; }
        inout(LogicalExp)  isLogicalExp() { return (op == EXP.andAnd || op == EXP.orOr) ? cast(typeof(return))this : null; }
        //inout(CmpExp)    isCmpExp() { return op == EXP. ? cast(typeof(return))this : null; }
        inout(InExp)       isInExp() { return op == EXP.in_ ? cast(typeof(return))this : null; }
        inout(RemoveExp)   isRemoveExp() { return op == EXP.remove ? cast(typeof(return))this : null; }
        inout(EqualExp)    isEqualExp() { return (op == EXP.equal || op == EXP.notEqual) ? cast(typeof(return))this : null; }
        inout(IdentityExp) isIdentityExp() { return (op == EXP.identity || op == EXP.notIdentity) ? cast(typeof(return))this : null; }
        inout(CondExp)     isCondExp() { return op == EXP.question ? cast(typeof(return))this : null; }
        inout(GenericExp)  isGenericExp() { return op == EXP._Generic ? cast(typeof(return))this : null; }
        inout(DefaultInitExp)    isDefaultInitExp() { return op == EXP.defaultInit ? cast(typeof(return))this : null; }
        inout(ObjcClassReferenceExp) isObjcClassReferenceExp() { return op == EXP.objcClassReference ? cast(typeof(return))this : null; }
        inout(ClassReferenceExp) isClassReferenceExp() { return op == EXP.classReference ? cast(typeof(return))this : null; }
        inout(ThrownExceptionExp) isThrownExceptionExp() { return op == EXP.thrownException ? cast(typeof(return))this : null; }

        inout(UnaExp) isUnaExp() pure inout nothrow @nogc
        {
            return exptab[op] & EXPFLAGS.unary ? cast(typeof(return))this : null;
        }

        inout(BinExp) isBinExp() pure inout nothrow @nogc
        {
            return exptab[op] & EXPFLAGS.binary ? cast(typeof(return))this : null;
        }

        inout(BinAssignExp) isBinAssignExp() pure inout nothrow @nogc
        {
            return exptab[op] & EXPFLAGS.binaryAssign ? cast(typeof(return))this : null;
        }
    }

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

// Approximate Non-semantic version of the `Type.isScalar` function in `typesem`
bool _isRoughlyScalar(Type _this)
{
    if (auto tb = _this.isTypeBasic())
        return (tb.flags & TFlags.integral | TFlags.floating) != 0;
    else if (_this.ty == Tenum || _this.ty == Tpointer) // the enum is possibly scalar
        return true;
    return false;
}

/***********************************************************
 * A compile-time known integer value
 */
extern (C++) final class IntegerExp : Expression
{
    dinteger_t value;

    extern (D) this(Loc loc, dinteger_t value, Type type)
    {
        super(loc, EXP.int64);
        //printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type.toChars() : "");
        assert(type);

        /* Verify no path to the following assert failure.
         * Weirdly, the isScalar() includes floats - see enumsem.enumMemberSemantic() for the
         * base type. This is possibly a bug.
         */
        assert(_isRoughlyScalar(type) || type.ty == Terror);

        this.type = type;
        this.value = normalize(type.toBaseTypeNonSemantic().ty, value);
    }

    extern (D) this(dinteger_t value)
    {
        super(Loc.initial, EXP.int64);
        this.type = Type.tint32;
        this.value = cast(int)value;
    }

    static IntegerExp create(Loc loc, dinteger_t value, Type type)
    {
        return new IntegerExp(loc, value, type);
    }

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

    dinteger_t getInteger()
    {
        return value;
    }

    extern (D) void setInteger(dinteger_t value)
    {
        this.value = normalize(type.toBaseTypeNonSemantic().ty, value);
    }

    extern (D) static dinteger_t normalize(TY ty, dinteger_t value)
    {
        /* 'Normalize' the value of the integer to be in range of the type
         */
        dinteger_t result;
        if (ty == Tpointer)
            ty = Type.tsize_t.ty;
        switch (ty)
        {
        case Tbool:
            result = (value != 0);
            break;

        case Tint8:
            result = cast(byte)value;
            break;

        case Tchar:
        case Tuns8:
            result = cast(ubyte)value;
            break;

        case Tint16:
            result = cast(short)value;
            break;

        case Twchar:
        case Tuns16:
            result = cast(ushort)value;
            break;

        case Tint32:
            result = cast(int)value;
            break;

        case Tdchar:
        case Tuns32:
            result = cast(uint)value;
            break;

        case Tint64:
            result = cast(long)value;
            break;

        case Tuns64:
            result = cast(ulong)value;
            break;

        default:
            break;
        }
        return result;
    }

    override IntegerExp syntaxCopy()
    {
        return this;
    }

    /**
     * Use this instead of creating new instances for commonly used literals
     * such as 0 or 1.
     *
     * Parameters:
     *      v = The value of the expression
     * Returns:
     *      A static instance of the expression, typed as `Tint32`.
     */
    static IntegerExp literal(int v)()
    {
        __gshared IntegerExp theConstant;
        if (!theConstant)
            theConstant = new IntegerExp(v);
        return theConstant;
    }

    /**
     * Use this instead of creating new instances for commonly used bools.
     *
     * Parameters:
     *      b = The value of the expression
     * Returns:
     *      A static instance of the expression, typed as `Type.tbool`.
     */
    static IntegerExp createBool(bool b)
    {
        __gshared IntegerExp trueExp, falseExp;
        if (!trueExp)
        {
            trueExp = new IntegerExp(Loc.initial, 1, Type.tbool);
            falseExp = new IntegerExp(Loc.initial, 0, Type.tbool);
        }
        return b ? trueExp : falseExp;
    }
}

/***********************************************************
 * 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.initial, EXP.error);
        type = Type.terror;
    }

    static ErrorExp get ()
    {
        if (errorexp is null)
            errorexp = new ErrorExp();

        import dmd.globals : global;
        if (global.errors == 0 && global.gaggedErrors == 0)
        {
            /* Unfortunately, errors can still leak out of gagged errors,
              * and we need to set the error count to prevent bogus code
              * generation. At least give a message.
              */
            .error(Loc.initial, "unknown, please file report at https://github.com/dlang/dmd/issues/new");
        }

        return errorexp;
    }

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

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


/***********************************************************
 * An uninitialized value,
 * generated from void initializers.
 *
 * https://dlang.org/spec/declaration.html#void_init
 */
extern (C++) final class VoidInitExp : Expression
{
    VarDeclaration var; /// the variable from where the void value came from, null if not known
                        /// Useful for error messages

    extern (D) this(VarDeclaration var) @safe
    {
        super(var.loc, EXP.void_);
        this.var = var;
        this.type = var.type;
    }

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


/***********************************************************
 * A compile-time known floating point number
 */
extern (C++) final class RealExp : Expression
{
    real_t value;

    extern (D) this(Loc loc, real_t value, Type type) @safe
    {
        super(loc, EXP.float64);
        //printf("RealExp::RealExp(%Lg)\n", value);
        this.value = value;
        this.type = type;
    }

    static RealExp create(Loc loc, real_t value, Type type) @safe
    {
        return new RealExp(loc, value, type);
    }

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

/***********************************************************
 * A compile-time complex number (deprecated)
 */
extern (C++) final class ComplexExp : Expression
{
    complex_t value;

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

    static ComplexExp create(Loc loc, complex_t value, Type type) @safe
    {
        return new ComplexExp(loc, value, type);
    }

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

/***********************************************************
 * An identifier in the context of an expression (as opposed to a declaration)
 *
 * ---
 * int x; // VarDeclaration with Identifier
 * x++; // PostExp with IdentifierExp
 * ---
 */
extern (C++) class IdentifierExp : Expression
{
    Identifier ident;

    extern (D) this(Loc loc, Identifier ident) scope @safe
    {
        super(loc, EXP.identifier);
        this.ident = ident;
    }

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

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

/***********************************************************
 * The dollar operator used when indexing or slicing an array. E.g `a[$]`, `a[1 .. $]` etc.
 *
 * https://dlang.org/spec/arrays.html#array-length
 */
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.
 */
extern (C++) final class DsymbolExp : Expression
{
    Dsymbol s;
    bool hasOverloads;

    extern (D) this(Loc loc, Dsymbol s, bool hasOverloads = true) @safe
    {
        super(loc, EXP.dSymbol);
        this.s = s;
        this.hasOverloads = hasOverloads;
    }

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

/***********************************************************
 * https://dlang.org/spec/expression.html#this
 */
extern (C++) class ThisExp : Expression
{
    VarDeclaration var;

    extern (D) this(Loc loc) @safe
    {
        super(loc, EXP.this_);
        //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
    }

    this(Loc loc, const EXP tok) @safe
    {
        super(loc, tok);
        //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
    }

    override ThisExp syntaxCopy()
    {
        auto r = cast(ThisExp) super.syntaxCopy();
        // require new semantic (possibly new `var` etc.)
        r.type = null;
        r.var = null;
        return r;
    }

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

/***********************************************************
 * https://dlang.org/spec/expression.html#super
 */
extern (C++) final class SuperExp : ThisExp
{
    extern (D) this(Loc loc) @safe
    {
        super(loc, EXP.super_);
    }

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

/***********************************************************
 * A compile-time known `null` value
 *
 * https://dlang.org/spec/expression.html#null
 */
extern (C++) final class NullExp : Expression
{
    extern (D) this(Loc loc, Type type = null) scope @safe
    {
        super(loc, EXP.null_);
        this.type = type;
    }

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

/***********************************************************
 * https://dlang.org/spec/expression.html#string_literals
 */
extern (C++) final class StringExp : Expression
{
    char postfix = NoPostfix;   // 'c', 'w', 'd'
    OwnedBy ownedByCtfe = OwnedBy.code;
    private union
    {
        char* string;   // if sz == 1
        wchar* wstring; // if sz == 2
        dchar* dstring; // if sz == 4
        ulong* lstring; // if sz == 8
    }                   // (const if ownedByCtfe == OwnedBy.code)
    size_t len;         // number of code units
    ubyte sz = 1;       // 1: char, 2: wchar, 4: dchar

    /**
     *  Whether the string literal's type is fixed
     *  Example:
     *  ---
     *  wstring x = "abc"; // OK, string literal is flexible
     *  wstring y = cast(string) "abc"; // Error: type was committed after cast
     *  ---
     */
    bool committed;

    /// If the string is parsed from a hex string literal
    bool hexString = false;
    /// If the string is from a collected C macro
    bool cMacro = false;

    enum char NoPostfix = 0;

    extern (D) this(Loc loc, const(void)[] string) scope
    {
        super(loc, EXP.string_);
        this.string = cast(char*)string.ptr; // note that this.string should be const
        this.len = string.length;
        this.sz = 1;                    // work around LDC bug #1286
    }

    extern (D) this(Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix = NoPostfix, bool cMacro = false) scope
    {
        super(loc, EXP.string_);
        this.string = cast(char*)string.ptr; // note that this.string should be const
        this.len = len;
        this.sz = sz;
        this.postfix = postfix;
        this.cMacro = cMacro;
    }

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

    static StringExp create(Loc loc, const(void)* string, size_t len)
    {
        return new StringExp(loc, string[0 .. len]);
    }

    /**********************************
     * 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
     *      s = set to error message on invalid string
     * Returns:
     *      number of code units
     */
    extern (D) size_t numberOfCodeUnits(int tynto, out .string s) 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;)
            {
                s = utf_decodeChar(string[0 .. len], u, c);
                if (s)
                    return 0;
                result += utf_codeLength(encSize, c);
            }
            break;

        case 2:
            for (size_t u = 0; u < len;)
            {
                s = utf_decodeWchar(wstring[0 .. len], u, c);
                if (s)
                    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
     */
    dchar getCodeUnit(size_t i) const pure
    {
        assert(this.sz <= dchar.sizeof);
        return cast(dchar) getIndex(i);
    }

    /// Returns: integer at index `i`
    dinteger_t getIndex(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];
        case 8:
            return lstring[i];
        }
    }

    /*********************************************
     * Set the code unit at index i to c
     * Params:
     *  i = index
     *  c = code unit to set it to
     */
    extern (D) void setCodeUnit(size_t i, dchar c)
    {
        return setIndex(i, c);
    }

    extern (D) void setIndex(size_t i, long 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] = cast(dchar) c;
            break;
        case 8:
            lstring[i] = c;
            break;
        }
    }

    /**
     * Compare two `StringExp` by length, then value
     *
     * The comparison is not the usual C-style comparison as seen with
     * `strcmp` or `memcmp`, but instead first compare based on the length.
     * This allows both faster lookup and sorting when comparing sparse data.
     *
     * This ordering scheme is relied on by the string-switching feature.
     * Code in Druntime's `core.internal.switch_` relies on this ordering
     * when doing a binary search among case statements.
     *
     * Both `StringExp` should be of the same encoding.
     *
     * Params:
     *   se2 = String expression to compare `this` to
     *
     * Returns:
     *   `0` when `this` is equal to se2, a value greater than `0` if
     *   `this` should be considered greater than `se2`,
     *   and a value less than `0` if `this` is lesser than `se2`.
     */
    int compare(const StringExp se2) const nothrow pure @nogc
    {
        //printf("StringExp::compare()\n");
        const len1 = len;
        const len2 = se2.len;

        assert(this.sz == se2.sz, "Comparing string expressions of different sizes");
        //printf("sz = %d, len1 = %d, len2 = %d\n", sz, cast(int)len1, cast(int)len2);
        if (len1 != len2)
            return cast(int)(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;
                foreach (u; 0 .. len)
                {
                    if (s1[u] != s2[u])
                        return s1[u] - s2[u];
                }
            }
            break;
        case 4:
            {
                dchar* s1 = cast(dchar*)string;
                dchar* s2 = cast(dchar*)se2.string;
                foreach (u; 0 .. len)
                {
                    if (s1[u] != s2[u])
                        return s1[u] - s2[u];
                }
            }
            break;
        default:
            assert(0);
        }
        return 0;
    }

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

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

    extern (D) const(wchar)[] peekWstring() const
    {
        assert(sz == 2);
        return this.wstring[0 .. len];
    }

    extern (D) const(dchar)[] peekDstring() const
    {
        assert(sz == 4);
        return this.dstring[0 .. len];
    }

    /*******************
     * Get a slice of the data.
     */
    extern (D) const(ubyte)[] peekData() const
    {
        return cast(const(ubyte)[])this.string[0 .. len * sz];
    }

    /*******************
     * Borrow a slice of the data, so the caller can modify
     * it in-place (!)
     */
    extern (D) ubyte[] borrowData()
    {
        return cast(ubyte[])this.string[0 .. len * sz];
    }

    /***********************
     * Set new string data.
     * `this` becomes the new owner of the data.
     */
    extern (D) void setData(void* s, size_t len, ubyte sz)
    {
        this.string = cast(char*)s;
        this.len = len;
        this.sz = sz;
    }

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

extern (C++) final class InterpExp : Expression
{
    char postfix = NoPostfix;   // 'c', 'w', 'd'
    OwnedBy ownedByCtfe = OwnedBy.code;
    InterpolatedSet* interpolatedSet;

    enum char NoPostfix = 0;

    extern (D) this(Loc loc, InterpolatedSet* set, char postfix = NoPostfix) scope @safe
    {
        super(loc, EXP.interpolated);
        this.interpolatedSet = set;
        this.postfix = postfix;
    }

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


/***********************************************************
 * A sequence of expressions
 *
 * ---
 * alias AliasSeq(T...) = T;
 * alias Tup = AliasSeq!(3, int, "abc");
 * ---
 */
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) @safe
    {
        super(loc, EXP.tuple);
        //printf("TupleExp(this = %p)\n", this);
        this.e0 = e0;
        this.exps = exps;
    }

    extern (D) this(Loc loc, Expressions* exps) @safe
    {
        super(loc, EXP.tuple);
        //printf("TupleExp(this = %p)\n", this);
        this.exps = exps;
    }

    extern (D) this(Loc loc, TupleDeclaration tup)
    {
        super(loc, EXP.tuple);
        this.exps = new Expressions();
        // the rest of the constructor is moved to expressionsem.d in fillTupleExpExps function
    }

    static TupleExp create(Loc loc, Expressions* exps) @safe
    {
        return new TupleExp(loc, exps);
    }

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

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

/***********************************************************
 * [ e1, e2, e3, ... ]
 *
 * https://dlang.org/spec/expression.html#array_literals
 */
extern (C++) final class ArrayLiteralExp : Expression
{
    OwnedBy ownedByCtfe = OwnedBy.code;
    bool onstack = false;

    /** 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;

    Expression lowering;

    // aaLiteral is set if this is an array of values of an AA literal
    // only used during CTFE to show the original AA in error messages instead
    AssocArrayLiteralExp aaLiteral;

    extern (D) this(Loc loc, Type type, Expressions* elements) @safe
    {
        super(loc, EXP.arrayLiteral);
        this.type = type;
        this.elements = elements;
    }

    extern (D) this(Loc loc, Type type, Expression e)
    {
        super(loc, EXP.arrayLiteral);
        this.type = type;
        elements = new Expressions(e);
    }

    extern (D) this(Loc loc, Type type, Expression basis, Expressions* elements) @safe
    {
        super(loc, EXP.arrayLiteral);
        this.type = type;
        this.basis = basis;
        this.elements = elements;
    }

    static ArrayLiteralExp create(Loc loc, Expressions* elements) @safe
    {
        return new ArrayLiteralExp(loc, null, elements);
    }

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

    Expression getElement(size_t i) // use opIndex instead
    {
        return this[i];
    }

    extern (D) inout(Expression) opIndex(size_t i) inout
    {
        auto el = (*elements)[i];
        return el ? el : basis;
    }

    extern (D) Expression opIndexAssign(Expression value, size_t i)
    {
        (*elements)[i] = value;
        return value;
    }

    extern (D) size_t length() const { return elements ? elements.length : 0; }

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

/***********************************************************
 * [ key0 : value0, key1 : value1, ... ]
 *
 * https://dlang.org/spec/expression.html#associative_array_literals
 */
extern (C++) final class AssocArrayLiteralExp : Expression
{
    OwnedBy ownedByCtfe = OwnedBy.code;

    Expressions* keys;
    Expressions* values;

    Expression lowering;     // call to _d_assocarrayliteralTX()
    Expression loweringCtfe; // result of interpreting lowering for static initializaton

    extern (D) this(Loc loc, Expressions* keys, Expressions* values) @safe
    {
        super(loc, EXP.assocArrayLiteral);
        assert(keys.length == values.length);
        this.keys = keys;
        this.values = values;
    }

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

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

/***********************************************************
 * sd( e1, e2, e3, ... )
 */
extern (C++) final class StructLiteralExp : Expression
{
    struct BitFields
    {
        bool useStaticInit;     /// if this is true, use the StructDeclaration's init symbol
        bool isOriginal = false; /// used when moving instances to indicate `this is this.origin`
        OwnedBy ownedByCtfe = OwnedBy.code;
    }
    import dmd.common.bitfields;
    mixin(generateBitFields!(BitFields, ubyte));
    StageFlags stageflags;

    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)

    // `inlineCopy` is only used temporarily in the `inline.d` pass,
    // while `sym` is only used in `e2ir/s2ir/tocsym` which comes after
    union
    {
        void* sym;            /// back end symbol to initialize with literal (used as a Symbol*)

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

    /** 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;


    /** 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.
     */
    enum StageFlags : ubyte
    {
        none              = 0x0,
        scrub             = 0x1,  /// scrubReturnValue is running
        searchPointers    = 0x2,  /// hasNonConstPointers is running
        optimize          = 0x4,  /// optimize is running
        apply             = 0x8,  /// apply is running
        inlineScan        = 0x10, /// inlineScan is running
        toCBuffer         = 0x20 /// toCBuffer is running
    }

    extern (D) this(Loc loc, StructDeclaration sd, Expressions* elements, Type stype = null) @safe
    {
        super(loc, EXP.structLiteral);
        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 StructLiteralExp syntaxCopy()
    {
        auto exp = new StructLiteralExp(loc, sd, arraySyntaxCopy(elements), type ? type : stype);
        exp.origin = this;
        return exp;
    }

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

/***********************************************************
 * C11 6.5.2.5
 * ( type-name ) { initializer-list }
 */
extern (C++) final class CompoundLiteralExp : Expression
{
    Initializer initializer; /// initializer-list

    extern (D) this(Loc loc, Type type_name, Initializer initializer) @safe
    {
        super(loc, EXP.compoundLiteral);
        super.type = type_name;
        this.initializer = initializer;
        //printf("CompoundLiteralExp::CompoundLiteralExp(%s)\n", toChars());
    }

    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) @safe
    {
        super(loc, EXP.type);
        //printf("TypeExp::TypeExp(%s)\n", type.toChars());
        this.type = type;
    }

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

    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) @safe
    {
        super(loc, EXP.scope_);
        //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 ScopeExp syntaxCopy()
    {
        return new ScopeExp(loc, sds.syntaxCopy(null));
    }

    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) @safe
    {
        super(loc, EXP.template_);
        //printf("TemplateExp(): %s\n", td.toChars());
        this.td = td;
        this.fd = fd;
    }

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

/***********************************************************
 * newtype(arguments)
 */
extern (C++) final class NewExp : Expression
{
    Expression thisexp;         // if !=null, 'this' for class being allocated
    Type newtype;
    Expressions* arguments;     // Array of Expression's
    ArgumentLabels* names;         // Array of names(name and location of name) corresponding to expressions
    Expression placement;       // if !=null, then PlacementExpression

    Expression argprefix;       // expression to be evaluated just before arguments[]
    CtorDeclaration member;     // constructor function
    bool onstack;               // allocate on stack
    bool thrownew;              // this NewExp is the expression of a ThrowStatement

    Expression lowering;        // lowered druntime hook: `_d_new{class,itemT}`

    /// Puts the `arguments` and `names` into an `ArgumentList` for easily passing them around.
    /// The fields are still separate for backwards compatibility

    extern (D) ArgumentList argumentList() { return ArgumentList(arguments, names); }

    extern (D) this(Loc loc, Expression placement, Expression thisexp, Type newtype, Expressions* arguments, ArgumentLabels* names = null) @safe
    {
        super(loc, EXP.new_);
        this.placement = placement;
        this.thisexp = thisexp;
        this.newtype = newtype;
        this.arguments = arguments;
        this.names = names;
    }

    static NewExp create(Loc loc, Expression placement, Expression thisexp, Type newtype, Expressions* arguments) @safe
    {
        return new NewExp(loc, placement, thisexp, newtype, arguments);
    }

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

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

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

    extern (D) this(Loc loc, Expression placement, Expression thisexp, ClassDeclaration cd, Expressions* arguments) @safe
    {
        super(loc, EXP.newAnonymousClass);
        this.placement = placement;
        this.thisexp = thisexp;
        this.cd = cd;
        this.arguments = arguments;
    }

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

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

/***********************************************************
 */
extern (C++) class SymbolExp : Expression
{
    Declaration var;
    Dsymbol originalScope; // original scope before inlining
    bool hasOverloads;

    extern (D) this(Loc loc, EXP op, Declaration var, bool hasOverloads) @safe
    {
        super(loc, op);
        assert(var);
        this.var = var;
        this.hasOverloads = hasOverloads;
    }

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

/***********************************************************
 * 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())
        {
            assert(!v.needThis()); // make sure the error message is no longer necessary
            hasOverloads = false;
        }
        super(loc, EXP.symbolOffset, var, hasOverloads);
        this.offset = offset;
    }

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

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

        super(loc, EXP.variable, 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) @safe
    {
        return new VarExp(loc, var, hasOverloads);
    }

    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, EXP.overloadSet);
        //printf("OverExp(this = %p, '%s')\n", this, var.toChars());
        vars = s;
        type = Type.tvoid;
    }

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

/***********************************************************
 * Function/Delegate literal
 */

extern (C++) final class FuncExp : Expression
{
    FuncLiteralDeclaration fd;
    TemplateDeclaration td;
    TOK tok;  // TOK.reserved, TOK.delegate_, TOK.function_

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

    override FuncExp syntaxCopy()
    {
        if (td)
            return new FuncExp(loc, td.syntaxCopy(null));
        if (fd.semanticRun == PASS.initial)
            return new FuncExp(loc, fd.syntaxCopy(null));
        // https://issues.dlang.org/show_bug.cgi?id=13481
        // Prevent multiple semantic analysis of lambda body.
        return new FuncExp(loc, fd);
    }

    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) @safe
    {
        super(loc, EXP.declaration);
        this.declaration = declaration;
    }

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

    override bool hasCode()
    {
        if (auto vd = declaration.isVarDeclaration())
        {
            return !(vd.storage_class & (STC.manifest | STC.static_));
        }
        return false;
    }

    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) @safe
    {
        super(loc, EXP.typeid_);
        this.obj = o;
    }

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

    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) @safe
    {
        super(loc, EXP.traits);
        this.ident = ident;
        this.args = args;
    }

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

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

/***********************************************************
 * Generates a halt instruction
 *
 * `assert(0)` gets rewritten to this with `CHECKACTION.halt`
 */
extern (C++) final class HaltExp : Expression
{
    extern (D) this(Loc loc) @safe
    {
        super(loc, EXP.halt);
    }

    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
    Type tspec;         // can be null
    TemplateParameters* parameters;
    TOK tok;            // ':' or '=='
    TOK tok2;           // 'struct', 'union', etc.

    extern (D) this(Loc loc, Type targ, Identifier id, TOK tok, Type tspec, TOK tok2, TemplateParameters* parameters) scope @safe
    {
        super(loc, EXP.is_);
        this.targ = targ;
        this.id = id;
        this.tok = tok;
        this.tspec = tspec;
        this.tok2 = tok2;
        this.parameters = parameters;
    }

    override IsExp syntaxCopy()
    {
        // This section is identical to that in TemplateDeclaration::syntaxCopy()
        TemplateParameters* p = null;
        if (parameters)
        {
            p = new TemplateParameters(parameters.length);
            foreach (i, el; *parameters)
                (*p)[i] = el.syntaxCopy();
        }
        return new IsExp(loc, targ.syntaxCopy(), id, tok, tspec ? tspec.syntaxCopy() : null, tok2, p);
    }

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

/***********************************************************
 * Base class for unary operators
 *
 * https://dlang.org/spec/expression.html#unary-expression
 */
extern (C++) abstract class UnaExp : Expression
{
    Expression e1;

    extern (D) this(Loc loc, EXP op, Expression e1) scope @safe
    {
        super(loc, op);
        this.e1 = e1;
    }

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

    /*********************
     * 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 (auto edi = e1.isDotIdExp())
            edi.noderef = true;

    }

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

/***********************************************************
 * Base class for binary operators
 */
extern (C++) abstract class BinExp : Expression
{
    Expression e1;
    Expression e2;

    extern (D) this(Loc loc, EXP op, Expression e1, Expression e2) scope @safe
    {
        super(loc, op);
        this.e1 = e1;
        this.e2 = e2;
    }

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

    /*********************
     * 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 (auto edi = e1.isDotIdExp())
            edi.noderef = true;
        if (auto edi = e2.isDotIdExp())
            edi.noderef = true;

    }

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

/***********************************************************
 * Binary operator assignment, `+=` `-=` `*=` etc.
 */
extern (C++) class BinAssignExp : BinExp
{
    extern (D) this(Loc loc, EXP op, Expression e1, Expression e2) scope @safe
    {
        super(loc, op, e1, e2);
    }

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

/***********************************************************
 * A string mixin, `mixin("x")`
 *
 * https://dlang.org/spec/expression.html#mixin_expressions
 */
extern (C++) final class MixinExp : Expression
{
    Expressions* exps;

    extern (D) this(Loc loc, Expressions* exps) @safe
    {
        super(loc, EXP.mixin_);
        this.exps = exps;
    }

    override MixinExp syntaxCopy()
    {
        return new MixinExp(loc, arraySyntaxCopy(exps));
    }

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

/***********************************************************
 * An import expression, `import("file.txt")`
 *
 * Not to be confused with module imports, `import std.stdio`, which is an `ImportStatement`
 *
 * https://dlang.org/spec/expression.html#import_expressions
 */
extern (C++) final class ImportExp : UnaExp
{
    extern (D) this(Loc loc, Expression e) @safe
    {
        super(loc, EXP.import_, e);
    }

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

/***********************************************************
 * An assert expression, `assert(x == y)`
 *
 * https://dlang.org/spec/expression.html#assert_expressions
 */
extern (C++) final class AssertExp : UnaExp
{
    Expression msg;
    Expression loweredFrom;

    extern (D) this(Loc loc, Expression e, Expression msg = null) @safe
    {
        super(loc, EXP.assert_, e);
        this.msg = msg;
    }

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

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

/***********************************************************
 * `throw <e1>` as proposed by DIP 1034.
 *
 * Replacement for the deprecated `ThrowStatement` that can be nested
 * in other expression.
 */
extern (C++) final class ThrowExp : UnaExp
{
    extern (D) this(Loc loc, Expression e)
    {
        super(loc, EXP.throw_, e);
    }

    override ThrowExp syntaxCopy()
    {
        return new ThrowExp(loc, e1.syntaxCopy());
    }

    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
    bool wantsym;       // do not replace Symbol with its initializer during semantic()
    bool arrow;         // ImportC: if -> instead of .

    extern (D) this(Loc loc, Expression e, Identifier ident) @safe
    {
        super(loc, EXP.dotIdentifier, e);
        this.ident = ident;
    }

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

    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) @safe
    {
        super(loc, EXP.dotTemplateDeclaration, e);
        this.td = td;
    }

    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) @safe
    {
        if (var.isVarDeclaration())
            hasOverloads = false;

        super(loc, EXP.dotVariable, e);
        //printf("DotVarExp()\n");
        this.var = var;
        this.hasOverloads = hasOverloads;
    }

    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, TemplateInstance ti) @safe
    {
        super(loc, EXP.dotTemplateInstance, e);
        this.ti = ti;
    }

    extern (D) this(Loc loc, Expression e, Identifier name, Objects* tiargs)
    {
        //printf("DotTemplateInstanceExp()\n");
        this(loc, e, new TemplateInstance(loc, name, tiargs));
    }

    override DotTemplateInstanceExp syntaxCopy()
    {
        return new DotTemplateInstanceExp(loc, e1.syntaxCopy(), ti.name, TemplateInstance.arraySyntaxCopy(ti.tiargs));
    }

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

/***********************************************************
 */
extern (C++) final class DelegateExp : UnaExp
{
    FuncDeclaration func;
    bool hasOverloads;
    VarDeclaration vthis2;  // container for multi-context

    extern (D) this(Loc loc, Expression e, FuncDeclaration f, bool hasOverloads = true, VarDeclaration vthis2 = null) @safe
    {
        super(loc, EXP.delegate_, e);
        this.func = f;
        this.hasOverloads = hasOverloads;
        this.vthis2 = vthis2;
    }

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

/***********************************************************
 */
extern (C++) final class DotTypeExp : UnaExp
{
    Dsymbol sym;        // symbol that represents a type

    extern (D) this(Loc loc, Expression e, Dsymbol s) @safe
    {
        super(loc, EXP.dotType, e);
        this.sym = s;
    }

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

/**
 * The arguments of a function call
 *
 * Contains a list of expressions. If it is a named argument, the `names`
 * list has a non-null entry at the same index.
 */
struct ArgumentList
{
    Expressions* arguments; // function arguments
    ArgumentLabels* names;  // named argument labels

    size_t length() const @nogc nothrow pure @safe { return arguments ? arguments.length : 0; }

    /// Returns: whether this argument list contains any named arguments
    bool hasArgNames() const @nogc nothrow pure @safe
    {
        if (names is null)
            return false;
        foreach (argLabel; *names)
            if (argLabel.name !is null)
                return true;

        return false;
    }
}

// Contains both `name` and `location of the name` for an expression.
struct ArgumentLabel
{
    Identifier name;    // name of the argument
    Loc loc;            // location of the name
 }

/***********************************************************
 */
extern (C++) final class CallExp : UnaExp
{
    Expressions* arguments; // function arguments
    ArgumentLabels *names;  // named argument labels
    FuncDeclaration f;      // symbol to call
    bool directcall;        // true if a virtual call is devirtualized
    bool inDebugStatement;  /// true if this was in a debug statement
    bool ignoreAttributes;  /// don't enforce attributes (e.g. call @gc function in @nogc code)
    bool isUfcsRewrite;     /// the first argument was pushed in here by a UFCS rewrite
    bool fromOpOverload;    // set for operator overload method call
    bool fromOpAssignment;  // set when operator overload method call from assignment (2024 edition)
    VarDeclaration vthis2;  // container for multi-context
    Expression loweredFrom; // set if this is the result of a lowering (not for opOverloads)

    /// Puts the `arguments` and `names` into an `ArgumentList` for easily passing them around.
    /// The fields are still separate for backwards compatibility
    extern (D) ArgumentList argumentList() { return ArgumentList(arguments, names); }

    extern (D) this(Loc loc, Expression e, Expressions* exps, ArgumentLabels *names = null) @safe
    {
        super(loc, EXP.call, e);
        this.arguments = exps;
        this.names = names;
    }

    extern (D) this(Loc loc, Expression e) @safe
    {
        super(loc, EXP.call, e);
    }

    extern (D) this(Loc loc, Expression e, Expression earg1)
    {
        super(loc, EXP.call, e);
        this.arguments = new Expressions();
        if (earg1)
            this.arguments.push(earg1);
    }

    extern (D) this(Loc loc, Expression e, Expression earg1, Expression earg2)
    {
        super(loc, EXP.call, e);
        auto arguments = new Expressions(2);
        (*arguments)[0] = earg1;
        (*arguments)[1] = earg2;
        this.arguments = arguments;
    }

    /***********************************************************
    * Instantiates a new function call expression
    * Params:
    *       loc   = location
    *       fd    = the declaration of the function to call
    *       earg1 = the function argument
    */
    extern(D) this(Loc loc, FuncDeclaration fd, Expression earg1)
    {
        this(loc, new VarExp(loc, fd, false), earg1);
        this.f = fd;
    }

    static CallExp create(Loc loc, Expression e, Expressions* exps) @safe
    {
        return new CallExp(loc, e, exps);
    }

    static CallExp create(Loc loc, Expression e) @safe
    {
        return new CallExp(loc, e);
    }

    static CallExp create(Loc loc, Expression e, Expression earg1)
    {
        return new CallExp(loc, e, earg1);
    }

    /***********************************************************
    * Creates a new function call expression
    * Params:
    *       loc   = location
    *       fd    = the declaration of the function to call
    *       earg1 = the function argument
    */
    static CallExp create(Loc loc, FuncDeclaration fd, Expression earg1)
    {
        return new CallExp(loc, fd, earg1);
    }

    override CallExp syntaxCopy()
    {
        return new CallExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments), names ? names.copy() : null);
    }

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

FuncDeclaration isFuncAddress(Expression e, bool* hasOverloads = null) @safe
{
    if (auto ae = e.isAddrExp())
    {
        auto ae1 = ae.e1;
        if (auto ve = ae1.isVarExp())
        {
            if (hasOverloads)
                *hasOverloads = ve.hasOverloads;
            return ve.var.isFuncDeclaration();
        }
        if (auto dve = ae1.isDotVarExp())
        {
            if (hasOverloads)
                *hasOverloads = dve.hasOverloads;
            return dve.var.isFuncDeclaration();
        }
    }
    else
    {
        if (auto soe = e.isSymOffExp())
        {
            if (hasOverloads)
                *hasOverloads = soe.hasOverloads;
            return soe.var.isFuncDeclaration();
        }
        if (auto dge = e.isDelegateExp())
        {
            if (hasOverloads)
                *hasOverloads = dge.hasOverloads;
            return dge.func.isFuncDeclaration();
        }
    }
    return null;
}

/***********************************************************
 * The 'address of' operator, `&p`
 */
extern (C++) final class AddrExp : UnaExp
{
    extern (D) this(Loc loc, Expression e) @safe
    {
        super(loc, EXP.address, e);
    }

    extern (D) this(Loc loc, Expression e, Type t) @safe
    {
        this(loc, e);
        type = t;
    }

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

/***********************************************************
 * The pointer dereference operator, `*p`
 */
extern (C++) final class PtrExp : UnaExp
{
    extern (D) this(Loc loc, Expression e) @safe
    {
        super(loc, EXP.star, e);
        //if (e.type)
        //  type = ((TypePointer *)e.type).next;
    }

    extern (D) this(Loc loc, Expression e, Type t) @safe
    {
        super(loc, EXP.star, e);
        type = t;
    }

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

/***********************************************************
 * The negation operator, `-x`
 */
extern (C++) final class NegExp : UnaExp
{
    extern (D) this(Loc loc, Expression e) @safe
    {
        super(loc, EXP.negate, e);
    }

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

/***********************************************************
 * The unary add operator, `+x`
 */
extern (C++) final class UAddExp : UnaExp
{
    extern (D) this(Loc loc, Expression e) scope @safe
    {
        super(loc, EXP.uadd, e);
    }

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

/***********************************************************
 * The bitwise complement operator, `~x`
 */
extern (C++) final class ComExp : UnaExp
{
    extern (D) this(Loc loc, Expression e) @safe
    {
        super(loc, EXP.tilde, e);
    }

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

/***********************************************************
 * The logical not operator, `!x`
 */
extern (C++) final class NotExp : UnaExp
{
    Expression loweredFrom; // for lowering of `aa1 != aa2` to `!_d_aaEqual(aa1, aa2)`

    extern (D) this(Loc loc, Expression e) @safe
    {
        super(loc, EXP.not, e);
    }

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

/***********************************************************
 * The delete operator, `delete x` (deprecated)
 *
 * https://dlang.org/spec/expression.html#delete_expressions
 */
extern (C++) final class DeleteExp : UnaExp
{
    bool isRAII;        // true if called automatically as a result of scoped destruction

    extern (D) this(Loc loc, Expression e, bool isRAII) @safe
    {
        super(loc, EXP.delete_, e);
        this.isRAII = isRAII;
    }

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

/***********************************************************
 * The type cast operator, `cast(T) x`
 *
 * It's possible to cast to one type while painting to another type
 *
 * https://dlang.org/spec/expression.html#cast_expressions
 */
extern (C++) final class CastExp : UnaExp
{
    Type to;                    // type to cast to
    ubyte mod = cast(ubyte)~0;  // MODxxxxx
    bool trusted; // assume cast is safe
    Expression lowering;

    extern (D) this(Loc loc, Expression e, Type t) @safe
    {
        super(loc, EXP.cast_, e);
        this.to = t;
    }

    /* For cast(const) and cast(immutable)
     */
    extern (D) this(Loc loc, Expression e, ubyte mod) @safe
    {
        super(loc, EXP.cast_, e);
        this.mod = mod;
    }

    override CastExp syntaxCopy()
    {
        return to ? new CastExp(loc, e1.syntaxCopy(), to.syntaxCopy()) : new CastExp(loc, e1.syntaxCopy(), mod);
    }

    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
    OwnedBy ownedByCtfe = OwnedBy.code;

    extern (D) this(Loc loc, Expression e, Type t) @trusted
    {
        super(loc, EXP.vector, e);
        assert(t.ty == Tvector);
        to = cast(TypeVector)t;
    }

    static VectorExp create(Loc loc, Expression e, Type t) @safe
    {
        return new VectorExp(loc, e, t);
    }

    override VectorExp syntaxCopy()
    {
        return new VectorExp(loc, e1.syntaxCopy(), to.syntaxCopy());
    }

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

/***********************************************************
 * e1.array property for vectors.
 *
 * https://dlang.org/spec/simd.html#properties
 */
extern (C++) final class VectorArrayExp : UnaExp
{
    extern (D) this(Loc loc, Expression e1) @safe
    {
        super(loc, EXP.vectorArray, e1);
    }

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

/***********************************************************
 * e1 [lwr .. upr]
 *
 * https://dlang.org/spec/expression.html#slice_expressions
 */
extern (C++) final class SliceExp : UnaExp
{
    Expression upr;             // null if implicit 0
    Expression lwr;             // null if implicit [length - 1]

    VarDeclaration lengthVar;

    private extern(D) static struct BitFields
    {
        bool upperIsInBounds;       // true if upr <= e1.length
        bool lowerIsLessThanUpper;  // true if lwr <= upr
        bool arrayop;               // an array operation, rather than a slice
    }
    import dmd.common.bitfields : generateBitFields;
    mixin(generateBitFields!(BitFields, ubyte));

    /************************************************************/
    extern (D) this(Loc loc, Expression e1, IntervalExp ie) @safe
    {
        super(loc, EXP.slice, e1);
        this.upr = ie ? ie.upr : null;
        this.lwr = ie ? ie.lwr : null;
    }

    extern (D) this(Loc loc, Expression e1, Expression lwr, Expression upr) @safe
    {
        super(loc, EXP.slice, e1);
        this.upr = upr;
        this.lwr = lwr;
    }

    override SliceExp syntaxCopy()
    {
        auto se = new SliceExp(loc, e1.syntaxCopy(), lwr ? lwr.syntaxCopy() : null, upr ? upr.syntaxCopy() : null);
        se.lengthVar = this.lengthVar; // bug7871
        return se;
    }

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

/***********************************************************
 * The `.length` property of an array
 */
extern (C++) final class ArrayLengthExp : UnaExp
{
    extern (D) this(Loc loc, Expression e1) @safe
    {
        super(loc, EXP.arrayLength, e1);
    }

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

/***********************************************************
 * e1 [ a0, a1, a2, a3 ,... ]
 *
 * https://dlang.org/spec/expression.html#index_expressions
 */
extern (C++) final class ArrayExp : UnaExp
{
    Expressions* arguments;     // Array of Expression's a0..an

    size_t currentDimension;    // for opDollar
    VarDeclaration lengthVar;
    bool modifiable = false;    // is this expected to be an lvalue in an AssignExp? propagate to IndexExp

    extern (D) this(Loc loc, Expression e1, Expression index = null)
    {
        super(loc, EXP.array, e1);
        arguments = new Expressions();
        if (index)
            arguments.push(index);
    }

    extern (D) this(Loc loc, Expression e1, Expressions* args) @safe
    {
        super(loc, EXP.array, e1);
        arguments = args;
    }

    override ArrayExp syntaxCopy()
    {
        auto ae = new ArrayExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments));
        ae.lengthVar = this.lengthVar; // bug7871
        return ae;
    }

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

/***********************************************************
 */
extern (C++) final class DotExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.dot, e1, e2);
    }

    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;

    /// `true` if this comma chain was introduced by inline expansion.
    bool isInlineSequence;

    /// 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;

    /// The original expression before any rewriting occurs.
    /// This is used in error messages.
    Expression originalExp;

    extern (D) this(Loc loc, Expression e1, Expression e2, bool generated = true) @safe
    {
        super(loc, EXP.comma, e1, e2);
        allowCommaExp = isGenerated = generated;
    }

    extern (D) this(Loc loc, Expression e1, Expression e2, Expression oe) @safe
    {
        this(loc, e1, e2);
        originalExp = oe;
    }

    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) @safe
    {
        if (exp)
            if (auto ce = exp.isCommaExp())
                ce.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) @safe
    {
        super(loc, EXP.interval);
        this.lwr = lwr;
        this.upr = upr;
    }

    override Expression syntaxCopy()
    {
        return new IntervalExp(loc, lwr.syntaxCopy(), upr.syntaxCopy());
    }

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

/***********************************************************
 * The `dg.ptr` property, pointing to the delegate's 'context'
 *
 * c.f.`DelegateFuncptrExp` for the delegate's function pointer `dg.funcptr`
 */
extern (C++) final class DelegatePtrExp : UnaExp
{
    extern (D) this(Loc loc, Expression e1) @safe
    {
        super(loc, EXP.delegatePointer, e1);
    }

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

/***********************************************************
 * The `dg.funcptr` property, pointing to the delegate's function
 *
 * c.f.`DelegatePtrExp` for the delegate's function pointer `dg.ptr`
 */
extern (C++) final class DelegateFuncptrExp : UnaExp
{
    extern (D) this(Loc loc, Expression e1) @safe
    {
        super(loc, EXP.delegateFunctionPointer, e1);
    }

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

/***********************************************************
 * e1 [ e2 ]
 */
extern (C++) final class IndexExp : BinExp
{
    VarDeclaration lengthVar;
    Expression loweredFrom;     // for associative array lowering to _d_aaGetY or _d_aaGetRvalueX
    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) @safe
    {
        super(loc, EXP.index, e1, e2);
        //printf("IndexExp::IndexExp('%s')\n", toChars());
    }

    extern (D) this(Loc loc, Expression e1, Expression e2, bool indexIsInBounds) @safe
    {
        super(loc, EXP.index, e1, e2);
        this.indexIsInBounds = indexIsInBounds;
        //printf("IndexExp::IndexExp('%s')\n", toChars());
    }

    override IndexExp syntaxCopy()
    {
        auto ie = new IndexExp(loc, e1.syntaxCopy(), e2.syntaxCopy());
        ie.lengthVar = this.lengthVar; // bug7871
        return ie;
    }

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

/***********************************************************
 * The postfix increment/decrement operator, `i++` / `i--`
 */
extern (C++) final class PostExp : BinExp
{
    extern (D) this(EXP op, Loc loc, Expression e)
    {
        super(loc, op, e, IntegerExp.literal!1);
        assert(op == EXP.minusMinus || op == EXP.plusPlus);
    }

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

/***********************************************************
 * The prefix increment/decrement operator, `++i` / `--i`
 */
extern (C++) final class PreExp : UnaExp
{
    extern (D) this(EXP op, Loc loc, Expression e) @safe
    {
        super(loc, op, e);
        assert(op == EXP.preMinusMinus || op == EXP.prePlusPlus);
    }

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

enum MemorySet
{
    none            = 0,    // simple assignment
    blockAssign     = 1,    // setting the contents of an array
    referenceInit   = 2,    // setting the reference of STC.ref_ variable
}

/***********************************************************
 * The assignment / initialization operator, `=`
 *
 * Note: operator assignment `op=` has a different base class, `BinAssignExp`
 */
extern (C++) class AssignExp : BinExp
{
    MemorySet memset;

    /************************************************************/
    /* op can be EXP.assign, EXP.construct, or EXP.blit */
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.assign, e1, e2);
    }

    this(Loc loc, EXP tok, Expression e1, Expression e2) @safe
    {
        super(loc, tok, e1, e2);
    }

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

/***********************************************************
 * When an assignment expression is lowered to a druntime call
 * this class is used to store the lowering.
 * It essentially behaves the same as an AssignExp, but it is
 * used to not waste space for other AssignExp that are not
 * lowered to anything.
 */
extern (C++) final class LoweredAssignExp : AssignExp
{
    Expression lowering;
    extern (D) this(AssignExp exp, Expression lowering) @safe
    {
        super(exp.loc, EXP.loweredAssignExp, exp.e1, exp.e2);
        this.lowering = lowering;
    }

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

/***********************************************************
 */
extern (C++) final class ConstructExp : AssignExp
{
    Expression lowering;

    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.construct, e1, e2);
    }

    // Internal use only. If `v` is a reference variable, the assignment
    // will become a reference initialization automatically.
    extern (D) this(Loc loc, VarDeclaration v, Expression e2) @safe
    {
        auto ve = new VarExp(loc, v);
        assert(v.type && ve.type);

        super(loc, EXP.construct, ve, e2);

        if (v.isReference())
            memset = MemorySet.referenceInit;
    }

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

/***********************************************************
 * A bit-for-bit copy from `e2` to `e1`
 */
extern (C++) final class BlitExp : AssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.blit, e1, e2);
    }

    // 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) @safe
    {
        auto ve = new VarExp(loc, v);
        assert(v.type && ve.type);

        super(loc, EXP.blit, ve, e2);

        if (v.isReference())
            memset = MemorySet.referenceInit;
    }

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

/***********************************************************
 * `x += y`
 */
extern (C++) final class AddAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.addAssign, e1, e2);
    }

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

/***********************************************************
 * `x -= y`
 */
extern (C++) final class MinAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.minAssign, e1, e2);
    }

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

/***********************************************************
 * `x *= y`
 */
extern (C++) final class MulAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.mulAssign, e1, e2);
    }

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

/***********************************************************
 * `x /= y`
 */
extern (C++) final class DivAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.divAssign, e1, e2);
    }

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

/***********************************************************
 * `x %= y`
 */
extern (C++) final class ModAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.modAssign, e1, e2);
    }

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

/***********************************************************
 * `x &= y`
 */
extern (C++) final class AndAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.andAssign, e1, e2);
    }

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

/***********************************************************
 * `x |= y`
 */
extern (C++) final class OrAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.orAssign, e1, e2);
    }

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

/***********************************************************
 * `x ^= y`
 */
extern (C++) final class XorAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.xorAssign, e1, e2);
    }

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

/***********************************************************
 * `x ^^= y`
 */
extern (C++) final class PowAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.powAssign, e1, e2);
    }

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

/***********************************************************
 * `x <<= y`
 */
extern (C++) final class ShlAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.leftShiftAssign, e1, e2);
    }

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

/***********************************************************
 * `x >>= y`
 */
extern (C++) final class ShrAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.rightShiftAssign, e1, e2);
    }

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

/***********************************************************
 * `x >>>= y`
 */
extern (C++) final class UshrAssignExp : BinAssignExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.unsignedRightShiftAssign, e1, e2);
    }

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

/***********************************************************
 * The `~=` operator.
 *
 * It can have one of the following operators:
 *
 * EXP.concatenateAssign      - appending T[] to T[]
 * EXP.concatenateElemAssign  - appending T to T[]
 * EXP.concatenateDcharAssign - appending dchar to T[]
 *
 * The parser initially sets it to EXP.concatenateAssign, and semantic() later decides which
 * of the three it will be set to.
 */
extern (C++) class CatAssignExp : BinAssignExp
{
    Expression lowering;    // lowered druntime hook `_d_arrayappend{cTX,T}`

    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.concatenateAssign, e1, e2);
    }

    extern (D) this(Loc loc, EXP tok, Expression e1, Expression e2) @safe
    {
        super(loc, tok, e1, e2);
    }

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

/***********************************************************
 * The `~=` operator when appending a single element
 */
extern (C++) final class CatElemAssignExp : CatAssignExp
{
    extern (D) this(Loc loc, Type type, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.concatenateElemAssign, e1, e2);
        this.type = type;
    }

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

/***********************************************************
 * The `~=` operator when appending a single `dchar`
 */
extern (C++) final class CatDcharAssignExp : CatAssignExp
{
    extern (D) this(Loc loc, Type type, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.concatenateDcharAssign, e1, e2);
        this.type = type;
    }

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

/***********************************************************
 * The addition operator, `x + y`
 *
 * https://dlang.org/spec/expression.html#add_expressions
 */
extern (C++) final class AddExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.add, e1, e2);
    }

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

/***********************************************************
 * The minus operator, `x - y`
 *
 * https://dlang.org/spec/expression.html#add_expressions
 */
extern (C++) final class MinExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.min, e1, e2);
    }

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

/***********************************************************
 * The concatenation operator, `x ~ y`
 *
 * https://dlang.org/spec/expression.html#cat_expressions
 */
extern (C++) final class CatExp : BinExp
{
    Expression lowering;  // call to druntime hook `_d_arraycatnTX`

    extern (D) this(Loc loc, Expression e1, Expression e2) scope @safe
    {
        super(loc, EXP.concatenate, e1, e2);
    }

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

/***********************************************************
 * The multiplication operator, `x * y`
 *
 * https://dlang.org/spec/expression.html#mul_expressions
 */
extern (C++) final class MulExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.mul, e1, e2);
    }

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

/***********************************************************
 * The division operator, `x / y`
 *
 * https://dlang.org/spec/expression.html#mul_expressions
 */
extern (C++) final class DivExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.div, e1, e2);
    }

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

/***********************************************************
 * The modulo operator, `x % y`
 *
 * https://dlang.org/spec/expression.html#mul_expressions
 */
extern (C++) final class ModExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.mod, e1, e2);
    }

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

/***********************************************************
 * The 'power' operator, `x ^^ y`
 *
 * https://dlang.org/spec/expression.html#pow_expressions
 */
extern (C++) final class PowExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.pow, e1, e2);
    }

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

/***********************************************************
 * The 'shift left' operator, `x << y`
 *
 * https://dlang.org/spec/expression.html#shift_expressions
 */
extern (C++) final class ShlExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.leftShift, e1, e2);
    }

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

/***********************************************************
 * The 'shift right' operator, `x >> y`
 *
 * https://dlang.org/spec/expression.html#shift_expressions
 */
extern (C++) final class ShrExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.rightShift, e1, e2);
    }

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

/***********************************************************
 * The 'unsigned shift right' operator, `x >>> y`
 *
 * https://dlang.org/spec/expression.html#shift_expressions
 */
extern (C++) final class UshrExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.unsignedRightShift, e1, e2);
    }

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

/***********************************************************
 * The bitwise 'and' operator, `x & y`
 *
 * https://dlang.org/spec/expression.html#and_expressions
 */
extern (C++) final class AndExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.and, e1, e2);
    }

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

/***********************************************************
 * The bitwise 'or' operator, `x | y`
 *
 * https://dlang.org/spec/expression.html#or_expressions
 */
extern (C++) final class OrExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.or, e1, e2);
    }

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

/***********************************************************
 * The bitwise 'xor' operator, `x ^ y`
 *
 * https://dlang.org/spec/expression.html#xor_expressions
 */
extern (C++) final class XorExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.xor, e1, e2);
    }

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

/***********************************************************
 * The logical 'and' / 'or' operator, `X && Y` / `X || Y`
 *
 * https://dlang.org/spec/expression.html#andand_expressions
 * https://dlang.org/spec/expression.html#oror_expressions
 */
extern (C++) final class LogicalExp : BinExp
{
    extern (D) this(Loc loc, EXP op, Expression e1, Expression e2) @safe
    {
        super(loc, op, e1, e2);
        assert(op == EXP.andAnd || op == EXP.orOr);
    }

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

/***********************************************************
 * A comparison operator, `<` `<=` `>` `>=`
 *
 * `op` is one of:
 *      EXP.lessThan, EXP.lessOrEqual, EXP.greaterThan, EXP.greaterOrEqual
 *
 * https://dlang.org/spec/expression.html#relation_expressions
 */
extern (C++) final class CmpExp : BinExp
{
    extern (D) this(EXP op, Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, op, e1, e2);
        assert(op == EXP.lessThan || op == EXP.lessOrEqual || op == EXP.greaterThan || op == EXP.greaterOrEqual);
    }

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

/***********************************************************
 * The `in` operator, `"a" in ["a": 1]`
 *
 * Note: `x !in y` is rewritten to `!(x in y)` in the parser
 *
 * https://dlang.org/spec/expression.html#in_expressions
 */
extern (C++) final class InExp : BinExp
{
    extern (D) this(Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, EXP.in_, e1, e2);
    }

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

/***********************************************************
 * Associative array removal, `aa.remove(key)`
 *
 * This deletes the key ekey from the associative array eaa
 */
extern (C++) final class RemoveExp : BinExp
{
    extern (D) this(Loc loc, Expression eaa, Expression ekey)
    {
        super(loc, EXP.remove, eaa, ekey);
    }

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

/***********************************************************
 * `==` and `!=`
 *
 * EXP.equal and EXP.notEqual
 *
 * https://dlang.org/spec/expression.html#equality_expressions
 */
extern (C++) final class EqualExp : BinExp
{
    Expression lowering;
    extern (D) this(EXP op, Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, op, e1, e2);
        assert(op == EXP.equal || op == EXP.notEqual);
    }

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

/***********************************************************
 * `is` and `!is`
 *
 * EXP.identity and EXP.notIdentity
 *
 *  https://dlang.org/spec/expression.html#identity_expressions
 */
extern (C++) final class IdentityExp : BinExp
{
    extern (D) this(EXP op, Loc loc, Expression e1, Expression e2) @safe
    {
        super(loc, op, e1, e2);
        assert(op == EXP.identity || op == EXP.notIdentity);
    }

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

/***********************************************************
 * The ternary operator, `econd ? e1 : e2`
 *
 * https://dlang.org/spec/expression.html#conditional_expressions
 */
extern (C++) final class CondExp : BinExp
{
    Expression econd;

    extern (D) this(Loc loc, Expression econd, Expression e1, Expression e2) scope @safe
    {
        super(loc, EXP.question, e1, e2);
        this.econd = econd;
    }

    override CondExp syntaxCopy()
    {
        return new CondExp(loc, econd.syntaxCopy(), e1.syntaxCopy(), e2.syntaxCopy());
    }

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

/***********************************************************
 * A special keyword when used as a function's default argument
 *
 * When possible, special keywords are resolved in the parser, but when
 * appearing as a default argument, they result in an expression deriving
 * from this base class that is resolved for each function call.
 *
 * ---
 * const x = __LINE__; // resolved in the parser
 * void foo(string file = __FILE__, int line = __LINE__); // DefaultInitExp
 * ---
 *
 * https://dlang.org/spec/expression.html#specialkeywords
 */
extern (C++) class DefaultInitExp : Expression
{
    TOK tok; /// which special token this is

    extern (D) this(Loc loc, TOK tok) @safe
    {
        super(loc, EXP.defaultInit);
        this.tok = tok;
    }

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

    override Expression syntaxCopy()
    {
        return new DefaultInitExp(loc, tok);
    }
}

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

    extern (D) this(Loc loc, StructLiteralExp lit, Type type) @safe
    {
        super(loc, EXP.classReference);
        assert(lit && lit.sd && lit.sd.isClassDeclaration());
        this.value = lit;
        this.type = type;
    }

    ClassDeclaration originalClass()
    {
        return value.sd.isClassDeclaration();
    }

    // Return index of the field, or -1 if not found
    // Same as getFieldIndex, but checks for a direct match with the VarDeclaration
    int findFieldIndexByName(VarDeclaration v)
    {
        ClassDeclaration cd = originalClass();
        size_t fieldsSoFar = 0;
        for (size_t j = 0; j < value.elements.length; j++)
        {
            while (j - fieldsSoFar >= cd.fields.length)
            {
                fieldsSoFar += cd.fields.length;
                cd = cd.baseClass;
            }
            VarDeclaration v2 = cd.fields[j - fieldsSoFar];
            if (v == v2)
            {
                return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar));
            }
        }
        return -1;
    }

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

/***********************************************************
 * This type is only used by the interpreter.
 */
extern (C++) final class CTFEExp : Expression
{
    extern (D) this(EXP tok)
    {
        super(Loc.initial, tok);
        type = Type.tvoid;
    }

    extern (D) __gshared CTFEExp cantexp;
    extern (D) __gshared CTFEExp voidexp;
    extern (D) __gshared CTFEExp breakexp;
    extern (D) __gshared CTFEExp continueexp;
    extern (D) __gshared CTFEExp gotoexp;
    /* Used when additional information is needed regarding
     * a ctfe error.
     */
    extern (D) __gshared CTFEExp showcontext;

    extern (D) static bool isCantExp(const Expression e) @safe
    {
        return e && e.op == EXP.cantExpression;
    }

    extern (D) static bool isGotoExp(const Expression e) @safe
    {
        return e && e.op == EXP.goto_;
    }
}

/***********************************************************
 * Fake class which holds the thrown exception.
 * Used for implementing exception handling.
 */
extern (C++) final class ThrownExceptionExp : Expression
{
    ClassReferenceExp thrown;   // the thing being tossed

    extern (D) this(Loc loc, ClassReferenceExp victim) @safe
    {
        super(loc, EXP.thrownException);
        this.thrown = victim;
        this.type = victim.type;
    }

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

/**
 * Objective-C class reference expression.
 *
 * Used to get the metaclass of an Objective-C class, `NSObject.Class`.
 */
extern (C++) final class ObjcClassReferenceExp : Expression
{
    ClassDeclaration classDeclaration;

    extern (D) this(Loc loc, ClassDeclaration classDeclaration) @safe
    {
        super(loc, EXP.objcClassReference);
        this.classDeclaration = classDeclaration;
    }

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

/*******************
 * C11 6.5.1.1 Generic Selection
 * For ImportC
 */
extern (C++) final class GenericExp : Expression
{
    Expression cntlExp; /// controlling expression of a generic selection (not evaluated)
    Types* types;       /// type-names for generic associations (null entry for `default`)
    Expressions* exps;  /// 1:1 mapping of typeNames to exps

    extern (D) this(Loc loc, Expression cntlExp, Types* types, Expressions* exps) @safe
    {
        super(loc, EXP._Generic);
        this.cntlExp = cntlExp;
        this.types = types;
        this.exps = exps;
        assert(types.length == exps.length);  // must be the same and >=1
    }

    override GenericExp syntaxCopy()
    {
        return new GenericExp(loc, cntlExp.syntaxCopy(), Type.arraySyntaxCopy(types), Expression.arraySyntaxCopy(exps));
    }

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

/**
 * Verify if the given identifier is _d_array{,set}ctor.
 *
 * Params:
 *  id = the identifier to verify
 *
 * Returns:
 *  `true` if the identifier corresponds to a construction runtime hook,
 *  `false` otherwise.
 */
bool isArrayConstruction(const Identifier id)
{
    import dmd.id : Id;

    return id == Id._d_arrayctor || id == Id._d_arraysetctor;
}

/******************************
 * Provide efficient way to implement isUnaExp(), isBinExp(), isBinAssignExp()
 */
private immutable ubyte[EXP.max + 1] exptab =
() {
    ubyte[EXP.max + 1] tab;
    with (EXPFLAGS)
    {
        foreach (i; Eunary)  { tab[i] |= unary;  }
        foreach (i; Ebinary) { tab[i] |= unary | binary; }
        foreach (i; EbinaryAssign) { tab[i] |= unary | binary | binaryAssign; }
    }
    return tab;
} ();

private enum EXPFLAGS : ubyte
{
    unary = 1,
    binary = 2,
    binaryAssign = 4,
}

private enum Eunary =
    [
        EXP.import_, EXP.assert_, EXP.throw_, EXP.dotIdentifier, EXP.dotTemplateDeclaration,
        EXP.dotVariable, EXP.dotTemplateInstance, EXP.delegate_, EXP.dotType, EXP.call,
        EXP.address, EXP.star, EXP.negate, EXP.uadd, EXP.tilde, EXP.not, EXP.delete_, EXP.cast_,
        EXP.vector, EXP.vectorArray, EXP.slice, EXP.arrayLength, EXP.array, EXP.delegatePointer,
        EXP.delegateFunctionPointer, EXP.preMinusMinus, EXP.prePlusPlus,
    ];

private enum Ebinary =
    [
        EXP.dot, EXP.comma, EXP.index, EXP.minusMinus, EXP.plusPlus, EXP.assign,
        EXP.add, EXP.min, EXP.concatenate, EXP.mul, EXP.div, EXP.mod, EXP.pow, EXP.leftShift,
        EXP.rightShift, EXP.unsignedRightShift, EXP.and, EXP.or, EXP.xor, EXP.andAnd, EXP.orOr,
        EXP.lessThan, EXP.lessOrEqual, EXP.greaterThan, EXP.greaterOrEqual,
        EXP.in_, EXP.remove, EXP.equal, EXP.notEqual, EXP.identity, EXP.notIdentity,
        EXP.question,
        EXP.construct, EXP.blit,
    ];

private enum EbinaryAssign =
    [
        EXP.addAssign, EXP.minAssign, EXP.mulAssign, EXP.divAssign, EXP.modAssign,
        EXP.andAssign, EXP.orAssign, EXP.xorAssign, EXP.powAssign,
        EXP.leftShiftAssign, EXP.rightShiftAssign, EXP.unsignedRightShiftAssign,
        EXP.concatenateAssign, EXP.concatenateElemAssign, EXP.concatenateDcharAssign,
    ];

alias AliasSeq(T...) = T;
template OpType(EXP e, T)
{
    enum op = e;
    alias type = T;
}
alias ExpOpTypePairs = AliasSeq!
(
    OpType!(EXP.negate, NegExp),
    OpType!(EXP.cast_, CastExp),
    OpType!(EXP.null_, NullExp),
    OpType!(EXP.assert_, AssertExp),
    OpType!(EXP.array, ArrayExp),
    OpType!(EXP.call, CallExp),
    OpType!(EXP.address, AddrExp),
    OpType!(EXP.type, TypeExp),
    OpType!(EXP.throw_, ThrowExp),
    OpType!(EXP.new_, NewExp),
    OpType!(EXP.delete_, DeleteExp),
    OpType!(EXP.star, PtrExp),
    OpType!(EXP.symbolOffset, SymOffExp),
    OpType!(EXP.variable, VarExp),
    OpType!(EXP.dotVariable, DotVarExp),
    OpType!(EXP.dotIdentifier, DotIdExp),
    OpType!(EXP.dotTemplateInstance, DotTemplateInstanceExp),
    OpType!(EXP.dotType, DotTypeExp),
    OpType!(EXP.slice, SliceExp),
    OpType!(EXP.arrayLength, ArrayLengthExp),
    OpType!(EXP.dollar, DollarExp),
    OpType!(EXP.template_, TemplateExp),
    OpType!(EXP.dotTemplateDeclaration, DotTemplateExp),
    OpType!(EXP.declaration, DeclarationExp),
    OpType!(EXP.dSymbol, DsymbolExp),
    OpType!(EXP.typeid_, TypeidExp),
    OpType!(EXP.uadd, UAddExp),
    OpType!(EXP.remove, RemoveExp),
    OpType!(EXP.newAnonymousClass, NewAnonClassExp),
    OpType!(EXP.arrayLiteral, ArrayLiteralExp),
    OpType!(EXP.assocArrayLiteral, AssocArrayLiteralExp),
    OpType!(EXP.structLiteral, StructLiteralExp),
    OpType!(EXP.classReference, ClassReferenceExp),
    OpType!(EXP.thrownException, ThrownExceptionExp),
    OpType!(EXP.delegatePointer, DelegatePtrExp),
    OpType!(EXP.delegateFunctionPointer, DelegateFuncptrExp),
    OpType!(EXP.lessThan, CmpExp),
    OpType!(EXP.greaterThan, CmpExp),
    OpType!(EXP.lessOrEqual, CmpExp),
    OpType!(EXP.greaterOrEqual, CmpExp),
    OpType!(EXP.equal, EqualExp),
    OpType!(EXP.notEqual, EqualExp),
    OpType!(EXP.identity, IdentityExp),
    OpType!(EXP.notIdentity, IdentityExp),
    OpType!(EXP.index, IndexExp),
    OpType!(EXP.is_, IsExp),
    OpType!(EXP.leftShift, ShlExp),
    OpType!(EXP.rightShift, ShrExp),
    OpType!(EXP.leftShiftAssign, ShlAssignExp),
    OpType!(EXP.rightShiftAssign, ShrAssignExp),
    OpType!(EXP.unsignedRightShift, UshrExp),
    OpType!(EXP.unsignedRightShiftAssign, UshrAssignExp),
    OpType!(EXP.concatenate, CatExp),
    OpType!(EXP.concatenateAssign, CatAssignExp),
    OpType!(EXP.concatenateElemAssign, CatElemAssignExp),
    OpType!(EXP.concatenateDcharAssign, CatDcharAssignExp),
    OpType!(EXP.add, AddExp),
    OpType!(EXP.min, MinExp),
    OpType!(EXP.addAssign, AddAssignExp),
    OpType!(EXP.minAssign, MinAssignExp),
    OpType!(EXP.mul, MulExp),
    OpType!(EXP.div, DivExp),
    OpType!(EXP.mod, ModExp),
    OpType!(EXP.mulAssign, MulAssignExp),
    OpType!(EXP.divAssign, DivAssignExp),
    OpType!(EXP.modAssign, ModAssignExp),
    OpType!(EXP.and, AndExp),
    OpType!(EXP.or, OrExp),
    OpType!(EXP.xor, XorExp),
    OpType!(EXP.andAssign, AndAssignExp),
    OpType!(EXP.orAssign, OrAssignExp),
    OpType!(EXP.xorAssign, XorAssignExp),
    OpType!(EXP.assign, AssignExp),
    OpType!(EXP.not, NotExp),
    OpType!(EXP.tilde, ComExp),
    OpType!(EXP.plusPlus, PostExp),
    OpType!(EXP.minusMinus, PostExp),
    OpType!(EXP.construct, ConstructExp),
    OpType!(EXP.blit, BlitExp),
    OpType!(EXP.dot, DotExp),
    OpType!(EXP.comma, CommaExp),
    OpType!(EXP.question, CondExp),
    OpType!(EXP.andAnd, LogicalExp),
    OpType!(EXP.orOr, LogicalExp),
    OpType!(EXP.prePlusPlus, PreExp),
    OpType!(EXP.preMinusMinus, PreExp),
    OpType!(EXP.identifier, IdentifierExp),
    OpType!(EXP.string_, StringExp),
    OpType!(EXP.interpolated, InterpExp),
    OpType!(EXP.this_, ThisExp),
    OpType!(EXP.super_, SuperExp),
    OpType!(EXP.halt, HaltExp),
    OpType!(EXP.tuple, TupleExp),
    OpType!(EXP.error, ErrorExp),
    OpType!(EXP.void_, VoidInitExp),
    OpType!(EXP.int64, IntegerExp),
    OpType!(EXP.float64, RealExp),
    OpType!(EXP.complex80, ComplexExp),
    OpType!(EXP.import_, ImportExp),
    OpType!(EXP.delegate_, DelegateExp),
    OpType!(EXP.function_, FuncExp),
    OpType!(EXP.mixin_, MixinExp),
    OpType!(EXP.in_, InExp),
    OpType!(EXP.break_, CTFEExp),
    OpType!(EXP.continue_, CTFEExp),
    OpType!(EXP.goto_, CTFEExp),
    OpType!(EXP.scope_, ScopeExp),
    OpType!(EXP.traits, TraitsExp),
    OpType!(EXP.overloadSet, OverExp),
    OpType!(EXP.defaultInit, DefaultInitExp),
    OpType!(EXP.pow, PowExp),
    OpType!(EXP.powAssign, PowAssignExp),
    OpType!(EXP.vector, VectorExp),
    OpType!(EXP.voidExpression, CTFEExp),
    OpType!(EXP.cantExpression, CTFEExp),
    OpType!(EXP.showCtfeContext, CTFEExp),
    OpType!(EXP.objcClassReference, ObjcClassReferenceExp),
    OpType!(EXP.vectorArray, VectorArrayExp),
    OpType!(EXP.compoundLiteral, CompoundLiteralExp),
    OpType!(EXP._Generic, GenericExp),
    OpType!(EXP.interval, IntervalExp),
    OpType!(EXP.loweredAssignExp, LoweredAssignExp),
);

/// Given a member of the EXP enum, get the class instance size of the corresponding Expression class.
/// Needed because the classes are `extern(C++)`
immutable ubyte[EXP.max+1] expSize = (){
    ubyte[EXP.max+1] expSize;
    foreach(optype; ExpOpTypePairs)
        expSize[optype.op] = __traits(classInstanceSize, optype.type);
    return expSize;
}();

immutable ubyte[EXP.max+1] expAlign = (){
    ubyte[EXP.max+1] expAlign;
    foreach(optype; ExpOpTypePairs)
        static if (__VERSION__ >= 2101) // support for classInstanceAlignment ?
            expAlign[optype.op] = __traits(classInstanceAlignment, optype.type);
        else
            expAlign[optype.op] = 16; // worst case, GC doesn't guarantee more anyway
    return expAlign;
}();
