blob: d65b163ee6683406bc00772e19e4d79d9010d48c [file] [log] [blame]
/**
* 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-2025 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.aggregate;
import dmd.arraytypes;
import dmd.astenums;
import dmd.ast_node;
import dmd.dcast : implicitConvTo;
import dmd.dclass;
import dmd.declaration;
import dmd.dimport;
import dmd.dmodule;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dtemplate;
import dmd.errors;
import dmd.errorsink;
import dmd.func;
import dmd.globals;
import dmd.hdrgen;
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.common.outbuffer;
import dmd.root.optional;
import dmd.root.rmem;
import dmd.rootobject;
import dmd.root.string;
import dmd.root.utf;
import dmd.target;
import dmd.tokens;
import dmd.typesem;
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;
}
/****************************************
* Expand tuples in-place.
*
* Example:
* When there's a call `f(10, pair: AliasSeq!(20, 30), single: 40)`, the input is:
* `exps = [10, (20, 30), 40]`
* `names = [null, "pair", "single"]`
* The arrays will be modified to:
* `exps = [10, 20, 30, 40]`
* `names = [null, "pair", null, "single"]`
*
* Params:
* exps = array of Expressions
* names = optional array of names corresponding to Expressions
*/
void expandTuples(Expressions* exps, Identifiers* names = null)
{
//printf("expandTuples()\n");
if (exps is null)
return;
if (names)
{
if (exps.length != names.length)
{
printf("exps.length = %d, names.length = %d\n", cast(int) exps.length, cast(int) names.length);
printf("exps = %s, names = %s\n", exps.toChars(), names.toChars());
if (exps.length > 0)
printf("%s\n", (*exps)[0].loc.toChars());
assert(0);
}
}
// At `index`, a tuple of length `length` is expanded. Insert corresponding nulls in `names`.
void expandNames(size_t index, size_t length)
{
if (names)
{
if (length == 0)
{
names.remove(index);
return;
}
foreach (i; 1 .. length)
{
names.insert(index + i, cast(Identifier) null);
}
}
}
for (size_t i = 0; i < exps.length; i++)
{
Expression arg = (*exps)[i];
if (!arg)
continue;
// Look for tuple with 0 members
if (auto e = arg.isTypeExp())
{
if (auto tt = e.type.toBasetype().isTypeTuple())
{
if (!tt.arguments || tt.arguments.length == 0)
{
exps.remove(i);
expandNames(i, 0);
if (i == exps.length)
return;
}
else // Expand a TypeTuple
{
exps.remove(i);
auto texps = new Expressions(tt.arguments.length);
foreach (j, a; *tt.arguments)
(*texps)[j] = new TypeExp(e.loc, a.type);
exps.insert(i, texps);
expandNames(i, texps.length);
}
i--;
continue;
}
}
// Inline expand all the tuples
while (arg.op == EXP.tuple)
{
TupleExp te = cast(TupleExp)arg;
exps.remove(i); // remove arg
exps.insert(i, te.exps); // replace with tuple contents
expandNames(i, te.exps.length);
if (i == exps.length)
return; // empty tuple, no more arguments
(*exps)[i] = Expression.combine(te.e0, (*exps)[i]);
arg = (*exps)[i];
}
}
}
/****************************************
* 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);
}
/***************************************************
* Given an Expression, find the variable it really is.
*
* For example, `a[index]` is really `a`, and `s.f` is really `s`.
* Params:
* e = Expression to look at
* deref = number of dereferences encountered
* Returns:
* variable if there is one, null if not
*/
VarDeclaration expToVariable(Expression e, out int deref)
{
deref = 0;
while (1)
{
switch (e.op)
{
case EXP.variable:
return e.isVarExp().var.isVarDeclaration();
case EXP.dotVariable:
e = e.isDotVarExp().e1;
if (e.type.toBasetype().isTypeClass())
deref++;
continue;
case EXP.index:
{
e = e.isIndexExp().e1;
if (!e.type.toBasetype().isTypeSArray())
deref++;
continue;
}
case EXP.slice:
{
e = e.isSliceExp().e1;
if (!e.type.toBasetype().isTypeSArray())
deref++;
continue;
}
case EXP.super_:
return e.isSuperExp().var.isVarDeclaration();
case EXP.this_:
return e.isThisExp().var.isVarDeclaration();
// Temporaries for rvalues that need destruction
// are of form: (T s = rvalue, s). For these cases
// we can just return var declaration of `s`. However,
// this is intentionally not calling `Expression.extractLast`
// because at this point we cannot infer the var declaration
// of more complex generated comma expressions such as the
// one for the array append hook.
case EXP.comma:
{
if (auto ve = e.isCommaExp().e2.isVarExp())
return ve.var.isVarDeclaration();
return null;
}
default:
return null;
}
}
}
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
}
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)allocmemory(size);
//printf("Expression::copy(op = %d) e = %p\n", op, e);
return cast(Expression)memcpy(cast(void*)e, cast(void*)this, size);
}
Expression syntaxCopy()
{
//printf("Expression::syntaxCopy()\n");
//print();
return copy();
}
// 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;
}
dinteger_t toInteger()
{
//printf("Expression %s\n", EXPtoString(op).ptr);
if (!type || !type.isTypeError())
error(loc, "integer constant expression expected instead of `%s`", toChars());
return 0;
}
uinteger_t toUInteger()
{
//printf("Expression %s\n", EXPtoString(op).ptr);
return cast(uinteger_t)toInteger();
}
real_t toReal()
{
error(loc, "floating point constant expression expected instead of `%s`", toChars());
return CTFloat.zero;
}
real_t toImaginary()
{
error(loc, "floating point constant expression expected instead of `%s`", toChars());
return CTFloat.zero;
}
complex_t toComplex()
{
error(loc, "floating point constant expression expected instead of `%s`", toChars());
return complex_t(CTFloat.zero);
}
StringExp toStringExp()
{
return null;
}
/***************************************
* Return !=0 if expression is an lvalue.
*/
bool isLvalue()
{
return false;
}
/****************************************
* Check that the expression has a valid type.
* If not, generates an error "... has no type".
* Returns:
* true if the expression is not valid.
* Note:
* When this function returns true, `checkValue()` should also return true.
*/
bool checkType()
{
return false;
}
/******************************
* Take address of expression.
*/
final Expression addressOf()
{
//printf("Expression::addressOf()\n");
debug
{
assert(op == EXP.error || isLvalue());
}
Expression e = new AddrExp(loc, this, type.pointerTo());
return e;
}
/******************************
* If this is a reference, dereference it.
*/
final Expression deref()
{
//printf("Expression::deref()\n");
// type could be null if forward referencing an 'auto' variable
if (type)
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);
}
/******
* Identical, not just equal. I.e. NaNs with different bit patterns are not identical
*/
bool isIdentical(const Expression e) const
{
return equals(e);
}
/// Statically evaluate this expression to a `bool` if possible
/// Returns: an optional thath either contains the value or is empty
Optional!bool toBool()
{
return typeof(return)();
}
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.prettyFunction || op == EXP.functionString ||
op == EXP.line || op == EXP.moduleString ||
op == EXP.file || op == EXP.fileFullPath ) ? cast(typeof(return))this : null; }
inout(FileInitExp) isFileInitExp() { return (op == EXP.file || op == EXP.fileFullPath) ? cast(typeof(return))this : null; }
inout(LineInitExp) isLineInitExp() { return op == EXP.line ? cast(typeof(return))this : null; }
inout(ModuleInitExp) isModuleInitExp() { return op == EXP.moduleString ? cast(typeof(return))this : null; }
inout(FuncInitExp) isFuncInitExp() { return op == EXP.functionString ? cast(typeof(return))this : null; }
inout(PrettyFuncInitExp) isPrettyFuncInitExp() { return op == EXP.prettyFunction ? 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);
}
}
/***********************************************************
* A compile-time known integer value
*/
extern (C++) final class IntegerExp : Expression
{
private 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);
if (!type.isScalar())
{
//printf("%s, loc = %d\n", toChars(), loc.linnum);
if (type.ty != Terror)
error(loc, "integral constant must be scalar type, not `%s`", type.toChars());
type = Type.terror;
}
this.type = type;
this.value = normalize(type.toBasetype().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 bool equals(const RootObject o) const
{
if (this == o)
return true;
if (auto ne = (cast(Expression)o).isIntegerExp())
{
if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && value == ne.value)
{
return true;
}
}
return false;
}
override dinteger_t toInteger()
{
// normalize() is necessary until we fix all the paints of 'type'
return value = normalize(type.toBasetype().ty, value);
}
override real_t toReal()
{
// normalize() is necessary until we fix all the paints of 'type'
const ty = type.toBasetype().ty;
const val = normalize(ty, value);
value = val;
return (ty == Tuns64)
? real_t(cast(ulong)val)
: real_t(cast(long)val);
}
override real_t toImaginary()
{
return CTFloat.zero;
}
override complex_t toComplex()
{
return complex_t(toReal());
}
override Optional!bool toBool()
{
bool r = toInteger() != 0;
return typeof(return)(r);
}
override void accept(Visitor v)
{
v.visit(this);
}
dinteger_t getInteger()
{
return value;
}
extern (D) void setInteger(dinteger_t value)
{
this.value = normalize(type.toBasetype().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;
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;
case Tpointer:
if (target.ptrsize == 8)
goto case Tuns64;
if (target.ptrsize == 4)
goto case Tuns32;
if (target.ptrsize == 2)
goto case Tuns16;
assert(0);
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();
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);
}
/********************************
* Test to see if two reals are the same.
* Regard NaN's as equivalent.
* Regard +0 and -0 as different.
* Params:
* x1 = first operand
* x2 = second operand
* Returns:
* true if x1 is x2
* else false
*/
private static bool RealIdentical(real_t x1, real_t x2) @safe
{
return (CTFloat.isNaN(x1) && CTFloat.isNaN(x2)) || CTFloat.isIdentical(x1, x2);
}
override bool equals(const RootObject o) const
{
if (this == o)
return true;
if (auto ne = (cast(Expression)o).isRealExp())
{
if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && RealIdentical(value, ne.value))
{
return true;
}
}
return false;
}
override bool isIdentical(const Expression e) const
{
if (!equals(e))
return false;
return CTFloat.isIdentical(value, e.isRealExp().value);
}
override dinteger_t toInteger()
{
return cast(sinteger_t)toReal();
}
override uinteger_t toUInteger()
{
return cast(uinteger_t)toReal();
}
override real_t toReal()
{
return type.isReal() ? value : CTFloat.zero;
}
override real_t toImaginary()
{
return type.isReal() ? CTFloat.zero : value;
}
override complex_t toComplex()
{
return complex_t(toReal(), toImaginary());
}
override Optional!bool toBool()
{
return typeof(return)(!!value);
}
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 bool equals(const RootObject o) const
{
if (this == o)
return true;
if (auto ne = (cast(Expression)o).isComplexExp())
{
if (type.toHeadMutable().equals(ne.type.toHeadMutable()) &&
RealExp.RealIdentical(creall(value), creall(ne.value)) &&
RealExp.RealIdentical(cimagl(value), cimagl(ne.value)))
{
return true;
}
}
return false;
}
override bool isIdentical(const Expression e) const
{
if (!equals(e))
return false;
// equals() regards different NaN values as 'equals'
auto c = e.isComplexExp();
return CTFloat.isIdentical(creall(value), creall(c.value)) &&
CTFloat.isIdentical(cimagl(value), cimagl(c.value));
}
override dinteger_t toInteger()
{
return cast(sinteger_t)toReal();
}
override uinteger_t toUInteger()
{
return cast(uinteger_t)toReal();
}
override real_t toReal()
{
return creall(value);
}
override real_t toImaginary()
{
return cimagl(value);
}
override complex_t toComplex()
{
return value;
}
override Optional!bool toBool()
{
return typeof(return)(!!value);
}
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 final bool isLvalue()
{
return !this.rvalue;
}
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 bool isLvalue()
{
return !rvalue;
}
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 Optional!bool toBool()
{
// `this` is never null (what about structs?)
return typeof(return)(true);
}
override final bool isLvalue()
{
// Class `this` should be an rvalue; struct `this` should be an lvalue.
return !rvalue && type.toBasetype().ty != Tclass;
}
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 bool equals(const RootObject o) const
{
if (auto e = o.isExpression())
{
if (e.op == EXP.null_ && type.equals(e.type))
{
return true;
}
}
return false;
}
override Optional!bool toBool()
{
// null in any type is false
return typeof(return)(false);
}
override StringExp toStringExp()
{
if (this.type.implicitConvTo(Type.tstring))
{
auto se = new StringExp(loc, (cast(char*)mem.xcalloc(1, 1))[0 .. 0]);
se.type = Type.tstring;
return se;
}
return null;
}
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;
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) 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;
}
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]);
}
override bool equals(const RootObject o) const
{
//printf("StringExp::equals('%s') %s\n", o.toChars(), toChars());
if (auto e = o.isExpression())
{
if (auto se = e.isStringExp())
{
return compare(se) == 0;
}
}
return false;
}
/**********************************
* Return the number of code units the string would be if it were re-encoded
* as tynto.
* Params:
* tynto = code unit type of the target encoding
* Returns:
* number of code units
*/
size_t numberOfCodeUnits(int tynto = 0) const
{
int encSize;
switch (tynto)
{
case 0: return len;
case Tchar: encSize = 1; break;
case Twchar: encSize = 2; break;
case Tdchar: encSize = 4; break;
default:
assert(0);
}
if (sz == encSize)
return len;
size_t result = 0;
dchar c;
switch (sz)
{
case 1:
for (size_t u = 0; u < len;)
{
if (const s = utf_decodeChar(string[0 .. len], u, c))
{
error(loc, "%.*s", cast(int)s.length, s.ptr);
return 0;
}
result += utf_codeLength(encSize, c);
}
break;
case 2:
for (size_t u = 0; u < len;)
{
if (const s = utf_decodeWchar(wstring[0 .. len], u, c))
{
error(loc, "%.*s", cast(int)s.length, s.ptr);
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;
}
}
override StringExp toStringExp()
{
return this;
}
/**
* 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)
{
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 cast(int)(len1 - len2);
}
override Optional!bool toBool()
{
// Keep the old behaviour for this refactoring
// Should probably match language spec instead and check for length
return typeof(return)(true);
}
override bool isLvalue()
{
/* string literal is rvalue in default, but
* conversion to reference of static array is only allowed.
*/
return !rvalue && (type && type.toBasetype().ty == Tsarray);
}
/********************************
* Convert string contents to a 0 terminated string,
* allocated by mem.xmalloc().
*/
extern (D) const(char)[] toStringz() const
{
auto nbytes = len * sz;
char* s = cast(char*)mem.xmalloc(nbytes + sz);
writeTo(s, true);
return s[0 .. nbytes];
}
extern (D) const(char)[] 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();
this.exps.reserve(tup.objects.length);
foreach (o; *tup.objects)
{
if (Dsymbol s = getDsymbol(o))
{
/* If tuple element represents a symbol, translate to DsymbolExp
* to supply implicit 'this' if needed later.
*/
Expression e = new DsymbolExp(loc, s);
this.exps.push(e);
}
else if (auto eo = o.isExpression())
{
auto e = eo.copy();
e.loc = loc; // https://issues.dlang.org/show_bug.cgi?id=15669
this.exps.push(e);
}
else if (auto t = o.isType())
{
Expression e = new TypeExp(loc, t);
this.exps.push(e);
}
else
{
error(loc, "`%s` is not an expression", o.toChars());
}
}
}
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 bool equals(const RootObject o) const
{
if (this == o)
return true;
if (auto e = o.isExpression())
if (auto te = e.isTupleExp())
{
if (exps.length != te.exps.length)
return false;
if (e0 && !e0.equals(te.e0) || !e0 && te.e0)
return false;
foreach (i, e1; *exps)
{
auto e2 = (*te.exps)[i];
if (!e1.equals(e2))
return false;
}
return true;
}
return false;
}
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;
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();
elements.push(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));
}
override bool equals(const RootObject o) const
{
if (this == o)
return true;
auto e = o.isExpression();
if (!e)
return false;
if (auto ae = e.isArrayLiteralExp())
{
if (elements.length != ae.elements.length)
return false;
if (elements.length == 0 && !type.equals(ae.type))
{
return false;
}
foreach (i, e1; *elements)
{
auto e2 = (*ae.elements)[i];
auto e1x = e1 ? e1 : basis;
auto e2x = e2 ? e2 : ae.basis;
if (e1x != e2x && (!e1x || !e2x || !e1x.equals(e2x)))
return false;
}
return true;
}
return false;
}
Expression getElement(size_t i) // use opIndex instead
{
return this[i];
}
extern (D) Expression opIndex(size_t i)
{
auto el = (*elements)[i];
return el ? el : basis;
}
override Optional!bool toBool()
{
size_t dim = elements ? elements.length : 0;
return typeof(return)(dim != 0);
}
override StringExp toStringExp()
{
TY telem = type.nextOf().toBasetype().ty;
if (telem.isSomeChar || (telem == Tvoid && (!elements || elements.length == 0)))
{
ubyte sz = 1;
if (telem == Twchar)
sz = 2;
else if (telem == Tdchar)
sz = 4;
OutBuffer buf;
if (elements)
{
foreach (i; 0 .. elements.length)
{
auto ch = this[i];
if (ch.op != EXP.int64)
return null;
if (sz == 1)
buf.writeByte(cast(uint)ch.toInteger());
else if (sz == 2)
buf.writeword(cast(uint)ch.toInteger());
else
buf.write4(cast(uint)ch.toInteger());
}
}
char prefix;
if (sz == 1)
{
prefix = 'c';
buf.writeByte(0);
}
else if (sz == 2)
{
prefix = 'w';
buf.writeword(0);
}
else
{
prefix = 'd';
buf.write4(0);
}
const size_t len = buf.length / sz - 1;
auto se = new StringExp(loc, buf.extractSlice()[0 .. len * sz], len, sz, prefix);
se.sz = sz;
se.type = type;
return se;
}
return null;
}
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;
/// Lower to core.internal.newaa for static initializaton
Expression lowering;
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 bool equals(const RootObject o) const
{
if (this == o)
return true;
auto e = o.isExpression();
if (!e)
return false;
if (auto ae = e.isAssocArrayLiteralExp())
{
if (keys.length != ae.keys.length)
return false;
size_t count = 0;
foreach (i, key; *keys)
{
foreach (j, akey; *ae.keys)
{
if (key.equals(akey))
{
if (!(*values)[i].equals((*ae.values)[j]))
return false;
++count;
}
}
}
return count == keys.length;
}
return false;
}
override AssocArrayLiteralExp syntaxCopy()
{
return new AssocArrayLiteralExp(loc, arraySyntaxCopy(keys), arraySyntaxCopy(values));
}
override Optional!bool toBool()
{
size_t dim = keys.length;
return typeof(return)(dim != 0);
}
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 a 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 bool equals(const RootObject o) const
{
if (this == o)
return true;
auto e = o.isExpression();
if (!e)
return false;
if (auto se = e.isStructLiteralExp())
{
if (!type.equals(se.type))
return false;
if (elements.length != se.elements.length)
return false;
foreach (i, e1; *elements)
{
auto e2 = (*se.elements)[i];
if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
return false;
}
return true;
}
return false;
}
override StructLiteralExp syntaxCopy()
{
auto exp = new StructLiteralExp(loc, sd, arraySyntaxCopy(elements), type ? type : stype);
exp.origin = this;
return exp;
}
/**************************************
* Gets expression at offset of type.
* Returns NULL if not found.
*/
extern (D) Expression getField(Type type, uint offset)
{
//printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n",
// /*toChars()*/"", type.toChars(), offset);
Expression e = null;
int i = getFieldIndex(type, offset);
if (i != -1)
{
//printf("\ti = %d\n", i);
if (i >= sd.nonHiddenFields())
return null;
assert(i < elements.length);
e = (*elements)[i];
if (e)
{
//printf("e = %s, e.type = %s\n", e.toChars(), e.type.toChars());
/* If type is a static array, and e is an initializer for that array,
* then the field initializer should be an array literal of e.
*/
auto tsa = type.isTypeSArray();
if (tsa && e.type.castMod(0) != type.castMod(0))
{
const length = cast(size_t)tsa.dim.toInteger();
auto z = new Expressions(length);
foreach (ref q; *z)
q = e.copy();
e = new ArrayLiteralExp(loc, type, z);
}
else
{
e = e.copy();
e.type = type;
}
if (useStaticInit && e.type.needsNested())
if (auto se = e.isStructLiteralExp())
{
se.useStaticInit = true;
}
}
}
return e;
}
/************************************
* Get index of field.
* Returns -1 if not found.
*/
extern (D) int getFieldIndex(Type type, uint offset)
{
/* Find which field offset is by looking at the field offsets
*/
if (elements.length)
{
const sz = type.size();
if (sz == SIZE_INVALID)
return -1;
foreach (i, v; sd.fields)
{
if (offset == v.offset && sz == v.type.size())
{
/* context fields might not be filled. */
if (i >= sd.nonHiddenFields())
return cast(int)i;
if (auto e = (*elements)[i])
{
return cast(int)i;
}
break;
}
}
}
return -1;
}
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 bool checkType()
{
error(loc, "type `%s` is not an expression", toChars());
return true;
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
* Mainly just a placeholder of
* Package, Module, Nspace, and TemplateInstance (including TemplateMixin)
*
* A template instance that requires IFTI:
* foo!tiargs(fargs) // foo!tiargs
* is left until CallExp::semantic() or resolveProperties()
*/
extern (C++) final class ScopeExp : Expression
{
ScopeDsymbol sds;
extern (D) this(Loc loc, ScopeDsymbol sds) @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 bool checkType()
{
if (sds.isPackage())
{
error(loc, "%s `%s` has no type", sds.kind(), sds.toChars());
return true;
}
if (auto ti = sds.isTemplateInstance())
{
//assert(ti.needsTypeInference(sc));
if (ti.tempdecl &&
ti.semantictiargsdone &&
ti.semanticRun == PASS.initial)
{
error(loc, "partial %s `%s` has no type", sds.kind(), toChars());
return true;
}
}
return false;
}
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 bool isLvalue()
{
return fd !is null;
}
override bool checkType()
{
error(loc, "%s `%s` has no type", td.kind(), toChars());
return true;
}
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
Identifiers* names; // Array of names 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, Identifiers* 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())
{
// FIXME: This error report will never be handled anyone.
// It should be done before the SymOffExp construction.
if (v.needThis())
{
auto t = v.isThis();
assert(t);
.error(loc, "taking the address of non-static variable `%s` requires an instance of `%s`", v.toChars(), t.toChars());
}
hasOverloads = false;
}
super(loc, EXP.symbolOffset, var, hasOverloads);
this.offset = offset;
}
override Optional!bool toBool()
{
return typeof(return)(true);
}
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 bool equals(const RootObject o) const
{
if (this == o)
return true;
if (auto ne = o.isExpression().isVarExp())
{
if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && var == ne.var)
{
return true;
}
}
return false;
}
override bool isLvalue()
{
if (rvalue || var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest))
return false;
return true;
}
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 bool isLvalue()
{
return true;
}
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 bool equals(const RootObject o) const
{
if (this == o)
return true;
auto e = o.isExpression();
if (!e)
return false;
if (auto fe = e.isFuncExp())
{
return fd == fe.fd;
}
return false;
}
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 bool checkType()
{
if (td)
{
error(loc, "template lambda has no type");
return true;
}
return false;
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
* Declaration of a symbol
*
* D grammar allows declarations only as statements. However in AST representation
* it can be part of any expression. This is used, for example, during internal
* syntax re-writes to inject hidden symbols.
*/
extern (C++) final class DeclarationExp : Expression
{
Dsymbol declaration;
extern (D) this(Loc loc, Dsymbol declaration) @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 final bool isLvalue()
{
return !rvalue;
}
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 bool equals(const RootObject o) const
{
if (this == o)
return true;
auto e = o.isExpression();
if (!e)
return false;
if (auto ce = e.isMixinExp())
{
if (exps.length != ce.exps.length)
return false;
foreach (i, e1; *exps)
{
auto e2 = (*ce.exps)[i];
if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
return false;
}
return true;
}
return false;
}
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;
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 bool checkType()
{
error(loc, "%s `%s` has no type", td.kind(), toChars());
return true;
}
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 bool isLvalue()
{
if (rvalue)
return false;
if (e1.op != EXP.structLiteral)
return true;
auto vd = var.isVarDeclaration();
return !(vd && vd.isField());
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
* foo.bar!(args)
*/
extern (C++) final class DotTemplateInstanceExp : UnaExp
{
TemplateInstance ti;
extern (D) this(Loc loc, Expression e, Identifier name, Objects* tiargs)
{
super(loc, EXP.dotTemplateInstance, e);
//printf("DotTemplateInstanceExp()\n");
this.ti = new TemplateInstance(loc, name, tiargs);
}
extern (D) this(Loc loc, Expression e, TemplateInstance ti) @safe
{
super(loc, EXP.dotTemplateInstance, e);
this.ti = ti;
}
override DotTemplateInstanceExp syntaxCopy()
{
return new DotTemplateInstanceExp(loc, e1.syntaxCopy(), ti.name, TemplateInstance.arraySyntaxCopy(ti.tiargs));
}
override bool checkType()
{
// Same logic as ScopeExp.checkType()
if (ti.tempdecl &&
ti.semantictiargsdone &&
ti.semanticRun == PASS.initial)
{
error(loc, "partial %s `%s` has no type", ti.kind(), toChars());
return true;
}
return false;
}
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
Identifiers* names; // named argument identifiers
size_t length() const @nogc nothrow pure @safe { return arguments ? arguments.length : 0; }
/// Returns: whether this argument list contains any named arguments
bool hasNames() const @nogc nothrow pure @safe
{
if (names is null)
return false;
foreach (name; *names)
if (name !is null)
return true;
return false;
}
}
/***********************************************************
*/
extern (C++) final class CallExp : UnaExp
{
Expressions* arguments; // function arguments
Identifiers* names; // named argument identifiers
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
VarDeclaration vthis2; // container for multi-context
/// 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, Identifiers* 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;
}
/***********************************************************
* Instatiates 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 bool isLvalue()
{
if (rvalue)
return false;
Type tb = e1.type.toBasetype();
if (tb.ty == Tdelegate || tb.ty == Tpointer)
tb = tb.nextOf();
auto tf = tb.isTypeFunction();
if (tf && tf.isRef)
{
if (auto dve = e1.isDotVarExp())
if (dve.var.isCtorDeclaration())
return false;
return true; // function returns a reference
}
return false;
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/**
* Get the called function type from a call expression
* Params:
* ce = function call expression. Must have had semantic analysis done.
* Returns: called function type, or `null` if error / no semantic analysis done
*/
TypeFunction calledFunctionType(CallExp ce)
{
Type t = ce.e1.type;
if (!t)
return null;
t = t.toBasetype();
if (auto tf = t.isTypeFunction())
return tf;
if (auto td = t.isTypeDelegate())
return td.nextOf().isTypeFunction();
return null;
}
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 bool isLvalue()
{
return !rvalue;
}
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
{
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
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 bool isLvalue()
{
//printf("e1.type = %s, to.type = %s\n", e1.type.toChars(), to.toChars());
if (rvalue || !e1.isLvalue())
return false;
return (to.ty == Tsarray && (e1.type.ty == Tvector || e1.type.ty == Tsarray)) ||
(to.ty == Taarray && e1.type.ty == Taarray) ||
e1.type.mutableOf.unSharedOf().equals(to.mutableOf().unSharedOf());
}
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 bool isLvalue()
{
return !rvalue && e1.isLvalue();
}
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 bool isLvalue()
{
/* slice expression is rvalue in default, but
* conversion to reference of static array is only allowed.
*/
return !rvalue && (type && type.toBasetype().ty == Tsarray);
}
override Optional!bool toBool()
{
return e1.toBool();
}
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;
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 bool isLvalue()
{
if (rvalue)
return false;
if (type && type.toBasetype().ty == Tvoid)
return false;
return true;
}
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;
/// Temporary variable to enable / disable deprecation of comma expression
/// depending on the context.
/// Since most constructor calls are rewritting, the only place where
/// false will be passed will be from the parser.
bool allowCommaExp;
extern (D) this(Loc loc, Expression e1, Expression e2, bool generated = true) @safe
{
super(loc, EXP.comma, e1, e2);
allowCommaExp = isGenerated = generated;
}
override bool isLvalue()
{
return !rvalue && e2.isLvalue();
}
override Optional!bool toBool()
{
return e2.toBool();
}
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 bool isLvalue()
{
return !rvalue && e1.isLvalue();
}
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 bool isLvalue()
{
return !rvalue && e1.isLvalue();
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
* e1 [ e2 ]
*/
extern (C++) final class IndexExp : BinExp
{
VarDeclaration lengthVar;
bool modifiable = false; // assume it is an rvalue
bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1
extern (D) this(Loc loc, Expression e1, Expression e2) @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 bool isLvalue()
{
if (rvalue)
return false;
auto t1b = e1.type.toBasetype();
if (t1b.isTypeAArray() || t1b.isTypeSArray() ||
(e1.isIndexExp() && t1b != t1b.isTypeDArray()))
{
return e1.isLvalue();
}
return true;
}
extern (D) Expression markSettingAAElem()
{
if (e1.type.toBasetype().ty == Taarray)
{
Type t2b = e2.type.toBasetype();
if (t2b.ty == Tarray && t2b.nextOf().isMutable())
{
error(loc, "associative arrays can only be assigned values with immutable keys, not `%s`", e2.type.toChars());
return ErrorExp.get();
}
modifiable = true;
if (auto ie = e1.isIndexExp())
{
Expression ex = ie.markSettingAAElem();
if (ex.op == EXP.error)
return ex;
assert(ex == e1);
}
}
return this;
}
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 final bool isLvalue()
{
// Array-op 'x[] = y[]' should make an rvalue.
// Setting array length 'x.length = v' should make an rvalue.
if (e1.op == EXP.slice || e1.op == EXP.arrayLength)
{
return false;
}
return !rvalue;
}
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
{
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(arg)`
*
* This deletes the key e1 from the associative array e2
*/
extern (C++) final class RemoveExp : BinExp
{
extern (D) this(Loc loc, Expression e1, Expression e2)
{
super(loc, EXP.remove, e1, e2);
type = Type.tbool;
}
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
{
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 bool isLvalue()
{
return !rvalue && e1.isLvalue() && e2.isLvalue();
}
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
{
/*************************
* Params:
* loc = location
* op = EXP.prettyFunction, EXP.functionString, EXP.moduleString,
* EXP.line, EXP.file, EXP.fileFullPath
*/
extern (D) this(Loc loc, EXP op) @safe
{
super(loc, op);
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
* The `__FILE__` token as a default argument
*/
extern (C++) final class FileInitExp : DefaultInitExp
{
extern (D) this(Loc loc, EXP tok) @safe
{
super(loc, tok);
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
* The `__LINE__` token as a default argument
*/
extern (C++) final class LineInitExp : DefaultInitExp
{
extern (D) this(Loc loc) @safe
{
super(loc, EXP.line);
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
* The `__MODULE__` token as a default argument
*/
extern (C++) final class ModuleInitExp : DefaultInitExp
{
extern (D) this(Loc loc) @safe
{
super(loc, EXP.moduleString);
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
* The `__FUNCTION__` token as a default argument
*/
extern (C++) final class FuncInitExp : DefaultInitExp
{
extern (D) this(Loc loc) @safe
{
super(loc, EXP.functionString);
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
* The `__PRETTY_FUNCTION__` token as a default argument
*/
extern (C++) final class PrettyFuncInitExp : DefaultInitExp
{
extern (D) this(Loc loc) @safe
{
super(loc, EXP.prettyFunction);
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
* 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
int getFieldIndex(Type fieldtype, uint fieldoffset)
{
ClassDeclaration cd = originalClass();
uint 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 (fieldoffset == v2.offset && fieldtype.size() == v2.type.size())
{
return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar));
}
}
return -1;
}
// 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,
];
/// Given a member of the EXP enum, get the class instance size of the corresponding Expression class.
/// Needed because the classes are `extern(C++)`
private immutable ubyte[EXP.max+1] expSize = [
EXP.reserved: 0,
EXP.negate: __traits(classInstanceSize, NegExp),
EXP.cast_: __traits(classInstanceSize, CastExp),
EXP.null_: __traits(classInstanceSize, NullExp),
EXP.assert_: __traits(classInstanceSize, AssertExp),
EXP.array: __traits(classInstanceSize, ArrayExp),
EXP.call: __traits(classInstanceSize, CallExp),
EXP.address: __traits(classInstanceSize, AddrExp),
EXP.type: __traits(classInstanceSize, TypeExp),
EXP.throw_: __traits(classInstanceSize, ThrowExp),
EXP.new_: __traits(classInstanceSize, NewExp),
EXP.delete_: __traits(classInstanceSize, DeleteExp),
EXP.star: __traits(classInstanceSize, PtrExp),
EXP.symbolOffset: __traits(classInstanceSize, SymOffExp),
EXP.variable: __traits(classInstanceSize, VarExp),
EXP.dotVariable: __traits(classInstanceSize, DotVarExp),
EXP.dotIdentifier: __traits(classInstanceSize, DotIdExp),
EXP.dotTemplateInstance: __traits(classInstanceSize, DotTemplateInstanceExp),
EXP.dotType: __traits(classInstanceSize, DotTypeExp),
EXP.slice: __traits(classInstanceSize, SliceExp),
EXP.arrayLength: __traits(classInstanceSize, ArrayLengthExp),
EXP.dollar: __traits(classInstanceSize, DollarExp),
EXP.template_: __traits(classInstanceSize, TemplateExp),
EXP.dotTemplateDeclaration: __traits(classInstanceSize, DotTemplateExp),
EXP.declaration: __traits(classInstanceSize, DeclarationExp),
EXP.dSymbol: __traits(classInstanceSize, DsymbolExp),
EXP.typeid_: __traits(classInstanceSize, TypeidExp),
EXP.uadd: __traits(classInstanceSize, UAddExp),
EXP.remove: __traits(classInstanceSize, RemoveExp),
EXP.newAnonymousClass: __traits(classInstanceSize, NewAnonClassExp),
EXP.arrayLiteral: __traits(classInstanceSize, ArrayLiteralExp),
EXP.assocArrayLiteral: __traits(classInstanceSize, AssocArrayLiteralExp),
EXP.structLiteral: __traits(classInstanceSize, StructLiteralExp),
EXP.classReference: __traits(classInstanceSize, ClassReferenceExp),
EXP.thrownException: __traits(classInstanceSize, ThrownExceptionExp),
EXP.delegatePointer: __traits(classInstanceSize, DelegatePtrExp),
EXP.delegateFunctionPointer: __traits(classInstanceSize, DelegateFuncptrExp),
EXP.lessThan: __traits(classInstanceSize, CmpExp),
EXP.greaterThan: __traits(classInstanceSize, CmpExp),
EXP.lessOrEqual: __traits(classInstanceSize, CmpExp),
EXP.greaterOrEqual: __traits(classInstanceSize, CmpExp),
EXP.equal: __traits(classInstanceSize, EqualExp),
EXP.notEqual: __traits(classInstanceSize, EqualExp),
EXP.identity: __traits(classInstanceSize, IdentityExp),
EXP.notIdentity: __traits(classInstanceSize, IdentityExp),
EXP.index: __traits(classInstanceSize, IndexExp),
EXP.is_: __traits(classInstanceSize, IsExp),
EXP.leftShift: __traits(classInstanceSize, ShlExp),
EXP.rightShift: __traits(classInstanceSize, ShrExp),
EXP.leftShiftAssign: __traits(classInstanceSize, ShlAssignExp),
EXP.rightShiftAssign: __traits(classInstanceSize, ShrAssignExp),
EXP.unsignedRightShift: __traits(classInstanceSize, UshrExp),
EXP.unsignedRightShiftAssign: __traits(classInstanceSize, UshrAssignExp),
EXP.concatenate: __traits(classInstanceSize, CatExp),
EXP.concatenateAssign: __traits(classInstanceSize, CatAssignExp),
EXP.concatenateElemAssign: __traits(classInstanceSize, CatElemAssignExp),
EXP.concatenateDcharAssign: __traits(classInstanceSize, CatDcharAssignExp),
EXP.add: __traits(classInstanceSize, AddExp),
EXP.min: __traits(classInstanceSize, MinExp),
EXP.addAssign: __traits(classInstanceSize, AddAssignExp),
EXP.minAssign: __traits(classInstanceSize, MinAssignExp),
EXP.mul: __traits(classInstanceSize, MulExp),
EXP.div: __traits(classInstanceSize, DivExp),
EXP.mod: __traits(classInstanceSize, ModExp),
EXP.mulAssign: __traits(classInstanceSize, MulAssignExp),
EXP.divAssign: __traits(classInstanceSize, DivAssignExp),
EXP.modAssign: __traits(classInstanceSize, ModAssignExp),
EXP.and: __traits(classInstanceSize, AndExp),
EXP.or: __traits(classInstanceSize, OrExp),
EXP.xor: __traits(classInstanceSize, XorExp),
EXP.andAssign: __traits(classInstanceSize, AndAssignExp),
EXP.orAssign: __traits(classInstanceSize, OrAssignExp),
EXP.xorAssign: __traits(classInstanceSize, XorAssignExp),
EXP.assign: __traits(classInstanceSize, AssignExp),
EXP.not: __traits(classInstanceSize, NotExp),
EXP.tilde: __traits(classInstanceSize, ComExp),
EXP.plusPlus: __traits(classInstanceSize, PostExp),
EXP.minusMinus: __traits(classInstanceSize, PostExp),
EXP.construct: __traits(classInstanceSize, ConstructExp),
EXP.blit: __traits(classInstanceSize, BlitExp),
EXP.dot: __traits(classInstanceSize, DotExp),
EXP.comma: __traits(classInstanceSize, CommaExp),
EXP.question: __traits(classInstanceSize, CondExp),
EXP.andAnd: __traits(classInstanceSize, LogicalExp),
EXP.orOr: __traits(classInstanceSize, LogicalExp),
EXP.prePlusPlus: __traits(classInstanceSize, PreExp),
EXP.preMinusMinus: __traits(classInstanceSize, PreExp),
EXP.identifier: __traits(classInstanceSize, IdentifierExp),
EXP.string_: __traits(classInstanceSize, StringExp),
EXP.interpolated: __traits(classInstanceSize, InterpExp),
EXP.this_: __traits(classInstanceSize, ThisExp),
EXP.super_: __traits(classInstanceSize, SuperExp),
EXP.halt: __traits(classInstanceSize, HaltExp),
EXP.tuple: __traits(classInstanceSize, TupleExp),
EXP.error: __traits(classInstanceSize, ErrorExp),
EXP.void_: __traits(classInstanceSize, VoidInitExp),
EXP.int64: __traits(classInstanceSize, IntegerExp),
EXP.float64: __traits(classInstanceSize, RealExp),
EXP.complex80: __traits(classInstanceSize, ComplexExp),
EXP.import_: __traits(classInstanceSize, ImportExp),
EXP.delegate_: __traits(classInstanceSize, DelegateExp),
EXP.function_: __traits(classInstanceSize, FuncExp),
EXP.mixin_: __traits(classInstanceSize, MixinExp),
EXP.in_: __traits(classInstanceSize, InExp),
EXP.break_: __traits(classInstanceSize, CTFEExp),
EXP.continue_: __traits(classInstanceSize, CTFEExp),
EXP.goto_: __traits(classInstanceSize, CTFEExp),
EXP.scope_: __traits(classInstanceSize, ScopeExp),
EXP.traits: __traits(classInstanceSize, TraitsExp),
EXP.overloadSet: __traits(classInstanceSize, OverExp),
EXP.line: __traits(classInstanceSize, LineInitExp),
EXP.file: __traits(classInstanceSize, FileInitExp),
EXP.fileFullPath: __traits(classInstanceSize, FileInitExp),
EXP.moduleString: __traits(classInstanceSize, ModuleInitExp),
EXP.functionString: __traits(classInstanceSize, FuncInitExp),
EXP.prettyFunction: __traits(classInstanceSize, PrettyFuncInitExp),
EXP.pow: __traits(classInstanceSize, PowExp),
EXP.powAssign: __traits(classInstanceSize, PowAssignExp),
EXP.vector: __traits(classInstanceSize, VectorExp),
EXP.voidExpression: __traits(classInstanceSize, CTFEExp),
EXP.cantExpression: __traits(classInstanceSize, CTFEExp),
EXP.showCtfeContext: __traits(classInstanceSize, CTFEExp),
EXP.objcClassReference: __traits(classInstanceSize, ObjcClassReferenceExp),
EXP.vectorArray: __traits(classInstanceSize, VectorArrayExp),
EXP.compoundLiteral: __traits(classInstanceSize, CompoundLiteralExp),
EXP._Generic: __traits(classInstanceSize, GenericExp),
EXP.interval: __traits(classInstanceSize, IntervalExp),
EXP.loweredAssignExp : __traits(classInstanceSize, LoweredAssignExp),
];