*
* Specification: ($LINK2 https://dlang.org/spec/function.html#interpretation, Compile Time Function Execution (CTFE))
*
- * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
- * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Copyright: Copyright (C) 1999-2023 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/src/dmd/dinterpret.d, _dinterpret.d)
* Documentation: https://dlang.org/phobos/dmd_dinterpret.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dinterpret.d
import dmd.expressionsem;
import dmd.func;
import dmd.globals;
+import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.init;
import dmd.initsem;
+import dmd.location;
import dmd.mtype;
import dmd.printast;
import dmd.root.rmem;
import dmd.root.ctfloat;
import dmd.root.region;
import dmd.root.rootobject;
+import dmd.root.utf;
import dmd.statement;
import dmd.tokens;
-import dmd.utf;
import dmd.visitor;
/*************************************
{
switch (e.op)
{
- case TOK.int64:
- case TOK.float64:
- case TOK.complex80:
- case TOK.null_:
- case TOK.void_:
- case TOK.string_:
- case TOK.this_:
- case TOK.super_:
- case TOK.type:
- case TOK.typeid_:
- case TOK.template_: // non-eponymous template/instance
- case TOK.scope_: // ditto
- case TOK.dotTemplateDeclaration: // ditto, e.e1 doesn't matter here
- case TOK.dotTemplateInstance: // ditto
- case TOK.dot: // ditto
+ case EXP.int64:
+ case EXP.float64:
+ case EXP.complex80:
+ case EXP.null_:
+ case EXP.void_:
+ case EXP.string_:
+ case EXP.this_:
+ case EXP.super_:
+ case EXP.type:
+ case EXP.typeid_:
+ case EXP.template_: // non-eponymous template/instance
+ case EXP.scope_: // ditto
+ case EXP.dotTemplateDeclaration: // ditto, e.e1 doesn't matter here
+ case EXP.dotTemplateInstance: // ditto
+ case EXP.dot: // ditto
if (e.type.ty == Terror)
return ErrorExp.get();
- goto case TOK.error;
+ goto case EXP.error;
- case TOK.error:
+ case EXP.error:
return e;
default:
Expression result = interpret(e, null);
- result = copyRegionExp(result);
+ // Report an error if the expression contained a `ThrowException` and
+ // hence generated an uncaught exception
+ if (auto tee = result.isThrownExceptionExp())
+ {
+ tee.generateUncaughtError();
+ result = CTFEExp.cantexp;
+ }
+ else
+ result = copyRegionExp(result);
if (!CTFEExp.isCantExp(result))
result = scrubReturnValue(e.loc, result);
*/
public Expression ctfeInterpretForPragmaMsg(Expression e)
{
- if (e.op == TOK.error || e.op == TOK.type)
+ if (e.op == EXP.error || e.op == EXP.type)
return e;
// It's also OK for it to be a function declaration (happens only with
return e;
}
-public extern (C++) Expression getValue(VarDeclaration vd)
+public Expression getValue(VarDeclaration vd)
{
return ctfeGlobals.stack.getValue(vd);
}
Expression localThis; // value of 'this', or NULL if none
public:
- extern (C++) size_t stackPointer()
+ size_t stackPointer()
{
- return values.dim;
+ return values.length;
}
// The current value of 'this', or NULL if none
- extern (C++) Expression getThis()
+ Expression getThis()
{
return localThis;
}
// Largest number of stack positions we've used
- extern (C++) size_t maxStackUsage()
+ size_t maxStackUsage()
{
return maxStackPointer;
}
// Start a new stack frame, using the provided 'this'.
- extern (C++) void startFrame(Expression thisexp)
+ void startFrame(Expression thisexp)
{
frames.push(cast(void*)cast(size_t)framepointer);
savedThis.push(localThis);
localThis = thisexp;
}
- extern (C++) void endFrame()
+ void endFrame()
{
- size_t oldframe = cast(size_t)frames[frames.dim - 1];
- localThis = savedThis[savedThis.dim - 1];
+ size_t oldframe = cast(size_t)frames[frames.length - 1];
+ localThis = savedThis[savedThis.length - 1];
popAll(framepointer);
framepointer = oldframe;
- frames.setDim(frames.dim - 1);
- savedThis.setDim(savedThis.dim - 1);
+ frames.setDim(frames.length - 1);
+ savedThis.setDim(savedThis.length - 1);
}
- extern (C++) bool isInCurrentFrame(VarDeclaration v)
+ bool isInCurrentFrame(VarDeclaration v)
{
if (v.isDataseg() && !v.isCTFE())
return false; // It's a global
return v.ctfeAdrOnStack >= framepointer;
}
- extern (C++) Expression getValue(VarDeclaration v)
+ Expression getValue(VarDeclaration v)
{
//printf("getValue() %s\n", v.toChars());
if ((v.isDataseg() || v.storage_class & STC.manifest) && !v.isCTFE())
{
- assert(v.ctfeAdrOnStack < globalValues.dim);
+ assert(v.ctfeAdrOnStack < globalValues.length);
return globalValues[v.ctfeAdrOnStack];
}
assert(v.ctfeAdrOnStack < stackPointer());
return values[v.ctfeAdrOnStack];
}
- extern (C++) void setValue(VarDeclaration v, Expression e)
+ void setValue(VarDeclaration v, Expression e)
{
//printf("setValue() %s : %s\n", v.toChars(), e.toChars());
assert(!v.isDataseg() || v.isCTFE());
values[v.ctfeAdrOnStack] = e;
}
- extern (C++) void push(VarDeclaration v)
+ void push(VarDeclaration v)
{
//printf("push() %s\n", v.toChars());
assert(!v.isDataseg() || v.isCTFE());
return;
}
savedId.push(cast(void*)cast(size_t)v.ctfeAdrOnStack);
- v.ctfeAdrOnStack = cast(uint)values.dim;
+ v.ctfeAdrOnStack = cast(uint)values.length;
vars.push(v);
values.push(null);
}
- extern (C++) void pop(VarDeclaration v)
+ void pop(VarDeclaration v)
{
assert(!v.isDataseg() || v.isCTFE());
assert(!v.isReference());
const oldid = v.ctfeAdrOnStack;
v.ctfeAdrOnStack = cast(uint)cast(size_t)savedId[oldid];
- if (v.ctfeAdrOnStack == values.dim - 1)
+ if (v.ctfeAdrOnStack == values.length - 1)
{
values.pop();
vars.pop();
}
}
- extern (C++) void popAll(size_t stackpointer)
+ void popAll(size_t stackpointer)
{
if (stackPointer() > maxStackPointer)
maxStackPointer = stackPointer();
- assert(values.dim >= stackpointer);
- for (size_t i = stackpointer; i < values.dim; ++i)
+ assert(values.length >= stackpointer);
+ for (size_t i = stackpointer; i < values.length; ++i)
{
VarDeclaration v = vars[i];
v.ctfeAdrOnStack = cast(uint)cast(size_t)savedId[i];
savedId.setDim(stackpointer);
}
- extern (C++) void saveGlobalConstant(VarDeclaration v, Expression e)
+ void saveGlobalConstant(VarDeclaration v, Expression e)
{
assert(v._init && (v.isConst() || v.isImmutable() || v.storage_class & STC.manifest) && !v.isCTFE());
- v.ctfeAdrOnStack = cast(uint)globalValues.dim;
+ v.ctfeAdrOnStack = cast(uint)globalValues.length;
globalValues.push(copyRegionExp(e));
}
}
* thisarg = 'this', if a needThis() function, NULL if not.
*
* Returns:
- * result expression if successful, TOK.cantExpression if not,
+ * result expression if successful, EXP.cantExpression if not,
* or CTFEExp if function returned void.
*/
private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterState* istate, Expressions* arguments, Expression thisarg)
auto tf = fd.type.toBasetype().isTypeFunction();
if (tf.parameterList.varargs != VarArg.none && arguments &&
- ((fd.parameters && arguments.dim != fd.parameters.dim) || (!fd.parameters && arguments.dim)))
+ ((fd.parameters && arguments.length != fd.parameters.length) || (!fd.parameters && arguments.length)))
{
fd.error("C-style variadic functions are not yet implemented in CTFE");
return CTFEExp.cantexp;
// Place to hold all the arguments to the function while
// we are evaluating them.
- size_t dim = arguments ? arguments.dim : 0;
- assert((fd.parameters ? fd.parameters.dim : 0) == dim);
+ size_t dim = arguments ? arguments.length : 0;
+ assert((fd.parameters ? fd.parameters.length : 0) == dim);
/* Evaluate all the arguments to the function,
* store the results in eargs[]
if (CTFEExp.isCantExp(earg))
return earg;
}
- else if (fparam.storageClass & STC.lazy_)
+ else if (fparam.isLazy())
{
}
else
/* Struct literals are passed by value, but we don't need to
* copy them if they are passed as const
*/
- if (earg.op == TOK.structLiteral && !(fparam.storageClass & (STC.const_ | STC.immutable_)))
+ if (earg.op == EXP.structLiteral && !(fparam.storageClass & (STC.const_ | STC.immutable_)))
earg = copyLiteral(earg).copy();
}
if (auto tee = earg.isThrownExceptionExp())
istatex.caller = istate;
istatex.fd = fd;
- if (fd.isThis2)
+ if (fd.hasDualContext())
{
Expression arg0 = thisarg;
if (arg0 && arg0.type.ty == Tstruct)
}
ctfeGlobals.stack.push(v);
- if (fparam.isReference() && earg.op == TOK.variable &&
+ if (fparam.isReference() && earg.op == EXP.variable &&
earg.isVarExp().var.toParent2() == fd)
{
VarDeclaration vx = earg.isVarExp().var.isVarDeclaration();
}
else
{
- assert(!e || (e.op != TOK.continue_ && e.op != TOK.break_));
+ assert(!e || (e.op != EXP.continue_ && e.op != EXP.break_));
break;
}
}
// If fell off the end of a void function, return void
- if (!e && tf.next.ty == Tvoid)
- e = CTFEExp.voidexp;
- if (tf.isref && e.op == TOK.variable && e.isVarExp().var == fd.vthis)
+ if (!e)
+ {
+ if (tf.next.ty == Tvoid)
+ e = CTFEExp.voidexp;
+ else
+ {
+ /* missing a return statement can happen with C functions
+ * https://issues.dlang.org/show_bug.cgi?id=23056
+ */
+ fd.error("no return value from function");
+ e = CTFEExp.cantexp;
+ }
+ }
+
+ if (tf.isref && e.op == EXP.variable && e.isVarExp().var == fd.vthis)
e = thisarg;
- if (tf.isref && fd.isThis2 && e.op == TOK.index)
+ if (tf.isref && fd.hasDualContext() && e.op == EXP.index)
{
auto ie = e.isIndexExp();
auto pe = ie.e1.isPtrExp();
}
}
}
- assert(e !is null);
// Leave the function
--ctfeGlobals.callDepth;
this.goal = goal;
}
- // If e is TOK.throw_exception or TOK.cantExpression,
+ // If e is EXP.throw_exception or EXP.cantExpression,
// set it to 'result' and returns true.
bool exceptionOrCant(Expression e)
{
if (exceptionOrCantInterpret(e))
{
// Make sure e is not pointing to a stack temporary
- result = (e.op == TOK.cantExpression) ? CTFEExp.cantexp : e;
+ result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e;
return true;
}
return false;
if (istate.start == s)
istate.start = null;
- const dim = s.statements ? s.statements.dim : 0;
+ const dim = s.statements ? s.statements.length : 0;
foreach (i; 0 .. dim)
{
Statement sx = (*s.statements)[i];
if (istate.start == s)
istate.start = null;
- const dim = s.statements ? s.statements.dim : 0;
+ const dim = s.statements ? s.statements.length : 0;
foreach (i; 0 .. dim)
{
Statement sx = (*s.statements)[i];
continue;
if (exceptionOrCant(e))
return;
- if (e.op == TOK.break_)
+ if (e.op == EXP.break_)
{
if (istate.gotoTarget && istate.gotoTarget != s)
{
result = null;
return;
}
- if (e.op == TOK.continue_)
+ if (e.op == EXP.continue_)
{
if (istate.gotoTarget && istate.gotoTarget != s)
{
if (isTrueBool(e))
result = interpret(pue, s.ifbody, istate);
- else if (e.isBool(false))
+ else if (e.toBool().hasValue(false))
result = interpret(pue, s.elsebody, istate);
else
{
if (auto eaddr = e.isAddrExp())
x = eaddr.e1;
VarDeclaration v;
- while (x.op == TOK.variable && (v = (cast(VarExp)x).var.isVarDeclaration()) !is null)
+ while (x.op == EXP.variable && (v = x.isVarExp().var.isVarDeclaration()) !is null)
{
if (v.storage_class & STC.ref_)
{
else
break;
}
- // TODO: If it is a TOK.dotVariable or TOK.index, we should check that it is not
+ // TODO: If it is a EXP.dotVariable or EXP.index, we should check that it is not
// pointing to a local struct or static array.
}
if (auto se = e.isStructLiteralExp())
result = interpret(pue, s.exp, istate, CTFEGoal.LValue);
return;
}
- if (tf.next && tf.next.ty == Tdelegate && istate.fd.closureVars.dim > 0)
+ if (tf.next && tf.next.ty == Tdelegate && istate.fd.closureVars.length > 0)
{
// To support this, we need to copy all the closure vars
// into the delegate literal.
return;
}
- // We need to treat pointers specially, because TOK.symbolOffset can be used to
+ // We need to treat pointers specially, because EXP.symbolOffset can be used to
// return a value OR a pointer
Expression e = interpret(pue, s.exp, istate);
if (exceptionOrCant(e))
return;
+ /**
+ * Interpret `return a ~= b` (i.e. `return _d_arrayappendT{,Trace}(a, b)`) as:
+ * a ~= b;
+ * return a;
+ * This is needed because `a ~= b` has to be interpreted as an lvalue, in order to avoid
+ * assigning a larger array into a smaller one, such as:
+ * `a = [1, 2], a ~= [3]` => `[1, 2] ~= [3]` => `[1, 2] = [1, 2, 3]`
+ */
+ if (isRuntimeHook(s.exp, Id._d_arrayappendT) || isRuntimeHook(s.exp, Id._d_arrayappendTTrace))
+ {
+ auto rs = new ReturnStatement(s.loc, e);
+ rs.accept(this);
+ return;
+ }
+
// Disallow returning pointers to stack-allocated variables (bug 7876)
if (!stopPointersEscaping(s.loc, e))
{
if (exceptionOrCant(e))
return;
- if (e && e.op == TOK.break_)
+ if (e && e.op == EXP.break_)
{
if (istate.gotoTarget && istate.gotoTarget != s)
{
istate.gotoTarget = null;
break;
}
- if (e && e.op == TOK.continue_)
+ if (e && e.op == EXP.continue_)
{
if (istate.gotoTarget && istate.gotoTarget != s)
{
result = CTFEExp.cantexp;
return;
}
- if (e.isBool(false))
+ if (e.toBool().hasValue(false))
break;
assert(isTrueBool(e));
}
Expression e = interpret(&ue, s.condition, istate);
if (exceptionOrCant(e))
return;
- if (e.isBool(false))
+ if (e.toBool().hasValue(false))
break;
assert(isTrueBool(e));
}
if (exceptionOrCant(e))
return;
- if (e && e.op == TOK.break_)
+ if (e && e.op == EXP.break_)
{
if (istate.gotoTarget && istate.gotoTarget != s)
{
istate.gotoTarget = null;
break;
}
- if (e && e.op == TOK.continue_)
+ if (e && e.op == EXP.continue_)
{
if (istate.gotoTarget && istate.gotoTarget != s)
{
return;
if (exceptionOrCant(e))
return;
- if (e && e.op == TOK.break_)
+ if (e && e.op == EXP.break_)
{
if (istate.gotoTarget && istate.gotoTarget != s)
{
Expression ecase = interpret(&uecase, cs.exp, istate);
if (exceptionOrCant(ecase))
return;
- if (ctfeEqual(cs.exp.loc, TOK.equal, econdition, ecase))
+ if (ctfeEqual(cs.exp.loc, EXP.equal, econdition, ecase))
{
scase = cs;
break;
istate.start = scase;
Expression e = interpret(pue, s._body, istate);
assert(!istate.start); // jump must not fail
- if (e && e.op == TOK.break_)
+ if (e && e.op == EXP.break_)
{
if (istate.gotoTarget && istate.gotoTarget != s)
{
result = e;
}
- static bool isAnErrorException(ClassDeclaration cd)
- {
- return cd == ClassDeclaration.errorException || ClassDeclaration.errorException.isBaseOf(cd, null);
- }
-
static ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp newest)
{
debug (LOG)
}
// Little sanity check to make sure it's really a Throwable
ClassReferenceExp boss = oldest.thrown;
- const next = 4; // index of Throwable.next
+ const next = 5; // index of Throwable.next
assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next
ClassReferenceExp collateral = newest.thrown;
- if (isAnErrorException(collateral.originalClass()) && !isAnErrorException(boss.originalClass()))
+ if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException())
{
/* Find the index of the Error.bypassException field
*/
(*collateral.value.elements)[bypass] = boss;
return newest;
}
- while ((*boss.value.elements)[next].op == TOK.classReference)
+ while ((*boss.value.elements)[next].op == EXP.classReference)
{
- boss = cast(ClassReferenceExp)(*boss.value.elements)[next];
+ boss = (*boss.value.elements)[next].isClassReferenceExp();
}
(*boss.value.elements)[next] = collateral;
return oldest;
istate.start = null;
}
- incUsageCtfe(istate, s.loc);
+ interpretThrow(s.exp, s.loc);
+ }
+
+ /// Interpret `throw <exp>` found at the specified location `loc`
+ private void interpretThrow(Expression exp, const ref Loc loc)
+ {
+ incUsageCtfe(istate, loc);
- Expression e = interpretRegion(s.exp, istate);
+ Expression e = interpretRegion(exp, istate);
if (exceptionOrCant(e))
return;
- assert(e.op == TOK.classReference);
- result = ctfeEmplaceExp!ThrownExceptionExp(s.loc, e.isClassReferenceExp());
+ if (e.op == EXP.classReference)
+ {
+ result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp());
+ }
+ else
+ {
+ exp.error("to be thrown `%s` must be non-null", exp.toChars());
+ result = ErrorExp.get();
+ }
}
override void visit(ScopeGuardStatement s)
}
// If it is with(Enum) {...}, just execute the body.
- if (s.exp.op == TOK.scope_ || s.exp.op == TOK.type)
+ if (s.exp.op == EXP.scope_ || s.exp.op == EXP.type)
{
result = interpret(pue, s._body, istate);
return;
{
debug (LOG)
{
- printf("%s Expression::interpret() '%s' %s\n", e.loc.toChars(), Token.toChars(e.op), e.toChars());
+ printf("%s Expression::interpret() '%s' %s\n", e.loc.toChars(), EXPtoString(e.op).ptr, e.toChars());
printf("type = %s\n", e.type.toChars());
showCtfeExpr(e);
}
if (istate && istate.fd.vthis)
{
result = ctfeEmplaceExp!VarExp(e.loc, istate.fd.vthis);
- if (istate.fd.isThis2)
+ if (istate.fd.hasDualContext())
{
result = ctfeEmplaceExp!PtrExp(e.loc, result);
result.type = Type.tvoidptr.sarrayOf(2);
result = ctfeGlobals.stack.getThis();
if (result)
{
- if (istate && istate.fd.isThis2)
+ if (istate && istate.fd.hasDualContext())
{
- assert(result.op == TOK.address);
- result = (cast(AddrExp)result).e1;
- assert(result.op == TOK.arrayLiteral);
- result = (*(cast(ArrayLiteralExp)result).elements)[0];
+ assert(result.op == EXP.address);
+ result = result.isAddrExp().e1;
+ assert(result.op == EXP.arrayLiteral);
+ result = (*result.isArrayLiteralExp().elements)[0];
if (e.type.ty == Tstruct)
{
- result = (cast(AddrExp)result).e1;
+ result = result.isAddrExp().e1;
}
return;
}
- assert(result.op == TOK.structLiteral || result.op == TOK.classReference || result.op == TOK.type);
+ assert(result.op == EXP.structLiteral || result.op == EXP.classReference || result.op == EXP.type);
return;
}
e.error("value of `this` is not known at compile time");
{
printf("%s StringExp::interpret() %s\n", e.loc.toChars(), e.toChars());
}
- /* Attempts to modify string literals are prevented
- * in BinExp::interpretAssignCommon.
- */
- result = e;
+ if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted the string
+ {
+ result = e;
+ return;
+ }
+
+ if (e.type.ty != Tsarray ||
+ (cast(TypeNext)e.type).next.mod & (MODFlags.const_ | MODFlags.immutable_))
+ {
+ // If it's immutable, we don't need to dup it. Attempts to modify
+ // string literals are prevented in BinExp::interpretAssignCommon.
+ result = e;
+ }
+ else
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=20811
+ // Create a copy of mutable string literals, so that any change in
+ // value via an index or slice will not survive CTFE.
+ *pue = copyLiteral(e);
+ result = pue.exp();
+ }
}
override void visit(FuncExp e)
{
fromType = (cast(TypeArray)e.var.type).next;
}
- if (e.var.isDataseg() && ((e.offset == 0 && isSafePointerCast(e.var.type, pointee)) || (fromType && isSafePointerCast(fromType, pointee))))
+ if (e.var.isDataseg() && ((e.offset == 0 && isSafePointerCast(e.var.type, pointee)) ||
+ (fromType && isSafePointerCast(fromType, pointee)) ||
+ (e.var.isCsymbol() && e.offset + pointee.size() <= e.var.type.size())))
{
result = e;
return;
{
// Check for unsupported type painting operations
Type elemtype = (cast(TypeArray)val.type).next;
- d_uns64 elemsize = elemtype.size();
+ const elemsize = elemtype.size();
// It's OK to cast from fixed length to fixed length array, eg &int[n] to int[d]*.
if (val.type.ty == Tsarray && pointee.ty == Tsarray && elemsize == pointee.nextOf().size())
dinteger_t indx = e.offset / sz;
assert(sz * indx == e.offset);
Expression aggregate = null;
- if (val.op == TOK.arrayLiteral || val.op == TOK.string_)
+ if (val.op == EXP.arrayLiteral || val.op == EXP.string_)
{
aggregate = val;
}
if (decl.isDataseg()) {
// Normally this is already done by optimize()
// Do it here in case optimize(WANTvalue) wasn't run before CTFE
- emplaceExp!(SymOffExp)(pue, e.loc, (cast(VarExp)e.e1).var, 0);
+ emplaceExp!(SymOffExp)(pue, e.loc, e.e1.isVarExp().var, 0);
result = pue.exp();
result.type = e.type;
return;
return CTFEExp.cantexp;
assert(e.type);
- if (e.op == TOK.construct || e.op == TOK.blit)
+ if (e.op == EXP.construct || e.op == EXP.blit)
{
AssignExp ae = cast(AssignExp)e;
e = ae.e2;
}
- if (e.op == TOK.error)
+ if (e.op == EXP.error)
{
// FIXME: Ultimately all errors should be detected in prior semantic analysis stage.
}
}
else if (SymbolDeclaration s = d.isSymbolDeclaration())
{
+ // exclude void[]-typed `__traits(initSymbol)`
+ if (auto ta = s.type.toBasetype().isTypeDArray())
+ {
+ assert(ta.next.ty == Tvoid);
+ error(loc, "cannot determine the address of the initializer symbol during CTFE");
+ return CTFEExp.cantexp;
+ }
+
// Struct static initializers, for example
e = s.dsym.type.defaultInitLiteral(loc);
- if (e.op == TOK.error)
+ if (e.op == EXP.error)
error(loc, "CTFE failed because of previous errors in `%s.init`", s.toChars());
e = e.expressionSemantic(null);
- if (e.op == TOK.error)
+ if (e.op == EXP.error)
e = CTFEExp.cantexp;
else // Convert NULL to CTFEExp
e = interpret(e, istate, goal);
{
// Strip off the nest of ref variables
Expression ev = getValue(v);
- if (ev.op == TOK.variable ||
- ev.op == TOK.index ||
- (ev.op == TOK.slice && ev.type.toBasetype().ty == Tsarray) ||
- ev.op == TOK.dotVariable)
+ if (ev.op == EXP.variable ||
+ ev.op == EXP.index ||
+ (ev.op == EXP.slice && ev.type.toBasetype().ty == Tsarray) ||
+ ev.op == EXP.dotVariable)
{
result = interpret(pue, ev, istate, goal);
return;
while (s.isAttribDeclaration())
{
auto ad = cast(AttribDeclaration)s;
- assert(ad.decl && ad.decl.dim == 1); // Currently, only one allowed when parsing
+ assert(ad.decl && ad.decl.length == 1); // Currently, only one allowed when parsing
s = (*ad.decl)[0];
}
if (VarDeclaration v = s.isVarDeclaration())
result = null;
// Reserve stack space for all tuple members
- if (!td.objects)
- return;
- foreach (o; *td.objects)
+ td.foreachVar((s)
{
- Expression ex = isExpression(o);
- DsymbolExp ds = ex ? ex.isDsymbolExp() : null;
- VarDeclaration v2 = ds ? ds.s.isVarDeclaration() : null;
+ VarDeclaration v2 = s.isVarDeclaration();
assert(v2);
if (v2.isDataseg() && !v2.isCTFE())
- continue;
+ return 0;
ctfeGlobals.stack.push(v2);
if (v2._init)
{
einit = interpretRegion(ie.exp, istate, goal);
if (exceptionOrCant(einit))
- return;
+ return 1;
}
else if (v2._init.isVoidInitializer())
{
{
e.error("declaration `%s` is not yet implemented in CTFE", e.toChars());
result = CTFEExp.cantexp;
- return;
+ return 1;
}
setValue(v2, einit);
}
- }
+ return 0;
+ });
return;
}
if (v.isStatic())
if (ExpInitializer ie = v._init.isExpInitializer())
{
result = interpretRegion(ie.exp, istate, goal);
+ return;
}
else if (v._init.isVoidInitializer())
{
// There is no AssignExp for void initializers,
// so set it here.
setValue(v, result);
+ return;
}
- else
+ else if (v._init.isArrayInitializer())
{
- e.error("declaration `%s` is not yet implemented in CTFE", e.toChars());
- result = CTFEExp.cantexp;
+ result = v._init.initializerToExpression(v.type);
+ if (result !is null)
+ return;
}
+ e.error("declaration `%s` is not yet implemented in CTFE", e.toChars());
+ result = CTFEExp.cantexp;
}
else if (v.type.size() == 0)
{
if (exceptionOrCant(ex))
return;
- if (result.op == TOK.null_)
+ if (result.op == EXP.null_)
{
e.error("null pointer dereference evaluating typeid. `%s` is `null`", ex.toChars());
result = CTFEExp.cantexp;
return;
}
- if (result.op != TOK.classReference)
+ if (result.op != EXP.classReference)
{
e.error("CTFE internal error: determining classinfo");
result = CTFEExp.cantexp;
return;
}
- ClassDeclaration cd = (cast(ClassReferenceExp)result).originalClass();
+ ClassDeclaration cd = result.isClassReferenceExp().originalClass();
assert(cd);
emplaceExp!(TypeidExp)(pue, e.loc, cd.type);
// A tuple of assignments can contain void (Bug 5676).
if (goal == CTFEGoal.Nothing)
continue;
- if (ex.op == TOK.voidExpression)
+ if (ex.op == EXP.voidExpression)
{
e.error("CTFE internal error: void element `%s` in tuple", exp.toChars());
assert(0);
return;
auto expsx = e.elements;
- size_t dim = expsx ? expsx.dim : 0;
+ size_t dim = expsx ? expsx.length : 0;
for (size_t i = 0; i < dim; i++)
{
Expression exp = (*expsx)[i];
else
{
// segfault bug 6250
- assert(exp.op != TOK.index || (cast(IndexExp)exp).e1 != e);
+ assert(exp.op != EXP.index || exp.isIndexExp().e1 != e);
ex = interpretRegion(exp, istate);
if (exceptionOrCant(ex))
{
// todo: all tuple expansions should go in semantic phase.
expandTuples(expsx);
- if (expsx.dim != dim)
+ if (expsx.length != dim)
{
e.error("CTFE internal error: invalid array literal");
result = CTFEExp.cantexp;
return;
}
emplaceExp!(ArrayLiteralExp)(pue, e.loc, e.type, basis, expsx);
- auto ale = cast(ArrayLiteralExp)pue.exp();
+ auto ale = pue.exp().isArrayLiteralExp();
ale.ownedByCtfe = OwnedBy.ctfe;
result = ale;
}
expandTuples(keysx);
if (valuesx !is e.values)
expandTuples(valuesx);
- if (keysx.dim != valuesx.dim)
+ if (keysx.length != valuesx.length)
{
e.error("CTFE internal error: invalid AA");
result = CTFEExp.cantexp;
/* Remove duplicate keys
*/
- for (size_t i = 1; i < keysx.dim; i++)
+ for (size_t i = 1; i < keysx.length; i++)
{
auto ekey = (*keysx)[i - 1];
- for (size_t j = i; j < keysx.dim; j++)
+ for (size_t j = i; j < keysx.length; j++)
{
auto ekey2 = (*keysx)[j];
- if (!ctfeEqual(e.loc, TOK.equal, ekey, ekey2))
+ if (!ctfeEqual(e.loc, EXP.equal, ekey, ekey2))
continue;
// Remove ekey
return;
}
- size_t dim = e.elements ? e.elements.dim : 0;
+ size_t dim = e.elements ? e.elements.length : 0;
auto expsx = e.elements;
- if (dim != e.sd.fields.dim)
+ if (dim != e.sd.fields.length)
{
// guaranteed by AggregateDeclaration.fill and TypeStruct.defaultInitLiteral
- const nvthis = e.sd.fields.dim - e.sd.nonHiddenFields();
- assert(e.sd.fields.dim - dim == nvthis);
+ const nvthis = e.sd.fields.length - e.sd.nonHiddenFields();
+ assert(e.sd.fields.length - dim == nvthis);
/* If a nested struct has no initialized hidden pointer,
* set it to null to match the runtime behaviour.
++dim;
}
}
- assert(dim == e.sd.fields.dim);
+ assert(dim == e.sd.fields.length);
foreach (i; 0 .. dim)
{
if (expsx !is e.elements)
{
expandTuples(expsx);
- if (expsx.dim != e.sd.fields.dim)
+ if (expsx.length != e.sd.fields.length)
{
e.error("CTFE internal error: invalid struct literal");
result = CTFEExp.cantexp;
return;
}
emplaceExp!(StructLiteralExp)(pue, e.loc, e.sd, expsx);
- auto sle = cast(StructLiteralExp)pue.exp();
+ auto sle = pue.exp().isStructLiteralExp();
sle.type = e.type;
sle.ownedByCtfe = OwnedBy.ctfe;
sle.origin = e.origin;
return lenExpr;
size_t len = cast(size_t)lenExpr.toInteger();
Type elemType = (cast(TypeArray)newtype).next;
- if (elemType.ty == Tarray && argnum < arguments.dim - 1)
+ if (elemType.ty == Tarray && argnum < arguments.length - 1)
{
Expression elem = recursivelyCreateArrayLiteral(pue, loc, elemType, istate, arguments, argnum + 1);
if (exceptionOrCantInterpret(elem))
foreach (ref element; *elements)
element = copyLiteral(elem).copy();
emplaceExp!(ArrayLiteralExp)(pue, loc, newtype, elements);
- auto ae = cast(ArrayLiteralExp)pue.exp();
+ auto ae = pue.exp().isArrayLiteralExp();
ae.ownedByCtfe = OwnedBy.ctfe;
return ae;
}
- assert(argnum == arguments.dim - 1);
+ assert(argnum == arguments.length - 1);
if (elemType.ty.isSomeChar)
{
const ch = cast(dchar)elemType.defaultInitLiteral(loc).toInteger();
{
StructDeclaration sd = ts.sym;
auto exps = new Expressions();
- exps.reserve(sd.fields.dim);
+ exps.reserve(sd.fields.length);
if (e.arguments)
{
- exps.setDim(e.arguments.dim);
+ exps.setDim(e.arguments.length);
foreach (i, ex; *e.arguments)
{
ex = interpretRegion(ex, istate);
(*exps)[i] = ex;
}
}
- sd.fill(e.loc, exps, false);
+ sd.fill(e.loc, *exps, false);
auto se = ctfeEmplaceExp!StructLiteralExp(e.loc, sd, exps, e.newtype);
se.origin = se;
ClassDeclaration cd = tc.sym;
size_t totalFieldCount = 0;
for (ClassDeclaration c = cd; c; c = c.baseClass)
- totalFieldCount += c.fields.dim;
+ totalFieldCount += c.fields.length;
auto elems = new Expressions(totalFieldCount);
size_t fieldsSoFar = totalFieldCount;
for (ClassDeclaration c = cd; c; c = c.baseClass)
{
- fieldsSoFar -= c.fields.dim;
+ fieldsSoFar -= c.fields.length;
foreach (i, v; c.fields)
{
if (v.inuse)
else
m = v.getConstInitializer(true);
}
+ else if (v.type.isTypeNoreturn())
+ {
+ // Noreturn field with default initializer
+ (*elems)[fieldsSoFar + i] = null;
+ continue;
+ }
else
m = v.type.defaultInitLiteral(e.loc);
if (exceptionOrCant(m))
if (e.newtype.toBasetype().isscalar())
{
Expression newval;
- if (e.arguments && e.arguments.dim)
+ if (e.arguments && e.arguments.length)
newval = (*e.arguments)[0];
else
newval = e.newtype.defaultInitLiteral(e.loc);
return;
switch (e.op)
{
- case TOK.negate:
+ case EXP.negate:
*pue = Neg(e.type, e1);
break;
- case TOK.tilde:
+ case EXP.tilde:
*pue = Com(e.type, e1);
break;
- case TOK.not:
+ case EXP.not:
*pue = Not(e.type, e1);
break;
result = e; // optimize: reuse this CTFE reference
else
{
- auto edt = cast(DotTypeExp)e.copy();
+ auto edt = e.copy().isDotTypeExp();
edt.e1 = (e1 == ue.exp()) ? e1.copy() : e1; // don't return pointer to ue
result = edt;
}
{
printf("%s BinExp::interpretCommon() %s\n", e.loc.toChars(), e.toChars());
}
- if (e.e1.type.ty == Tpointer && e.e2.type.ty == Tpointer && e.op == TOK.min)
+ if (e.e1.type.ty == Tpointer && e.e2.type.ty == Tpointer && e.op == EXP.min)
{
UnionExp ue1 = void;
Expression e1 = interpret(&ue1, e.e1, istate);
Expression e2 = interpret(&ue2, e.e2, istate);
if (exceptionOrCant(e2))
return;
- *pue = pointerDifference(e.loc, e.type, e1, e2);
- result = (*pue).exp();
+ result = pointerDifference(pue, e.loc, e.type, e1, e2);
return;
}
if (e.e1.type.ty == Tpointer && e.e2.type.isintegral())
Expression e2 = interpret(&ue2, e.e2, istate);
if (exceptionOrCant(e2))
return;
- *pue = pointerArithmetic(e.loc, e.op, e.type, e1, e2);
- result = (*pue).exp();
+ result = pointerArithmetic(pue, e.loc, e.op, e.type, e1, e2);
return;
}
- if (e.e2.type.ty == Tpointer && e.e1.type.isintegral() && e.op == TOK.add)
+ if (e.e2.type.ty == Tpointer && e.e1.type.isintegral() && e.op == EXP.add)
{
UnionExp ue1 = void;
Expression e1 = interpret(&ue1, e.e1, istate);
Expression e2 = interpret(&ue2, e.e2, istate);
if (exceptionOrCant(e2))
return;
- *pue = pointerArithmetic(e.loc, e.op, e.type, e2, e1);
- result = (*pue).exp();
+ result = pointerArithmetic(pue, e.loc, e.op, e.type, e2, e1);
return;
}
if (e.e1.type.ty == Tpointer || e.e2.type.ty == Tpointer)
if (!evalOperand(&ue2, e.e2, e2))
return;
- if (e.op == TOK.rightShift || e.op == TOK.leftShift || e.op == TOK.unsignedRightShift)
+ if (e.op == EXP.rightShift || e.op == EXP.leftShift || e.op == EXP.unsignedRightShift)
{
const sinteger_t i2 = e2.toInteger();
- const d_uns64 sz = e1.type.size() * 8;
+ const uinteger_t sz = e1.type.size() * 8;
if (i2 < 0 || i2 >= sz)
{
e.error("shift by %lld is outside the range 0..%llu", i2, cast(ulong)sz - 1);
{
// The following should really be an assert()
e1.error("CTFE internal error: non-constant value `%s`", e1.toChars());
- emplaceExp!CTFEExp(&ue, TOK.cantExpression);
+ emplaceExp!CTFEExp(&ue, EXP.cantExpression);
return ue;
}
if (e2.isConst() != 1)
{
e2.error("CTFE internal error: non-constant value `%s`", e2.toChars());
- emplaceExp!CTFEExp(&ue, TOK.cantExpression);
+ emplaceExp!CTFEExp(&ue, EXP.cantExpression);
return ue;
}
const cmp = comparePointers(e.op, agg1, ofs1, agg2, ofs2);
if (cmp == -1)
{
- char dir = (e.op == TOK.greaterThan || e.op == TOK.greaterOrEqual) ? '<' : '>';
+ char dir = (e.op == EXP.greaterThan || e.op == EXP.greaterOrEqual) ? '<' : '>';
e.error("the ordering of pointers to unrelated memory blocks is indeterminate in CTFE. To check if they point to the same memory block, use both `>` and `<` inside `&&` or `||`, eg `%s && %s %c= %s + 1`", e.toChars(), e.e1.toChars(), dir, e.e2.toChars());
result = CTFEExp.cantexp;
return;
{
switch (e.op)
{
- case TOK.add:
+ case EXP.add:
interpretCommon(e, &Add);
return;
- case TOK.min:
+ case EXP.min:
interpretCommon(e, &Min);
return;
- case TOK.mul:
+ case EXP.mul:
interpretCommon(e, &Mul);
return;
- case TOK.div:
+ case EXP.div:
interpretCommon(e, &Div);
return;
- case TOK.mod:
+ case EXP.mod:
interpretCommon(e, &Mod);
return;
- case TOK.leftShift:
+ case EXP.leftShift:
interpretCommon(e, &Shl);
return;
- case TOK.rightShift:
+ case EXP.rightShift:
interpretCommon(e, &Shr);
return;
- case TOK.unsignedRightShift:
+ case EXP.unsignedRightShift:
interpretCommon(e, &Ushr);
return;
- case TOK.and:
+ case EXP.and:
interpretCommon(e, &And);
return;
- case TOK.or:
+ case EXP.or:
interpretCommon(e, &Or);
return;
- case TOK.xor:
+ case EXP.xor:
interpretCommon(e, &Xor);
return;
- case TOK.pow:
+ case EXP.pow:
interpretCommon(e, &Pow);
return;
- case TOK.equal:
- case TOK.notEqual:
+ case EXP.equal:
+ case EXP.notEqual:
interpretCompareCommon(e, &ctfeEqual);
return;
- case TOK.identity:
- case TOK.notIdentity:
+ case EXP.identity:
+ case EXP.notIdentity:
interpretCompareCommon(e, &ctfeIdentity);
return;
- case TOK.lessThan:
- case TOK.lessOrEqual:
- case TOK.greaterThan:
- case TOK.greaterOrEqual:
+ case EXP.lessThan:
+ case EXP.lessOrEqual:
+ case EXP.greaterThan:
+ case EXP.greaterOrEqual:
interpretCompareCommon(e, &ctfeCmp);
return;
default:
- printf("be = '%s' %s at [%s]\n", Token.toChars(e.op), e.toChars(), e.loc.toChars());
+ printf("be = '%s' %s at [%s]\n", EXPtoString(e.op).ptr, e.toChars(), e.loc.toChars());
assert(0);
}
}
* So we need to recurse to determine if it is a block assignment.
*/
bool isBlockAssignment = false;
- if (e1.op == TOK.slice)
+ if (e1.op == EXP.slice)
{
// a[] = e can have const e. So we compare the naked types.
Type tdst = e1.type.toBasetype();
// Deal with reference assignment
// ---------------------------------------
// If it is a construction of a ref variable, it is a ref assignment
- if ((e.op == TOK.construct || e.op == TOK.blit) &&
+ if ((e.op == EXP.construct || e.op == EXP.blit) &&
((cast(AssignExp)e).memset == MemorySet.referenceInit))
{
assert(!fp);
if (exceptionOrCant(newval))
return;
- VarDeclaration v = (cast(VarExp)e1).var.isVarDeclaration();
+ VarDeclaration v = e1.isVarExp().var.isVarDeclaration();
setValue(v, newval);
// Get the value to return. Note that 'newval' is an Lvalue,
if (fp)
{
- while (e1.op == TOK.cast_)
+ while (e1.op == EXP.cast_)
{
- CastExp ce = cast(CastExp)e1;
+ CastExp ce = e1.isCastExp();
e1 = ce.e1;
}
}
AssocArrayLiteralExp existingAA = null;
Expression lastIndex = null;
Expression oldval = null;
- if (e1.op == TOK.index && (cast(IndexExp)e1).e1.type.toBasetype().ty == Taarray)
+ if (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray)
{
// ---------------------------------------
// Deal with AA index assignment
* (2) If the ultimate AA is null, no insertion happens at all. Instead,
* we create nested AA literals, and change it into a assignment.
*/
- IndexExp ie = cast(IndexExp)e1;
+ IndexExp ie = e1.isIndexExp();
int depth = 0; // how many nested AA indices are there?
- while (ie.e1.op == TOK.index && (cast(IndexExp)ie.e1).e1.type.toBasetype().ty == Taarray)
+ while (ie.e1.op == EXP.index && ie.e1.isIndexExp().e1.type.toBasetype().ty == Taarray)
{
assert(ie.modifiable);
- ie = cast(IndexExp)ie.e1;
+ ie = ie.e1.isIndexExp();
++depth;
}
// Normal case, ultimate parent AA already exists
// We need to walk from the deepest index up, checking that an AA literal
// already exists on each level.
- lastIndex = interpretRegion((cast(IndexExp)e1).e2, istate);
+ lastIndex = interpretRegion(e1.isIndexExp().e2, istate);
lastIndex = resolveSlice(lastIndex); // only happens with AA assignment
if (exceptionOrCant(lastIndex))
return;
while (depth > 0)
{
// Walk the syntax tree to find the indexExp at this depth
- IndexExp xe = cast(IndexExp)e1;
+ IndexExp xe = e1.isIndexExp();
foreach (d; 0 .. depth)
- xe = cast(IndexExp)xe.e1;
+ xe = xe.e1.isIndexExp();
Expression ekey = interpretRegion(xe.e2, istate);
if (exceptionOrCant(ekey))
oldval = copyLiteral(e.e1.type.defaultInitLiteral(e.loc)).copy();
Expression newaae = oldval;
- while (e1.op == TOK.index && (cast(IndexExp)e1).e1.type.toBasetype().ty == Taarray)
+ while (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray)
{
- Expression ekey = interpretRegion((cast(IndexExp)e1).e2, istate);
+ Expression ekey = interpretRegion(e1.isIndexExp().e2, istate);
if (exceptionOrCant(ekey))
return;
ekey = resolveSlice(ekey); // only happens with AA assignment
valuesx.push(newaae);
auto aae = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx);
- aae.type = (cast(IndexExp)e1).e1.type;
+ aae.type = e1.isIndexExp().e1.type;
aae.ownedByCtfe = OwnedBy.ctfe;
if (!existingAA)
{
lastIndex = ekey;
}
newaae = aae;
- e1 = (cast(IndexExp)e1).e1;
+ e1 = e1.isIndexExp().e1;
}
// We must set to aggregate with newaae
assert(existingAA && lastIndex);
e1 = null; // stomp
}
- else if (e1.op == TOK.arrayLength)
+ else if (e1.op == EXP.arrayLength)
{
oldval = interpretRegion(e1, istate);
if (exceptionOrCant(oldval))
return;
}
- else if (e.op == TOK.construct || e.op == TOK.blit)
+ else if (e.op == EXP.construct || e.op == EXP.blit)
{
// Unless we have a simple var assignment, we're
// only modifying part of the variable. So we need to make sure
if (exceptionOrCant(e1))
return;
- if (e1.op == TOK.index && (cast(IndexExp)e1).e1.type.toBasetype().ty == Taarray)
+ if (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray)
{
- IndexExp ie = cast(IndexExp)e1;
- assert(ie.e1.op == TOK.assocArrayLiteral);
- existingAA = cast(AssocArrayLiteralExp)ie.e1;
+ IndexExp ie = e1.isIndexExp();
+ assert(ie.e1.op == EXP.assocArrayLiteral);
+ existingAA = ie.e1.isAssocArrayLiteralExp();
lastIndex = ie.e2;
}
}
Expression newval = interpretRegion(e.e2, istate);
if (exceptionOrCant(newval))
return;
- if (e.op == TOK.blit && newval.op == TOK.int64)
+ if (e.op == EXP.blit && newval.op == EXP.int64)
{
Type tbn = e.type.baseElemOf();
if (tbn.ty == Tstruct)
/* Look for special case of struct being initialized with 0.
*/
newval = e.type.defaultInitLiteral(e.loc);
- if (newval.op == TOK.error)
+ if (newval.op == EXP.error)
{
result = CTFEExp.cantexp;
return;
if (e.e1.type.ty != Tpointer)
{
// ~= can create new values (see bug 6052)
- if (e.op == TOK.concatenateAssign || e.op == TOK.concatenateElemAssign || e.op == TOK.concatenateDcharAssign)
+ if (e.op == EXP.concatenateAssign || e.op == EXP.concatenateElemAssign || e.op == EXP.concatenateDcharAssign)
{
// We need to dup it and repaint the type. For a dynamic array
// we can skip duplication, because it gets copied later anyway.
newval = (*fp)(e.loc, e.type, oldval, newval).copy();
}
else if (e.e2.type.isintegral() &&
- (e.op == TOK.addAssign ||
- e.op == TOK.minAssign ||
- e.op == TOK.plusPlus ||
- e.op == TOK.minusMinus))
+ (e.op == EXP.addAssign ||
+ e.op == EXP.minAssign ||
+ e.op == EXP.plusPlus ||
+ e.op == EXP.minusMinus))
{
- newval = pointerArithmetic(e.loc, e.op, e.type, oldval, newval).copy();
+ newval = pointerArithmetic(pue, e.loc, e.op, e.type, oldval, newval).copy();
+ if (newval == pue.exp())
+ newval = pue.copy();
}
else
{
result = ctfeCast(pue, e.loc, e.type, e.type, fp && post ? oldval : newval);
return;
}
- if (e1.op == TOK.arrayLength)
+ if (e1.op == EXP.arrayLength)
{
/* Change the assignment from:
* arr.length = n;
// We have changed it into a reference assignment
// Note that returnValue is still the new length.
- e1 = (cast(ArrayLengthExp)e1).e1;
+ e1 = e1.isArrayLengthExp().e1;
Type t = e1.type.toBasetype();
if (t.ty != Tarray)
{
UnionExp utmp = void;
oldval = resolveSlice(oldval, &utmp);
- newval = changeArrayLiteralLength(e.loc, cast(TypeArray)t, oldval, oldlen, newlen).copy();
+ newval = changeArrayLiteralLength(pue, e.loc, cast(TypeArray)t, oldval, oldlen, newlen);
+ if (newval == pue.exp())
+ newval = pue.copy();
e1 = assignToLvalue(e, e1, newval);
if (exceptionOrCant(e1))
/* Block assignment or element-wise assignment.
*/
- if (e1.op == TOK.slice ||
- e1.op == TOK.vector ||
- e1.op == TOK.arrayLiteral ||
- e1.op == TOK.string_ ||
- e1.op == TOK.null_ && e1.type.toBasetype().ty == Tarray)
+ if (e1.op == EXP.slice ||
+ e1.op == EXP.vector ||
+ e1.op == EXP.arrayLiteral ||
+ e1.op == EXP.string_ ||
+ e1.op == EXP.null_ && e1.type.toBasetype().ty == Tarray)
{
// Note that slice assignments don't support things like ++, so
// we don't need to remember 'returnValue'.
if (auto dve = e1x.isDotVarExp())
{
auto ex = dve.e1;
- auto sle = ex.op == TOK.structLiteral ? (cast(StructLiteralExp)ex)
- : ex.op == TOK.classReference ? (cast(ClassReferenceExp)ex).value
+ auto sle = ex.op == EXP.structLiteral ? ex.isStructLiteralExp()
+ : ex.op == EXP.classReference ? ex.isClassReferenceExp().value
: null;
auto v = dve.var.isVarDeclaration();
if (!sle || !v)
if (v is v2 || !v.isOverlappedWith(v2))
continue;
auto e = (*sle.elements)[i];
- if (e.op != TOK.void_)
+ if (e.op != EXP.void_)
(*sle.elements)[i] = voidInitLiteral(e.type, v).copy();
}
}
* e.v = newval
*/
auto ex = dve.e1;
- auto sle = ex.op == TOK.structLiteral ? (cast(StructLiteralExp)ex)
- : ex.op == TOK.classReference ? (cast(ClassReferenceExp)ex).value
+ auto sle = ex.op == EXP.structLiteral ? ex.isStructLiteralExp()
+ : ex.op == EXP.classReference ? ex.isClassReferenceExp().value
: null;
- auto v = (cast(DotVarExp)e1).var.isVarDeclaration();
+ auto v = e1.isDotVarExp().var.isVarDeclaration();
if (!sle || !v)
{
e.error("CTFE internal error: dotvar assignment");
return CTFEExp.cantexp;
}
- int fieldi = ex.op == TOK.structLiteral ? findFieldIndexByName(sle.sd, v)
- : (cast(ClassReferenceExp)ex).findFieldIndexByName(v);
+ int fieldi = ex.op == EXP.structLiteral ? findFieldIndexByName(sle.sd, v)
+ : ex.isClassReferenceExp().findFieldIndexByName(v);
if (fieldi == -1)
{
e.error("CTFE internal error: cannot find field `%s` in `%s`", v.toChars(), ex.toChars());
return CTFEExp.cantexp;
}
- assert(0 <= fieldi && fieldi < sle.elements.dim);
+ assert(0 <= fieldi && fieldi < sle.elements.length);
// If it's a union, set all other members of this union to void
stompOverlappedFields(sle, v);
payload = &(*sle.elements)[fieldi];
oldval = *payload;
+ if (auto ival = newval.isIntegerExp())
+ {
+ if (auto bf = v.isBitFieldDeclaration())
+ {
+ sinteger_t value = ival.toInteger();
+ if (bf.type.isunsigned())
+ value &= (1L << bf.fieldWidth) - 1; // zero extra bits
+ else
+ { // sign extend extra bits
+ value = value << (64 - bf.fieldWidth);
+ value = value >> (64 - bf.fieldWidth);
+ }
+ ival.setInteger(value);
+ }
+ }
}
else if (auto ie = e1.isIndexExp())
{
existingSE.setCodeUnit(index, cast(dchar)newval.toInteger());
return null;
}
- if (aggregate.op != TOK.arrayLiteral)
+ if (aggregate.op != EXP.arrayLiteral)
{
e.error("index assignment `%s` is not yet supported in CTFE ", e.toChars());
return CTFEExp.cantexp;
}
- ArrayLiteralExp existingAE = cast(ArrayLiteralExp)aggregate;
+ ArrayLiteralExp existingAE = aggregate.isArrayLiteralExp();
if (existingAE.ownedByCtfe != OwnedBy.ctfe)
{
e.error("cannot modify read-only constant `%s`", existingAE.toChars());
if (auto ve = newval.isVectorExp())
{
// Ensure ve is an array literal, and not a broadcast
- if (ve.e1.op == TOK.int64 || ve.e1.op == TOK.float64) // if broadcast
+ if (ve.e1.op == EXP.int64 || ve.e1.op == EXP.float64) // if broadcast
{
UnionExp ue = void;
Expression ex = interpretVectorToArray(&ue, ve);
}
}
- if (newval.op == TOK.structLiteral && oldval)
+ if (newval.op == EXP.structLiteral && oldval)
{
- assert(oldval.op == TOK.structLiteral || oldval.op == TOK.arrayLiteral || oldval.op == TOK.string_);
+ assert(oldval.op == EXP.structLiteral || oldval.op == EXP.arrayLiteral || oldval.op == EXP.string_);
newval = copyLiteral(newval).copy();
assignInPlace(oldval, newval);
}
- else if (wantCopy && e.op == TOK.assign)
+ else if (wantCopy && e.op == EXP.assign)
{
// Currently postblit/destructor calls on static array are done
// in the druntime internal functions so they don't appear in AST.
return CTFEExp.cantexp;
}
}
- assert(oldval.op == TOK.arrayLiteral);
- assert(newval.op == TOK.arrayLiteral);
+ assert(oldval.op == EXP.arrayLiteral);
+ assert(newval.op == EXP.arrayLiteral);
- Expressions* oldelems = (cast(ArrayLiteralExp)oldval).elements;
- Expressions* newelems = (cast(ArrayLiteralExp)newval).elements;
- assert(oldelems.dim == newelems.dim);
+ Expressions* oldelems = oldval.isArrayLiteralExp().elements;
+ Expressions* newelems = newval.isArrayLiteralExp().elements;
+ assert(oldelems.length == newelems.length);
Type elemtype = oldval.type.nextOf();
foreach (i, ref oldelem; *oldelems)
if (wantCopy)
newval = copyLiteral(newval).copy();
- if (t1b.ty == Tsarray && e.op == TOK.construct && e.e2.isLvalue())
+ if (t1b.ty == Tsarray && e.op == EXP.construct && e.e2.isLvalue())
{
// https://issues.dlang.org/show_bug.cgi?id=9245
if (Expression ex = evaluatePostblit(istate, newval))
*payload = oldval;
// Blit assignment should return the newly created value.
- if (e.op == TOK.blit)
+ if (e.op == EXP.blit)
return oldval;
return null;
* This could be a slice assignment or a block assignment, and
* dest could be either an array literal, or a string.
*
- * Returns TOK.cantExpression on failure. If there are no errors,
+ * Returns EXP.cantExpression on failure. If there are no errors,
* it returns aggregate[low..upp], except that as an optimisation,
* if goal == CTFEGoal.Nothing, it will return NULL
*/
// aggregate[] = newval
// aggregate[low..upp] = newval
// ------------------------------
- version (all) // should be move in interpretAssignCommon as the evaluation of e1
- {
- Expression oldval = interpretRegion(se.e1, istate);
-
- // Set the $ variable
- uinteger_t dollar = resolveArrayLength(oldval);
- if (se.lengthVar)
- {
- Expression dollarExp = ctfeEmplaceExp!IntegerExp(e1.loc, dollar, Type.tsize_t);
- ctfeGlobals.stack.push(se.lengthVar);
- setValue(se.lengthVar, dollarExp);
- }
- Expression lwr = interpretRegion(se.lwr, istate);
- if (exceptionOrCantInterpret(lwr))
- {
- if (se.lengthVar)
- ctfeGlobals.stack.pop(se.lengthVar);
- return lwr;
- }
- Expression upr = interpretRegion(se.upr, istate);
- if (exceptionOrCantInterpret(upr))
- {
- if (se.lengthVar)
- ctfeGlobals.stack.pop(se.lengthVar);
- return upr;
- }
- if (se.lengthVar)
- ctfeGlobals.stack.pop(se.lengthVar); // $ is defined only in [L..U]
-
- const dim = dollar;
- lowerbound = lwr ? lwr.toInteger() : 0;
- upperbound = upr ? upr.toInteger() : dim;
-
- if (lowerbound < 0 || dim < upperbound)
- {
- e.error("array bounds `[0..%llu]` exceeded in slice `[%llu..%llu]`",
- ulong(dim), ulong(lowerbound), ulong(upperbound));
- return CTFEExp.cantexp;
- }
- }
- aggregate = oldval;
- firstIndex = lowerbound;
+ aggregate = interpretRegion(se.e1, istate);
+ lowerbound = se.lwr ? se.lwr.toInteger() : 0;
+ upperbound = se.upr ? se.upr.toInteger() : resolveArrayLength(aggregate);
+ // Slice of a slice --> change the bounds
if (auto oldse = aggregate.isSliceExp())
{
- // Slice of a slice --> change the bounds
- if (oldse.upr.toInteger() < upperbound + oldse.lwr.toInteger())
- {
- e.error("slice `[%llu..%llu]` exceeds array bounds `[0..%llu]`",
- ulong(lowerbound), ulong(upperbound), oldse.upr.toInteger() - oldse.lwr.toInteger());
- return CTFEExp.cantexp;
- }
aggregate = oldse.e1;
firstIndex = lowerbound + oldse.lwr.toInteger();
}
+ else
+ firstIndex = lowerbound;
}
else
{
if (auto ale = e1.isArrayLiteralExp())
{
lowerbound = 0;
- upperbound = ale.elements.dim;
+ upperbound = ale.elements.length;
}
else if (auto se = e1.isStringExp())
{
lowerbound = 0;
upperbound = se.len;
}
- else if (e1.op == TOK.null_)
+ else if (e1.op == EXP.null_)
{
lowerbound = 0;
upperbound = 0;
return CTFEExp.cantexp;
}
}
- assert(newval.op != TOK.slice);
+ assert(newval.op != EXP.slice);
}
if (auto se = newval.isStringExp())
{
return CTFEExp.cantexp;
}
- if (newval.op == TOK.slice && !isBlockAssignment)
+ if (newval.op == EXP.slice && !isBlockAssignment)
{
- auto se = cast(SliceExp)newval;
+ auto se = newval.isSliceExp();
auto aggr2 = se.e1;
const srclower = se.lwr.toInteger();
const srcupper = se.upr.toInteger();
// Currently overlapping for struct array is allowed.
// The order of elements processing depends on the overlapping.
// https://issues.dlang.org/show_bug.cgi?id=14024
- assert(aggr2.op == TOK.arrayLiteral);
+ assert(aggr2.op == EXP.arrayLiteral);
Expressions* oldelems = existingAE.elements;
- Expressions* newelems = (cast(ArrayLiteralExp)aggr2).elements;
+ Expressions* newelems = aggr2.isArrayLiteralExp().elements;
Type elemtype = aggregate.type.nextOf();
bool needsPostblit = e.e2.isLvalue();
}
// no overlapping
//length?
- assert(newval.op != TOK.slice);
+ assert(newval.op != EXP.slice);
}
- if (newval.op == TOK.string_ && !isBlockAssignment)
+ if (newval.op == EXP.string_ && !isBlockAssignment)
{
/* Mixed slice: it was initialized as an array literal of chars/integers.
* Now a slice of it is being set with a string.
*/
- sliceAssignArrayLiteralFromString(existingAE, cast(StringExp)newval, cast(size_t)firstIndex);
+ sliceAssignArrayLiteralFromString(existingAE, newval.isStringExp(), cast(size_t)firstIndex);
return newval;
}
- if (newval.op == TOK.arrayLiteral && !isBlockAssignment)
+ if (newval.op == EXP.arrayLiteral && !isBlockAssignment)
{
Expressions* oldelems = existingAE.elements;
- Expressions* newelems = (cast(ArrayLiteralExp)newval).elements;
+ Expressions* newelems = newval.isArrayLiteralExp().elements;
Type elemtype = existingAE.type.nextOf();
- bool needsPostblit = e.op != TOK.blit && e.e2.isLvalue();
+ bool needsPostblit = e.op != EXP.blit && e.e2.isLvalue();
foreach (j, newelem; *newelems)
{
newelem = paintTypeOntoLiteral(elemtype, newelem);
bool needsPostblit;
bool needsDtor;
- extern (C++) Expression assignTo(ArrayLiteralExp ae)
+ Expression assignTo(ArrayLiteralExp ae)
{
- return assignTo(ae, 0, ae.elements.dim);
+ return assignTo(ae, 0, ae.elements.length);
}
- extern (C++) Expression assignTo(ArrayLiteralExp ae, size_t lwr, size_t upr)
+ Expression assignTo(ArrayLiteralExp ae, size_t lwr, size_t upr)
{
Expressions* w = ae.elements;
assert(ae.type.ty == Tsarray || ae.type.ty == Tarray);
bool directblk = (cast(TypeArray)ae.type).next.equivalent(newval.type);
for (size_t k = lwr; k < upr; k++)
{
- if (!directblk && (*w)[k].op == TOK.arrayLiteral)
+ if (!directblk && (*w)[k].op == EXP.arrayLiteral)
{
// Multidimensional array block assign
- if (Expression ex = assignTo(cast(ArrayLiteralExp)(*w)[k]))
+ if (Expression ex = assignTo((*w)[k].isArrayLiteralExp()))
return ex;
}
else if (refCopy)
Type tn = newval.type.toBasetype();
bool wantRef = (tn.ty == Tarray || isAssocArray(tn) || tn.ty == Tclass);
- bool cow = newval.op != TOK.structLiteral && newval.op != TOK.arrayLiteral && newval.op != TOK.string_;
+ bool cow = newval.op != EXP.structLiteral && newval.op != EXP.arrayLiteral && newval.op != EXP.string_;
Type tb = tn.baseElemOf();
StructDeclaration sd = (tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null);
rb.istate = istate;
rb.newval = newval;
rb.refCopy = wantRef || cow;
- rb.needsPostblit = sd && sd.postblit && e.op != TOK.blit && e.e2.isLvalue();
- rb.needsDtor = sd && sd.dtor && e.op == TOK.assign;
+ rb.needsPostblit = sd && sd.postblit && e.op != EXP.blit && e.e2.isLvalue();
+ rb.needsDtor = sd && sd.dtor && e.op == EXP.assign;
if (Expression ex = rb.assignTo(existingAE, cast(size_t)lowerbound, cast(size_t)upperbound))
return ex;
{
switch (e.op)
{
- case TOK.addAssign:
+ case EXP.addAssign:
interpretAssignCommon(e, &Add);
return;
- case TOK.minAssign:
+ case EXP.minAssign:
interpretAssignCommon(e, &Min);
return;
- case TOK.concatenateAssign:
- case TOK.concatenateElemAssign:
- case TOK.concatenateDcharAssign:
+ case EXP.concatenateAssign:
+ case EXP.concatenateElemAssign:
+ case EXP.concatenateDcharAssign:
interpretAssignCommon(e, &ctfeCat);
return;
- case TOK.mulAssign:
+ case EXP.mulAssign:
interpretAssignCommon(e, &Mul);
return;
- case TOK.divAssign:
+ case EXP.divAssign:
interpretAssignCommon(e, &Div);
return;
- case TOK.modAssign:
+ case EXP.modAssign:
interpretAssignCommon(e, &Mod);
return;
- case TOK.leftShiftAssign:
+ case EXP.leftShiftAssign:
interpretAssignCommon(e, &Shl);
return;
- case TOK.rightShiftAssign:
+ case EXP.rightShiftAssign:
interpretAssignCommon(e, &Shr);
return;
- case TOK.unsignedRightShiftAssign:
+ case EXP.unsignedRightShiftAssign:
interpretAssignCommon(e, &Ushr);
return;
- case TOK.andAssign:
+ case EXP.andAssign:
interpretAssignCommon(e, &And);
return;
- case TOK.orAssign:
+ case EXP.orAssign:
interpretAssignCommon(e, &Or);
return;
- case TOK.xorAssign:
+ case EXP.xorAssign:
interpretAssignCommon(e, &Xor);
return;
- case TOK.powAssign:
+ case EXP.powAssign:
interpretAssignCommon(e, &Pow);
return;
{
printf("%s PostExp::interpret() %s\n", e.loc.toChars(), e.toChars());
}
- if (e.op == TOK.plusPlus)
+ if (e.op == EXP.plusPlus)
interpretAssignCommon(e, &Add, 1);
else
interpretAssignCommon(e, &Min, 1);
static int isPointerCmpExp(Expression e, Expression* p1, Expression* p2)
{
int ret = 1;
- while (e.op == TOK.not)
+ while (e.op == EXP.not)
{
ret *= -1;
- e = (cast(NotExp)e).e1;
+ e = e.isNotExp().e1;
}
switch (e.op)
{
- case TOK.lessThan:
- case TOK.lessOrEqual:
+ case EXP.lessThan:
+ case EXP.lessOrEqual:
ret *= -1;
goto case; /+ fall through +/
- case TOK.greaterThan:
- case TOK.greaterOrEqual:
- *p1 = (cast(BinExp)e).e1;
- *p2 = (cast(BinExp)e).e2;
+ case EXP.greaterThan:
+ case EXP.greaterOrEqual:
+ *p1 = e.isBinExp().e1;
+ *p2 = e.isBinExp().e2;
if (!(isPointer((*p1).type) && isPointer((*p2).type)))
ret = 0;
break;
*/
private void interpretFourPointerRelation(UnionExp* pue, BinExp e)
{
- assert(e.op == TOK.andAnd || e.op == TOK.orOr);
+ assert(e.op == EXP.andAnd || e.op == EXP.orOr);
/* It can only be an isInside expression, if both e1 and e2 are
* directional pointer comparisons.
Expression agg1 = getAggregateFromPointer(p1, &ofs1);
Expression agg2 = getAggregateFromPointer(p2, &ofs2);
- if (!pointToSameMemoryBlock(agg1, agg2) && agg1.op != TOK.null_ && agg2.op != TOK.null_)
+ if (!pointToSameMemoryBlock(agg1, agg2) && agg1.op != EXP.null_ && agg2.op != EXP.null_)
{
// Here it is either CANT_INTERPRET,
// or an IsInside comparison returning false.
(dir1 != dir2 && pointToSameMemoryBlock(agg1, agg3) && pointToSameMemoryBlock(agg2, agg4)))
{
// it's a legal two-sided comparison
- emplaceExp!(IntegerExp)(pue, e.loc, (e.op == TOK.andAnd) ? 0 : 1, e.type);
+ emplaceExp!(IntegerExp)(pue, e.loc, (e.op == EXP.andAnd) ? 0 : 1, e.type);
result = pue.exp();
return;
}
* Returns:
* negate operator
*/
- static TOK negateRelation(TOK op) pure
+ static EXP negateRelation(EXP op) pure
{
switch (op)
{
- case TOK.greaterOrEqual: op = TOK.lessThan; break;
- case TOK.greaterThan: op = TOK.lessOrEqual; break;
- case TOK.lessOrEqual: op = TOK.greaterThan; break;
- case TOK.lessThan: op = TOK.greaterOrEqual; break;
+ case EXP.greaterOrEqual: op = EXP.lessThan; break;
+ case EXP.greaterThan: op = EXP.lessOrEqual; break;
+ case EXP.lessOrEqual: op = EXP.greaterThan; break;
+ case EXP.lessThan: op = EXP.greaterOrEqual; break;
default: assert(0);
}
return op;
}
- const TOK cmpop = nott ? negateRelation(ex.op) : ex.op;
+ const EXP cmpop = nott ? negateRelation(ex.op) : ex.op;
const cmp = comparePointers(cmpop, agg1, ofs1, agg2, ofs2);
// We already know this is a valid comparison.
assert(cmp >= 0);
- if (e.op == TOK.andAnd && cmp == 1 || e.op == TOK.orOr && cmp == 0)
+ if (e.op == EXP.andAnd && cmp == 1 || e.op == EXP.orOr && cmp == 0)
{
result = interpret(pue, e.e2, istate);
return;
}
- emplaceExp!(IntegerExp)(pue, e.loc, (e.op == TOK.andAnd) ? 0 : 1, e.type);
+ emplaceExp!(IntegerExp)(pue, e.loc, (e.op == EXP.andAnd) ? 0 : 1, e.type);
result = pue.exp();
}
return;
bool res;
- const andand = e.op == TOK.andAnd;
- if (andand ? result.isBool(false) : isTrueBool(result))
+ const andand = e.op == EXP.andAnd;
+ if (andand ? result.toBool().hasValue(false) : isTrueBool(result))
res = !andand;
- else if (andand ? isTrueBool(result) : result.isBool(false))
+ else if (andand ? isTrueBool(result) : result.toBool().hasValue(false))
{
UnionExp ue2 = void;
result = interpret(&ue2, e.e2, istate);
if (exceptionOrCant(result))
return;
- if (result.op == TOK.voidExpression)
+ if (result.op == EXP.voidExpression)
{
assert(e.type.ty == Tvoid);
result = null;
return;
}
- if (result.isBool(false))
+ if (result.toBool().hasValue(false))
res = false;
else if (isTrueBool(result))
res = true;
if (fd.ident == Id.__ArrayPostblit || fd.ident == Id.__ArrayDtor)
{
- assert(e.arguments.dim == 1);
+ assert(e.arguments.length == 1);
Expression ea = (*e.arguments)[0];
// printf("1 ea = %s %s\n", ea.type.toChars(), ea.toChars());
if (auto se = ea.isSliceExp())
if (auto ce = ea.isCastExp())
ea = ce.e1;
- // printf("2 ea = %s, %s %s\n", ea.type.toChars(), Token.toChars(ea.op), ea.toChars());
- if (ea.op == TOK.variable || ea.op == TOK.symbolOffset)
+ // printf("2 ea = %s, %s %s\n", ea.type.toChars(), EXPtoString(ea.op).ptr, ea.toChars());
+ if (ea.op == EXP.variable || ea.op == EXP.symbolOffset)
result = getVarExp(e.loc, istate, (cast(SymbolExp)ea).var, CTFEGoal.RValue);
else if (auto ae = ea.isAddrExp())
result = interpretRegion(ae.e1, istate);
{
// In expressionsem.d `ea.length = eb;` got lowered to `_d_arraysetlengthT(ea, eb);`.
// The following code will rewrite it back to `ea.length = eb` and then interpret that expression.
- assert(e.arguments.dim == 2);
+ assert(e.arguments.length == 2);
Expression ea = (*e.arguments)[0];
Expression eb = (*e.arguments)[1];
result = interpretRegion(ae, istate);
return;
}
+ else if (isArrayConstructionOrAssign(fd.ident))
+ {
+ // In expressionsem.d, the following lowerings were performed:
+ // * `T[x] ea = eb;` to `_d_array{,set}ctor(ea[], eb[]);`.
+ // * `ea = eb` to `_d_array{,setassign,assign_l,assign_r}(ea[], eb)`.
+ // The following code will rewrite them back to `ea = eb` and
+ // then interpret that expression.
+
+ if (fd.ident == Id._d_arrayctor)
+ assert(e.arguments.length == 3);
+ else
+ assert(e.arguments.length == 2);
+
+ Expression ea = (*e.arguments)[0];
+ if (ea.isCastExp)
+ ea = ea.isCastExp.e1;
+
+ Expression eb = (*e.arguments)[1];
+ if (eb.isCastExp() && fd.ident != Id._d_arraysetctor)
+ eb = eb.isCastExp.e1;
+
+ Expression rewrittenExp;
+ if (fd.ident == Id._d_arrayctor || fd.ident == Id._d_arraysetctor)
+ rewrittenExp = new ConstructExp(e.loc, ea, eb);
+ else
+ rewrittenExp = new AssignExp(e.loc, ea, eb);
+
+ rewrittenExp.type = ea.type;
+ result = interpret(rewrittenExp, istate);
+
+ return;
+ }
+ else if (fd.ident == Id._d_arrayappendT || fd.ident == Id._d_arrayappendTTrace)
+ {
+ // In expressionsem.d `ea ~= eb` was lowered to `_d_arrayappendT{,Trace}({file, line, funcname}, ea, eb);`.
+ // The following code will rewrite it back to `ea ~= eb` and then interpret that expression.
+ Expression lhs, rhs;
+
+ if (fd.ident == Id._d_arrayappendT)
+ {
+ assert(e.arguments.length == 2);
+ lhs = (*e.arguments)[0];
+ rhs = (*e.arguments)[1];
+ }
+ else
+ {
+ assert(e.arguments.length == 5);
+ lhs = (*e.arguments)[3];
+ rhs = (*e.arguments)[4];
+ }
+
+ auto cae = new CatAssignExp(e.loc, lhs, rhs);
+ cae.type = e.type;
+
+ result = interpretRegion(cae, istate, CTFEGoal.LValue);
+ return;
+ }
+ else if (fd.ident == Id._d_arrayappendcTX)
+ assert(0, "CTFE cannot interpret _d_arrayappendcTX!");
}
else if (auto soe = ecall.isSymOffExp())
{
// Currently this is satisfied because closure is not yet supported.
assert(!fd.isNested() || fd.needThis());
- if (pthis.op == TOK.typeid_)
+ if (pthis.op == EXP.typeid_)
{
pthis.error("static variable `%s` cannot be read at compile time", pthis.toChars());
result = CTFEExp.cantexp;
}
assert(pthis);
- if (pthis.op == TOK.null_)
+ if (pthis.op == EXP.null_)
{
assert(pthis.type.toBasetype().ty == Tclass);
e.error("function call through null class reference `%s`", pthis.toChars());
return;
}
- assert(pthis.op == TOK.structLiteral || pthis.op == TOK.classReference || pthis.op == TOK.type);
+ assert(pthis.op == EXP.structLiteral || pthis.op == EXP.classReference || pthis.op == EXP.type);
if (fd.isVirtual() && !e.directcall)
{
}
}
- if (fd && fd.semanticRun >= PASS.semantic3done && fd.semantic3Errors)
+ if (fd && fd.semanticRun >= PASS.semantic3done && fd.hasSemantic3Errors())
{
e.error("CTFE failed because of previous errors in `%s`", fd.toChars());
result = CTFEExp.cantexp;
}
result = interpretFunction(pue, fd, istate, e.arguments, pthis);
- if (result.op == TOK.voidExpression)
+ if (result.op == EXP.voidExpression)
return;
if (!exceptionOrCantInterpret(result))
{
printf("%s CommaExp::interpret() %s\n", e.loc.toChars(), e.toChars());
}
+ bool isNewThrowableHook()
+ {
+ auto de = e.e1.isDeclarationExp();
+ if (de is null)
+ return false;
+
+ auto vd = de.declaration.isVarDeclaration();
+ if (vd is null)
+ return false;
+
+ auto ei = vd._init.isExpInitializer();
+ if (ei is null)
+ return false;
+
+ auto ce = ei.exp.isConstructExp();
+ if (ce is null)
+ return false;
+
+ return isRuntimeHook(ce.e2, Id._d_newThrowable) !is null;
+ }
+
+ if (auto ce = isRuntimeHook(e.e1, Id._d_arrayappendcTX))
+ {
+ // In expressionsem.d `arr ~= elem` was lowered to
+ // `_d_arrayappendcTX(arr, elem), arr[arr.length - 1] = elem, elem;`.
+ // The following code will rewrite it back to `arr ~= elem`
+ // and then interpret that expression.
+ assert(ce.arguments.length == 2);
+
+ auto arr = (*ce.arguments)[0];
+ auto elem = e.e2.isConstructExp().e2;
+ assert(elem);
+
+ auto cae = new CatAssignExp(e.loc, arr, elem);
+ cae.type = arr.type;
+
+ result = interpret(cae, istate);
+ return;
+ }
+ else if (isNewThrowableHook())
+ {
+ // In expressionsem.d `throw new Exception(args)` was lowered to
+ // `throw (tmp = _d_newThrowable!Exception(), tmp.ctor(args), tmp)`.
+ // The following code will rewrite it back to `throw new Exception(args)`
+ // and then interpret this expression instead.
+ auto ce = e.e2.isCallExp();
+ assert(ce);
+
+ auto ne = new NewExp(e.loc, null, e.type, ce.arguments);
+ ne.type = e.e1.type;
+
+ result = interpret(ne, istate);
+ return;
+ }
+
// If it creates a variable, and there's no context for
// the variable to be created in, we need to create one now.
InterState istateComma;
- if (!istate && firstComma(e.e1).op == TOK.declaration)
+ if (!istate && firstComma(e.e1).op == EXP.declaration)
{
ctfeGlobals.stack.startFrame(null);
istate = &istateComma;
// If the comma returns a temporary variable, it needs to be an lvalue
// (this is particularly important for struct constructors)
- if (e.e1.op == TOK.declaration &&
- e.e2.op == TOK.variable &&
+ if (e.e1.op == EXP.declaration &&
+ e.e2.op == EXP.variable &&
e.e1.isDeclarationExp().declaration == e.e2.isVarExp().var &&
e.e2.isVarExp().var.storage_class & STC.ctfe)
{
newval = interpretRegion(newval, istate);
if (exceptionOrCant(newval))
return endTempStackFrame();
- if (newval.op != TOK.voidExpression)
+ if (newval.op != EXP.voidExpression)
{
// v isn't necessarily null.
setValueWithoutChecking(v, copyLiteral(newval).copy());
if (isPointer(e.econd.type))
{
- if (econd.op != TOK.null_)
+ if (econd.op != EXP.null_)
{
econd = IntegerExp.createBool(true);
}
result = interpret(pue, e.e1, istate, goal);
incUsageCtfe(istate, e.e1.loc);
}
- else if (econd.isBool(false))
+ else if (econd.toBool().hasValue(false))
{
result = interpret(pue, e.e2, istate, goal);
incUsageCtfe(istate, e.e2.loc);
assert(e1);
if (exceptionOrCant(e1))
return;
- if (e1.op != TOK.string_ && e1.op != TOK.arrayLiteral && e1.op != TOK.slice && e1.op != TOK.null_)
+ if (e1.op != EXP.string_ && e1.op != EXP.arrayLiteral && e1.op != EXP.slice && e1.op != EXP.null_)
{
e.error("`%s` cannot be evaluated at compile time", e.toChars());
result = CTFEExp.cantexp;
{
if (auto ale = e.e1.isArrayLiteralExp())
return ale; // it's already an array literal
- if (e.e1.op == TOK.int64 || e.e1.op == TOK.float64)
+ if (e.e1.op == EXP.int64 || e.e1.op == EXP.float64)
{
// Convert literal __vector(int) -> __vector([array])
auto elements = new Expressions(e.dim);
assert(e1);
if (exceptionOrCant(e1))
return;
- if (e1.op != TOK.arrayLiteral && e1.op != TOK.int64 && e1.op != TOK.float64)
+ if (e1.op != EXP.arrayLiteral && e1.op != EXP.int64 && e1.op != EXP.float64)
{
e.error("`%s` cannot be evaluated at compile time", e.toChars());
result = CTFEExp.cantexp;
if (auto ve = e1.isVectorExp())
{
result = interpretVectorToArray(pue, ve);
- if (result.op != TOK.vector)
+ if (result.op != EXP.vector)
return;
}
e.error("`%s` cannot be evaluated at compile time", e.toChars());
dinteger_t ofs;
Expression agg = getAggregateFromPointer(e1, &ofs);
- if (agg.op == TOK.null_)
+ if (agg.op == EXP.null_)
{
e.error("cannot index through null pointer `%s`", e.e1.toChars());
return false;
}
- if (agg.op == TOK.int64)
+ if (agg.op == EXP.int64)
{
e.error("cannot index through invalid pointer `%s` of value `%s`", e.e1.toChars(), e1.toChars());
return false;
}
// Pointer to a non-array variable
- if (agg.op == TOK.symbolOffset)
+ if (agg.op == EXP.symbolOffset)
{
- e.error("mutable variable `%s` cannot be %s at compile time, even through a pointer", cast(char*)(modify ? "modified" : "read"), (cast(SymOffExp)agg).var.toChars());
+ e.error("mutable variable `%s` cannot be %s at compile time, even through a pointer", cast(char*)(modify ? "modified" : "read"), agg.isSymOffExp().var.toChars());
return false;
}
- if (agg.op == TOK.arrayLiteral || agg.op == TOK.string_)
+ if (agg.op == EXP.arrayLiteral || agg.op == EXP.string_)
{
dinteger_t len = resolveArrayLength(agg);
if (ofs + indx >= len)
Expression e1 = interpretRegion(e.e1, istate);
if (exceptionOrCantInterpret(e1))
return false;
- if (e1.op == TOK.null_)
+ if (e1.op == EXP.null_)
{
e.error("cannot index null array `%s`", e.e1.toChars());
return false;
// Set the $ variable, and find the array literal to modify
dinteger_t len;
- if (e1.op == TOK.variable && e1.type.toBasetype().ty == Tsarray)
+ if (e1.op == EXP.variable && e1.type.toBasetype().ty == Tsarray)
len = e1.type.toBasetype().isTypeSArray().dim.toInteger();
else
{
- if (e1.op != TOK.arrayLiteral && e1.op != TOK.string_ && e1.op != TOK.slice && e1.op != TOK.vector)
+ if (e1.op != EXP.arrayLiteral && e1.op != EXP.string_ && e1.op != EXP.slice && e1.op != EXP.vector)
{
e.error("cannot determine length of `%s` at compile time", e.e1.toChars());
return false;
ctfeGlobals.stack.pop(e.lengthVar); // $ is defined only inside []
if (exceptionOrCantInterpret(e2))
return false;
- if (e2.op != TOK.int64)
+ if (e2.op != EXP.int64)
{
e.error("CTFE internal error: non-integral index `[%s]`", e.e2.toChars());
return false;
e.error("index %llu exceeds array length %llu", index, iupr - ilwr);
return false;
}
- *pagg = (cast(SliceExp)e1).e1;
+ *pagg = e1.isSliceExp().e1;
*pidx = index + ilwr;
}
else
result = CTFEExp.cantexp;
return;
}
- if (agg.op == TOK.arrayLiteral || agg.op == TOK.string_)
+ if (agg.op == EXP.arrayLiteral || agg.op == EXP.string_)
{
if (goal == CTFEGoal.LValue)
{
Expression e1 = interpretRegion(e.e1, istate);
if (exceptionOrCant(e1))
return;
- if (e1.op == TOK.null_)
+ if (e1.op == EXP.null_)
{
if (goal == CTFEGoal.LValue && e1.type.ty == Taarray && e.modifiable)
{
return;
}
- assert(e1.op == TOK.assocArrayLiteral);
+ assert(e1.op == EXP.assocArrayLiteral);
UnionExp e2tmp = void;
e2 = resolveSlice(e2, &e2tmp);
- result = findKeyInAA(e.loc, cast(AssocArrayLiteralExp)e1, e2);
+ result = findKeyInAA(e.loc, e1.isAssocArrayLiteralExp(), e2);
if (!result)
{
e.error("key `%s` not found in associative array `%s`", e2.toChars(), e.e1.toChars());
result = ctfeIndex(pue, e.loc, e.type, agg, indexToAccess);
if (exceptionOrCant(result))
return;
- if (result.op == TOK.void_)
+ if (result.op == EXP.void_)
{
e.error("`%s` is used before initialized", e.toChars());
errorSupplemental(result.loc, "originally uninitialized here");
Expression e1 = interpretRegion(e.e1, istate);
if (exceptionOrCant(e1))
return;
- if (e1.op == TOK.int64)
+ if (e1.op == EXP.int64)
{
e.error("cannot slice invalid pointer `%s` of value `%s`", e.e1.toChars(), e1.toChars());
result = CTFEExp.cantexp;
Expression agg = getAggregateFromPointer(e1, &ofs);
ilwr += ofs;
iupr += ofs;
- if (agg.op == TOK.null_)
+ if (agg.op == EXP.null_)
{
if (iupr == ilwr)
{
result = CTFEExp.cantexp;
return;
}
- if (agg.op == TOK.symbolOffset)
+ if (agg.op == EXP.symbolOffset)
{
e.error("slicing pointers to static variables is not supported in CTFE");
result = CTFEExp.cantexp;
return;
}
- if (agg.op != TOK.arrayLiteral && agg.op != TOK.string_)
+ if (agg.op != EXP.arrayLiteral && agg.op != EXP.string_)
{
e.error("pointer `%s` cannot be sliced at compile time (it does not point to an array)", e.e1.toChars());
result = CTFEExp.cantexp;
return;
}
- assert(agg.op == TOK.arrayLiteral || agg.op == TOK.string_);
+ assert(agg.op == EXP.arrayLiteral || agg.op == EXP.string_);
dinteger_t len = ArrayLength(Type.tsize_t, agg).exp().toInteger();
//Type *pointee = ((TypePointer *)agg.type)->next;
- if (iupr > (len + 1) || iupr < ilwr)
+ if (sliceBoundsCheck(0, len, ilwr, iupr))
{
e.error("pointer slice `[%lld..%lld]` exceeds allocated memory block `[0..%lld]`", ilwr, iupr, len);
result = CTFEExp.cantexp;
/* Set dollar to the length of the array
*/
uinteger_t dollar;
- if ((e1.op == TOK.variable || e1.op == TOK.dotVariable) && e1.type.toBasetype().ty == Tsarray)
+ if ((e1.op == EXP.variable || e1.op == EXP.dotVariable) && e1.type.toBasetype().ty == Tsarray)
dollar = e1.type.toBasetype().isTypeSArray().dim.toInteger();
else
{
- if (e1.op != TOK.arrayLiteral && e1.op != TOK.string_ && e1.op != TOK.null_ && e1.op != TOK.slice && e1.op != TOK.vector)
+ if (e1.op != EXP.arrayLiteral && e1.op != EXP.string_ && e1.op != EXP.null_ && e1.op != EXP.slice && e1.op != EXP.vector)
{
e.error("cannot determine length of `%s` at compile time", e1.toChars());
result = CTFEExp.cantexp;
uinteger_t ilwr = lwr.toInteger();
uinteger_t iupr = upr.toInteger();
- if (e1.op == TOK.null_)
+ if (e1.op == EXP.null_)
{
if (ilwr == 0 && iupr == 0)
{
// aggregate[lo1..up1][lwr..upr] ---> aggregate[lwr'..upr']
uinteger_t lo1 = se.lwr.toInteger();
uinteger_t up1 = se.upr.toInteger();
- if (ilwr > iupr || iupr > up1 - lo1)
+ if (sliceBoundsCheck(0, up1 - lo1, ilwr, iupr))
{
- e.error("slice `[%llu..%llu]` exceeds array bounds `[%llu..%llu]`", ilwr, iupr, lo1, up1);
+ e.error("slice `[%llu..%llu]` exceeds array bounds `[0..%llu]`", ilwr, iupr, up1 - lo1);
result = CTFEExp.cantexp;
return;
}
result.type = e.type;
return;
}
- if (e1.op == TOK.arrayLiteral || e1.op == TOK.string_)
+ if (e1.op == EXP.arrayLiteral || e1.op == EXP.string_)
{
- if (iupr < ilwr || dollar < iupr)
+ if (sliceBoundsCheck(0, dollar, ilwr, iupr))
{
e.error("slice `[%lld..%lld]` exceeds array bounds `[0..%lld]`", ilwr, iupr, dollar);
result = CTFEExp.cantexp;
Expression e2 = interpretRegion(e.e2, istate);
if (exceptionOrCant(e2))
return;
- if (e2.op == TOK.null_)
+ if (e2.op == EXP.null_)
{
emplaceExp!(NullExp)(pue, e.loc, e.type);
result = pue.exp();
return;
}
- if (e2.op != TOK.assocArrayLiteral)
+ if (e2.op != EXP.assocArrayLiteral)
{
e.error("`%s` cannot be interpreted at compile time", e.toChars());
result = CTFEExp.cantexp;
}
e1 = resolveSlice(e1);
- result = findKeyInAA(e.loc, cast(AssocArrayLiteralExp)e2, e1);
+ result = findKeyInAA(e.loc, e2.isAssocArrayLiteralExp(), e1);
if (exceptionOrCant(result))
return;
if (!result)
* result in [x,y] and then x or y is on the stack.
* But if they are both strings, we can, because it isn't the x~[y] case.
*/
- if (!(e1.op == TOK.string_ && e2.op == TOK.string_))
+ if (!(e1.op == EXP.string_ && e2.op == EXP.string_))
{
if (e1 == ue1.exp())
e1 = ue1.copy();
if (exceptionOrCant(result))
return;
- if (result.op == TOK.null_)
+ if (result.op == EXP.null_)
{
result = CTFEExp.voidexp;
return;
switch (tb.ty)
{
case Tclass:
- if (result.op != TOK.classReference)
+ if (result.op != EXP.classReference)
{
e.error("`delete` on invalid class reference `%s`", result.toChars());
result = CTFEExp.cantexp;
return;
}
- auto cre = cast(ClassReferenceExp)result;
+ auto cre = result.isClassReferenceExp();
auto cd = cre.originalClass();
// Find dtor(s) in inheritance chain
break;
- case Tpointer:
- tb = (cast(TypePointer)tb).next.toBasetype();
- if (tb.ty == Tstruct)
- {
- if (result.op != TOK.address ||
- (cast(AddrExp)result).e1.op != TOK.structLiteral)
- {
- e.error("`delete` on invalid struct pointer `%s`", result.toChars());
- result = CTFEExp.cantexp;
- return;
- }
-
- auto sd = (cast(TypeStruct)tb).sym;
- auto sle = cast(StructLiteralExp)(cast(AddrExp)result).e1;
-
- if (sd.dtor)
- {
- result = interpretFunction(pue, sd.dtor, istate, null, sle);
- if (exceptionOrCant(result))
- return;
- }
- }
- break;
-
- case Tarray:
- auto tv = tb.nextOf().baseElemOf();
- if (tv.ty == Tstruct)
- {
- if (result.op != TOK.arrayLiteral)
- {
- e.error("`delete` on invalid struct array `%s`", result.toChars());
- result = CTFEExp.cantexp;
- return;
- }
-
- auto sd = (cast(TypeStruct)tv).sym;
-
- if (sd.dtor)
- {
- auto ale = cast(ArrayLiteralExp)result;
- foreach (el; *ale.elements)
- {
- result = interpretFunction(pue, sd.dtor, istate, null, el);
- if (exceptionOrCant(result))
- return;
- }
- }
- }
- break;
-
default:
assert(0);
}
result = CTFEExp.voidexp;
return;
}
- if (e.to.ty == Tpointer && e1.op != TOK.null_)
+ if (e.to.ty == Tpointer && e1.op != EXP.null_)
{
Type pointee = (cast(TypePointer)e.type).next;
// Implement special cases of normally-unsafe casts
- if (e1.op == TOK.int64)
+ if (e1.op == EXP.int64)
{
// Happens with Windows HANDLEs, for example.
result = paintTypeOntoLiteral(pue, e.to, e1);
if (auto se = e1.isSliceExp())
{
- if (se.e1.op == TOK.null_)
+ if (se.e1.op == EXP.null_)
{
result = paintTypeOntoLiteral(pue, e.type, se.e1);
return;
result = pue.exp();
return;
}
- if (e1.op == TOK.arrayLiteral || e1.op == TOK.string_)
+ if (e1.op == EXP.arrayLiteral || e1.op == EXP.string_)
{
// Create a CTFE pointer &[1,2,3][0] or &"abc"[0]
auto ei = ctfeEmplaceExp!IndexExp(e.loc, e1, ctfeEmplaceExp!IntegerExp(e.loc, 0, Type.tsize_t));
result = pue.exp();
return;
}
- if (e1.op == TOK.index && !(cast(IndexExp)e1).e1.type.equals(e1.type))
+ if (e1.op == EXP.index && !e1.isIndexExp().e1.type.equals(e1.type))
{
// type painting operation
- IndexExp ie = cast(IndexExp)e1;
+ IndexExp ie = e1.isIndexExp();
if (castBackFromVoid)
{
// get the original type. For strings, it's just the type...
Type origType = ie.e1.type.nextOf();
// ..but for arrays of type void*, it's the type of the element
- if (ie.e1.op == TOK.arrayLiteral && ie.e2.op == TOK.int64)
+ if (ie.e1.op == EXP.arrayLiteral && ie.e2.op == EXP.int64)
{
- ArrayLiteralExp ale = cast(ArrayLiteralExp)ie.e1;
+ ArrayLiteralExp ale = ie.e1.isArrayLiteralExp();
const indx = cast(size_t)ie.e2.toInteger();
- if (indx < ale.elements.dim)
+ if (indx < ale.elements.length)
{
if (Expression xx = (*ale.elements)[indx])
{
return;
}
- if (castToSarrayPointer && pointee.toBasetype().ty == Tsarray && ae.e1.op == TOK.index)
+ if (castToSarrayPointer && pointee.toBasetype().ty == Tsarray && ae.e1.op == EXP.index)
{
// &val[idx]
dinteger_t dim = (cast(TypeSArray)pointee.toBasetype()).dim.toInteger();
- IndexExp ie = cast(IndexExp)ae.e1;
+ IndexExp ie = ae.e1.isIndexExp();
Expression lwr = ie.e2;
Expression upr = ctfeEmplaceExp!IntegerExp(ie.e2.loc, ie.e2.toInteger() + dim, Type.tsize_t);
}
}
- if (e1.op == TOK.variable || e1.op == TOK.symbolOffset)
+ if (e1.op == EXP.variable || e1.op == EXP.symbolOffset)
{
// type painting operation
Type origType = (cast(SymbolExp)e1).var.type;
if (auto ve = e1.isVarExp())
emplaceExp!(VarExp)(pue, e.loc, ve.var);
else
- emplaceExp!(SymOffExp)(pue, e.loc, (cast(SymOffExp)e1).var, (cast(SymOffExp)e1).offset);
+ emplaceExp!(SymOffExp)(pue, e.loc, e1.isSymOffExp().var, e1.isSymOffExp().offset);
result = pue.exp();
result.type = e.to;
return;
// Check if we have a null pointer (eg, inside a struct)
e1 = interpretRegion(e1, istate);
- if (e1.op != TOK.null_)
+ if (e1.op != EXP.null_)
{
e.error("pointer cast from `%s` to `%s` is not supported at compile time", e1.type.toChars(), e.to.toChars());
result = CTFEExp.cantexp;
e1 = interpretRegion(e.e1, istate);
if (exceptionOrCant(e1))
return;
- assert(e1.op == TOK.vector);
+ assert(e1.op == EXP.vector);
e1 = interpretVectorToArray(pue, e1.isVectorExp());
}
- if (e.to.ty == Tarray && e1.op == TOK.slice)
+ if (e.to.ty == Tarray && e1.op == EXP.slice)
{
// Note that the slice may be void[], so when checking for dangerous
// casts, we need to use the original type, which is se.e1.
- SliceExp se = cast(SliceExp)e1;
+ SliceExp se = e1.isSliceExp();
if (!isSafePointerCast(se.e1.type.nextOf(), e.to.nextOf()))
{
e.error("array cast from `%s` to `%s` is not supported at compile time", se.e1.type.toChars(), e.to.toChars());
auto tobt = e.to.toBasetype();
if (tobt.ty == Tbool && e1.type.ty == Tpointer)
{
- emplaceExp!(IntegerExp)(pue, e.loc, e1.op != TOK.null_, e.to);
+ emplaceExp!(IntegerExp)(pue, e.loc, e1.op != EXP.null_, e.to);
result = pue.exp();
return;
}
- else if (tobt.isTypeBasic() && e1.op == TOK.null_)
+ else if (tobt.isTypeBasic() && e1.op == EXP.null_)
{
if (tobt.isintegral())
emplaceExp!(IntegerExp)(pue, e.loc, 0, e.to);
result = pue.exp();
return;
}
- result = ctfeCast(pue, e.loc, e.type, e.to, e1);
+ result = ctfeCast(pue, e.loc, e.type, e.to, e1, true);
}
override void visit(AssertExp e)
if (isTrueBool(e1))
{
}
- else if (e1.isBool(false))
+ else if (e1.toBool().hasValue(false))
{
if (e.msg)
{
return;
}
+ override void visit(ThrowExp te)
+ {
+ debug (LOG)
+ {
+ printf("%s ThrowExpression::interpret()\n", te.loc.toChars());
+ }
+ interpretThrow(te.e1, te.loc);
+ }
+
override void visit(PtrExp e)
{
debug (LOG)
// Constant fold *(&structliteral + offset)
if (auto ae = e.e1.isAddExp())
{
- if (ae.e1.op == TOK.address && ae.e2.op == TOK.int64)
+ if (ae.e1.op == EXP.address && ae.e2.op == EXP.int64)
{
- AddrExp ade = cast(AddrExp)ae.e1;
+ AddrExp ade = ae.e1.isAddrExp();
Expression ex = interpretRegion(ade.e1, istate);
if (exceptionOrCant(ex))
return;
if (exceptionOrCant(result))
return;
- if (result.op == TOK.function_)
+ if (result.op == EXP.function_)
return;
if (auto soe = result.isSymOffExp())
{
return;
}
- if (result.isStringExp())
+ if (result.isStringExp() || result.isArrayLiteralExp())
return;
- if (result.op != TOK.address)
+ if (result.op != EXP.address)
{
- if (result.op == TOK.null_)
+ if (result.op == EXP.null_)
e.error("dereference of null pointer `%s`", e.e1.toChars());
else
e.error("dereference of invalid pointer `%s`", result.toChars());
}
// *(&x) ==> x
- result = (cast(AddrExp)result).e1;
+ result = result.isAddrExp().e1;
- if (result.op == TOK.slice && e.type.toBasetype().ty == Tsarray)
+ if (result.op == EXP.slice && e.type.toBasetype().ty == Tsarray)
{
/* aggr[lwr..upr]
* upr may exceed the upper boundary of aggr, but the check is deferred
return;
}
- if (ex.op == TOK.null_)
+ if (ex.op == EXP.null_)
{
if (ex.type.toBasetype().ty == Tclass)
e.error("class `%s` is `null` and cannot be dereferenced", e.e1.toChars());
StructLiteralExp se;
int i;
- if (ex.op != TOK.structLiteral && ex.op != TOK.classReference && ex.op != TOK.typeid_)
+ if (ex.op != EXP.structLiteral && ex.op != EXP.classReference && ex.op != EXP.typeid_)
{
return notImplementedYet();
}
// We can't use getField, because it makes a copy
- if (ex.op == TOK.classReference)
+ if (ex.op == EXP.classReference)
{
- se = (cast(ClassReferenceExp)ex).value;
- i = (cast(ClassReferenceExp)ex).findFieldIndexByName(v);
+ se = ex.isClassReferenceExp().value;
+ i = ex.isClassReferenceExp().findFieldIndexByName(v);
}
- else if (ex.op == TOK.typeid_)
+ else if (ex.op == EXP.typeid_)
{
if (v.ident == Identifier.idPool("name"))
{
}
else
{
- se = cast(StructLiteralExp)ex;
+ se = ex.isStructLiteralExp();
i = findFieldIndexByName(se.sd, v);
}
if (i == -1)
result = (*se.elements)[i];
if (!result)
{
- e.error("Internal Compiler Error: null field `%s`", v.toChars());
+ e.error("internal compiler error: null field `%s`", v.toChars());
result = CTFEExp.cantexp;
return;
}
Expression index = interpret(e.e2, istate);
if (exceptionOrCant(index))
return;
- if (agg.op == TOK.null_)
+ if (agg.op == EXP.null_)
{
result = CTFEExp.voidexp;
return;
foreach (j, evalue; *valuesx)
{
Expression ekey = (*keysx)[j];
- int eq = ctfeEqual(e.loc, TOK.equal, ekey, index);
+ int eq = ctfeEqual(e.loc, EXP.equal, ekey, index);
if (eq)
++removed;
else if (removed != 0)
(*valuesx)[j - removed] = evalue;
}
}
- valuesx.dim = valuesx.dim - removed;
- keysx.dim = keysx.dim - removed;
+ valuesx.length = valuesx.length - removed;
+ keysx.length = keysx.length - removed;
result = IntegerExp.createBool(removed != 0);
}
{
assert(0); // This should never be interpreted
}
+
+ /*********************************************
+ * Checks if the given expresion is a call to the runtime hook `id`.
+ * Params:
+ * e = the expression to check
+ * id = the identifier of the runtime hook
+ * Returns:
+ * `e` cast to `CallExp` if it's the hook, `null` otherwise
+ */
+ private CallExp isRuntimeHook(Expression e, Identifier id)
+ {
+ if (auto ce = e.isCallExp())
+ {
+ if (auto ve = ce.e1.isVarExp())
+ {
+ if (auto fd = ve.var.isFuncDeclaration())
+ {
+ // If `_d_HookTraceImpl` is found, resolve the underlying
+ // hook and replace `e` and `fd` with it.
+ removeHookTraceImpl(ce, fd);
+ return fd.ident == id ? ce : null;
+ }
+ }
+ }
+
+ return null;
+ }
}
/********************************************
// mimicking UnionExp.copy, but with region allocation
switch (uexp.op)
{
- case TOK.cantExpression: return CTFEExp.cantexp;
- case TOK.voidExpression: return CTFEExp.voidexp;
- case TOK.break_: return CTFEExp.breakexp;
- case TOK.continue_: return CTFEExp.continueexp;
- case TOK.goto_: return CTFEExp.gotoexp;
+ case EXP.cantExpression: return CTFEExp.cantexp;
+ case EXP.voidExpression: return CTFEExp.voidexp;
+ case EXP.break_: return CTFEExp.breakexp;
+ case EXP.continue_: return CTFEExp.continueexp;
+ case EXP.goto_: return CTFEExp.gotoexp;
default: break;
}
auto p = ctfeGlobals.region.malloc(uexp.size);
* istate = context
* Returns:
* NULL continue to next statement
- * TOK.cantExpression cannot interpret statement at compile time
+ * EXP.cantExpression cannot interpret statement at compile time
* !NULL expression from return statement, or thrown exception
*/
Expression interpret(UnionExp* pue, Statement s, InterState* istate)
*/
static bool isVoid(const Expression e, bool checkArrayType = false) pure
{
- if (e.op == TOK.void_)
+ if (e.op == EXP.void_)
return true;
static bool isEntirelyVoid(const Expressions* elems)
else
{
e = scrubReturnValue(loc, e);
- if (CTFEExp.isCantExp(e) || e.op == TOK.error)
+ if (CTFEExp.isCantExp(e) || e.op == EXP.error)
return e;
}
}
return null;
}
- if (e.op == TOK.classReference)
+ if (e.op == EXP.classReference)
{
- StructLiteralExp sle = (cast(ClassReferenceExp)e).value;
+ StructLiteralExp sle = e.isClassReferenceExp().value;
if (auto ex = scrubSE(sle))
return ex;
}
return null;
}
- if (e.op == TOK.classReference)
+ if (e.op == EXP.classReference)
{
- if (auto ex = scrubSE((cast(ClassReferenceExp)e).value))
+ if (auto ex = scrubSE(e.isClassReferenceExp().value))
return ex;
}
else if (auto sle = e.isStructLiteralExp())
switch (e.op)
{
- case TOK.classReference:
+ case EXP.classReference:
{
auto cre = e.isClassReferenceExp();
cre.value = copyRegionExp(cre.value).isStructLiteralExp();
break;
}
- case TOK.structLiteral:
+ case EXP.structLiteral:
{
auto sle = e.isStructLiteralExp();
return slec;
}
- case TOK.arrayLiteral:
+ case EXP.arrayLiteral:
{
auto ale = e.isArrayLiteralExp();
ale.basis = copyRegionExp(ale.basis);
break;
}
- case TOK.assocArrayLiteral:
+ case EXP.assocArrayLiteral:
copyArray(e.isAssocArrayLiteralExp().keys);
copyArray(e.isAssocArrayLiteralExp().values);
break;
- case TOK.slice:
+ case EXP.slice:
{
auto se = e.isSliceExp();
se.e1 = copyRegionExp(se.e1);
break;
}
- case TOK.tuple:
+ case EXP.tuple:
{
auto te = e.isTupleExp();
te.e0 = copyRegionExp(te.e0);
break;
}
- case TOK.address:
- case TOK.delegate_:
- case TOK.vector:
- case TOK.dotVariable:
+ case EXP.address:
+ case EXP.delegate_:
+ case EXP.vector:
+ case EXP.dotVariable:
{
- UnaExp ue = cast(UnaExp)e;
+ UnaExp ue = e.isUnaExp();
ue.e1 = copyRegionExp(ue.e1);
break;
}
- case TOK.index:
+ case EXP.index:
{
- BinExp be = cast(BinExp)e;
+ BinExp be = e.isBinExp();
be.e1 = copyRegionExp(be.e1);
be.e2 = copyRegionExp(be.e2);
break;
}
- case TOK.this_:
- case TOK.super_:
- case TOK.variable:
- case TOK.type:
- case TOK.function_:
- case TOK.typeid_:
- case TOK.string_:
- case TOK.int64:
- case TOK.error:
- case TOK.float64:
- case TOK.complex80:
- case TOK.null_:
- case TOK.void_:
- case TOK.symbolOffset:
- case TOK.char_:
+ case EXP.this_:
+ case EXP.super_:
+ case EXP.variable:
+ case EXP.type:
+ case EXP.function_:
+ case EXP.typeid_:
+ case EXP.string_:
+ case EXP.int64:
+ case EXP.error:
+ case EXP.float64:
+ case EXP.complex80:
+ case EXP.null_:
+ case EXP.void_:
+ case EXP.symbolOffset:
+ case EXP.char_:
break;
- case TOK.cantExpression:
- case TOK.voidExpression:
- case TOK.showCtfeContext:
+ case EXP.cantExpression:
+ case EXP.voidExpression:
+ case EXP.showCtfeContext:
return e;
default:
- printf("e: %s, %s\n", Token.toChars(e.op), e.toChars());
+ printf("e: %s, %s\n", EXPtoString(e.op).ptr, e.toChars());
assert(0);
}
return earg;
dinteger_t len = 0;
if (auto aae = earg.isAssocArrayLiteralExp())
- len = aae.keys.dim;
+ len = aae.keys.length;
else
- assert(earg.op == TOK.null_);
+ assert(earg.op == EXP.null_);
emplaceExp!(IntegerExp)(pue, earg.loc, len, Type.tsize_t);
return pue.exp();
}
earg = interpret(pue, earg, istate);
if (exceptionOrCantInterpret(earg))
return earg;
- if (earg.op == TOK.null_)
+ if (earg.op == EXP.null_)
{
emplaceExp!(NullExp)(pue, earg.loc, earg.type);
return pue.exp();
}
- if (earg.op != TOK.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
+ if (earg.op != EXP.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
return null;
AssocArrayLiteralExp aae = earg.isAssocArrayLiteralExp();
auto ae = ctfeEmplaceExp!ArrayLiteralExp(aae.loc, returnType, aae.keys);
earg = interpret(pue, earg, istate);
if (exceptionOrCantInterpret(earg))
return earg;
- if (earg.op == TOK.null_)
+ if (earg.op == EXP.null_)
{
emplaceExp!(NullExp)(pue, earg.loc, earg.type);
return pue.exp();
}
- if (earg.op != TOK.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
+ if (earg.op != EXP.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
return null;
auto aae = earg.isAssocArrayLiteralExp();
auto ae = ctfeEmplaceExp!ArrayLiteralExp(aae.loc, returnType, aae.values);
earg = interpret(pue, earg, istate);
if (exceptionOrCantInterpret(earg))
return earg;
- if (earg.op == TOK.null_)
+ if (earg.op == EXP.null_)
{
emplaceExp!(NullExp)(pue, earg.loc, earg.type);
return pue.exp();
}
- if (earg.op != TOK.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
+ if (earg.op != EXP.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
return null;
auto aae = copyLiteral(earg).copy().isAssocArrayLiteralExp();
- for (size_t i = 0; i < aae.keys.dim; i++)
+ for (size_t i = 0; i < aae.keys.length; i++)
{
if (Expression e = evaluatePostblit(istate, (*aae.keys)[i]))
return e;
aa = interpret(aa, istate);
if (exceptionOrCantInterpret(aa))
return aa;
- if (aa.op != TOK.assocArrayLiteral)
+ if (aa.op != EXP.assocArrayLiteral)
{
emplaceExp!(IntegerExp)(pue, deleg.loc, 0, Type.tsize_t);
return pue.exp();
assert(fd && fd.fbody);
assert(fd.parameters);
- size_t numParams = fd.parameters.dim;
+ size_t numParams = fd.parameters.length;
assert(numParams == 1 || numParams == 2);
Parameter fparam = fd.type.isTypeFunction().parameterList[numParams - 1];
Expressions args = Expressions(numParams);
- AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)aa;
- if (!ae.keys || ae.keys.dim == 0)
+ AssocArrayLiteralExp ae = aa.isAssocArrayLiteralExp();
+ if (!ae.keys || ae.keys.length == 0)
return ctfeEmplaceExp!IntegerExp(deleg.loc, 0, Type.tsize_t);
Expression eresult;
- for (size_t i = 0; i < ae.keys.dim; ++i)
+ for (size_t i = 0; i < ae.keys.length; ++i)
{
Expression ekey = (*ae.keys)[i];
Expression evalue = (*ae.values)[i];
assert(fd && fd.fbody);
assert(fd.parameters);
- size_t numParams = fd.parameters.dim;
+ size_t numParams = fd.parameters.length;
assert(numParams == 1 || numParams == 2);
Type charType = (*fd.parameters)[numParams - 1].type;
Type indexType = numParams == 2 ? (*fd.parameters)[0].type : Type.tsize_t;
private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, const ref Loc loc, FuncDeclaration fd, Expressions* arguments, Expression pthis)
{
Expression e = null;
- size_t nargs = arguments ? arguments.dim : 0;
+ size_t nargs = arguments ? arguments.length : 0;
if (!pthis)
{
if (isBuiltin(fd) != BUILTIN.unimp)
}
if (pthis && !fd.fbody && fd.isCtorDeclaration() && fd.parent && fd.parent.parent && fd.parent.parent.ident == Id.object)
{
- if (pthis.op == TOK.classReference && fd.parent.ident == Id.Throwable)
+ if (pthis.op == EXP.classReference && fd.parent.ident == Id.Throwable)
{
// At present, the constructors just copy their arguments into the struct.
// But we might need some magic if stack tracing gets added to druntime.
- StructLiteralExp se = (cast(ClassReferenceExp)pthis).value;
- assert(arguments.dim <= se.elements.dim);
+ StructLiteralExp se = pthis.isClassReferenceExp().value;
+ assert(arguments.length <= se.elements.length);
foreach (i, arg; *arguments)
{
auto elem = interpret(arg, istate);
}
return null;
}
- if (e.op == TOK.structLiteral)
+ if (e.op == EXP.structLiteral)
{
// e.__postblit()
UnionExp ue = void;
foreach_reverse (elem; *ale.elements)
e = evaluateDtor(istate, elem);
}
- else if (e.op == TOK.structLiteral)
+ else if (e.op == EXP.structLiteral)
{
// e.__dtor()
e = interpretFunction(&ue, sd.dtor, istate, null, e);
// Remove the first three trace parameters
auto arguments = new Expressions();
- arguments.reserve(ce.arguments.dim - 3);
+ arguments.reserve(ce.arguments.length - 3);
arguments.pushSlice((*ce.arguments)[3 .. $]);
ce = ctfeEmplaceExp!CallExp(ce.loc, ctfeEmplaceExp!VarExp(ce.loc, fd, false), arguments);