blob: ab9d88c660c806b2ad73f7a01d6e95881f3b66e0 [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/interpret.c
*/
#include "root/dsystem.h" // mem{cpy|set}()
#include "root/rmem.h"
#include "mars.h"
#include "statement.h"
#include "expression.h"
#include "cond.h"
#include "init.h"
#include "staticassert.h"
#include "mtype.h"
#include "scope.h"
#include "declaration.h"
#include "aggregate.h"
#include "id.h"
#include "utf.h"
#include "attrib.h" // for AttribDeclaration
#include "template.h"
#include "ctfe.h"
/* Interpreter: what form of return value expression is required?
*/
enum CtfeGoal
{
ctfeNeedRvalue, // Must return an Rvalue (== CTFE value)
ctfeNeedLvalue, // Must return an Lvalue (== CTFE reference)
ctfeNeedNothing // The return value is not required
};
bool walkPostorder(Expression *e, StoppableVisitor *v);
Expression *interpret(Statement *s, InterState *istate);
Expression *interpret(Expression *e, InterState *istate, CtfeGoal goal = ctfeNeedRvalue);
static Expression *interpret(UnionExp *pue, Expression *e, InterState *istate, CtfeGoal goal = ctfeNeedRvalue);
static Expression *interpret(UnionExp *pue, Statement *s, InterState *istate);
#define SHOWPERFORMANCE 0
// Maximum allowable recursive function calls in CTFE
#define CTFE_RECURSION_LIMIT 1000
/**
The values of all CTFE variables
*/
struct CtfeStack
{
private:
/* The stack. Every declaration we encounter is pushed here,
together with the VarDeclaration, and the previous
stack address of that variable, so that we can restore it
when we leave the stack frame.
Note that when a function is forward referenced, the interpreter must
run semantic3, and that may start CTFE again with a NULL istate. Thus
the stack might not be empty when CTFE begins.
Ctfe Stack addresses are just 0-based integers, but we save
them as 'void *' because Array can only do pointers.
*/
Expressions values; // values on the stack
VarDeclarations vars; // corresponding variables
Array<void *> savedId; // id of the previous state of that var
Array<void *> frames; // all previous frame pointers
Expressions savedThis; // all previous values of localThis
/* Global constants get saved here after evaluation, so we never
* have to redo them. This saves a lot of time and memory.
*/
Expressions globalValues; // values of global constants
size_t framepointer; // current frame pointer
size_t maxStackPointer; // most stack we've ever used
Expression *localThis; // value of 'this', or NULL if none
public:
CtfeStack();
size_t stackPointer();
// The current value of 'this', or NULL if none
Expression *getThis();
// Largest number of stack positions we've used
size_t maxStackUsage();
// Start a new stack frame, using the provided 'this'.
void startFrame(Expression *thisexp);
void endFrame();
bool isInCurrentFrame(VarDeclaration *v);
Expression *getValue(VarDeclaration *v);
void setValue(VarDeclaration *v, Expression *e);
void push(VarDeclaration *v);
void pop(VarDeclaration *v);
void popAll(size_t stackpointer);
void saveGlobalConstant(VarDeclaration *v, Expression *e);
};
struct InterState
{
InterState *caller; // calling function's InterState
FuncDeclaration *fd; // function being interpreted
Statement *start; // if !=NULL, start execution at this statement
/* target of CTFEExp result; also
* target of labelled CTFEExp or
* CTFEExp. (NULL if no label).
*/
Statement *gotoTarget;
InterState();
};
/************** CtfeStack ********************************************/
CtfeStack ctfeStack;
CtfeStack::CtfeStack() : framepointer(0), maxStackPointer(0)
{
}
size_t CtfeStack::stackPointer()
{
return values.length;
}
Expression *CtfeStack::getThis()
{
return localThis;
}
// Largest number of stack positions we've used
size_t CtfeStack::maxStackUsage()
{
return maxStackPointer;
}
void CtfeStack::startFrame(Expression *thisexp)
{
frames.push((void *)(size_t)(framepointer));
savedThis.push(localThis);
framepointer = stackPointer();
localThis = thisexp;
}
void CtfeStack::endFrame()
{
size_t oldframe = (size_t)(frames[frames.length-1]);
localThis = savedThis[savedThis.length-1];
popAll(framepointer);
framepointer = oldframe;
frames.setDim(frames.length - 1);
savedThis.setDim(savedThis.length -1);
}
bool CtfeStack::isInCurrentFrame(VarDeclaration *v)
{
if (v->isDataseg() && !v->isCTFE())
return false; // It's a global
return v->ctfeAdrOnStack >= (int)framepointer;
}
Expression *CtfeStack::getValue(VarDeclaration *v)
{
if ((v->isDataseg() || v->storage_class & STCmanifest) && !v->isCTFE())
{
assert(v->ctfeAdrOnStack >= 0 &&
v->ctfeAdrOnStack < (int)globalValues.length);
return globalValues[v->ctfeAdrOnStack];
}
assert(v->ctfeAdrOnStack >= 0 && v->ctfeAdrOnStack < (int)stackPointer());
return values[v->ctfeAdrOnStack];
}
void CtfeStack::setValue(VarDeclaration *v, Expression *e)
{
assert(!v->isDataseg() || v->isCTFE());
assert(v->ctfeAdrOnStack >= 0 && v->ctfeAdrOnStack < (int)stackPointer());
values[v->ctfeAdrOnStack] = e;
}
void CtfeStack::push(VarDeclaration *v)
{
assert(!v->isDataseg() || v->isCTFE());
if (v->ctfeAdrOnStack != -1 &&
v->ctfeAdrOnStack >= (int)framepointer)
{
// Already exists in this frame, reuse it.
values[v->ctfeAdrOnStack] = NULL;
return;
}
savedId.push((void *)(size_t)(v->ctfeAdrOnStack));
v->ctfeAdrOnStack = (int)values.length;
vars.push(v);
values.push(NULL);
}
void CtfeStack::pop(VarDeclaration *v)
{
assert(!v->isDataseg() || v->isCTFE());
assert(!(v->storage_class & (STCref | STCout)));
int oldid = v->ctfeAdrOnStack;
v->ctfeAdrOnStack = (int)(size_t)(savedId[oldid]);
if (v->ctfeAdrOnStack == (int)values.length - 1)
{
values.pop();
vars.pop();
savedId.pop();
}
}
void CtfeStack::popAll(size_t stackpointer)
{
if (stackPointer() > maxStackPointer)
maxStackPointer = stackPointer();
assert(values.length >= stackpointer);
for (size_t i = stackpointer; i < values.length; ++i)
{
VarDeclaration *v = vars[i];
v->ctfeAdrOnStack = (int)(size_t)(savedId[i]);
}
values.setDim(stackpointer);
vars.setDim(stackpointer);
savedId.setDim(stackpointer);
}
void CtfeStack::saveGlobalConstant(VarDeclaration *v, Expression *e)
{
assert(v->_init && (v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) && !v->isCTFE());
v->ctfeAdrOnStack = (int)globalValues.length;
globalValues.push(e);
}
/************** InterState ********************************************/
InterState::InterState()
{
memset(this, 0, sizeof(InterState));
}
/************** CtfeStatus ********************************************/
int CtfeStatus::callDepth = 0;
int CtfeStatus::stackTraceCallsToSuppress = 0;
int CtfeStatus::maxCallDepth = 0;
int CtfeStatus::numArrayAllocs = 0;
int CtfeStatus::numAssignments = 0;
// CTFE diagnostic information
void printCtfePerformanceStats()
{
#if SHOWPERFORMANCE
printf(" ---- CTFE Performance ----\n");
printf("max call depth = %d\tmax stack = %d\n", CtfeStatus::maxCallDepth, ctfeStack.maxStackUsage());
printf("array allocs = %d\tassignments = %d\n\n", CtfeStatus::numArrayAllocs, CtfeStatus::numAssignments);
#endif
}
static Expression *evaluateIfBuiltin(UnionExp *pue, InterState *istate, Loc loc,
FuncDeclaration *fd, Expressions *arguments, Expression *pthis);
static Expression *evaluatePostblit(InterState *istate, Expression *e);
static Expression *evaluateDtor(InterState *istate, Expression *e);
static bool isEntirelyVoid(Expressions* elems);
static Expression *scrubArray(Loc loc, Expressions *elems, bool structlit = false);
static Expression *scrubStructLiteral(Loc loc, StructLiteralExp *sle);
static Expression *scrubReturnValue(Loc loc, Expression *e);
static Expression *scrubArrayCache(Expressions *elems);
static Expression *scrubStructLiteralCache(StructLiteralExp *sle);
static Expression *scrubCacheValue(Expression *e);
/*************************************
* CTFE-object code for a single function
*
* Currently only counts the number of local variables in the function
*/
struct CompiledCtfeFunction
{
FuncDeclaration *func; // Function being compiled, NULL if global scope
int numVars; // Number of variables declared in this function
Loc callingloc;
CompiledCtfeFunction(FuncDeclaration *f)
{
func = f;
numVars = 0;
}
void onDeclaration(VarDeclaration *)
{
//printf("%s CTFE declare %s\n", v->loc.toChars(), v->toChars());
++numVars;
}
void onExpression(Expression *e)
{
class VarWalker : public StoppableVisitor
{
public:
CompiledCtfeFunction *ccf;
VarWalker(CompiledCtfeFunction *ccf)
: ccf(ccf)
{
}
void visit(Expression *)
{
}
void visit(ErrorExp *e)
{
// Currently there's a front-end bug: silent errors
// can occur inside delegate literals inside is(typeof()).
// Suppress the check in this case.
if (global.gag && ccf->func)
{
stop = 1;
return;
}
::error(e->loc, "CTFE internal error: ErrorExp in %s\n", ccf->func ? ccf->func->loc.toChars() : ccf->callingloc.toChars());
assert(0);
}
void visit(DeclarationExp *e)
{
VarDeclaration *v = e->declaration->isVarDeclaration();
if (!v)
return;
TupleDeclaration *td = v->toAlias()->isTupleDeclaration();
if (td)
{
if (!td->objects)
return;
for (size_t i= 0; i < td->objects->length; ++i)
{
RootObject *o = td->objects->tdata()[i];
Expression *ex = isExpression(o);
DsymbolExp *s = (ex && ex->op == TOKdsymbol) ? (DsymbolExp *)ex : NULL;
assert(s);
VarDeclaration *v2 = s->s->isVarDeclaration();
assert(v2);
if (!v2->isDataseg() || v2->isCTFE())
ccf->onDeclaration(v2);
}
}
else if (!(v->isDataseg() || v->storage_class & STCmanifest) || v->isCTFE())
ccf->onDeclaration(v);
Dsymbol *s = v->toAlias();
if (s == v && !v->isStatic() && v->_init)
{
ExpInitializer *ie = v->_init->isExpInitializer();
if (ie)
ccf->onExpression(ie->exp);
}
}
void visit(IndexExp *e)
{
if (e->lengthVar)
ccf->onDeclaration(e->lengthVar);
}
void visit(SliceExp *e)
{
if (e->lengthVar)
ccf->onDeclaration(e->lengthVar);
}
};
VarWalker v(this);
walkPostorder(e, &v);
}
};
class CtfeCompiler : public Visitor
{
public:
CompiledCtfeFunction *ccf;
CtfeCompiler(CompiledCtfeFunction *ccf)
: ccf(ccf)
{
}
void visit(Statement *)
{
assert(0);
}
void visit(ExpStatement *s)
{
if (s->exp)
ccf->onExpression(s->exp);
}
void visit(CompoundStatement *s)
{
for (size_t i = 0; i < s->statements->length; i++)
{
Statement *sx = (*s->statements)[i];
if (sx)
ctfeCompile(sx);
}
}
void visit(UnrolledLoopStatement *s)
{
for (size_t i = 0; i < s->statements->length; i++)
{
Statement *sx = (*s->statements)[i];
if (sx)
ctfeCompile(sx);
}
}
void visit(IfStatement *s)
{
ccf->onExpression(s->condition);
if (s->ifbody)
ctfeCompile(s->ifbody);
if (s->elsebody)
ctfeCompile(s->elsebody);
}
void visit(ScopeStatement *s)
{
if (s->statement)
ctfeCompile(s->statement);
}
void visit(ScopeGuardStatement *)
{
// rewritten to try/catch/finally
assert(0);
}
void visit(DoStatement *s)
{
ccf->onExpression(s->condition);
if (s->_body)
ctfeCompile(s->_body);
}
void visit(WhileStatement *)
{
// rewritten to ForStatement
assert(0);
}
void visit(ForStatement *s)
{
if (s->_init)
ctfeCompile(s->_init);
if (s->condition)
ccf->onExpression(s->condition);
if (s->increment)
ccf->onExpression(s->increment);
if (s->_body)
ctfeCompile(s->_body);
}
void visit(ForeachStatement *)
{
// rewritten for ForStatement
assert(0);
}
void visit(SwitchStatement *s)
{
ccf->onExpression(s->condition);
// Note that the body contains the the Case and Default
// statements, so we only need to compile the expressions
for (size_t i = 0; i < s->cases->length; i++)
{
ccf->onExpression((*s->cases)[i]->exp);
}
if (s->_body)
ctfeCompile(s->_body);
}
void visit(CaseStatement *s)
{
if (s->statement)
ctfeCompile(s->statement);
}
void visit(DefaultStatement *s)
{
if (s->statement)
ctfeCompile(s->statement);
}
void visit(GotoDefaultStatement *)
{
}
void visit(GotoCaseStatement *)
{
}
void visit(SwitchErrorStatement *)
{
}
void visit(ReturnStatement *s)
{
if (s->exp)
ccf->onExpression(s->exp);
}
void visit(BreakStatement *)
{
}
void visit(ContinueStatement *)
{
}
void visit(WithStatement *s)
{
// If it is with(Enum) {...}, just execute the body.
if (s->exp->op == TOKscope || s->exp->op == TOKtype)
{
}
else
{
ccf->onDeclaration(s->wthis);
ccf->onExpression(s->exp);
}
if (s->_body)
ctfeCompile(s->_body);
}
void visit(TryCatchStatement *s)
{
if (s->_body)
ctfeCompile(s->_body);
for (size_t i = 0; i < s->catches->length; i++)
{
Catch *ca = (*s->catches)[i];
if (ca->var)
ccf->onDeclaration(ca->var);
if (ca->handler)
ctfeCompile(ca->handler);
}
}
void visit(TryFinallyStatement *s)
{
if (s->_body)
ctfeCompile(s->_body);
if (s->finalbody)
ctfeCompile(s->finalbody);
}
void visit(ThrowStatement *s)
{
ccf->onExpression(s->exp);
}
void visit(GotoStatement *)
{
}
void visit(LabelStatement *s)
{
if (s->statement)
ctfeCompile(s->statement);
}
void visit(ImportStatement *)
{
// Contains no variables or executable code
}
void visit(ForeachRangeStatement *)
{
// rewritten for ForStatement
assert(0);
}
void visit(AsmStatement *)
{
// we can't compile asm statements
}
void ctfeCompile(Statement *s)
{
s->accept(this);
}
};
/*************************************
* Compile this function for CTFE.
* At present, this merely allocates variables.
*/
void ctfeCompile(FuncDeclaration *fd)
{
assert(!fd->ctfeCode);
assert(!fd->semantic3Errors);
assert(fd->semanticRun == PASSsemantic3done);
fd->ctfeCode = new CompiledCtfeFunction(fd);
if (fd->parameters)
{
Type *tb = fd->type->toBasetype();
assert(tb->ty == Tfunction);
for (size_t i = 0; i < fd->parameters->length; i++)
{
VarDeclaration *v = (*fd->parameters)[i];
fd->ctfeCode->onDeclaration(v);
}
}
if (fd->vresult)
fd->ctfeCode->onDeclaration(fd->vresult);
CtfeCompiler v(fd->ctfeCode);
v.ctfeCompile(fd->fbody);
}
/*************************************
* Entry point for CTFE.
* A compile-time result is required. Give an error if not possible.
*
* `e` must be semantically valid expression. In other words, it should not
* contain any `ErrorExp`s in it. But, CTFE interpretation will cross over
* functions and may invoke a function that contains `ErrorStatement` in its body.
* If that, the "CTFE failed because of previous errors" error is raised.
*/
Expression *ctfeInterpret(Expression *e)
{
switch (e->op)
{
case TOKint64:
case TOKfloat64:
case TOKcomplex80:
case TOKnull:
case TOKvoid:
case TOKstring:
case TOKthis:
case TOKsuper:
case TOKtype:
case TOKtypeid:
case TOKtemplate: // non-eponymous template/instance
case TOKscope: // ditto
case TOKdottd: // ditto, e.e1 doesn't matter here
case TOKdot: // ditto
if (e->type->ty == Terror)
return new ErrorExp();
/* fall through */
case TOKerror:
return e;
default:
break;
}
assert(e->type); // Bugzilla 14642
//assert(e->type->ty != Terror); // FIXME
if (e->type->ty == Terror)
return new ErrorExp();
// This code is outside a function, but still needs to be compiled
// (there are compiler-generated temporary variables such as __dollar).
// However, this will only be run once and can then be discarded.
CompiledCtfeFunction ctfeCodeGlobal(NULL);
ctfeCodeGlobal.callingloc = e->loc;
ctfeCodeGlobal.onExpression(e);
Expression *result = interpret(e, NULL);
if (!CTFEExp::isCantExp(result))
result = scrubReturnValue(e->loc, result);
if (CTFEExp::isCantExp(result))
result = new ErrorExp();
return result;
}
/* Run CTFE on the expression, but allow the expression to be a TypeExp
* or a tuple containing a TypeExp. (This is required by pragma(msg)).
*/
Expression *ctfeInterpretForPragmaMsg(Expression *e)
{
if (e->op == TOKerror || e->op == TOKtype)
return e;
// It's also OK for it to be a function declaration (happens only with
// __traits(getOverloads))
if (e->op == TOKvar && ((VarExp *)e)->var->isFuncDeclaration())
{
return e;
}
if (e->op != TOKtuple)
return e->ctfeInterpret();
// Tuples need to be treated seperately, since they are
// allowed to contain a TypeExp in this case.
TupleExp *tup = (TupleExp *)e;
Expressions *expsx = NULL;
for (size_t i = 0; i < tup->exps->length; ++i)
{
Expression *g = (*tup->exps)[i];
Expression *h = g;
h = ctfeInterpretForPragmaMsg(g);
if (h != g)
{
if (!expsx)
{
expsx = new Expressions();
expsx->setDim(tup->exps->length);
for (size_t j = 0; j < tup->exps->length; j++)
(*expsx)[j] = (*tup->exps)[j];
}
(*expsx)[i] = h;
}
}
if (expsx)
{
TupleExp *te = new TupleExp(e->loc, expsx);
expandTuples(te->exps);
te->type = new TypeTuple(te->exps);
return te;
}
return e;
}
/*************************************
* Attempt to interpret a function given the arguments.
* Input:
* pue storage for result
* fd function being called
* istate state for calling function (NULL if none)
* arguments function arguments
* thisarg 'this', if a needThis() function, NULL if not.
*
* Return result expression if successful, TOKcantexp if not,
* or CTFEExp if function returned void.
*/
static Expression *interpretFunction(UnionExp *pue, FuncDeclaration *fd, InterState *istate, Expressions *arguments, Expression *thisarg)
{
assert(pue);
if (fd->semanticRun == PASSsemantic3)
{
fd->error("circular dependency. Functions cannot be interpreted while being compiled");
return CTFEExp::cantexp;
}
if (!fd->functionSemantic3())
return CTFEExp::cantexp;
if (fd->semanticRun < PASSsemantic3done)
return CTFEExp::cantexp;
// CTFE-compile the function
if (!fd->ctfeCode)
ctfeCompile(fd);
Type *tb = fd->type->toBasetype();
assert(tb->ty == Tfunction);
TypeFunction *tf = (TypeFunction *)tb;
if (tf->parameterList.varargs != VARARGnone && arguments &&
((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;
}
// Nested functions always inherit the 'this' pointer from the parent,
// except for delegates. (Note that the 'this' pointer may be null).
// Func literals report isNested() even if they are in global scope,
// so we need to check that the parent is a function.
if (fd->isNested() && fd->toParent2()->isFuncDeclaration() && !thisarg && istate)
thisarg = ctfeStack.getThis();
if (fd->needThis() && !thisarg)
{
// error, no this. Prevent segfault.
// Here should be unreachable by the strict 'this' check in front-end.
fd->error("need `this` to access member %s", fd->toChars());
return CTFEExp::cantexp;
}
// Place to hold all the arguments to the function while
// we are evaluating them.
Expressions eargs;
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[]
*/
eargs.setDim(dim);
for (size_t i = 0; i < dim; i++)
{
Expression *earg = (*arguments)[i];
Parameter *fparam = tf->parameterList[i];
if (fparam->storageClass & (STCout | STCref))
{
if (!istate && (fparam->storageClass & STCout))
{
// initializing an out parameter involves writing to it.
earg->error("global %s cannot be passed as an `out` parameter at compile time", earg->toChars());
return CTFEExp::cantexp;
}
// Convert all reference arguments into lvalue references
earg = interpret(earg, istate, ctfeNeedLvalue);
if (CTFEExp::isCantExp(earg))
return earg;
}
else if (fparam->storageClass & STClazy)
{
}
else
{
/* Value parameters
*/
Type *ta = fparam->type->toBasetype();
if (ta->ty == Tsarray && earg->op == TOKaddress)
{
/* Static arrays are passed by a simple pointer.
* Skip past this to get at the actual arg.
*/
earg = ((AddrExp *)earg)->e1;
}
earg = interpret(earg, istate);
if (CTFEExp::isCantExp(earg))
return earg;
/* Struct literals are passed by value, but we don't need to
* copy them if they are passed as const
*/
if (earg->op == TOKstructliteral && !(fparam->storageClass & (STCconst | STCimmutable)))
earg = copyLiteral(earg).copy();
}
if (earg->op == TOKthrownexception)
{
if (istate)
return earg;
((ThrownExceptionExp *)earg)->generateUncaughtError();
return CTFEExp::cantexp;
}
eargs[i] = earg;
}
// Now that we've evaluated all the arguments, we can start the frame
// (this is the moment when the 'call' actually takes place).
InterState istatex;
istatex.caller = istate;
istatex.fd = fd;
ctfeStack.startFrame(thisarg);
if (fd->vthis && thisarg)
{
ctfeStack.push(fd->vthis);
setValue(fd->vthis, thisarg);
}
for (size_t i = 0; i < dim; i++)
{
Expression *earg = eargs[i];
Parameter *fparam = tf->parameterList[i];
VarDeclaration *v = (*fd->parameters)[i];
ctfeStack.push(v);
if ((fparam->storageClass & (STCout | STCref)) &&
earg->op == TOKvar && ((VarExp *)earg)->var->toParent2() == fd)
{
VarDeclaration *vx = ((VarExp *)earg)->var->isVarDeclaration();
if (!vx)
{
fd->error("cannot interpret %s as a ref parameter", earg->toChars());
return CTFEExp::cantexp;
}
/* vx is a variable that is declared in fd.
* It means that fd is recursively called. e.g.
*
* void fd(int n, ref int v = dummy) {
* int vx;
* if (n == 1) fd(2, vx);
* }
* fd(1);
*
* The old value of vx on the stack in fd(1)
* should be saved at the start of fd(2, vx) call.
*/
int oldadr = vx->ctfeAdrOnStack;
ctfeStack.push(vx);
assert(!hasValue(vx)); // vx is made uninitialized
// Bugzilla 14299: v->ctfeAdrOnStack should be saved already
// in the stack before the overwrite.
v->ctfeAdrOnStack = oldadr;
assert(hasValue(v)); // ref parameter v should refer existing value.
}
else
{
// Value parameters and non-trivial references
setValueWithoutChecking(v, earg);
}
}
if (fd->vresult)
ctfeStack.push(fd->vresult);
// Enter the function
++CtfeStatus::callDepth;
if (CtfeStatus::callDepth > CtfeStatus::maxCallDepth)
CtfeStatus::maxCallDepth = CtfeStatus::callDepth;
Expression *e = NULL;
while (1)
{
if (CtfeStatus::callDepth > CTFE_RECURSION_LIMIT)
{
// This is a compiler error. It must not be suppressed.
global.gag = 0;
fd->error("CTFE recursion limit exceeded");
e = CTFEExp::cantexp;
break;
}
e = interpret(pue, fd->fbody, &istatex);
if (istatex.start)
{
fd->error("CTFE internal error: failed to resume at statement %s", istatex.start->toChars());
return CTFEExp::cantexp;
}
/* This is how we deal with a recursive statement AST
* that has arbitrary goto statements in it.
* Bubble up a 'result' which is the target of the goto
* statement, then go recursively down the AST looking
* for that statement, then execute starting there.
*/
if (CTFEExp::isGotoExp(e))
{
istatex.start = istatex.gotoTarget; // set starting statement
istatex.gotoTarget = NULL;
}
else
{
assert(!e || (e->op != TOKcontinue && e->op != TOKbreak));
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 == TOKvar && ((VarExp *)e)->var == fd->vthis)
e = thisarg;
assert(e != NULL);
// Leave the function
--CtfeStatus::callDepth;
ctfeStack.endFrame();
// If it generated an uncaught exception, report error.
if (!istate && e->op == TOKthrownexception)
{
if (e == pue->exp())
e = pue->copy();
((ThrownExceptionExp *)e)->generateUncaughtError();
e = CTFEExp::cantexp;
}
return e;
}
class Interpreter : public Visitor
{
public:
InterState *istate;
CtfeGoal goal;
Expression *result;
UnionExp *pue; // storage for `result`
Interpreter(UnionExp *pue, InterState *istate, CtfeGoal goal)
: istate(istate), goal(goal), pue(pue)
{
result = NULL;
}
// If e is TOKthrowexception or TOKcantexp,
// 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 == TOKcantexp) ? CTFEExp::cantexp : e;
return true;
}
return false;
}
static Expressions *copyArrayOnWrite(Expressions *exps, Expressions *original)
{
if (exps == original)
{
if (!original)
exps = new Expressions();
else
exps = original->copy();
++CtfeStatus::numArrayAllocs;
}
return exps;
}
/******************************** Statement ***************************/
void visit(Statement *s)
{
if (istate->start)
{
if (istate->start != s)
return;
istate->start = NULL;
}
s->error("statement %s cannot be interpreted at compile time", s->toChars());
result = CTFEExp::cantexp;
}
void visit(ExpStatement *s)
{
if (istate->start)
{
if (istate->start != s)
return;
istate->start = NULL;
}
Expression *e = interpret(pue, s->exp, istate, ctfeNeedNothing);
if (exceptionOrCant(e))
return;
}
void visit(CompoundStatement *s)
{
if (istate->start == s)
istate->start = NULL;
const size_t dim = s->statements ? s->statements->length : 0;
for (size_t i = 0; i < dim; i++)
{
Statement *sx = (*s->statements)[i];
result = interpret(pue, sx, istate);
if (result)
break;
}
}
void visit(UnrolledLoopStatement *s)
{
if (istate->start == s)
istate->start = NULL;
const size_t dim = s->statements ? s->statements->length : 0;
for (size_t i = 0; i < dim; i++)
{
Statement *sx = (*s->statements)[i];
Expression *e = interpret(pue, sx, istate);
if (!e) // suceeds to interpret, or goto target
continue; // was not fonnd when istate->start != NULL
if (exceptionOrCant(e))
return;
if (e->op == TOKbreak)
{
if (istate->gotoTarget && istate->gotoTarget != s)
{
result = e; // break at a higher level
return;
}
istate->gotoTarget = NULL;
result = NULL;
return;
}
if (e->op == TOKcontinue)
{
if (istate->gotoTarget && istate->gotoTarget != s)
{
result = e; // continue at a higher level
return;
}
istate->gotoTarget = NULL;
continue;
}
// expression from return statement, or thrown exception
result = e;
break;
}
}
void visit(IfStatement *s)
{
if (istate->start == s)
istate->start = NULL;
if (istate->start)
{
Expression *e = NULL;
e = interpret(s->ifbody, istate);
if (!e && istate->start)
e = interpret(s->elsebody, istate);
result = e;
return;
}
UnionExp ue;
Expression *e = interpret(&ue, s->condition, istate);
assert(e);
if (exceptionOrCant(e))
return;
if (isTrueBool(e))
result = interpret(pue, s->ifbody, istate);
else if (e->isBool(false))
result = interpret(pue, s->elsebody, istate);
else
{
// no error, or assert(0)?
result = CTFEExp::cantexp;
}
}
void visit(ScopeStatement *s)
{
if (istate->start == s)
istate->start = NULL;
result = interpret(pue, s->statement, istate);
}
/**
Given an expression e which is about to be returned from the current
function, generate an error if it contains pointers to local variables.
Only checks expressions passed by value (pointers to local variables
may already be stored in members of classes, arrays, or AAs which
were passed as mutable function parameters).
Returns:
true if it is safe to return, false if an error was generated.
*/
static bool stopPointersEscaping(Loc loc, Expression *e)
{
if (!e->type->hasPointers())
return true;
if (isPointer(e->type))
{
Expression *x = e;
if (e->op == TOKaddress)
x = ((AddrExp *)e)->e1;
VarDeclaration *v;
while (x->op == TOKvar &&
(v = ((VarExp *)x)->var->isVarDeclaration()) != NULL)
{
if (v->storage_class & STCref)
{
x = getValue(v);
if (e->op == TOKaddress)
((AddrExp *)e)->e1 = x;
continue;
}
if (ctfeStack.isInCurrentFrame(v))
{
error(loc, "returning a pointer to a local stack variable");
return false;
}
else
break;
}
// TODO: If it is a TOKdotvar or TOKindex, we should check that it is not
// pointing to a local struct or static array.
}
if (e->op == TOKstructliteral)
{
StructLiteralExp *se = (StructLiteralExp *)e;
return stopPointersEscapingFromArray(loc, se->elements);
}
if (e->op == TOKarrayliteral)
{
return stopPointersEscapingFromArray(loc, ((ArrayLiteralExp *)e)->elements);
}
if (e->op == TOKassocarrayliteral)
{
AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e;
if (!stopPointersEscapingFromArray(loc, aae->keys))
return false;
return stopPointersEscapingFromArray(loc, aae->values);
}
return true;
}
// Check all members of an array for escaping local variables. Return false if error
static bool stopPointersEscapingFromArray(Loc loc, Expressions *elems)
{
for (size_t i = 0; i < elems->length; i++)
{
Expression *m = (*elems)[i];
if (!m)
continue;
if (!stopPointersEscaping(loc, m))
return false;
}
return true;
}
void visit(ReturnStatement *s)
{
if (istate->start)
{
if (istate->start != s)
return;
istate->start = NULL;
}
if (!s->exp)
{
result = CTFEExp::voidexp;
return;
}
assert(istate && istate->fd && istate->fd->type && istate->fd->type->ty == Tfunction);
TypeFunction *tf = (TypeFunction *)istate->fd->type;
/* If the function returns a ref AND it's been called from an assignment,
* we need to return an lvalue. Otherwise, just do an (rvalue) interpret.
*/
if (tf->isref)
{
result = interpret(pue, s->exp, istate, ctfeNeedLvalue);
return;
}
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.
s->error("closures are not yet supported in CTFE");
result = CTFEExp::cantexp;
return;
}
// We need to treat pointers specially, because TOKsymoff can be used to
// return a value OR a pointer
Expression *e = interpret(pue, s->exp, istate);
if (exceptionOrCant(e))
return;
// Disallow returning pointers to stack-allocated variables (bug 7876)
if (!stopPointersEscaping(s->loc, e))
{
result = CTFEExp::cantexp;
return;
}
if (needToCopyLiteral(e))
e = copyLiteral(e).copy();
result = e;
}
static Statement *findGotoTarget(InterState *istate, Identifier *ident)
{
Statement *target = NULL;
if (ident)
{
LabelDsymbol *label = istate->fd->searchLabel(ident);
assert(label && label->statement);
LabelStatement *ls = label->statement;
target = ls->gotoTarget ? ls->gotoTarget : ls->statement;
}
return target;
}
void visit(BreakStatement *s)
{
if (istate->start)
{
if (istate->start != s)
return;
istate->start = NULL;
}
istate->gotoTarget = findGotoTarget(istate, s->ident);
result = CTFEExp::breakexp;
}
void visit(ContinueStatement *s)
{
if (istate->start)
{
if (istate->start != s)
return;
istate->start = NULL;
}
istate->gotoTarget = findGotoTarget(istate, s->ident);
result = CTFEExp::continueexp;
}
void visit(WhileStatement *)
{
assert(0); // rewritten to ForStatement
}
void visit(DoStatement *s)
{
if (istate->start == s)
istate->start = NULL;
while (1)
{
Expression *e = interpret(s->_body, istate);
if (!e && istate->start) // goto target was not found
return;
assert(!istate->start);
if (exceptionOrCant(e))
return;
if (e && e->op == TOKbreak)
{
if (istate->gotoTarget && istate->gotoTarget != s)
{
result = e; // break at a higher level
return;
}
istate->gotoTarget = NULL;
break;
}
if (e && e->op == TOKcontinue)
{
if (istate->gotoTarget && istate->gotoTarget != s)
{
result = e; // continue at a higher level
return;
}
istate->gotoTarget = NULL;
e = NULL;
}
if (e)
{
result = e; // bubbled up from ReturnStatement
return;
}
UnionExp ue;
e = interpret(&ue, s->condition, istate);
if (exceptionOrCant(e))
return;
if (!e->isConst())
{
result = CTFEExp::cantexp;
return;
}
if (e->isBool(false))
break;
assert(isTrueBool(e));
}
assert(result == NULL);
}
void visit(ForStatement *s)
{
if (istate->start == s)
istate->start = NULL;
UnionExp ueinit;
Expression *ei = interpret(&ueinit, s->_init, istate);
if (exceptionOrCant(ei))
return;
assert(!ei); // s->init never returns from function, or jumps out from it
while (1)
{
if (s->condition && !istate->start)
{
UnionExp ue;
Expression *e = interpret(&ue, s->condition, istate);
if (exceptionOrCant(e))
return;
if (e->isBool(false))
break;
assert(isTrueBool(e));
}
Expression *e = interpret(pue, s->_body, istate);
if (!e && istate->start) // goto target was not found
return;
assert(!istate->start);
if (exceptionOrCant(e))
return;
if (e && e->op == TOKbreak)
{
if (istate->gotoTarget && istate->gotoTarget != s)
{
result = e; // break at a higher level
return;
}
istate->gotoTarget = NULL;
break;
}
if (e && e->op == TOKcontinue)
{
if (istate->gotoTarget && istate->gotoTarget != s)
{
result = e; // continue at a higher level
return;
}
istate->gotoTarget = NULL;
e = NULL;
}
if (e)
{
result = e; // bubbled up from ReturnStatement
return;
}
UnionExp uei;
e = interpret(&uei, s->increment, istate, ctfeNeedNothing);
if (exceptionOrCant(e))
return;
}
assert(result == NULL);
}
void visit(ForeachStatement *)
{
assert(0); // rewritten to ForStatement
}
void visit(ForeachRangeStatement *)
{
assert(0); // rewritten to ForStatement
}
void visit(SwitchStatement *s)
{
if (istate->start == s)
istate->start = NULL;
if (istate->start)
{
Expression *e = interpret(s->_body, istate);
if (istate->start) // goto target was not found
return;
if (exceptionOrCant(e))
return;
if (e && e->op == TOKbreak)
{
if (istate->gotoTarget && istate->gotoTarget != s)
{
result = e; // break at a higher level
return;
}
istate->gotoTarget = NULL;
e = NULL;
}
result = e;
return;
}
UnionExp uecond;
Expression *econdition = interpret(&uecond, s->condition, istate);
if (exceptionOrCant(econdition))
return;
Statement *scase = NULL;
size_t dim = s->cases ? s->cases->length : 0;
for (size_t i = 0; i < dim; i++)
{
CaseStatement *cs = (*s->cases)[i];
UnionExp uecase;
Expression *ecase = interpret(&uecase, cs->exp, istate);
if (exceptionOrCant(ecase))
return;
if (ctfeEqual(cs->exp->loc, TOKequal, econdition, ecase))
{
scase = cs;
break;
}
}
if (!scase)
{
if (s->hasNoDefault)
s->error("no default or case for %s in switch statement", econdition->toChars());
scase = s->sdefault;
}
assert(scase);
/* Jump to scase
*/
istate->start = scase;
Expression *e = interpret(pue, s->_body, istate);
assert(!istate->start); // jump must not fail
if (e && e->op == TOKbreak)
{
if (istate->gotoTarget && istate->gotoTarget != s)
{
result = e; // break at a higher level
return;
}
istate->gotoTarget = NULL;
e = NULL;
}
result = e;
}
void visit(CaseStatement *s)
{
if (istate->start == s)
istate->start = NULL;
result = interpret(pue, s->statement, istate);
}
void visit(DefaultStatement *s)
{
if (istate->start == s)
istate->start = NULL;
result = interpret(pue, s->statement, istate);
}
void visit(GotoStatement *s)
{
if (istate->start)
{
if (istate->start != s)
return;
istate->start = NULL;
}
assert(s->label && s->label->statement);
istate->gotoTarget = s->label->statement;
result = CTFEExp::gotoexp;
}
void visit(GotoCaseStatement *s)
{
if (istate->start)
{
if (istate->start != s)
return;
istate->start = NULL;
}
assert(s->cs);
istate->gotoTarget = s->cs;
result = CTFEExp::gotoexp;
}
void visit(GotoDefaultStatement *s)
{
if (istate->start)
{
if (istate->start != s)
return;
istate->start = NULL;
}
assert(s->sw && s->sw->sdefault);
istate->gotoTarget = s->sw->sdefault;
result = CTFEExp::gotoexp;
}
void visit(LabelStatement *s)
{
if (istate->start == s)
istate->start = NULL;
result = interpret(pue, s->statement, istate);
}
void visit(TryCatchStatement *s)
{
if (istate->start == s)
istate->start = NULL;
if (istate->start)
{
Expression *e = NULL;
e = interpret(pue, s->_body, istate);
for (size_t i = 0; i < s->catches->length; i++)
{
if (e || !istate->start) // goto target was found
break;
Catch *ca = (*s->catches)[i];
e = interpret(ca->handler, istate);
}
result = e;
return;
}
Expression *e = interpret(pue, s->_body, istate);
// An exception was thrown
if (e && e->op == TOKthrownexception)
{
ThrownExceptionExp *ex = (ThrownExceptionExp *)e;
Type *extype = ex->thrown->originalClass()->type;
// Search for an appropriate catch clause.
for (size_t i = 0; i < s->catches->length; i++)
{
Catch *ca = (*s->catches)[i];
Type *catype = ca->type;
if (!catype->equals(extype) && !catype->isBaseOf(extype, NULL))
continue;
// Execute the handler
if (ca->var)
{
ctfeStack.push(ca->var);
setValue(ca->var, ex->thrown);
}
e = interpret(ca->handler, istate);
if (CTFEExp::isGotoExp(e))
{
/* This is an optimization that relies on the locality of the jump target.
* If the label is in the same catch handler, the following scan
* would find it quickly and can reduce jump cost.
* Otherwise, the catch block may be unnnecessary scanned again
* so it would make CTFE speed slower.
*/
InterState istatex = *istate;
istatex.start = istate->gotoTarget; // set starting statement
istatex.gotoTarget = NULL;
Expression *eh = interpret(ca->handler, &istatex);
if (!istatex.start)
{
istate->gotoTarget = NULL;
e = eh;
}
}
break;
}
}
result = e;
}
static bool isAnErrorException(ClassDeclaration *cd)
{
return cd == ClassDeclaration::errorException || ClassDeclaration::errorException->isBaseOf(cd, NULL);
}
static ThrownExceptionExp *chainExceptions(ThrownExceptionExp *oldest, ThrownExceptionExp *newest)
{
// Little sanity check to make sure it's really a Throwable
ClassReferenceExp *boss = oldest->thrown;
const int next = 4; // 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()))
{
/* Find the index of the Error.bypassException field
*/
int bypass = next + 1;
if ((*collateral->value->elements)[bypass]->type->ty == Tuns32)
bypass += 1; // skip over _refcount field
assert((*collateral->value->elements)[bypass]->type->ty == Tclass);
// The new exception bypass the existing chain
(*collateral->value->elements)[bypass] = boss;
return newest;
}
while ((*boss->value->elements)[next]->op == TOKclassreference)
{
boss = (ClassReferenceExp *)(*boss->value->elements)[next];
}
(*boss->value->elements)[next] = collateral;
return oldest;
}
void visit(TryFinallyStatement *s)
{
if (istate->start == s)
istate->start = NULL;
if (istate->start)
{
Expression *e = NULL;
e = interpret(pue, s->_body, istate);
// Jump into/out from finalbody is disabled in semantic analysis.
// and jump inside will be handled by the ScopeStatement == finalbody.
result = e;
return;
}
Expression *ex = interpret(s->_body, istate);
if (CTFEExp::isCantExp(ex))
{
result = ex;
return;
}
while (CTFEExp::isGotoExp(ex))
{
// If the goto target is within the body, we must not interpret the finally statement,
// because that will call destructors for objects within the scope, which we should not do.
InterState istatex = *istate;
istatex.start = istate->gotoTarget; // set starting statement
istatex.gotoTarget = NULL;
Expression *bex = interpret(s->_body, &istatex);
if (istatex.start)
{
// The goto target is outside the current scope.
break;
}
// The goto target was within the body.
if (CTFEExp::isCantExp(bex))
{
result = bex;
return;
}
*istate = istatex;
ex = bex;
}
Expression *ey = interpret(s->finalbody, istate);
if (CTFEExp::isCantExp(ey))
{
result = ey;
return;
}
if (ey && ey->op == TOKthrownexception)
{
// Check for collided exceptions
if (ex && ex->op == TOKthrownexception)
ex = chainExceptions((ThrownExceptionExp *)ex, (ThrownExceptionExp *)ey);
else
ex = ey;
}
result = ex;
}
void visit(ThrowStatement *s)
{
if (istate->start)
{
if (istate->start != s)
return;
istate->start = NULL;
}
Expression *e = interpret(s->exp, istate);
if (exceptionOrCant(e))
return;
assert(e->op == TOKclassreference);
result = new ThrownExceptionExp(s->loc, (ClassReferenceExp *)e);
}
void visit(ScopeGuardStatement *)
{
assert(0);
}
void visit(WithStatement *s)
{
if (istate->start == s)
istate->start = NULL;
if (istate->start)
{
result = s->_body ? interpret(s->_body, istate) : NULL;
return;
}
// If it is with(Enum) {...}, just execute the body.
if (s->exp->op == TOKscope || s->exp->op == TOKtype)
{
result = interpret(pue, s->_body, istate);
return;
}
Expression *e = interpret(s->exp, istate);
if (exceptionOrCant(e))
return;
if (s->wthis->type->ty == Tpointer && s->exp->type->ty != Tpointer)
{
e = new AddrExp(s->loc, e, s->wthis->type);
}
ctfeStack.push(s->wthis);
setValue(s->wthis, e);
e = interpret(s->_body, istate);
if (CTFEExp::isGotoExp(e))
{
/* This is an optimization that relies on the locality of the jump target.
* If the label is in the same WithStatement, the following scan
* would find it quickly and can reduce jump cost.
* Otherwise, the statement body may be unnnecessary scanned again
* so it would make CTFE speed slower.
*/
InterState istatex = *istate;
istatex.start = istate->gotoTarget; // set starting statement
istatex.gotoTarget = NULL;
Expression *ex = interpret(s->_body, &istatex);
if (!istatex.start)
{
istate->gotoTarget = NULL;
e = ex;
}
}
ctfeStack.pop(s->wthis);
result = e;
}
void visit(AsmStatement *s)
{
if (istate->start)
{
if (istate->start != s)
return;
istate->start = NULL;
}
s->error("asm statements cannot be interpreted at compile time");
result = CTFEExp::cantexp;
}
void visit(ImportStatement *s)
{
if (istate->start)
{
if (istate->start != s)
return;
istate->start = NULL;
}
}
/******************************** Expression ***************************/
void visit(Expression *e)
{
e->error("cannot interpret %s at compile time", e->toChars());
result = CTFEExp::cantexp;
}
void visit(ThisExp *e)
{
if (goal == ctfeNeedLvalue)
{
// We might end up here with istate being zero (see bugzilla 16382)
if (istate && istate->fd->vthis)
{
result = new VarExp(e->loc, istate->fd->vthis);
result->type = e->type;
}
else
result = e;
return;
}
result = ctfeStack.getThis();
if (result)
{
assert(result->op == TOKstructliteral ||
result->op == TOKclassreference);
return;
}
e->error("value of `this` is not known at compile time");
result = CTFEExp::cantexp;
}
void visit(NullExp *e)
{
result = e;
}
void visit(IntegerExp *e)
{
result = e;
}
void visit(RealExp *e)
{
result = e;
}
void visit(ComplexExp *e)
{
result = e;
}
void visit(StringExp *e)
{
/* Attempts to modify string literals are prevented
* in BinExp::interpretAssignCommon.
*/
result = e;
}
void visit(FuncExp *e)
{
result = e;
}
void visit(SymOffExp *e)
{
if (e->var->isFuncDeclaration() && e->offset == 0)
{
result = e;
return;
}
if (isTypeInfo_Class(e->type) && e->offset == 0)
{
result = e;
return;
}
if (e->type->ty != Tpointer)
{
// Probably impossible
e->error("cannot interpret %s at compile time", e->toChars());
result = CTFEExp::cantexp;
return;
}
Type *pointee = ((TypePointer *)e->type)->next;
if (e->var->isThreadlocal())
{
e->error("cannot take address of thread-local variable %s at compile time", e->var->toChars());
result = CTFEExp::cantexp;
return;
}
// Check for taking an address of a shared variable.
// If the shared variable is an array, the offset might not be zero.
Type *fromType = NULL;
if (e->var->type->ty == Tarray || e->var->type->ty == Tsarray)
{
fromType = ((TypeArray *)(e->var->type))->next;
}
if (e->var->isDataseg() &&
((e->offset == 0 && isSafePointerCast(e->var->type, pointee)) ||
(fromType && isSafePointerCast(fromType, pointee))))
{
result = e;
return;
}
Expression *val = getVarExp(e->loc, istate, e->var, goal);
if (exceptionOrCant(val))
return;
if (val->type->ty == Tarray || val->type->ty == Tsarray)
{
// Check for unsupported type painting operations
Type *elemtype = ((TypeArray *)(val->type))->next;
d_uns64 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())
{
size_t d = (size_t)((TypeSArray *)pointee)->dim->toInteger();
Expression *elwr = new IntegerExp(e->loc, e->offset / elemsize, Type::tsize_t);
Expression *eupr = new IntegerExp(e->loc, e->offset / elemsize + d, Type::tsize_t);
// Create a CTFE pointer &val[ofs..ofs+d]
SliceExp *se = new SliceExp(e->loc, val, elwr, eupr);
se->type = pointee;
new(pue) AddrExp(e->loc, se, e->type);
result = pue->exp();
return;
}
if (!isSafePointerCast(elemtype, pointee))
{
// It's also OK to cast from &string to string*.
if (e->offset == 0 && isSafePointerCast(e->var->type, pointee))
{
// Create a CTFE pointer &var
VarExp *ve = new VarExp(e->loc, e->var);
ve->type = elemtype;
new(pue) AddrExp(e->loc, ve, e->type);
result = pue->exp();
return;
}
e->error("reinterpreting cast from %s to %s is not supported in CTFE",
val->type->toChars(), e->type->toChars());
result = CTFEExp::cantexp;
return;
}
const dinteger_t sz = pointee->size();
dinteger_t indx = e->offset / sz;
assert(sz * indx == e->offset);
Expression *aggregate = NULL;
if (val->op == TOKarrayliteral || val->op == TOKstring)
{
aggregate = val;
}
else if (val->op == TOKslice)
{
aggregate = ((SliceExp *)val)->e1;
UnionExp uelwr;
Expression *lwr = interpret(&uelwr, ((SliceExp *)val)->lwr, istate);
indx += lwr->toInteger();
}
if (aggregate)
{
// Create a CTFE pointer &aggregate[ofs]
IntegerExp *ofs = new IntegerExp(e->loc, indx, Type::tsize_t);
IndexExp *ei = new IndexExp(e->loc, aggregate, ofs);
ei->type = elemtype;
new(pue) AddrExp(e->loc, ei, e->type);
result = pue->exp();
return;
}
}
else if (e->offset == 0 && isSafePointerCast(e->var->type, pointee))
{
// Create a CTFE pointer &var
VarExp *ve = new VarExp(e->loc, e->var);
ve->type = e->var->type;
new(pue) AddrExp(e->loc, ve, e->type);
result = pue->exp();
return;
}
e->error("cannot convert &%s to %s at compile time", e->var->type->toChars(), e->type->toChars());
result = CTFEExp::cantexp;
}
void visit(AddrExp *e)
{
if (e->e1->op == TOKvar && ((VarExp *)e->e1)->var->isDataseg())
{
// Normally this is already done by optimize()
// Do it here in case optimize(WANTvalue) wasn't run before CTFE
new(pue) SymOffExp(e->loc, ((VarExp *)e->e1)->var, 0);
result = pue->exp();
result->type = e->type;
return;
}
Expression *er = interpret(e->e1, istate, ctfeNeedLvalue);
if (er->op == TOKvar && ((VarExp *)er)->var == istate->fd->vthis)
er = interpret(er, istate);
if (exceptionOrCant(er))
return;
// Return a simplified address expression
new(pue) AddrExp(e->loc, er, e->type);
result = pue->exp();
}
void visit(DelegateExp *e)
{
// TODO: Really we should create a CTFE-only delegate expression
// of a pointer and a funcptr.
// If it is &nestedfunc, just return it
// TODO: We should save the context pointer
if (e->e1->op == TOKvar && ((VarExp *)e->e1)->var == e->func)
{
result = e;
return;
}
Expression *er = interpret(pue, e->e1, istate);
if (exceptionOrCant(er))
return;
if (er == e->e1)
{
// If it has already been CTFE'd, just return it
result = e;
}
else
{
er = (er == pue->exp()) ? pue->copy() : er;
new(pue) DelegateExp(e->loc, er, e->func, false);
result = pue->exp();
result->type = e->type;
}
}
static Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal)
{
Expression *e = CTFEExp::cantexp;
if (VarDeclaration *v = d->isVarDeclaration())
{
/* Magic variable __ctfe always returns true when interpreting
*/
if (v->ident == Id::ctfe)
return new IntegerExp(loc, 1, Type::tbool);
if (!v->originalType && v->semanticRun < PASSsemanticdone) // semantic() not yet run
{
dsymbolSemantic(v, NULL);
if (v->type->ty == Terror)
return CTFEExp::cantexp;
}
if ((v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) &&
!hasValue(v) &&
v->_init && !v->isCTFE())
{
if (v->inuse)
{
error(loc, "circular initialization of %s `%s`", v->kind(), v->toPrettyChars());
return CTFEExp::cantexp;
}
if (v->_scope)
{
v->inuse++;
v->_init = initializerSemantic(v->_init, v->_scope, v->type, INITinterpret); // might not be run on aggregate members
v->inuse--;
}
e = initializerToExpression(v->_init, v->type);
if (!e)
return CTFEExp::cantexp;
assert(e->type);
if (e->op == TOKconstruct || e->op == TOKblit)
{
AssignExp *ae = (AssignExp *)e;
e = ae->e2;
}
if (e->op == TOKerror)
{
// FIXME: Ultimately all errors should be detected in prior semantic analysis stage.
}
else if (v->isDataseg() || (v->storage_class & STCmanifest))
{
/* Bugzilla 14304: e is a value that is not yet owned by CTFE.
* Mark as "cached", and use it directly during interpretation.
*/
e = scrubCacheValue(e);
ctfeStack.saveGlobalConstant(v, e);
}
else
{
v->inuse++;
e = interpret(e, istate);
v->inuse--;
if (CTFEExp::isCantExp(e) && !global.gag && !CtfeStatus::stackTraceCallsToSuppress)
errorSupplemental(loc, "while evaluating %s.init", v->toChars());
if (exceptionOrCantInterpret(e))
return e;
}
}
else if (v->isCTFE() && !hasValue(v))
{
if (v->_init && v->type->size() != 0)
{
if (v->_init->isVoidInitializer())
{
// var should have been initialized when it was created
error(loc, "CTFE internal error: trying to access uninitialized var");
assert(0);
return CTFEExp::cantexp;
}
e = initializerToExpression(v->_init);
}
else
e = v->type->defaultInitLiteral(e->loc);
e = interpret(e, istate);
}
else if (!(v->isDataseg() || v->storage_class & STCmanifest) && !v->isCTFE() && !istate)
{
error(loc, "variable %s cannot be read at compile time", v->toChars());
return CTFEExp::cantexp;
}
else
{
e = hasValue(v) ? getValue(v) : NULL;
if (!e && !v->isCTFE() && v->isDataseg())
{
error(loc, "static variable %s cannot be read at compile time", v->toChars());
return CTFEExp::cantexp;
}
if (!e)
{
assert(!(v->_init && v->_init->isVoidInitializer()));
// CTFE initiated from inside a function
error(loc, "variable %s cannot be read at compile time", v->toChars());
return CTFEExp::cantexp;
}
if (e->op == TOKvoid)
{
VoidInitExp *ve = (VoidInitExp *)e;
error(loc, "cannot read uninitialized variable %s in ctfe", v->toPrettyChars());
errorSupplemental(ve->var->loc, "%s was uninitialized and used before set", ve->var->toChars());
return CTFEExp::cantexp;
}
if (goal != ctfeNeedLvalue && (v->isRef() || v->isOut()))
e = interpret(e, istate, goal);
}
if (!e)
e = CTFEExp::cantexp;
}
else if (SymbolDeclaration *s = d->isSymbolDeclaration())
{
// Struct static initializers, for example
e = s->dsym->type->defaultInitLiteral(loc);
if (e->op == TOKerror)
error(loc, "CTFE failed because of previous errors in %s.init", s->toChars());
e = expressionSemantic(e, NULL);
if (e->op == TOKerror)
e = CTFEExp::cantexp;
else // Convert NULL to CTFEExp
e = interpret(e, istate, goal);
}
else
error(loc, "cannot interpret declaration %s at compile time", d->toChars());
return e;
}
void visit(VarExp *e)
{
if (e->var->isFuncDeclaration())
{
result = e;
return;
}
if (goal == ctfeNeedLvalue)
{
VarDeclaration *v = e->var->isVarDeclaration();
if (v && !v->isDataseg() && !v->isCTFE() && !istate)
{
e->error("variable %s cannot be read at compile time", v->toChars());
result = CTFEExp::cantexp;
return;
}
if (v && !hasValue(v))
{
if (!v->isCTFE() && v->isDataseg())
e->error("static variable %s cannot be read at compile time", v->toChars());
else // CTFE initiated from inside a function
e->error("variable %s cannot be read at compile time", v->toChars());
result = CTFEExp::cantexp;
return;
}
if (v && (v->storage_class & (STCout | STCref)) && hasValue(v))
{
// Strip off the nest of ref variables
Expression *ev = getValue(v);
if (ev->op == TOKvar || ev->op == TOKindex || ev->op == TOKdotvar)
{
result = interpret(pue, ev, istate, goal);
return;
}
}
result = e;
return;
}
result = getVarExp(e->loc, istate, e->var, goal);
if (exceptionOrCant(result))
return;
if ((e->var->storage_class & (STCref | STCout)) == 0 &&
e->type->baseElemOf()->ty != Tstruct)
{
/* Ultimately, STCref|STCout check should be enough to see the
* necessity of type repainting. But currently front-end paints
* non-ref struct variables by the const type.
*
* auto foo(ref const S cs);
* S s;
* foo(s); // VarExp('s') will have const(S)
*/
// A VarExp may include an implicit cast. It must be done explicitly.
result = paintTypeOntoLiteral(pue, e->type, result);
}
}
void visit(DeclarationExp *e)
{
Dsymbol *s = e->declaration;
if (VarDeclaration *v = s->isVarDeclaration())
{
if (TupleDeclaration *td = v->toAlias()->isTupleDeclaration())
{
result = NULL;
// Reserve stack space for all tuple members
if (!td->objects)
return;
for (size_t i = 0; i < td->objects->length; ++i)
{
RootObject * o = (*td->objects)[i];
Expression *ex = isExpression(o);
DsymbolExp *ds = (ex && ex->op == TOKdsymbol) ? (DsymbolExp *)ex : NULL;
VarDeclaration *v2 = ds ? ds->s->isVarDeclaration() : NULL;
assert(v2);
if (v2->isDataseg() && !v2->isCTFE())
continue;
ctfeStack.push(v2);
if (v2->_init)
{
Expression *einit;
if (ExpInitializer *ie = v2->_init->isExpInitializer())
{
einit = interpret(ie->exp, istate, goal);
if (exceptionOrCant(einit))
return;
}
else if (v2->_init->isVoidInitializer())
{
einit = voidInitLiteral(v2->type, v2).copy();
}
else
{
e->error("declaration %s is not yet implemented in CTFE", e->toChars());
result = CTFEExp::cantexp;
return;
}
setValue(v2, einit);
}
}
return;
}
if (v->isStatic())
{
// Just ignore static variables which aren't read or written yet
result = NULL;
return;
}
if (!(v->isDataseg() || v->storage_class & STCmanifest) || v->isCTFE())
ctfeStack.push(v);
if (v->_init)
{
if (ExpInitializer *ie = v->_init->isExpInitializer())
{
result = interpret(ie->exp, istate, goal);
}
else if (v->_init->isVoidInitializer())
{
result = voidInitLiteral(v->type, v).copy();
// There is no AssignExp for void initializers,
// so set it here.
setValue(v, result);
}
else
{
e->error("declaration %s is not yet implemented in CTFE", e->toChars());
result = CTFEExp::cantexp;
}
}
else if (v->type->size() == 0)
{
// Zero-length arrays don't need an initializer
result = v->type->defaultInitLiteral(e->loc);
}
else
{
e->error("variable %s cannot be modified at compile time", v->toChars());
result = CTFEExp::cantexp;
}
return;
}
if (s->isAttribDeclaration() ||
s->isTemplateMixin() ||
s->isTupleDeclaration())
{
// Check for static struct declarations, which aren't executable
AttribDeclaration *ad = e->declaration->isAttribDeclaration();
if (ad && ad->decl && ad->decl->length == 1)
{
Dsymbol *sparent = (*ad->decl)[0];
if (sparent->isAggregateDeclaration() ||
sparent->isTemplateDeclaration() ||
sparent->isAliasDeclaration())
{
result = NULL;
return; // static (template) struct declaration. Nothing to do.
}
}
// These can be made to work, too lazy now
e->error("declaration %s is not yet implemented in CTFE", e->toChars());
result = CTFEExp::cantexp;
return;
}
// Others should not contain executable code, so are trivial to evaluate
result = NULL;
}
void visit(TypeidExp *e)
{
if (isType(e->obj))
{
result = e;
return;
}
if (Expression *ex = isExpression(e->obj))
{
result = interpret(pue, ex, istate);
if (exceptionOrCant(ex))
return;
if (result->op == TOKnull)
{
e->error("null pointer dereference evaluating typeid. `%s` is null", ex->toChars());
result = CTFEExp::cantexp;
return;
}
if (result->op != TOKclassreference)
{
e->error("CTFE internal error: determining classinfo");
result = CTFEExp::cantexp;
return;
}
ClassDeclaration *cd = ((ClassReferenceExp *)result)->originalClass();
assert(cd);
new(pue) TypeidExp(e->loc, cd->type);
result = pue->exp();
result->type = e->type;
return;
}
visit((Expression *)e);
}
void visit(TupleExp *e)
{
if (exceptionOrCant(interpret(e->e0, istate, ctfeNeedNothing)))
return;
Expressions *expsx = e->exps;
for (size_t i = 0; i < expsx->length; i++)
{
Expression *exp = (*expsx)[i];
Expression *ex = interpret(exp, istate);
if (exceptionOrCant(ex))
return;
// A tuple of assignments can contain void (Bug 5676).
if (goal == ctfeNeedNothing)
continue;
if (ex->op == TOKvoidexp)
{
e->error("CTFE internal error: void element %s in tuple", exp->toChars());
assert(0);
}
/* If any changes, do Copy On Write
*/
if (ex != exp)
{
expsx = copyArrayOnWrite(expsx, e->exps);
(*expsx)[i] = ex;
}
}
if (expsx != e->exps)
{
expandTuples(expsx);
new(pue) TupleExp(e->loc, expsx);
result = pue->exp();
result->type = new TypeTuple(expsx);
}
else
result = e;
}
void visit(ArrayLiteralExp *e)
{
if (e->ownedByCtfe >= OWNEDctfe) // We've already interpreted all the elements
{
result = e;
return;
}
Type *tn = e->type->toBasetype()->nextOf()->toBasetype();
bool wantCopy = (tn->ty == Tsarray || tn->ty == Tstruct);
Expression *basis = interpret(e->basis, istate);
if (exceptionOrCant(basis))
return;
Expressions *expsx = e->elements;
size_t dim = expsx ? expsx->length : 0;
for (size_t i = 0; i < dim; i++)
{
Expression *exp = (*expsx)[i];
Expression *ex;
if (!exp)
{
ex = copyLiteral(basis).copy();
}
else
{
// segfault bug 6250
assert(exp->op != TOKindex || ((IndexExp *)exp)->e1 != e);
ex = interpret(exp, istate);
if (exceptionOrCant(ex))
return;
/* Each elements should have distinct CFE memory.
* int[1] z = 7;
* int[1][] pieces = [z,z]; // here
*/
if (wantCopy)
ex = copyLiteral(ex).copy();
}
/* If any changes, do Copy On Write
*/
if (ex != exp)
{
expsx = copyArrayOnWrite(expsx, e->elements);
(*expsx)[i] = ex;
}
}
if (expsx != e->elements)
{
// todo: all tuple expansions should go in semantic phase.
expandTuples(expsx);
if (expsx->length != dim)
{
e->error("CTFE internal error: invalid array literal");
result = CTFEExp::cantexp;
return;
}
new(pue) ArrayLiteralExp(e->loc, e->type, basis, expsx);
ArrayLiteralExp *ale = (ArrayLiteralExp *)pue->exp();
ale->ownedByCtfe = OWNEDctfe;
result = ale;
}
else if (((TypeNext *)e->type)->next->mod & (MODconst | MODimmutable))
{
// If it's immutable, we don't need to dup it
result = e;
}
else
{
*pue = copyLiteral(e);
result = pue->exp();
}
}
void visit(AssocArrayLiteralExp *e)
{
if (e->ownedByCtfe >= OWNEDctfe) // We've already interpreted all the elements
{
result = e;
return;
}
Expressions *keysx = e->keys;
Expressions *valuesx = e->values;
for (size_t i = 0; i < keysx->length; i++)
{
Expression *ekey = (*keysx)[i];
Expression *evalue = (*valuesx)[i];
Expression *ek = interpret(ekey, istate);
if (exceptionOrCant(ek))
return;
Expression *ev = interpret(evalue, istate);
if (exceptionOrCant(ev))
return;
/* If any changes, do Copy On Write
*/
if (ek != ekey ||
ev != evalue)
{
keysx = copyArrayOnWrite(keysx, e->keys);
valuesx = copyArrayOnWrite(valuesx, e->values);
(*keysx)[i] = ek;
(*valuesx)[i] = ev;
}
}
if (keysx != e->keys)
expandTuples(keysx);
if (valuesx != e->values)
expandTuples(valuesx);
if (keysx->length != valuesx->length)
{
e->error("CTFE internal error: invalid AA");
result = CTFEExp::cantexp;
return;
}
/* Remove duplicate keys
*/
for (size_t i = 1; i < keysx->length; i++)
{
Expression *ekey = (*keysx)[i - 1];
for (size_t j = i; j < keysx->length; j++)
{
Expression *ekey2 = (*keysx)[j];
if (!ctfeEqual(e->loc, TOKequal, ekey, ekey2))
continue;
// Remove ekey
keysx = copyArrayOnWrite(keysx, e->keys);
valuesx = copyArrayOnWrite(valuesx, e->values);
keysx->remove(i - 1);
valuesx->remove(i - 1);
i -= 1; // redo the i'th iteration
break;
}
}
if (keysx != e->keys ||
valuesx != e->values)
{
assert(keysx != e->keys &&
valuesx != e->values);
AssocArrayLiteralExp *ae = new AssocArrayLiteralExp(e->loc, keysx, valuesx);
ae->type = e->type;
ae->ownedByCtfe = OWNEDctfe;
result = ae;
}
else
{
*pue = copyLiteral(e);
result = pue->exp();
}
}
void visit(StructLiteralExp *e)
{
if (e->ownedByCtfe >= OWNEDctfe)
{
result = e;
return;
}
size_t dim = e->elements ? e->elements->length : 0;
Expressions *expsx = e->elements;
if (dim != e->sd->fields.length)
{
// guaranteed by AggregateDeclaration.fill and TypeStruct.defaultInitLiteral
assert(e->sd->isNested() && dim == e->sd->fields.length - 1);
/* If a nested struct has no initialized hidden pointer,
* set it to null to match the runtime behaviour.
*/
NullExp *ne = new NullExp(e->loc);
ne->type = e->sd->vthis->type;
expsx = copyArrayOnWrite(expsx, e->elements);
expsx->push(ne);
++dim;
}
assert(dim == e->sd->fields.length);
for (size_t i = 0; i < dim; i++)
{
VarDeclaration *v = e->sd->fields[i];
Expression *exp = (*expsx)[i];
Expression *ex = NULL;
if (!exp)
{
ex = voidInitLiteral(v->type, v).copy();
}
else
{
ex = interpret(exp, istate);
if (exceptionOrCant(ex))
return;
if ((v->type->ty != ex->type->ty) && v->type->ty == Tsarray)
{
// Block assignment from inside struct literals
TypeSArray *tsa = (TypeSArray *)v->type;
size_t len = (size_t)tsa->dim->toInteger();
UnionExp ue;
ex = createBlockDuplicatedArrayLiteral(&ue, ex->loc, v->type, ex, len);
if (ex == ue.exp())
ex = ue.copy();
}
}
/* If any changes, do Copy On Write
*/
if (ex != exp)
{
expsx = copyArrayOnWrite(expsx, e->elements);
(*expsx)[i] = ex;
}
}
if (expsx != e->elements)
{
expandTuples(expsx);
if (expsx->length != e->sd->fields.length)
{
e->error("CTFE internal error: invalid struct literal");
result = CTFEExp::cantexp;
return;
}
new(pue) StructLiteralExp(e->loc, e->sd, expsx);
StructLiteralExp *sle = (StructLiteralExp *)pue->exp();
sle->type = e->type;
sle->ownedByCtfe = OWNEDctfe;
sle->origin = e->origin;
result = sle;
}
else
{
*pue = copyLiteral(e);
result = pue->exp();
}
}
// Create an array literal of type 'newtype' with dimensions given by
// 'arguments'[argnum..$]
static Expression *recursivelyCreateArrayLiteral(UnionExp *pue, Loc loc, Type *newtype, InterState *istate,
Expressions *arguments, int argnum)
{
Expression *lenExpr = interpret(pue, (*arguments)[argnum], istate);
if (exceptionOrCantInterpret(lenExpr))
return lenExpr;
size_t len = (size_t)(lenExpr->toInteger());
Type *elemType = ((TypeArray *)newtype)->next;
if (elemType->ty == Tarray && argnum < (int)arguments->length - 1)
{
Expression *elem = recursivelyCreateArrayLiteral(pue, loc, elemType, istate,
arguments, argnum + 1);
if (exceptionOrCantInterpret(elem))
return elem;
Expressions *elements = new Expressions();
elements->setDim(len);
for (size_t i = 0; i < len; i++)
(*elements)[i] = copyLiteral(elem).copy();
new(pue) ArrayLiteralExp(loc, newtype, elements);
ArrayLiteralExp *ae = (ArrayLiteralExp *)pue->exp();
ae->ownedByCtfe = OWNEDctfe;
return ae;
}
assert(argnum == (int)arguments->length - 1);
if (elemType->ty == Tchar || elemType->ty == Twchar || elemType->ty == Tdchar)
{
const unsigned ch = (unsigned)elemType->defaultInitLiteral(loc)->toInteger();
const unsigned char sz = (unsigned char)elemType->size();
return createBlockDuplicatedStringLiteral(pue, loc, newtype, ch, len, sz);
}
else
{
Expression *el = interpret(elemType->defaultInitLiteral(loc), istate);
return createBlockDuplicatedArrayLiteral(pue, loc, newtype, el, len);
}
}
void visit(NewExp *e)
{
if (e->allocator)
{
e->error("member allocators not supported by CTFE");
result = CTFEExp::cantexp;
return;
}
Expression *epre = interpret(pue, e->argprefix, istate, ctfeNeedNothing);
if (exceptionOrCant(epre))
return;
if (e->newtype->ty == Tarray && e->arguments)
{
result = recursivelyCreateArrayLiteral(pue, e->loc, e->newtype, istate, e->arguments, 0);
return;
}
if (e->newtype->toBasetype()->ty == Tstruct)
{
if (e->member)
{
Expression *se = e->newtype->defaultInitLiteral(e->loc);
se = interpret(se, istate);
if (exceptionOrCant(se))
return;
result = interpretFunction(pue, e->member, istate, e->arguments, se);
// Repaint as same as CallExp::interpret() does.
result->loc = e->loc;
}
else
{
StructDeclaration *sd = ((TypeStruct *)e->newtype->toBasetype())->sym;
Expressions *exps = new Expressions();
exps->reserve(sd->fields.length);
if (e->arguments)
{
exps->setDim(e->arguments->length);
for (size_t i = 0; i < exps->length; i++)
{
Expression *ex = (*e->arguments)[i];
ex = interpret(ex, istate);
if (exceptionOrCant(ex))
return;
(*exps)[i] = ex;
}
}
sd->fill(e->loc, exps, false);
StructLiteralExp *se = new StructLiteralExp(e->loc, sd, exps, e->newtype);
se->type = e->newtype;
se->ownedByCtfe = OWNEDctfe;
result = interpret(pue, se, istate);
}
if (exceptionOrCant(result))
return;
Expression *ev = (result == pue->exp()) ? pue->copy() : result;
new(pue) AddrExp(e->loc, ev, e->type);
result = pue->exp();
return;
}
if (e->newtype->toBasetype()->ty == Tclass)
{
ClassDeclaration *cd = ((TypeClass *)e->newtype->toBasetype())->sym;
size_t totalFieldCount = 0;
for (ClassDeclaration *c = cd; c; c = c->baseClass)
totalFieldCount += c->fields.length;
Expressions *elems = new Expressions;
elems->setDim(totalFieldCount);
size_t fieldsSoFar = totalFieldCount;
for (ClassDeclaration *c = cd; c; c = c->baseClass)
{
fieldsSoFar -= c->fields.length;
for (size_t i = 0; i < c->fields.length; i++)
{
VarDeclaration *v = c->fields[i];
if (v->inuse)
{
e->error("circular reference to `%s`", v->toPrettyChars());
result = CTFEExp::cantexp;
return;
}
Expression *m;
if (v->_init)
{
if (v->_init->isVoidInitializer())
m = voidInitLiteral(v->type, v).copy();
else
m = v->getConstInitializer(true);
}
else
m = v->type->defaultInitLiteral(e->loc);
if (exceptionOrCant(m))
return;
(*elems)[fieldsSoFar+i] = copyLiteral(m).copy();
}
}
// Hack: we store a ClassDeclaration instead of a StructDeclaration.
// We probably won't get away with this.
StructLiteralExp *se = new StructLiteralExp(e->loc, (StructDeclaration *)cd, elems, e->newtype);
se->ownedByCtfe = OWNEDctfe;
new(pue) ClassReferenceExp(e->loc, se, e->type);
Expression *eref = pue->exp();
if (e->member)
{
// Call constructor
if (!e->member->fbody)
{
Expression *ctorfail = evaluateIfBuiltin(pue, istate, e->loc, e->member, e->arguments, eref);
if (ctorfail)
{
if (exceptionOrCant(ctorfail))
return;
result = eref;
return;
}
e->member->error("%s cannot be constructed at compile time, because the constructor has no available source code", e->newtype->toChars());
result = CTFEExp::cantexp;
return;
}
UnionExp ue;
Expression *ctorfail = interpretFunction(&ue, e->member, istate, e->arguments, eref);
if (exceptionOrCant(ctorfail))
return;
/* Bugzilla 14465: Repaint the loc, because a super() call
* in the constructor modifies the loc of ClassReferenceExp
* in CallExp::interpret().
*/
eref->loc = e->loc;
}
result = eref;
return;
}
if (e->newtype->toBasetype()->isscalar())
{
Expression *newval;
if (e->arguments && e->arguments->length)
newval = (*e->arguments)[0];
else
newval = e->newtype->defaultInitLiteral(e->loc);
newval = interpret(newval, istate);
if (exceptionOrCant(newval))
return;
// Create a CTFE pointer &[newval][0]
Expressions *elements = new Expressions();
elements->setDim(1);
(*elements)[0] = newval;
ArrayLiteralExp *ae = new ArrayLiteralExp(e->loc, e->newtype->arrayOf(), elements);
ae->ownedByCtfe = OWNEDctfe;
IndexExp *ei = new IndexExp(e->loc, ae, new IntegerExp(Loc(), 0, Type::tsize_t));
ei->type = e->newtype;
new(pue) AddrExp(e->loc, ei, e->type);
result = pue->exp();
return;
}
e->error("cannot interpret %s at compile time", e->toChars());
result = CTFEExp::cantexp;
}
void visit(UnaExp *e)
{
UnionExp ue;
Expression *e1 = interpret(&ue, e->e1, istate);
if (exceptionOrCant(e1))
return;
switch (e->op)
{
case TOKneg: *pue = Neg(e->type, e1); break;
case TOKtilde: *pue = Com(e->type, e1); break;
case TOKnot: *pue = Not(e->type, e1); break;
default: assert(0);
}
result = (*pue).exp();
}
void visit(DotTypeExp *e)
{
UnionExp ue;
Expression *e1 = interpret(&ue, e->e1, istate);
if (exceptionOrCant(e1))
return;
if (e1 == e->e1)
result = e; // optimize: reuse this CTFE reference
else
{
DotTypeExp *edt = (DotTypeExp *)e->copy();
edt->e1 = (e1 == ue.exp()) ? e1->copy() : e1; // don't return pointer to ue
result = edt;
}
}
bool evalOperand(UnionExp *pue, Expression *e, Expression *ex, Expression *&er)
{
er = interpret(pue, ex, istate);
if (exceptionOrCant(er))
return false;
if (er->isConst() != 1)
{
if (er->op == TOKarrayliteral)
// Until we get it to work, issue a reasonable error message
e->error("cannot interpret array literal expression %s at compile time", e->toChars());
else
e->error("CTFE internal error: non-constant value %s", ex->toChars());
result = CTFEExp::cantexp;
return false;
}
return true;
}
void interpretCommon(BinExp *e, fp_t fp)
{
if (e->e1->type->ty == Tpointer && e->e2->type->ty == Tpointer && e->op == TOKmin)
{
UnionExp ue1;
Expression *e1 = interpret(&ue1, e->e1, istate);
if (exceptionOrCant(e1))
return;
UnionExp ue2;
Expression *e2 = interpret(&ue2, e->e2, istate);
if (exceptionOrCant(e2))
return;
*pue = pointerDifference(e->loc, e->type, e1, e2);
result = (*pue).exp();
return;
}
if (e->e1->type->ty == Tpointer && e->e2->type->isintegral())
{
UnionExp ue1;
Expression *e1 = interpret(&ue1, e->e1, istate);
if (exceptionOrCant(e1))
return;
UnionExp ue2;
Expression *e2 = interpret(&ue2, e->e2, istate);
if (exceptionOrCant(e2))
return;
*pue = pointerArithmetic(e->loc, e->op, e->type, e1, e2);
result = (*pue).exp();
return;
}
if (e->e2->type->ty == Tpointer && e->e1->type->isintegral() && e->op == TOKadd)
{
UnionExp ue1;
Expression *e1 = interpret(&ue1, e->e1, istate);
if (exceptionOrCant(e1))
return;
UnionExp ue2;
Expression *e2 = interpret(&ue2, e->e2, istate);
if (exceptionOrCant(e2))
return;
*pue = pointerArithmetic(e->loc, e->op, e->type, e2, e1);
result = (*pue).exp();
return;
}
if (e->e1->type->ty == Tpointer || e->e2->type->ty == Tpointer)
{
e->error("pointer expression %s cannot be interpreted at compile time", e->toChars());
result = CTFEExp::cantexp;
return;
}
UnionExp ue1;
Expression *e1;
if (!evalOperand(&ue1, e, e->e1, e1))
return;
UnionExp ue2;
Expression *e2;
if (!evalOperand(&ue2, e, e->e2, e2))
return;
if (e->op == TOKshr || e->op == TOKshl || e->op == TOKushr)
{
const sinteger_t i2 = e2->toInteger();
const d_uns64 sz = e1->type->size() * 8;
if (i2 < 0 || (d_uns64)i2 >= sz)
{
e->error("shift by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1);
result = CTFEExp::cantexp;
return;
}
}
*pue = (*fp)(e->loc, e->type, e1, e2);
result = (*pue).exp();
if (CTFEExp::isCantExp(result))
e->error("%s cannot be interpreted at compile time", e->toChars());
}
void interpretCompareCommon(BinExp *e, fp2_t fp)
{
UnionExp ue1;
UnionExp ue2;
if (e->e1->type->ty == Tpointer && e->e2->type->ty == Tpointer)
{
Expression *e1 = interpret(&ue1, e->e1, istate);
if (exceptionOrCant(e1))
return;
Expression *e2 = interpret(&ue2, e->e2, istate);
if (exceptionOrCant(e2))
return;
//printf("e1 = %s %s, e2 = %s %s\n", e1->type->toChars(), e1->toChars(), e2->type->toChars(), e2->toChars());
dinteger_t ofs1, ofs2;
Expression *agg1 = getAggregateFromPointer(e1, &ofs1);
Expression *agg2 = getAggregateFromPointer(e2, &ofs2);
//printf("agg1 = %p %s, agg2 = %p %s\n", agg1, agg1->toChars(), agg2, agg2->toChars());
const int cmp = comparePointers(e->op, agg1, ofs1, agg2, ofs2);
if (cmp == -1)
{
char dir = (e->op == TOKgt || e->op == TOKge) ? '<' : '>';
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;
}
new(pue) IntegerExp(e->loc, cmp, e->type);
result = (*pue).exp();
return;
}
Expression *e1 = interpret(&ue1, e->e1, istate);
if (exceptionOrCant(e1))
return;
if (!isCtfeComparable(e1))
{
e->error("cannot compare %s at compile time", e1->toChars());
result = CTFEExp::cantexp;
return;
}
Expression *e2 = interpret(&ue2, e->e2, istate);
if (exceptionOrCant(e2))
return;
if (!isCtfeComparable(e2))
{
e->error("cannot compare %s at compile time", e2->toChars());
result = CTFEExp::cantexp;
return;
}
const int cmp = (*fp)(e->loc, e->op, e1, e2);
new(pue) IntegerExp(e->loc, cmp, e->type);
result = (*pue).exp();
}
void visit(BinExp *e)
{
switch (e->op)
{
case TOKadd: interpretCommon(e, &Add); return;
case TOKmin: interpretCommon(e, &Min); return;
case TOKmul: interpretCommon(e, &Mul); return;
case TOKdiv: interpretCommon(e, &Div); return;
case TOKmod: interpretCommon(e, &Mod); return;
case TOKshl: interpretCommon(e, &Shl); return;
case TOKshr: interpretCommon(e, &Shr); return;
case TOKushr: interpretCommon(e, &Ushr); return;
case TOKand: interpretCommon(e, &And); return;
case TOKor: interpretCommon(e, &Or); return;
case TOKxor: interpretCommon(e, &Xor); return;
case TOKpow: interpretCommon(e, &Pow); return;
case TOKequal:
case TOKnotequal:
interpretCompareCommon(e, &ctfeEqual);
return;
case TOKidentity:
case TOKnotidentity:
interpretCompareCommon(e, &ctfeIdentity);
return;
case TOKlt:
case TOKle:
case TOKgt:
case TOKge:
interpretCompareCommon(e, &ctfeCmp);
return;
default:
printf("be = '%s' %s at [%s]\n", Token::toChars(e->op), e->toChars(), e->loc.toChars());
assert(0);
return;
}
}
/* Helper functions for BinExp::interpretAssignCommon
*/
// Returns the variable which is eventually modified, or NULL if an rvalue.
// thisval is the current value of 'this'.
static VarDeclaration *findParentVar(Expression *e)
{
for (;;)
{
if (e->op == TOKvar)
break;
if (e->op == TOKindex)
e = ((IndexExp *)e)->e1;
else if (e->op == TOKdotvar)
e = ((DotVarExp *)e)->e1;
else if (e->op == TOKdotti)
e = ((DotTemplateInstanceExp *)e)->e1;
else if (e->op == TOKslice)
e = ((SliceExp *)e)->e1;
else
return NULL;
}
VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration();
assert(v);
return v;
}
void interpretAssignCommon(BinExp *e, fp_t fp, int post = 0)
{
result = CTFEExp::cantexp;
Expression *e1 = e->e1;
if (!istate)
{
e->error("value of %s is not known at compile time", e1->toChars());
return;
}
++CtfeStatus::numAssignments;
/* Before we begin, we need to know if this is a reference assignment
* (dynamic array, AA, or class) or a value assignment.
* Determining this for slice assignments are tricky: we need to know
* if it is a block assignment (a[] = e) rather than a direct slice
* assignment (a[] = b[]). Note that initializers of multi-dimensional
* static arrays can have 2D block assignments (eg, int[7][7] x = 6;).
* So we need to recurse to determine if it is a block assignment.
*/
bool isBlockAssignment = false;
if (e1->op == TOKslice)
{
// a[] = e can have const e. So we compare the naked types.
Type *tdst = e1->type->toBasetype();
Type *tsrc = e->e2->type->toBasetype();
while (tdst->ty == Tsarray || tdst->ty == Tarray)
{
tdst = ((TypeArray *)tdst)->next->toBasetype();
if (tsrc->equivalent(tdst))
{
isBlockAssignment = true;
break;
}
}
}
// ---------------------------------------
// Deal with reference assignment
// ---------------------------------------
// If it is a construction of a ref variable, it is a ref assignment
if ((e->op == TOKconstruct || e->op == TOKblit) &&
(((AssignExp *)e)->memset & referenceInit))
{
assert(!fp);
Expression *newval = interpret(e->e2, istate, ctfeNeedLvalue);
if (exceptionOrCant(newval))
return;
VarDeclaration *v = ((VarExp *)e1)->var->isVarDeclaration();
setValue(v, newval);
// Get the value to return. Note that 'newval' is an Lvalue,
// so if we need an Rvalue, we have to interpret again.
if (goal == ctfeNeedRvalue)
result = interpret(newval, istate);
else
result = e1; // VarExp is a CTFE reference
return;
}
if (fp)
{
while (e1->op == TOKcast)
{
CastExp *ce = (CastExp *)e1;
e1 = ce->e1;
}
}
// ---------------------------------------
// Interpret left hand side
// ---------------------------------------
AssocArrayLiteralExp *existingAA = NULL;
Expression *lastIndex = NULL;
Expression *oldval = NULL;
if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)
{
// ---------------------------------------
// Deal with AA index assignment
// ---------------------------------------
/* This needs special treatment if the AA doesn't exist yet.
* There are two special cases:
* (1) If the AA is itself an index of another AA, we may need to create
* multiple nested AA literals before we can insert the new value.
* (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 = (IndexExp *)e1;
int depth = 0; // how many nested AA indices are there?
while (ie->e1->op == TOKindex &&
((IndexExp *)ie->e1)->e1->type->toBasetype()->ty == Taarray)
{
assert(ie->modifiable);
ie = (IndexExp *)ie->e1;
++depth;
}
// Get the AA value to be modified.
Expression *aggregate = interpret(ie->e1, istate);
if (exceptionOrCant(aggregate))
return;
if (aggregate->op == TOKassocarrayliteral)
{
existingAA = (AssocArrayLiteralExp *)aggregate;
// 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 = interpret(((IndexExp *)e1)->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 = (IndexExp *)e1;
for (int d= 0; d < depth; ++d)
xe = (IndexExp *)xe->e1;
Expression *ekey = interpret(xe->e2, istate);
if (exceptionOrCant(ekey))
return;
UnionExp ekeyTmp;
ekey = resolveSlice(ekey, &ekeyTmp); // only happens with AA assignment
// Look up this index in it up in the existing AA, to get the next level of AA.
AssocArrayLiteralExp *newAA = (AssocArrayLiteralExp *)findKeyInAA(e->loc, existingAA, ekey);
if (exceptionOrCant(newAA))
return;
if (!newAA)
{
// Doesn't exist yet, create an empty AA...
Expressions *keysx = new Expressions();
Expressions *valuesx = new Expressions();
newAA = new AssocArrayLiteralExp(e->loc, keysx, valuesx);
newAA->type = xe->type;
newAA->ownedByCtfe = OWNEDctfe;
//... and insert it into the existing AA.
existingAA->keys->push(ekey);
existingAA->values->push(newAA);
}
existingAA = newAA;
--depth;
}
if (fp)
{
oldval = findKeyInAA(e->loc, existingAA, lastIndex);
if (!oldval)
oldval = copyLiteral(e->e1->type->defaultInitLiteral(e->loc)).copy();
}
}
else
{
/* The AA is currently null. 'aggregate' is actually a reference to
* whatever contains it. It could be anything: var, dotvarexp, ...
* We rewrite the assignment from:
* aa[i][j] op= newval;
* into:
* aa = [i:[j:T.init]];
* aa[j] op= newval;
*/
oldval = copyLiteral(e->e1->type->defaultInitLiteral(e->loc)).copy();
Expression *newaae = oldval;
while (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)
{
Expression *ekey = interpret(((IndexExp *)e1)->e2, istate);
if (exceptionOrCant(ekey))
return;
ekey = resolveSlice(ekey); // only happens with AA assignment
Expressions *keysx = new Expressions();
Expressions *valuesx = new Expressions();
keysx->push(ekey);
valuesx->push(newaae);
AssocArrayLiteralExp *aae = new AssocArrayLiteralExp(e->loc, keysx, valuesx);
aae->type = ((IndexExp *)e1)->e1->type;
aae->ownedByCtfe = OWNEDctfe;
if (!existingAA)
{
existingAA = aae;
lastIndex = ekey;
}
newaae = aae;
e1 = ((IndexExp *)e1)->e1;
}
// We must set to aggregate with newaae
e1 = interpret(e1, istate, ctfeNeedLvalue);
if (exceptionOrCant(e1))
return;
e1 = assignToLvalue(e, e1, newaae);
if (exceptionOrCant(e1))
return;
}
assert(existingAA && lastIndex);
e1 = NULL; // stomp
}
else if (e1->op == TOKarraylength)
{
oldval = interpret(e1, istate);
if (exceptionOrCant(oldval))
return;
}
else if (e->op == TOKconstruct || e->op == TOKblit)
{
// Unless we have a simple var assignment, we're
// only modifying part of the variable. So we need to make sure
// that the parent variable exists.
VarDeclaration *ultimateVar = findParentVar(e1);
if (e1->op == TOKvar)
{
VarDeclaration *v = ((VarExp *)e1)->var->isVarDeclaration();
assert(v);
if (v->storage_class & STCout)
goto L1;
}
else if (ultimateVar && !getValue(ultimateVar))
{
Expression *ex = interpret(ultimateVar->type->defaultInitLiteral(e->loc), istate);
if (exceptionOrCant(ex))
return;
setValue(ultimateVar, ex);
}
else
goto L1;
}
else
{
L1:
e1 = interpret(e1, istate, ctfeNeedLvalue);
if (exceptionOrCant(e1))
return;
if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)
{
IndexExp *ie = (IndexExp *)e1;
assert(ie->e1->op == TOKassocarrayliteral);
existingAA = (AssocArrayLiteralExp *)ie->e1;
lastIndex = ie->e2;
}
}
// ---------------------------------------
// Interpret right hand side
// ---------------------------------------
Expression *newval = interpret(e->e2, istate);
if (exceptionOrCant(newval))
return;
if (e->op == TOKblit && newval->op == TOKint64)
{
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 == TOKerror)
{
result = CTFEExp::cantexp;
return;
}
newval = interpret(newval, istate); // copy and set ownedByCtfe flag
if (exceptionOrCant(newval))
return;
}
}
// ----------------------------------------------------
// Deal with read-modify-write assignments.
// Set 'newval' to the final assignment value
// Also determine the return value (except for slice
// assignments, which are more complicated)
// ----------------------------------------------------
if (fp)
{
if (!oldval)
{
// Load the left hand side after interpreting the right hand side.
oldval = interpret(e1, istate);
if (exceptionOrCant(oldval))
return;
}
if (e->e1->type->ty != Tpointer)
{
// ~= can create new values (see bug 6052)
if (e->op == TOKcatass)
{
// We need to dup it and repaint the type. For a dynamic array
// we can skip duplication, because it gets copied later anyway.
if (newval->type->ty != Tarray)
{
newval = copyLiteral(newval).copy();
newval->type = e->e2->type; // repaint type
}
else
{
newval = paintTypeOntoLiteral(e->e2->type, newval);
newval = resolveSlice(newval);
}
}
oldval = resolveSlice(oldval);
newval = (*fp)(e->loc, e->type, oldval, newval).copy();
}
else if (e->e2->type->isintegral() &&
(e->op == TOKaddass ||
e->op == TOKminass ||
e->op == TOKplusplus ||
e->op == TOKminusminus))
{
newval = pointerArithmetic(e->loc, e->op, e->type, oldval, newval).copy();
}
else
{
e->error("pointer expression %s cannot be interpreted at compile time", e->toChars());
result = CTFEExp::cantexp;
return;
}
if (exceptionOrCant(newval))
{
if (CTFEExp::isCantExp(newval))
e->error("cannot interpret %s at compile time", e->toChars());
return;
}
}
if (existingAA)
{
if (existingAA->ownedByCtfe != OWNEDctfe)
{
e->error("cannot modify read-only constant %s", existingAA->toChars());
result = CTFEExp::cantexp;
return;
}
//printf("\t+L%d existingAA = %s, lastIndex = %s, oldval = %s, newval = %s\n",
// __LINE__, existingAA->toChars(), lastIndex->toChars(), oldval ? oldval->toChars() : NULL, newval->toChars());
assignAssocArrayElement(e->loc, existingAA, lastIndex, newval);
// Determine the return value
result = ctfeCast(pue, e->loc, e->type, e->type, fp && post ? oldval : newval);
return;
}
if (e1->op == TOKarraylength)
{
/* Change the assignment from:
* arr.length = n;
* into:
* arr = new_length_array; (result is n)
*/
// Determine the return value
result = ctfeCast(pue, e->loc, e->type, e->type, fp && post ? oldval : newval);
if (exceptionOrCant(result))
return;
if (result == pue->exp())
result = pue->copy();
size_t oldlen = (size_t)oldval->toInteger();
size_t newlen = (size_t)newval->toInteger();
if (oldlen == newlen) // no change required -- we're done!
return;
// We have changed it into a reference assignment
// Note that returnValue is still the new length.
e1 = ((ArrayLengthExp *)e1)->e1;
Type *t = e1->type->toBasetype();
if (t->ty != Tarray)
{
e->error("%s is not yet supported at compile time", e->toChars());
result = CTFEExp::cantexp;
return;
}
e1 = interpret(e1, istate, ctfeNeedLvalue);
if (exceptionOrCant(e1))
return;
if (oldlen != 0) // Get the old array literal.
oldval = interpret(e1, istate);
newval = changeArrayLiteralLength(e->loc, (TypeArray *)t, oldval,
oldlen, newlen).copy();
e1 = assignToLvalue(e, e1, newval);
if (exceptionOrCant(e1))
return;
return;
}
if (!isBlockAssignment)
{
newval = ctfeCast(pue, e->loc, e->type, e->type, newval);
if (exceptionOrCant(newval))
return;
if (newval == pue->exp())
newval = pue->copy();
// Determine the return value
if (goal == ctfeNeedLvalue) // Bugzilla 14371
result = e1;
else
{
result = ctfeCast(pue, e->loc, e->type, e->type, fp && post ? oldval : newval);
if (result == pue->exp())
result = pue->copy();
}
if (exceptionOrCant(result))
return;
}
if (exceptionOrCant(newval))
return;
/* Block assignment or element-wise assignment.
*/
if (e1->op == TOKslice ||
e1->op == TOKvector ||
e1->op == TOKarrayliteral ||
e1->op == TOKstring ||
(e1->op == TOKnull && e1->type->toBasetype()->ty == Tarray))
{
// Note that slice assignments don't support things like ++, so
// we don't need to remember 'returnValue'.
result = interpretAssignToSlice(pue, e, e1, newval, isBlockAssignment);
if (exceptionOrCant(result))
return;
if (e->e1->op == TOKslice)
{
Expression *e1x = interpret(((SliceExp*)e->e1)->e1, istate, ctfeNeedLvalue);
if (e1x->op == TOKdotvar)
{
DotVarExp *dve = (DotVarExp*)e1x;
Expression *ex = dve->e1;
StructLiteralExp *sle = ex->op == TOKstructliteral ? ((StructLiteralExp *)ex)
: ex->op == TOKclassreference ? ((ClassReferenceExp *)ex)->value
: NULL;
VarDeclaration *v = dve->var->isVarDeclaration();
if (!sle || !v)
{
e->error("CTFE internal error: dotvar slice assignment");
result = CTFEExp::cantexp;
return;
}
stompOverlappedFields(sle, v);
}
}
return;
}
assert(result);
/* Assignment to a CTFE reference.
*/
if (Expression *ex = assignToLvalue(e, e1, newval))
result = ex;
return;
}
/* Set all sibling fields which overlap with v to VoidExp.
*/
void stompOverlappedFields(StructLiteralExp *sle, VarDeclaration *v)
{
if (!v->overlapped)
return;
for (size_t i = 0; i < sle->sd->fields.length; i++)
{
VarDeclaration *v2 = sle->sd->fields[i];
if (v == v2 || !v->isOverlappedWith(v2))
continue;
Expression *e = (*sle->elements)[i];
if (e->op != TOKvoid)
(*sle->elements)[i] = voidInitLiteral(e->type, v).copy();
}
}
Expression *assignToLvalue(BinExp *e, Expression *e1, Expression *newval)
{
VarDeclaration *vd = NULL;
Expression **payload = NULL; // dead-store to prevent spurious warning
Expression *oldval;
if (e1->op == TOKvar)
{
vd = ((VarExp *)e1)->var->isVarDeclaration();
oldval = getValue(vd);
}
else if (e1->op == TOKdotvar)
{
/* Assignment to member variable of the form:
* e.v = newval
*/
Expression *ex = ((DotVarExp *)e1)->e1;
StructLiteralExp *sle =
ex->op == TOKstructliteral ? ((StructLiteralExp *)ex):
ex->op == TOKclassreference ? ((ClassReferenceExp *)ex)->value
: NULL;
VarDeclaration *v = ((DotVarExp *)e1)->var->isVarDeclaration();
if (!sle || !v)
{
e->error("CTFE internal error: dotvar assignment");
return CTFEExp::cantexp;
}
if (sle->ownedByCtfe != OWNEDctfe)
{
e->error("cannot modify read-only constant %s", sle->toChars());
return CTFEExp::cantexp;
}
int fieldi = ex->op == TOKstructliteral
? findFieldIndexByName(sle->sd, v)
: ((ClassReferenceExp *)ex)->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 < (int)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;
}
else if (e1->op == TOKindex)
{
IndexExp *ie = (IndexExp *)e1;
assert(ie->e1->type->toBasetype()->ty != Taarray);
Expression *aggregate;
uinteger_t indexToModify;
if (!resolveIndexing(ie, istate, &aggregate, &indexToModify, true))
{
return CTFEExp::cantexp;
}
size_t index = (size_t)indexToModify;
if (aggregate->op == TOKstring)
{
StringExp *existingSE = (StringExp *)aggregate;
if (existingSE->ownedByCtfe != OWNEDctfe)
{
e->error("cannot modify read-only string literal %s", ie->e1->toChars());
return CTFEExp::cantexp;
}
void *s = existingSE->string;
dinteger_t value = newval->toInteger();
switch (existingSE->sz)
{
case 1: (( utf8_t *)s)[index] = ( utf8_t)value; break;
case 2: ((utf16_t *)s)[index] = (utf16_t)value; break;
case 4: ((utf32_t *)s)[index] = (utf32_t)value; break;
default: assert(0); break;
}
return NULL;
}
if (aggregate->op != TOKarrayliteral)
{
e->error("index assignment %s is not yet supported in CTFE ", e->toChars());
return CTFEExp::cantexp;
}
ArrayLiteralExp *existingAE = (ArrayLiteralExp *)aggregate;
if (existingAE->ownedByCtfe != OWNEDctfe)
{
e->error("cannot modify read-only constant %s", existingAE->toChars());
return CTFEExp::cantexp;
}
payload = &(*existingAE->elements)[index];
oldval = *payload;
}
else
{
e->error("%s cannot be evaluated at compile time", e->toChars());
return CTFEExp::cantexp;
}
Type *t1b = e1->type->toBasetype();
bool wantCopy = t1b->baseElemOf()->ty == Tstruct;
if (newval->op == TOKstructliteral && oldval)
{
newval = copyLiteral(newval).copy();
assignInPlace(oldval, newval);
}
else if (wantCopy && e->op == TOKassign)
{
// Currently postblit/destructor calls on static array are done
// in the druntime internal functions so they don't appear in AST.
// Therefore interpreter should handle them specially.
assert(oldval);
#if 1 // todo: instead we can directly access to each elements of the slice
newval = resolveSlice(newval);
if (CTFEExp::isCantExp(newval))
{
e->error("CTFE internal error: assignment %s", e->toChars());
return CTFEExp::cantexp;
}
#endif
assert(oldval->op == TOKarrayliteral);
assert(newval->op == TOKarrayliteral);
Expressions *oldelems = ((ArrayLiteralExp *)oldval)->elements;
Expressions *newelems = ((ArrayLiteralExp *)newval)->elements;
assert(oldelems->length == newelems->length);
Type *elemtype = oldval->type->nextOf();
for (size_t i = 0; i < newelems->length; i++)
{
Expression *oldelem = (*oldelems)[i];
Expression *newelem = paintTypeOntoLiteral(elemtype, (*newelems)[i]);
// Bugzilla 9245
if (e->e2->isLvalue())
{
if (Expression *ex = evaluatePostblit(istate, newelem))
return ex;
}
// Bugzilla 13661
if (Expression *ex = evaluateDtor(istate, oldelem))
return ex;
(*oldelems)[i] = newelem;
}
}
else
{
// e1 has its own payload, so we have to create a new literal.
if (wantCopy)
newval = copyLiteral(newval).copy();
if (t1b->ty == Tsarray && e->op == TOKconstruct && e->e2->isLvalue())
{
// Bugzilla 9245
if (Expression *ex = evaluatePostblit(istate, newval))
return ex;
}
oldval = newval;
}
if (vd)
setValue(vd, oldval);
else
*payload = oldval;
// Blit assignment should return the newly created value.
if (e->op == TOKblit)
return oldval;
return NULL;
}
/*************
* Deal with assignments of the form:
* dest[] = newval
* dest[low..upp] = newval
* where newval has already been interpreted
*
* This could be a slice assignment or a block assignment, and
* dest could be either an array literal, or a string.
*
* Returns TOKcantexp on failure. If there are no errors,
* it returns aggregate[low..upp], except that as an optimisation,
* if goal == ctfeNeedNothing, it will return NULL
*/
Expression *interpretAssignToSlice(UnionExp *pue, BinExp *e,
Expression *e1, Expression *newval, bool isBlockAssignment)
{
dinteger_t lowerbound;
dinteger_t upperbound;
Expression *aggregate;
dinteger_t firstIndex;
if (e1->op == TOKslice)
{
// ------------------------------
// aggregate[] = newval
// aggregate[low..upp] = newval
// ------------------------------
SliceExp *se = (SliceExp *)e1;
#if 1 // should be move in interpretAssignCommon as the evaluation of e1
Expression *oldval = interpret(se->e1, istate);
// Set the $ variable
uinteger_t dollar = resolveArrayLength(oldval);
if (se->lengthVar)
{
Expression *dollarExp = new IntegerExp(e1->loc, dollar, Type::tsize_t);
ctfeStack.push(se->lengthVar);
setValue(se->lengthVar, dollarExp);
}
Expression *lwr = interpret(se->lwr, istate);
if (exceptionOrCantInterpret(lwr))
{
if (se->lengthVar)
ctfeStack.pop(se->lengthVar);
return lwr;
}
Expression *upr = interpret(se->upr, istate);
if (exceptionOrCantInterpret(upr))
{
if (se->lengthVar)
ctfeStack.pop(se->lengthVar);
return upr;
}
if (se->lengthVar)
ctfeStack.pop(se->lengthVar); // $ is defined only in [L..U]
unsigned dim = (unsigned)dollar;
lowerbound = (int)(lwr ? lwr->toInteger() : 0);
upperbound = (size_t)(upr ? upr->toInteger() : dim);
if ((int)lowerbound < 0 || dim < upperbound)
{
e->error("array bounds [0..%d] exceeded in slice [%d..%d]",
dim, lowerbound, upperbound);
return CTFEExp::cantexp;
}
#endif
aggregate = oldval;
firstIndex = lowerbound;
if (aggregate->op == TOKslice)
{
// Slice of a slice --> change the bounds
SliceExp *oldse = (SliceExp *)aggregate;
if (oldse->upr->toInteger() < upperbound + oldse->lwr->toInteger())
{
e->error("slice [%d..%d] exceeds array bounds [0..%lld]",
lowerbound, upperbound,
oldse->upr->toInteger() - oldse->lwr->toInteger());
return CTFEExp::cantexp;
}
aggregate = oldse->e1;
firstIndex = lowerbound + oldse->lwr->toInteger();
}
}
else
{
if (e1->op == TOKarrayliteral)
{
lowerbound = 0;
upperbound = ((ArrayLiteralExp *)e1)->elements->length;
}
else if (e1->op == TOKstring)
{
lowerbound = 0;
upperbound = ((StringExp *)e1)->len;
}
else if (e1->op == TOKnull)
{
lowerbound = 0;
upperbound = 0;
}
else
assert(0);
aggregate = e1;
firstIndex = lowerbound;
}
if (upperbound == lowerbound)
return newval;
// For slice assignment, we check that the lengths match.
if (!isBlockAssignment)
{
size_t srclen = (size_t)resolveArrayLength(newval);
if (srclen != (upperbound - lowerbound))
{
e->error("array length mismatch assigning [0..%d] to [%d..%d]",
srclen, lowerbound, upperbound);
return CTFEExp::cantexp;
}
}
if (aggregate->op == TOKstring)
{
StringExp *existingSE = (StringExp *)aggregate;
if (existingSE->ownedByCtfe != OWNEDctfe)
{
e->error("cannot modify read-only string literal %s", existingSE->toChars());
return CTFEExp::cantexp;
}
if (newval->op == TOKslice)
{
SliceExp *se = (SliceExp *)newval;
Expression *aggr2 = se->e1;
const dinteger_t srclower = se->lwr->toInteger();
const dinteger_t srcupper = se->upr->toInteger();
if (aggregate == aggr2 &&
lowerbound < srcupper && srclower < upperbound)
{
e->error("overlapping slice assignment [%d..%d] = [%llu..%llu]",
lowerbound, upperbound, srclower, srcupper);
return CTFEExp::cantexp;
}
#if 1 // todo: instead we can directly access to each elements of the slice
Expression *orignewval = newval;
newval = resolveSlice(newval);
if (CTFEExp::isCantExp(newval))
{
e->error("CTFE internal error: slice %s", orignewval->toChars());
return CTFEExp::cantexp;
}
#endif
assert(newval->op != TOKslice);
}
if (newval->op == TOKstring)
{
sliceAssignStringFromString((StringExp *)existingSE, (StringExp *)newval, (size_t)firstIndex);
return newval;
}
if (newval->op == TOKarrayliteral)
{
/* Mixed slice: it was initialized as a string literal.
* Now a slice of it is being set with an array literal.
*/
sliceAssignStringFromArrayLiteral(existingSE, (ArrayLiteralExp *)newval, (size_t)firstIndex);
return newval;
}
// String literal block slice assign
dinteger_t value = newval->toInteger();
void *s = existingSE->string;
for (size_t i = 0; i < upperbound - lowerbound; i++)
{
switch (existingSE->sz)
{
case 1: (( utf8_t *)s)[(size_t)(i + firstIndex)] = ( utf8_t)value; break;
case 2: ((utf16_t *)s)[(size_t)(i + firstIndex)] = (utf16_t)value; break;
case 4: ((utf32_t *)s)[(size_t)(i + firstIndex)] = (utf32_t)value; break;
default: assert(0); break;
}
}
if (goal == ctfeNeedNothing)
return NULL; // avoid creating an unused literal
SliceExp *retslice = new SliceExp(e->loc, existingSE,
new IntegerExp(e->loc, firstIndex, Type::tsize_t),
new IntegerExp(e->loc, firstIndex + upperbound - lowerbound, Type::tsize_t));
retslice->type = e->type;
return interpret(pue, retslice, istate);
}
if (aggregate->op == TOKarrayliteral)
{
ArrayLiteralExp *existingAE = (ArrayLiteralExp *)aggregate;
if (existingAE->ownedByCtfe != OWNEDctfe)
{
e->error("cannot modify read-only constant %s", existingAE->toChars());
return CTFEExp::cantexp;
}
if (newval->op == TOKslice && !isBlockAssignment)
{
SliceExp *se = (SliceExp *)newval;
Expression *aggr2 = se->e1;
const dinteger_t srclower = se->lwr->toInteger();
const dinteger_t srcupper = se->upr->toInteger();
const bool wantCopy = (newval->type->toBasetype()->nextOf()->baseElemOf()->ty == Tstruct);
//printf("oldval = %p %s[%d..%u]\nnewval = %p %s[%llu..%llu] wantCopy = %d\n",
// aggregate, aggregate->toChars(), lowerbound, upperbound,
// aggr2, aggr2->toChars(), srclower, srcupper, wantCopy);
if (wantCopy)
{
// Currently overlapping for struct array is allowed.
// The order of elements processing depends on the overlapping.
// See bugzilla 14024.
assert(aggr2->op == TOKarrayliteral);
Expressions *oldelems = existingAE->elements;
Expressions *newelems = ((ArrayLiteralExp *)aggr2)->elements;
Type *elemtype = aggregate->type->nextOf();
bool needsPostblit = e->e2->isLvalue();
if (aggregate == aggr2 &&
srclower < lowerbound && lowerbound < srcupper)
{
// reverse order
for (size_t i = upperbound - lowerbound; 0 < i--; )
{
Expression *oldelem = (*oldelems)[(size_t)(i + firstIndex)];
Expression *newelem = (*newelems)[(size_t)(i + srclower)];
newelem = copyLiteral(newelem).copy();
newelem->type = elemtype;
if (needsPostblit)
{
if (Expression *x = evaluatePostblit(istate, newelem))
return x;
}
if (Expression *x = evaluateDtor(istate, oldelem))
return x;
(*oldelems)[lowerbound + i] = newelem;
}
}
else
{
// normal order
for (size_t i = 0; i < upperbound - lowerbound; i++)
{
Expression *oldelem = (*oldelems)[(size_t)(i + firstIndex)];
Expression *newelem = (*newelems)[(size_t)(i + srclower)];
newelem = copyLiteral(newelem).copy();
newelem->type = elemtype;
if (needsPostblit)
{
if (Expression *x = evaluatePostblit(istate, newelem))
return x;
}
if (Expression *x = evaluateDtor(istate, oldelem))
return x;
(*oldelems)[lowerbound + i] = newelem;
}
}
//assert(0);
return newval; // oldval?
}
if (aggregate == aggr2 &&
lowerbound < srcupper && srclower < upperbound)
{
e->error("overlapping slice assignment [%d..%d] = [%llu..%llu]",
lowerbound, upperbound, srclower, srcupper);
return CTFEExp::cantexp;
}
#if 1 // todo: instead we can directly access to each elements of the slice
Expression *orignewval = newval;
newval = resolveSlice(newval);
if (CTFEExp::isCantExp(newval))
{
e->error("CTFE internal error: slice %s", orignewval->toChars());
return CTFEExp::cantexp;
}
#endif
// no overlapping
//length?
assert(newval->op != TOKslice);
}
if (newval->op == TOKstring && !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, (StringExp *)newval, (size_t)firstIndex);
return newval;
}
if (newval->op == TOKarrayliteral && !isBlockAssignment)
{
Expressions *oldelems = existingAE->elements;
Expressions *newelems = ((ArrayLiteralExp *)newval)->elements;
Type *elemtype = existingAE->type->nextOf();
bool needsPostblit = e->op != TOKblit && e->e2->isLvalue();
for (size_t j = 0; j < newelems->length; j++)
{
Expression *newelem = (*newelems)[j];
newelem = paintTypeOntoLiteral(elemtype, newelem);
if (needsPostblit)
{
Expression *x = evaluatePostblit(istate, newelem);
if (exceptionOrCantInterpret(x))
return x;
}
(*oldelems)[(size_t)(j + firstIndex)] = newelem;
}
return newval;
}
/* Block assignment, initialization of static arrays
* x[] = newval
* x may be a multidimensional static array. (Note that this
* only happens with array literals, never with strings).
*/
struct RecursiveBlock
{
InterState *istate;
Expression *newval;
bool refCopy;
bool needsPostblit;
bool needsDtor;
Expression *assignTo(ArrayLiteralExp *ae)
{
return assignTo(ae, 0, ae->elements->length);
}
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 = ((TypeArray *)ae->type)->next->equivalent(newval->type);
for (size_t k = lwr; k < upr; k++)
{
if (!directblk && (*w)[k]->op == TOKarrayliteral)
{
// Multidimensional array block assign
if (Expression *ex = assignTo((ArrayLiteralExp *)(*w)[k]))
return ex;
}
else if (refCopy)
{
(*w)[k] = newval;
}
else if (!needsPostblit && !needsDtor)
{
assignInPlace((*w)[k], newval);
}
else
{
Expression *oldelem = (*w)[k];
Expression *tmpelem = needsDtor ? copyLiteral(oldelem).copy() : NULL;
assignInPlace(oldelem, newval);
if (needsPostblit)
{
if (Expression *ex = evaluatePostblit(istate, oldelem))
return ex;
}
if (needsDtor)
{
// Bugzilla 14860
if (Expression *ex = evaluateDtor(istate, tmpelem))
return ex;
}
}
}
return NULL;
}
};
Type *tn = newval->type->toBasetype();
bool wantRef = (tn->ty == Tarray || isAssocArray(tn) ||tn->ty == Tclass);
bool cow = newval->op != TOKstructliteral &&
newval->op != TOKarrayliteral &&
newval->op != TOKstring;
Type *tb = tn->baseElemOf();
StructDeclaration *sd = (tb->ty == Tstruct ? ((TypeStruct *)tb)->sym : NULL);
RecursiveBlock rb;
rb.istate = istate;
rb.newval = newval;
rb.refCopy = wantRef || cow;
rb.needsPostblit = sd && sd->postblit && e->op != TOKblit && e->e2->isLvalue();
rb.needsDtor = sd && sd->dtor && e->op == TOKassign;
if (Expression *ex = rb.assignTo(existingAE, lowerbound, upperbound))
return ex;
if (goal == ctfeNeedNothing)
return NULL; // avoid creating an unused literal
SliceExp *retslice = new SliceExp(e->loc, existingAE,
new IntegerExp(e->loc, firstIndex, Type::tsize_t),
new IntegerExp(e->loc, firstIndex + upperbound - lowerbound, Type::tsize_t));
retslice->type = e->type;
return interpret(pue, retslice, istate);
}
e->error("slice operation %s = %s cannot be evaluated at compile time",
e1->toChars(), newval->toChars());
return CTFEExp::cantexp;
}
void visit(AssignExp *e)
{
interpretAssignCommon(e, NULL);
}
void visit(BinAssignExp *e)
{
switch (e->op)
{
case TOKaddass: interpretAssignCommon(e, &Add); return;
case TOKminass: interpretAssignCommon(e, &Min); return;
case TOKcatass: interpretAssignCommon(e, &ctfeCat); return;
case TOKmulass: interpretAssignCommon(e, &Mul); return;
case TOKdivass: interpretAssignCommon(e, &Div); return;
case TOKmodass: interpretAssignCommon(e, &Mod); return;
case TOKshlass: interpretAssignCommon(e, &Shl); return;
case TOKshrass: interpretAssignCommon(e, &Shr); return;
case TOKushrass: interpretAssignCommon(e, &Ushr); return;
case TOKandass: interpretAssignCommon(e, &And); return;
case TOKorass: interpretAssignCommon(e, &Or); return;
case TOKxorass: interpretAssignCommon(e, &Xor); return;
case TOKpowass: interpretAssignCommon(e, &Pow); return;
default:
assert(0);
return;
}
}
void visit(PostExp *e)
{
if (e->op == TOKplusplus)
interpretAssignCommon(e, &Add, 1);
else
interpretAssignCommon(e, &Min, 1);
}
/* Return 1 if e is a p1 > p2 or p1 >= p2 pointer comparison;
* -1 if e is a p1 < p2 or p1 <= p2 pointer comparison;
* 0 otherwise
*/
static int isPointerCmpExp(Expression *e, Expression **p1, Expression **p2)
{
int ret = 1;
while (e->op == TOKnot)
{
ret *= -1;
e = ((NotExp *)e)->e1;
}
switch (e->op)
{
case TOKlt:
case TOKle:
ret *= -1;
/* fall through */
case TOKgt:
case TOKge:
*p1 = ((BinExp *)e)->e1;
*p2 = ((BinExp *)e)->e2;
if (!(isPointer((*p1)->type) && isPointer((*p2)->type)))
ret = 0;
break;
default:
ret = 0;
break;
}
return ret;
}
/** Negate a relational operator, eg >= becomes <
*/
static TOK reverseRelation(TOK op)
{
switch (op)
{
case TOKge: return TOKlt;
case TOKgt: return TOKle;
case TOKle: return TOKgt;
case TOKlt: return TOKge;
default:
return assert(0), TOKreserved;
}
}
/** If this is a four pointer relation, evaluate it, else return NULL.
*
* This is an expression of the form (p1 > q1 && p2 < q2) or (p1 < q1 || p2 > q2)
* where p1, p2 are expressions yielding pointers to memory block p,
* and q1, q2 are expressions yielding pointers to memory block q.
* This expression is valid even if p and q are independent memory
* blocks and are therefore not normally comparable; the && form returns true
* if [p1..p2] lies inside [q1..q2], and false otherwise; the || form returns
* true if [p1..p2] lies outside [q1..q2], and false otherwise.
*
* Within the expression, any ordering of p1, p2, q1, q2 is permissible;
* the comparison operators can be any of >, <, <=, >=, provided that
* both directions (p > q and p < q) are checked. Additionally the
* relational sub-expressions can be negated, eg
* (!(q1 < p1) && p2 <= q2) is valid.
*/
void interpretFourPointerRelation(UnionExp *pue, BinExp *e)
{
assert(e->op == TOKandand || e->op == TOKoror);
/* It can only be an isInside expression, if both e1 and e2 are
* directional pointer comparisons.
* Note that this check can be made statically; it does not depends on
* any runtime values. This allows a JIT implementation to compile a
* special AndAndPossiblyInside, keeping the normal AndAnd case efficient.
*/
// Save the pointer expressions and the comparison directions,
// so we can use them later.
Expression *p1 = NULL;
Expression *p2 = NULL;
Expression *p3 = NULL;
Expression *p4 = NULL;
int dir1 = isPointerCmpExp(e->e1, &p1, &p2);
int dir2 = isPointerCmpExp(e->e2, &p3, &p4);
if (dir1 == 0 || dir2 == 0)
{
result = NULL;
return;
}
//printf("FourPointerRelation %s\n", toChars());
UnionExp ue1;
UnionExp ue2;
UnionExp ue3;
UnionExp ue4;
// Evaluate the first two pointers
p1 = interpret(&ue1, p1, istate);
if (exceptionOrCant(p1))
return;
p2 = interpret(&ue2, p2, istate);
if (exceptionOrCant(p2))
return;
dinteger_t ofs1, ofs2;
Expression *agg1 = getAggregateFromPointer(p1, &ofs1);
Expression *agg2 = getAggregateFromPointer(p2, &ofs2);
if (!pointToSameMemoryBlock(agg1, agg2) &&
agg1->op != TOKnull &&
agg2->op != TOKnull)
{
// Here it is either CANT_INTERPRET,
// or an IsInside comparison returning false.
p3 = interpret(&ue3, p3, istate);
if (CTFEExp::isCantExp(p3))
return;
// Note that it is NOT legal for it to throw an exception!
Expression *except = NULL;
if (exceptionOrCantInterpret(p3))
except = p3;
else
{
p4 = interpret(&ue4, p4, istate);
if (CTFEExp::isCantExp(p4))
{
result = p4;
return;
}
if (exceptionOrCantInterpret(p4))
except = p4;
}
if (except)
{
e->error("comparison %s of pointers to unrelated memory blocks remains "
"indeterminate at compile time "
"because exception %s was thrown while evaluating %s",
e->e1->toChars(), except->toChars(), e->e2->toChars());
result = CTFEExp::cantexp;
return;
}
dinteger_t ofs3, ofs4;
Expression *agg3 = getAggregateFromPointer(p3, &ofs3);
Expression *agg4 = getAggregateFromPointer(p4, &ofs4);
// The valid cases are:
// p1 > p2 && p3 > p4 (same direction, also for < && <)
// p1 > p2 && p3 < p4 (different direction, also < && >)
// Changing any > into >= doesnt affect the result
if ((dir1 == dir2 && pointToSameMemoryBlock(agg1, agg4) && pointToSameMemoryBlock(agg2, agg3)) ||
(dir1 != dir2 && pointToSameMemoryBlock(agg1, agg3) && pointToSameMemoryBlock(agg2, agg4)))
{
// it's a legal two-sided comparison
new(pue) IntegerExp(e->loc, (e->op == TOKandand) ? 0 : 1, e->type);
result = pue->exp();
return;
}
// It's an invalid four-pointer comparison. Either the second
// comparison is in the same direction as the first, or else
// more than two memory blocks are involved (either two independent
// invalid comparisons are present, or else agg3 == agg4).
e->error("comparison %s of pointers to unrelated memory blocks is "
"indeterminate at compile time, even when combined with %s.",
e->e1->toChars(), e->e2->toChars());
result = CTFEExp::cantexp;
return;
}
// The first pointer expression didn't need special treatment, so we
// we need to interpret the entire expression exactly as a normal && or ||.
// This is easy because we haven't evaluated e2 at all yet, and we already
// know it will return a bool.
// But we mustn't evaluate the pointer expressions in e1 again, in case
// they have side-effects.
bool nott = false;
Expression *ex = e->e1;
while (ex->op == TOKnot)
{
nott = !nott;
ex = ((NotExp *)ex)->e1;
}
const TOK cmpop = nott ? reverseRelation(ex->op) : ex->op;
const int cmp = comparePointers(cmpop, agg1, ofs1, agg2, ofs2);
// We already know this is a valid comparison.
assert(cmp >= 0);
if ((e->op == TOKandand && cmp == 1) ||
(e->op == TOKoror && cmp == 0))
{
result = interpret(pue, e->e2, istate);
return;
}
new(pue) IntegerExp(e->loc, (e->op == TOKandand) ? 0 : 1, e->type);
result = pue->exp();
}
void visit(LogicalExp *e)
{
// Check for an insidePointer expression, evaluate it if so
interpretFourPointerRelation(pue, e);
if (result)
return;
result = interpret(e->e1, istate);
if (exceptionOrCant(result))
return;
int res;
const bool andand = e->op == TOKandand;
if (andand ? result->isBool(false) : isTrueBool(result))
res = !andand;
else if (andand ? isTrueBool(result) : result->isBool(false))
{
UnionExp ue2;
result = interpret(&ue2, e->e2, istate);
if (exceptionOrCant(result))
return;
if (result->op == TOKvoidexp)
{
assert(e->type->ty == Tvoid);
result = NULL;
return;
}
if (result->isBool(false))
res = 0;
else if (isTrueBool(result))
res = 1;
else
{
result->error("`%s` does not evaluate to a boolean", result->toChars());
result = CTFEExp::cantexp;
return;
}
}
else
{
result->error("`%s` cannot be interpreted as a boolean", result->toChars());
result = CTFEExp::cantexp;
return;
}
if (goal != ctfeNeedNothing)
{
new(pue) IntegerExp(e->loc, res, e->type);
result = pue->exp();
}
}
// Print a stack trace, starting from callingExp which called fd.
// To shorten the stack trace, try to detect recursion.
void showCtfeBackTrace(CallExp * callingExp, FuncDeclaration *fd)
{
if (CtfeStatus::stackTraceCallsToSuppress > 0)
{
--CtfeStatus::stackTraceCallsToSuppress;
return;
}
errorSupplemental(callingExp->loc, "called from here: %s", callingExp->toChars());
// Quit if it's not worth trying to compress the stack trace
if (CtfeStatus::callDepth < 6 || global.params.verbose)
return;
// Recursion happens if the current function already exists in the call stack.
int numToSuppress = 0;
int recurseCount = 0;
int depthSoFar = 0;
InterState *lastRecurse = istate;
for (InterState * cur = istate; cur; cur = cur->caller)
{
if (cur->fd == fd)
{
++recurseCount;
numToSuppress = depthSoFar;
lastRecurse = cur;
}
++depthSoFar;
}
// We need at least three calls to the same function, to make compression worthwhile
if (recurseCount < 2)
return;
// We found a useful recursion. Print all the calls involved in the recursion
errorSupplemental(fd->loc, "%d recursive calls to function %s", recurseCount, fd->toChars());
for (InterState *cur = istate; cur->fd != fd; cur = cur->caller)
{
errorSupplemental(cur->fd->loc, "recursively called from function %s", cur->fd->toChars());
}
// We probably didn't enter the recursion in this function.
// Go deeper to find the real beginning.
InterState * cur = istate;
while (lastRecurse->caller && cur->fd == lastRecurse->caller->fd)
{
cur = cur->caller;
lastRecurse = lastRecurse->caller;
++numToSuppress;
}
CtfeStatus::stackTraceCallsToSuppress = numToSuppress;
}
void visit(CallExp *e)
{
Expression *pthis = NULL;
FuncDeclaration *fd = NULL;
Expression *ecall = interpret(e->e1, istate);
if (exceptionOrCant(ecall))
return;
if (ecall->op == TOKdotvar)
{
DotVarExp *dve = (DotVarExp *)ecall;
// Calling a member function
pthis = dve->e1;
fd = dve->var->isFuncDeclaration();
assert(fd);
if (pthis->op == TOKdottype)
pthis = ((DotTypeExp *)dve->e1)->e1;
}
else if (ecall->op == TOKvar)
{
fd = ((VarExp *)ecall)->var->isFuncDeclaration();
assert(fd);
if (fd->ident == Id::__ArrayPostblit ||
fd->ident == Id::__ArrayDtor)
{
assert(e->arguments->length == 1);
Expression *ea = (*e->arguments)[0];
//printf("1 ea = %s %s\n", ea->type->toChars(), ea->toChars());
if (ea->op == TOKslice)
ea = ((SliceExp *)ea)->e1;
if (ea->op == TOKcast)
ea = ((CastExp *)ea)->e1;
//printf("2 ea = %s, %s %s\n", ea->type->toChars(), Token::toChars(ea->op), ea->toChars());
if (ea->op == TOKvar || ea->op == TOKsymoff)
result = getVarExp(e->loc, istate, ((SymbolExp *)ea)->var, ctfeNeedRvalue);
else if (ea->op == TOKaddress)
result = interpret(((AddrExp *)ea)->e1, istate);
// https://issues.dlang.org/show_bug.cgi?id=18871
// https://issues.dlang.org/show_bug.cgi?id=18819
else if (ea->op == TOKarrayliteral)
result = interpret((ArrayLiteralExp *)ea, istate);
else
assert(0);
if (CTFEExp::isCantExp(result))
return;
if (fd->ident == Id::__ArrayPostblit)
result = evaluatePostblit(istate, result);
else
result = evaluateDtor(istate, result);
if (!result)
result = CTFEExp::voidexp;
return;
}
}
else if (ecall->op == TOKsymoff)
{
SymOffExp *soe = (SymOffExp *)ecall;
fd = soe->var->isFuncDeclaration();
assert(fd && soe->offset == 0);
}
else if (ecall->op == TOKdelegate)
{
// Calling a delegate
fd = ((DelegateExp *)ecall)->func;
pthis = ((DelegateExp *)ecall)->e1;
// Special handling for: &nestedfunc --> DelegateExp(VarExp(nestedfunc), nestedfunc)
if (pthis->op == TOKvar && ((VarExp *)pthis)->var == fd)
pthis = NULL; // context is not necessary for CTFE
}
else if (ecall->op == TOKfunction)
{
// Calling a delegate literal
fd = ((FuncExp *)ecall)->fd;
}
else
{
// delegate.funcptr()
// others
e->error("cannot call %s at compile time", e->toChars());
result = CTFEExp::cantexp;
return;
}
if (!fd)
{
e->error("CTFE internal error: cannot evaluate %s at compile time", e->toChars());
result = CTFEExp::cantexp;
return;
}
if (pthis)
{
// Member function call
// Currently this is satisfied because closure is not yet supported.
assert(!fd->isNested());
if (pthis->op == TOKtypeid)
{
pthis->error("static variable %s cannot be read at compile time", pthis->toChars());
result = CTFEExp::cantexp;
return;
}
assert(pthis);
if (pthis->op == TOKnull)
{
assert(pthis->type->toBasetype()->ty == Tclass);
e->error("function call through null class reference %s", pthis->toChars());
result = CTFEExp::cantexp;
return;
}
assert(pthis->op == TOKstructliteral || pthis->op == TOKclassreference);
if (fd->isVirtual() && !e->directcall)
{
// Make a virtual function call.
// Get the function from the vtable of the original class
assert(pthis->op == TOKclassreference);
ClassDeclaration *cd = ((ClassReferenceExp *)pthis)->originalClass();
// We can't just use the vtable index to look it up, because
// vtables for interfaces don't get populated until the glue layer.
fd = cd->findFunc(fd->ident, (TypeFunction *)fd->type);
assert(fd);
}
}
if (fd && fd->semanticRun >= PASSsemantic3done && fd->semantic3Errors)
{
e->error("CTFE failed because of previous errors in %s", fd->toChars());
result = CTFEExp::cantexp;
return;
}
// Check for built-in functions
result = evaluateIfBuiltin(pue, istate, e->loc, fd, e->arguments, pthis);
if (result)
return;
if (!fd->fbody)
{
e->error("%s cannot be interpreted at compile time,"
" because it has no available source code", fd->toChars());
result = CTFEExp::cantexp;
return;
}
result = interpretFunction(pue, fd, istate, e->arguments, pthis);
if (result->op == TOKvoidexp)
return;
if (!exceptionOrCantInterpret(result))
{
if (goal != ctfeNeedLvalue) // Peel off CTFE reference if it's unnecessary
{
if (result == pue->exp())
result = pue->copy();
result = interpret(pue, result, istate);
}
}
if (!exceptionOrCantInterpret(result))
{
result = paintTypeOntoLiteral(e->type, result);
result->loc = e->loc;
}
else if (CTFEExp::isCantExp(result) && !global.gag)
showCtfeBackTrace(e, fd); // Print a stack trace.
}
void endTempStackFrame(InterState *pistateComma)
{
// If we created a temporary stack frame, end it now.
if (istate == pistateComma)
ctfeStack.endFrame();
}
void visit(CommaExp *e)
{
CommaExp *firstComma = e;
while (firstComma->e1->op == TOKcomma)
firstComma = (CommaExp *)firstComma->e1;
// 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->e1->op == TOKdeclaration)
{
ctfeStack.startFrame(NULL);
istate = &istateComma;
}
result = CTFEExp::cantexp;
// If the comma returns a temporary variable, it needs to be an lvalue
// (this is particularly important for struct constructors)
if (e->e1->op == TOKdeclaration && e->e2->op == TOKvar &&
((DeclarationExp *)e->e1)->declaration == ((VarExp*)e->e2)->var &&
((VarExp*)e->e2)->var->storage_class & STCctfe) // same as Expression::isTemp
{
VarExp *ve = (VarExp *)e->e2;
VarDeclaration *v = ve->var->isVarDeclaration();
ctfeStack.push(v);
if (!v->_init && !getValue(v))
{
setValue(v, copyLiteral(v->type->defaultInitLiteral(e->loc)).copy());
}
if (!getValue(v))
{
Expression *newval = initializerToExpression(v->_init);
// Bug 4027. Copy constructors are a weird case where the
// initializer is a void function (the variable is modified
// through a reference parameter instead).
newval = interpret(newval, istate);
if (exceptionOrCant(newval))
return endTempStackFrame(&istateComma);
if (newval->op != TOKvoidexp)
{
// v isn't necessarily null.
setValueWithoutChecking(v, copyLiteral(newval).copy());
}
}
}
else
{
UnionExp ue;
Expression *e1 = interpret(&ue, e->e1, istate, ctfeNeedNothing);
if (exceptionOrCant(e1))
return endTempStackFrame(&istateComma);
}
result = interpret(pue, e->e2, istate, goal);
return endTempStackFrame(&istateComma);
}
void visit(CondExp *e)
{
UnionExp uecond;
Expression *econd;
econd = interpret(&uecond, e->econd, istate);
if (exceptionOrCant(econd))
return;
if (isPointer(e->econd->type))
{
if (econd->op != TOKnull)
{
new(&uecond) IntegerExp(e->loc, 1, Type::tbool);
econd = uecond.exp();
}
}
if (isTrueBool(econd))
result = interpret(pue, e->e1, istate, goal);
else if (econd->isBool(false))
result = interpret(pue, e->e2, istate, goal);
else
{
e->error("%s does not evaluate to boolean result at compile time", e->econd->toChars());
result = CTFEExp::cantexp;
}
}
void visit(ArrayLengthExp *e)
{
UnionExp ue1;
Expression *e1 = interpret(&ue1, e->e1, istate);
assert(e1);
if (exceptionOrCant(e1))
return;
if (e1->op != TOKstring &&
e1->op != TOKarrayliteral &&
e1->op != TOKslice &&
e1->op != TOKnull)
{
e->error("%s cannot be evaluated at compile time", e->toChars());
result = CTFEExp::cantexp;
return;
}
new(pue) IntegerExp(e->loc, resolveArrayLength(e1), e->type);
result = pue->exp();
}
/**
* Interpret the vector expression as an array literal.
* Params:
* pue = non-null pointer to temporary storage that can be used to store the return value
* e = Expression to interpret
* Returns:
* resulting array literal or 'e' if unable to interpret
*/
static Expression *interpretVectorToArray(UnionExp *pue, VectorExp *e)
{
if (e->e1->op == TOKarrayliteral)
return (ArrayLiteralExp *)e->e1;
if (e->e1->op == TOKint64 || e->e1->op == TOKfloat64)
{
// Convert literal __vector(int) -> __vector([array])
Expressions *elements = new Expressions();
elements->setDim(e->dim);
for (size_t i = 0; i < elements->length; i++)
(*elements)[i] = copyLiteral(e->e1).copy();
TypeSArray *type = NULL;
if (e->type->ty == Tvector)
{
TypeVector *tv = (TypeVector *)e->type;
if (tv->basetype->ty == Tsarray)
type = (TypeSArray *)tv->basetype;
}
else if (e->type->ty == Tsarray)
type = (TypeSArray *)e->type;
assert(type);
new(pue) ArrayLiteralExp(e->loc, type, elements);
ArrayLiteralExp *ale = (ArrayLiteralExp *)pue->exp();
ale->ownedByCtfe = OWNEDctfe;
return ale;
}
return e;
}
void visit(VectorExp *e)
{
if (e->ownedByCtfe >= OWNEDctfe) // We've already interpreted all the elements
{
result = e;
return;
}
Expression *e1 = interpret(pue, e->e1, istate);
assert(e1);
if (exceptionOrCant(e1))
return;
if (e1->op != TOKarrayliteral && e1->op != TOKint64 && e1->op != TOKfloat64)
{
e->error("`%s` cannot be evaluated at compile time", e->toChars());
result = CTFEExp::cantexp;
return;
}
if (e1 == pue->exp())
e1 = pue->copy();
new(pue) VectorExp(e->loc, e1, e->to);
VectorExp *ve = (VectorExp *)pue->exp();
ve->type = e->type;
ve->dim = e->dim;
ve->ownedByCtfe = OWNEDctfe;
result = ve;
}
void visit(VectorArrayExp *e)
{
Expression *e1 = interpret(pue, e->e1, istate);
assert(e1);
if (exceptionOrCant(e1))
return;
if (e1->op == TOKvector)
{
VectorExp *ve = (VectorExp *)e1;
result = interpretVectorToArray(pue, ve);
if (result->op != TOKvector)
return;
}
e->error("`%s` cannot be evaluated at compile time", e->toChars());
result = CTFEExp::cantexp;
}
void visit(DelegatePtrExp *e)
{
Expression *e1 = interpret(pue, e->e1, istate);
assert(e1);
if (exceptionOrCant(e1))
return;
e->error("%s cannot be evaluated at compile time", e->toChars());
result = CTFEExp::cantexp;
}
void visit(DelegateFuncptrExp *e)
{
Expression *e1 = interpret(pue, e->e1, istate);
assert(e1);
if (exceptionOrCant(e1))
return;
e->error("%s cannot be evaluated at compile time", e->toChars());
result = CTFEExp::cantexp;
}
static bool resolveIndexing(IndexExp *e, InterState *istate, Expression **pagg, uinteger_t *pidx, bool modify)
{
assert(e->e1->type->toBasetype()->ty != Taarray);
if (e->e1->type->toBasetype()->ty == Tpointer)
{
// Indexing a pointer. Note that there is no $ in this case.
Expression *e1 = interpret(e->e1, istate);
if (exceptionOrCantInterpret(e1))
return false;
Expression *e2 = interpret(e->e2, istate);
if (exceptionOrCantInterpret(e2))
return false;
sinteger_t indx = e2->toInteger();
dinteger_t ofs;
Expression *agg = getAggregateFromPointer(e1, &ofs);
if (agg->op == TOKnull)
{
e->error("cannot index through null pointer %s", e->e1->toChars());
return false;
}
if (agg->op == TOKint64)
{
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 == TOKsymoff)
{
e->error("mutable variable %s cannot be %s at compile time, even through a pointer",
(modify ? "modified" : "read"), ((SymOffExp *)agg)->var->toChars());
return false;
}
if (agg->op == TOKarrayliteral || agg->op == TOKstring)
{
dinteger_t len = resolveArrayLength(agg);
if (ofs + indx >= len)
{
e->error("pointer index [%lld] exceeds allocated memory block [0..%lld]",
ofs + indx, len);
return false;
}
}
else
{
if (ofs + indx != 0)
{
e->error("pointer index [%lld] lies outside memory block [0..1]",
ofs + indx);
return false;
}
}
*pagg = agg;
*pidx = ofs + indx;
return true;
}
Expression *e1 = interpret(e->e1, istate);
if (exceptionOrCantInterpret(e1))
return false;
if (e1->op == TOKnull)
{
e->error("cannot index null array %s", e->e1->toChars());
return false;
}
if (e1->op == TOKvector)
{
UnionExp ue;
e1 = interpretVectorToArray(&ue, (VectorExp *)e1);
e1 = (e1 == ue.exp()) ? ue.copy() : e1;
}
// Set the $ variable, and find the array literal to modify
if (e1->op != TOKarrayliteral &&
e1->op != TOKstring &&
e1->op != TOKslice &&
e1->op != TOKvector)
{
e->error("cannot determine length of %s at compile time",
e->e1->toChars());
return false;
}
dinteger_t len = resolveArrayLength(e1);
if (e->lengthVar)
{
Expression *dollarExp = new IntegerExp(e->loc, len, Type::tsize_t);
ctfeStack.push(e->lengthVar);
setValue(e->lengthVar, dollarExp);
}
Expression *e2 = interpret(e->e2, istate);
if (e->lengthVar)
ctfeStack.pop(e->lengthVar); // $ is defined only inside []
if (exceptionOrCantInterpret(e2))
return false;
if (e2->op != TOKint64)
{
e->error("CTFE internal error: non-integral index [%s]", e->e2->toChars());
return false;
}
if (e1->op == TOKslice)
{
// Simplify index of slice: agg[lwr..upr][indx] --> agg[indx']
uinteger_t index = e2->toInteger();
uinteger_t ilwr = ((SliceExp *)e1)->lwr->toInteger();
uinteger_t iupr = ((SliceExp *)e1)->upr->toInteger();
if (index > iupr - ilwr)
{
e->error("index %llu exceeds array length %llu", index, iupr - ilwr);
return false;
}
*pagg = ((SliceExp *)e1)->e1;
*pidx = index + ilwr;
}
else
{
*pagg = e1;
*pidx = e2->toInteger();
if (len <= *pidx)
{
e->error("array index %lld is out of bounds [0..%lld]",
*pidx, len);
return false;
}
}
return true;
}
void visit(IndexExp *e)
{
if (e->e1->type->toBasetype()->ty == Tpointer)
{
Expression *agg;
uinteger_t indexToAccess;
if (!resolveIndexing(e, istate, &agg, &indexToAccess, false))
{
result = CTFEExp::cantexp;
return;
}
if (agg->op == TOKarrayliteral || agg->op == TOKstring)
{
if (goal == ctfeNeedLvalue)
{
// if we need a reference, IndexExp shouldn't be interpreting
// the expression to a value, it should stay as a reference
new(pue) IndexExp(e->loc, agg, new IntegerExp(e->e2->loc, indexToAccess, e->e2->type));
result = pue->exp();
result->type = e->type;
return;
}
result = ctfeIndex(e->loc, e->type, agg, indexToAccess);
return;
}
else
{
assert(indexToAccess == 0);
result = interpret(agg, istate, goal);
if (exceptionOrCant(result))
return;
result = paintTypeOntoLiteral(e->type, result);
return;
}
}
if (e->e1->type->toBasetype()->ty == Taarray)
{
Expression *e1 = interpret(e->e1, istate);
if (exceptionOrCant(e1))
return;
if (e1->op == TOKnull)
{
if (goal == ctfeNeedLvalue && e1->type->ty == Taarray && e->modifiable)
{
assert(0); // does not reach here?
return;
}
e->error("cannot index null array %s", e->e1->toChars());
result = CTFEExp::cantexp;
return;
}
Expression *e2 = interpret(e->e2, istate);
if (exceptionOrCant(e2))
return;
if (goal == ctfeNeedLvalue)
{
// Pointer or reference of a scalar type
if (e1 == e->e1 && e2 == e->e2)
result = e;
else
{
new(pue) IndexExp(e->loc, e1, e2);
result = pue->exp();
result->type = e->type;
}
return;
}
assert(e1->op == TOKassocarrayliteral);
UnionExp e2tmp;
e2 = resolveSlice(e2, &e2tmp);
result = findKeyInAA(e->loc, (AssocArrayLiteralExp *)e1, e2);
if (!result)
{
e->error("key %s not found in associative array %s", e2->toChars(), e->e1->toChars());
result = CTFEExp::cantexp;
}
return;
}
Expression *agg;
uinteger_t indexToAccess;
if (!resolveIndexing(e, istate, &agg, &indexToAccess, false))
{
result = CTFEExp::cantexp;
return;
}
if (goal == ctfeNeedLvalue)
{
Expression *e2 = new IntegerExp(e->e2->loc, indexToAccess, Type::tsize_t);
new(pue) IndexExp(e->loc, agg, e2);
result = pue->exp();
result->type = e->type;
return;
}
result = ctfeIndex(e->loc, e->type, agg, indexToAccess);
if (exceptionOrCant(result))
return;
if (result->op == TOKvoid)
{
e->error("%s is used before initialized", e->toChars());
errorSupplemental(result->loc, "originally uninitialized here");
result = CTFEExp::cantexp;
return;
}
result = paintTypeOntoLiteral(e->type, result);
}
void visit(SliceExp *e)
{
if (e->e1->type->toBasetype()->ty == Tpointer)
{
// Slicing a pointer. Note that there is no $ in this case.
Expression *e1 = interpret(e->e1, istate);
if (exceptionOrCant(e1))
return;
if (e1->op == TOKint64)
{
e->error("cannot slice invalid pointer %s of value %s", e->e1->toChars(), e1->toChars());
result = CTFEExp::cantexp;
return;
}
/* Evaluate lower and upper bounds of slice
*/
Expression *lwr = interpret(e->lwr, istate);
if (exceptionOrCant(lwr))
return;
Expression *upr = interpret(e->upr, istate);
if (exceptionOrCant(upr))
return;
uinteger_t ilwr = lwr->toInteger();
uinteger_t iupr = upr->toInteger();
dinteger_t ofs;
Expression *agg = getAggregateFromPointer(e1, &ofs);
ilwr += ofs;
iupr += ofs;
if (agg->op == TOKnull)
{
if (iupr == ilwr)
{
result = new NullExp(e->loc);
result->type = e->type;
return;
}
e->error("cannot slice null pointer %s", e->e1->toChars());
result = CTFEExp::cantexp;
return;
}
if (agg->op == TOKsymoff)
{
e->error("slicing pointers to static variables is not supported in CTFE");
result = CTFEExp::cantexp;
return;
}
if (agg->op != TOKarrayliteral && agg->op != TOKstring)
{
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 == TOKarrayliteral || agg->op == TOKstring);
dinteger_t len = ArrayLength(Type::tsize_t, agg).exp()->toInteger();
//Type *pointee = ((TypePointer *)agg->type)->next;
if (iupr > (len + 1) || iupr < ilwr)
{
e->error("pointer slice [%lld..%lld] exceeds allocated memory block [0..%lld]", ilwr, iupr, len);
result = CTFEExp::cantexp;
return;
}
if (ofs != 0)
{
lwr = new IntegerExp(e->loc, ilwr, lwr->type);
upr = new IntegerExp(e->loc, iupr, upr->type);
}
new(pue) SliceExp(e->loc, agg, lwr, upr);
result = pue->exp();
result->type = e->type;
return;
}
Expression *e1 = interpret(e->e1, istate);
if (exceptionOrCant(e1))
return;
if (!e->lwr)
{
result = paintTypeOntoLiteral(e->type, e1);
return;
}
if (e1->op == TOKvector)
{
e1 = interpretVectorToArray(pue, (VectorExp *)e1);
e1 = (e1 == pue->exp()) ? pue->copy() : e1;
}
/* Set the $ variable
*/
if (e1->op != TOKarrayliteral && e1->op != TOKstring && e1->op != TOKnull && e1->op != TOKslice && e1->op != TOKvector)
{
e->error("cannot determine length of %s at compile time", e1->toChars());
result = CTFEExp::cantexp;
return;
}
uinteger_t dollar = resolveArrayLength(e1);
if (e->lengthVar)
{
IntegerExp *dollarExp = new IntegerExp(e->loc, dollar, Type::tsize_t);
ctfeStack.push(e->lengthVar);
setValue(e->lengthVar, dollarExp);
}
/* Evaluate lower and upper bounds of slice
*/
Expression *lwr = interpret(e->lwr, istate);
if (exceptionOrCant(lwr))
{
if (e->lengthVar)
ctfeStack.pop(e->lengthVar);
return;
}
Expression *upr = interpret(e->upr, istate);
if (exceptionOrCant(upr))
{
if (e->lengthVar)
ctfeStack.pop(e->lengthVar);
return;
}
if (e->lengthVar)
ctfeStack.pop(e->lengthVar); // $ is defined only inside [L..U]
uinteger_t ilwr = lwr->toInteger();
uinteger_t iupr = upr->toInteger();
if (e1->op == TOKnull)
{
if (ilwr == 0 && iupr == 0)
{
result = e1;
return;
}
e1->error("slice [%llu..%llu] is out of bounds", ilwr, iupr);
result = CTFEExp::cantexp;
return;
}
if (e1->op == TOKslice)
{
SliceExp *se = (SliceExp *)e1;
// Simplify slice of slice:
// 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)
{
e->error("slice[%llu..%llu] exceeds array bounds[%llu..%llu]", ilwr, iupr, lo1, up1);
result = CTFEExp::cantexp;
return;
}
ilwr += lo1;
iupr += lo1;
new(pue) SliceExp(e->loc, se->e1, new IntegerExp(e->loc, ilwr, lwr->type), new IntegerExp(e->loc, iupr, upr->type));
result = pue->exp();
result->type = e->type;
return;
}
if (e1->op == TOKarrayliteral || e1->op == TOKstring)
{
if (iupr < ilwr || dollar < iupr)
{
e->error("slice [%lld..%lld] exceeds array bounds [0..%lld]", ilwr, iupr, dollar);
result = CTFEExp::cantexp;
return;
}
}
new(pue) SliceExp(e->loc, e1, lwr, upr);
result = pue->exp();
result->type = e->type;
}
void visit(InExp *e)
{
Expression *e1 = interpret(e->e1, istate);
if (exceptionOrCant(e1))
return;
Expression *e2 = interpret(e->e2, istate);
if (exceptionOrCant(e2))
return;
if (e2->op == TOKnull)
{
new(pue) NullExp(e->loc, e->type);
result = pue->exp();
return;
}
if (e2->op != TOKassocarrayliteral)
{
e->error("%s cannot be interpreted at compile time", e->toChars());
result = CTFEExp::cantexp;
return;
}
e1 = resolveSlice(e1);
result = findKeyInAA(e->loc, (AssocArrayLiteralExp *)e2, e1);
if (exceptionOrCant(result))
return;
if (!result)
{
new(pue) NullExp(e->loc, e->type);
result = pue->exp();
}
else
{
// Create a CTFE pointer &aa[index]
result = new IndexExp(e->loc, e2, e1);
result->type = e->type->nextOf();
new(pue) AddrExp(e->loc, result, e->type);
result = pue->exp();
}
}
void visit(CatExp *e)
{
UnionExp ue1;
Expression *e1 = interpret(&ue1, e->e1, istate);
if (exceptionOrCant(e1))
return;
UnionExp ue2;
Expression *e2 = interpret(&ue2, e->e2, istate);
if (exceptionOrCant(e2))
return;
UnionExp e1tmp;
e1 = resolveSlice(e1, &e1tmp);
UnionExp e2tmp;
e2 = resolveSlice(e2, &e2tmp);
/* e1 and e2 can't go on the stack because of x~[y] and [x]~y will
* 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 == TOKstring && e2->op == TOKstring))
{
if (e1 == ue1.exp())
e1 = ue1.copy();
if (e2 == ue2.exp())
e2 = ue2.copy();
}
*pue = ctfeCat(e->loc, e->type, e1, e2);
result = pue->exp();
if (CTFEExp::isCantExp(result))
{
e->error("%s cannot be interpreted at compile time", e->toChars());
return;
}
// We know we still own it, because we interpreted both e1 and e2
if (result->op == TOKarrayliteral)
{
ArrayLiteralExp *ale = (ArrayLiteralExp *)result;
ale->ownedByCtfe = OWNEDctfe;
// Bugzilla 14686
for (size_t i = 0; i < ale->elements->length; i++)
{
Expression *ex = evaluatePostblit(istate, (*ale->elements)[i]);
if (exceptionOrCant(ex))
return;
}
}
if (result->op == TOKstring)
((StringExp *)result)->ownedByCtfe = OWNEDctfe;
}
void visit(DeleteExp *e)
{
result = interpret(e->e1, istate);
if (exceptionOrCant(result))
return;
if (result->op == TOKnull)
{
result = CTFEExp::voidexp;
return;
}
Type *tb = e->e1->type->toBasetype();
switch (tb->ty)
{
case Tclass:
{
if (result->op != TOKclassreference)
{
e->error("delete on invalid class reference `%s`", result->toChars());
result = CTFEExp::cantexp;
return;
}
ClassReferenceExp *cre = (ClassReferenceExp *)result;
ClassDeclaration *cd = cre->originalClass();
if (cd->aggDelete)
{
e->error("member deallocators not supported by CTFE");
result = CTFEExp::cantexp;
return;
}
if (cd->dtor)
{
result = interpretFunction(pue, cd->dtor, istate, NULL, cre);
if (exceptionOrCant(result))
return;
}
break;
}
case Tpointer:
{
tb = ((TypePointer *)tb)->next->toBasetype();
if (tb->ty == Tstruct)
{
if (result->op != TOKaddress ||
((AddrExp *)result)->e1->op != TOKstructliteral)
{
e->error("delete on invalid struct pointer `%s`", result->toChars());
result = CTFEExp::cantexp;
return;
}
StructDeclaration *sd = ((TypeStruct *)tb)->sym;
StructLiteralExp *sle = (StructLiteralExp *)((AddrExp *)result)->e1;
if (sd->aggDelete)
{
e->error("member deallocators not supported by CTFE");
result = CTFEExp::cantexp;
return;
}
if (sd->dtor)
{
result = interpretFunction(pue, sd->dtor, istate, NULL, sle);
if (exceptionOrCant(result))
return;
}
}
break;
}
case Tarray:
{
Type *tv = tb->nextOf()->baseElemOf();
if (tv->ty == Tstruct)
{
if (result->op != TOKarrayliteral)
{
e->error("delete on invalid struct array `%s`", result->toChars());
result = CTFEExp::cantexp;
return;
}
StructDeclaration *sd = ((TypeStruct *)tv)->sym;
if (sd->aggDelete)
{
e->error("member deallocators not supported by CTFE");
result = CTFEExp::cantexp;
return;
}
if (sd->dtor)
{
ArrayLiteralExp *ale = (ArrayLiteralExp *)result;
for (size_t i = 0; i < ale->elements->length; i++)
{
Expression *el = (*ale->elements)[i];
result = interpretFunction(pue, sd->dtor, istate, NULL, el);
if (exceptionOrCant(result))
return;
}
}
}
break;
}
default:
assert(0);
}
result = CTFEExp::voidexp;
}
void visit(CastExp *e)
{
Expression *e1 = interpret(e->e1, istate, goal);
if (exceptionOrCant(e1))
return;
// If the expression has been cast to void, do nothing.
if (e->to->ty == Tvoid)
{
result = CTFEExp::voidexp;
return;
}
if (e->to->ty == Tpointer && e1->op != TOKnull)
{
Type *pointee = ((TypePointer *)e->type)->next;
// Implement special cases of normally-unsafe casts
if (e1->op == TOKint64)
{
// Happens with Windows HANDLEs, for example.
result = paintTypeOntoLiteral(pue, e->to, e1);
return;
}
bool castToSarrayPointer = false;
bool castBackFromVoid = false;
if (e1->type->ty == Tarray || e1->type->ty == Tsarray || e1->type->ty == Tpointer)
{
// Check for unsupported type painting operations
// For slices, we need the type being sliced,
// since it may have already been type painted
Type *elemtype = e1->type->nextOf();
if (e1->op == TOKslice)
elemtype = ((SliceExp *)e1)->e1->type->nextOf();
// Allow casts from X* to void *, and X** to void** for any X.
// But don't allow cast from X* to void**.
// So, we strip all matching * from source and target to find X.
// Allow casts to X* from void* only if the 'void' was originally an X;
// we check this later on.
Type *ultimatePointee = pointee;
Type *ultimateSrc = elemtype;
while (ultimatePointee->ty == Tpointer && ultimateSrc->ty == Tpointer)
{
ultimatePointee = ultimatePointee->nextOf();
ultimateSrc = ultimateSrc->nextOf();
}
if (ultimatePointee->ty == Tsarray && ultimatePointee->nextOf()->equivalent(ultimateSrc))
{
castToSarrayPointer = true;
}
else if (ultimatePointee->ty != Tvoid && ultimateSrc->ty != Tvoid &&
!isSafePointerCast(elemtype, pointee))
{
e->error("reinterpreting cast from %s* to %s* is not supported in CTFE",
elemtype->toChars(), pointee->toChars());
result = CTFEExp::cantexp;
return;
}
if (ultimateSrc->ty == Tvoid)
castBackFromVoid = true;
}
if (e1->op == TOKslice)
{
SliceExp *se = (SliceExp *)e1;
if (se->e1->op == TOKnull)
{
result = paintTypeOntoLiteral(pue, e->type, se->e1);
return;
}
// Create a CTFE pointer &aggregate[1..2]
IndexExp *ei = new IndexExp(e->loc, se->e1, se->lwr);
ei->type = e->type->nextOf();
new(pue) AddrExp(e->loc, ei, e->type);
result = pue->exp();
return;
}
if (e1->op == TOKarrayliteral || e1->op == TOKstring)
{
// Create a CTFE pointer &[1,2,3][0] or &"abc"[0]
IndexExp *ei = new IndexExp(e->loc, e1, new IntegerExp(e->loc, 0, Type::tsize_t));
ei->type = e->type->nextOf();
new(pue) AddrExp(e->loc, ei, e->type);
result = pue->exp();
return;
}
if (e1->op == TOKindex && !((IndexExp *)e1)->e1->type->equals(e1->type))
{
// type painting operation
IndexExp *ie = (IndexExp *)e1;
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 == TOKarrayliteral && ie->e2->op == TOKint64)
{
ArrayLiteralExp *ale = (ArrayLiteralExp *)ie->e1;
const size_t indx = (size_t)ie->e2->toInteger();
if (indx < ale->elements->length)
{
Expression *xx = (*ale->elements)[indx];
if (xx)
{
if (xx->op == TOKindex)
origType = ((IndexExp *)xx)->e1->type->nextOf();
else if (xx->op == TOKaddress)
origType= ((AddrExp *)xx)->e1->type;
else if (xx->op == TOKvar)
origType = ((VarExp *)xx)->var->type;
}
}
}
if (!isSafePointerCast(origType, pointee))
{
e->error("using void* to reinterpret cast from %s* to %s* is not supported in CTFE", origType->toChars(), pointee->toChars());
result = CTFEExp::cantexp;
return;
}
}
new(pue) IndexExp(e1->loc, ie->e1, ie->e2);
result = pue->exp();
result->type = e->type;
return;
}
if (e1->op == TOKaddress)
{
AddrExp *ae = (AddrExp *)e1;
Type *origType = ae->e1->type;
if (isSafePointerCast(origType, pointee))
{
new(pue) AddrExp(e->loc, ae->e1, e->type);
result = pue->exp();
return;
}
if (castToSarrayPointer && pointee->toBasetype()->ty == Tsarray && ae->e1->op == TOKindex)
{
// &val[idx]
dinteger_t dim = ((TypeSArray *)pointee->toBasetype())->dim->toInteger();
IndexExp *ie = (IndexExp *)ae->e1;
Expression *lwr = ie->e2;
Expression *upr = new IntegerExp(ie->e2->loc, ie->e2->toInteger() + dim, Type::tsize_t);
// Create a CTFE pointer &val[idx..idx+dim]
SliceExp *er = new SliceExp(e->loc, ie->e1, lwr, upr);
er->type = pointee;
new(pue) AddrExp(e->loc, er, e->type);
result = pue->exp();
return;
}
}
if (e1->op == TOKvar || e1->op == TOKsymoff)
{
// type painting operation
Type *origType = ((SymbolExp *)e1)->var->type;
if (castBackFromVoid && !isSafePointerCast(origType, pointee))
{
e->error("using void* to reinterpret cast from %s* to %s* is not supported in CTFE", origType->toChars(), pointee->toChars());
result = CTFEExp::cantexp;
return;
}
if (e1->op == TOKvar)
new(pue) VarExp(e->loc, ((VarExp *)e1)->var);
else
new(pue) SymOffExp(e->loc, ((SymOffExp *)e1)->var, ((SymOffExp *)e1)->offset);
result = pue->exp();
result->type = e->to;
return;
}
// Check if we have a null pointer (eg, inside a struct)
e1 = interpret(e1, istate);
if (e1->op != TOKnull)
{
e->error("pointer cast from %s to %s is not supported at compile time", e1->type->toChars(), e->to->toChars());
result = CTFEExp::cantexp;
return;
}
}
if (e->to->ty == Tsarray && e->e1->type->ty == Tvector)
{
// Special handling for: cast(float[4])__vector([w, x, y, z])
e1 = interpret(e->e1, istate);
if (exceptionOrCant(e1))
return;
assert(e1->op == TOKvector);
e1 = interpretVectorToArray(pue, (VectorExp *)e1);
}
if (e->to->ty == Tarray && e1->op == TOKslice)
{
// 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 = (SliceExp *)e1;
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());
result = CTFEExp::cantexp;
return;
}
new(pue) SliceExp(e1->loc, se->e1, se->lwr, se->upr);
result = pue->exp();
result->type = e->to;
return;
}
// Disallow array type painting, except for conversions between built-in
// types of identical size.
if ((e->to->ty == Tsarray || e->to->ty == Tarray) && (e1->type->ty == Tsarray || e1->type->ty == Tarray) && !isSafePointerCast(e1->type->nextOf(), e->to->nextOf()))
{
e->error("array cast from %s to %s is not supported at compile time", e1->type->toChars(), e->to->toChars());
result = CTFEExp::cantexp;
return;
}
if (e->to->ty == Tsarray)
e1 = resolveSlice(e1);
if (e->to->toBasetype()->ty == Tbool && e1->type->ty == Tpointer)
{
new(pue) IntegerExp(e->loc, e1->op != TOKnull, e->to);
result = pue->exp();
return;
}
result = ctfeCast(pue, e->loc, e->type, e->to, e1);
}
void visit(AssertExp *e)
{
Expression *e1 = interpret(pue, e->e1, istate);
if (exceptionOrCant(e1))
return;
if (isTrueBool(e1))
{
}
else if (e1->isBool(false))
{
if (e->msg)
{
UnionExp ue;
result = interpret(&ue, e->msg, istate);
if (exceptionOrCant(result))
return;
e->error("%s", result->toChars());
}
else
e->error("%s failed", e->toChars());
result = CTFEExp::cantexp;
return;
}
else
{
e->error("%s is not a compile time boolean expression", e1->toChars());
result = CTFEExp::cantexp;
return;
}
result = e1;
return;
}
void visit(PtrExp *e)
{
// Check for int<->float and long<->double casts.
if (e->e1->op == TOKsymoff)
{
SymOffExp *soe = (SymOffExp *)e->e1;
if (soe->offset == 0 && soe->var->isVarDeclaration() && isFloatIntPaint(e->type, soe->var->type))
{
// *(cast(int*)&v), where v is a float variable
result = paintFloatInt(pue, getVarExp(e->loc, istate, soe->var, ctfeNeedRvalue), e->type);
return;
}
}
if (e->e1->op == TOKcast)
{
CastExp *ce1 = (CastExp *)e->e1;
if (ce1->e1->op == TOKaddress)
{
AddrExp *ae11 = (AddrExp *)ce1->e1;
// *(cast(int*)&x), where x is a float expression
Expression *x = ae11->e1;
if (isFloatIntPaint(e->type, x->type))
{
result = paintFloatInt(pue, interpret(x, istate), e->type);
return;
}
}
}
// Constant fold *(&structliteral + offset)
if (e->e1->op == TOKadd)
{
AddExp *ae = (AddExp *)e->e1;
if (ae->e1->op == TOKaddress && ae->e2->op == TOKint64)
{
AddrExp *ade = (AddrExp *)ae->e1;
Expression *ex = interpret(ade->e1, istate);
if (exceptionOrCant(ex))
return;
if (ex->op == TOKstructliteral)
{
StructLiteralExp *se = (StructLiteralExp *)ex;
dinteger_t offset = ae->e2->toInteger();
result = se->getField(e->type, (unsigned)offset);
if (result)
return;
}
}
}
// It's possible we have an array bounds error. We need to make sure it
// errors with this line number, not the one where the pointer was set.
result = interpret(e->e1, istate);
if (exceptionOrCant(result))
return;
if (result->op == TOKfunction)
return;
if (result->op == TOKsymoff)
{
SymOffExp *soe = (SymOffExp *)result;
if (soe->offset == 0 && soe->var->isFuncDeclaration())
return;
e->error("cannot dereference pointer to static variable %s at compile time", soe->var->toChars());
result = CTFEExp::cantexp;
return;
}
if (result->op != TOKaddress)
{
if (result->op == TOKnull)
e->error("dereference of null pointer `%s`", e->e1->toChars());
else
e->error("dereference of invalid pointer `%s`", result->toChars());
result = CTFEExp::cantexp;
return;
}
// *(&x) ==> x
result = ((AddrExp *)result)->e1;
if (result->op == TOKslice && e->type->toBasetype()->ty == Tsarray)
{
/* aggr[lwr..upr]
* upr may exceed the upper boundary of aggr, but the check is deferred
* until those out-of-bounds elements will be touched.
*/
return;
}
result = interpret(pue, result, istate, goal);
if (exceptionOrCant(result))
return;
}
void visit(DotVarExp *e)
{
Expression *ex = interpret(e->e1, istate);
if (exceptionOrCant(ex))
return;
if (FuncDeclaration *f = e->var->isFuncDeclaration())
{
if (ex == e->e1)
result = e; // optimize: reuse this CTFE reference
else
{
new(pue) DotVarExp(e->loc, ex, f, false);
result = pue->exp();
result->type = e->type;
}
return;
}
VarDeclaration *v = e->var->isVarDeclaration();
if (!v)
{
e->error("CTFE internal error: %s", e->toChars());
result = CTFEExp::cantexp;
return;
}
if (ex->op == TOKnull)
{
if (ex->type->toBasetype()->ty == Tclass)
e->error("class `%s` is null and cannot be dereferenced", e->e1->toChars());
else
e->error("CTFE internal error: null this `%s`", e->e1->toChars());
result = CTFEExp::cantexp;
return;
}
if (ex->op != TOKstructliteral && ex->op != TOKclassreference)
{
e->error("%s.%s is not yet implemented at compile time", e->e1->toChars(), e->var->toChars());
result = CTFEExp::cantexp;
return;
}
StructLiteralExp *se;
int i;
// We can't use getField, because it makes a copy
if (ex->op == TOKclassreference)
{
se = ((ClassReferenceExp *)ex)->value;
i = ((ClassReferenceExp *)ex)->findFieldIndexByName(v);
}
else
{
se = (StructLiteralExp *)ex;
i = findFieldIndexByName(se->sd, v);
}
if (i == -1)
{
e->error("couldn't find field %s of type %s in %s", v->toChars(), e->type->toChars(), se->toChars());
result = CTFEExp::cantexp;
return;
}
if (goal == ctfeNeedLvalue)
{
Expression *ev = (*se->elements)[i];
if (!ev || ev->op == TOKvoid)
(*se->elements)[i] = voidInitLiteral(e->type, v).copy();
// just return the (simplified) dotvar expression as a CTFE reference
if (e->e1 == ex)
result = e;
else
{
new(pue) DotVarExp(e->loc, ex, v);
result = pue->exp();
result->type = e->type;
}
return;
}
result = (*se->elements)[i];
if (!result)
{
// https://issues.dlang.org/show_bug.cgi?id=19897
// Zero-length fields don't have an initializer.
if (v->type->size() == 0)
result = voidInitLiteral(e->type, v).copy();
else
{
e->error("Internal Compiler Error: null field %s", v->toChars());
result = CTFEExp::cantexp;
return;
}
}
if (result->op == TOKvoid)
{
VoidInitExp *ve = (VoidInitExp *)result;
const char *s = ve->var->toChars();
if (v->overlapped)
{
e->error("reinterpretation through overlapped field %s is not allowed in CTFE", s);
result = CTFEExp::cantexp;
return;
}
e->error("cannot read uninitialized variable %s in CTFE", s);
result = CTFEExp::cantexp;
return;
}
if (v->type->ty != result->type->ty && v->type->ty == Tsarray)
{
// Block assignment from inside struct literals
TypeSArray *tsa = (TypeSArray *)v->type;
size_t len = (size_t)tsa->dim->toInteger();
UnionExp ue;
result = createBlockDuplicatedArrayLiteral(&ue, ex->loc, v->type, ex, len);
if (result == ue.exp())
result = ue.copy();
(*se->elements)[i] = result;
}
}
void visit(RemoveExp *e)
{
Expression *agg = interpret(e->e1, istate);
if (exceptionOrCant(agg))
return;
Expression *index = interpret(e->e2, istate);
if (exceptionOrCant(index))
return;
if (agg->op == TOKnull)
{
result = CTFEExp::voidexp;
return;
}
assert(agg->op == TOKassocarrayliteral);
AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)agg;
Expressions *keysx = aae->keys;
Expressions *valuesx = aae->values;
size_t removed = 0;
for (size_t j = 0; j < valuesx->length; ++j)
{
Expression *ekey = (*keysx)[j];
int eq = ctfeEqual(e->loc, TOKequal, ekey, index);
if (eq)
++removed;
else if (removed != 0)
{
(*keysx)[j - removed] = ekey;
(*valuesx)[j - removed] = (*valuesx)[j];
}
}
valuesx->length = valuesx->length - removed;
keysx->length = keysx->length - removed;
new(pue) IntegerExp(e->loc, removed ? 1 : 0, Type::tbool);
result = pue->exp();
}
void visit(ClassReferenceExp *e)
{
//printf("ClassReferenceExp::interpret() %s\n", e->value->toChars());
result = e;
}
void visit(VoidInitExp *e)
{
e->error("CTFE internal error: trying to read uninitialized variable");
assert(0);
result = CTFEExp::cantexp;
}
void visit(ThrownExceptionExp *e)
{
assert(0); // This should never be interpreted
result = e;
}
};
/********************************************
* Interpret the expression.
* Params:
* pue = non-null pointer to temporary storage that can be used to store the return value
* e = Expression to interpret
* istate = context
* goal = what the result will be used for
* Returns:
* resulting expression
*/
static Expression *interpret(UnionExp *pue, Expression *e, InterState *istate, CtfeGoal goal)
{
if (!e)
return NULL;
Interpreter v(pue, istate, goal);
e->accept(&v);
Expression *ex = v.result;
assert(goal == ctfeNeedNothing || ex != NULL);
return ex;
}
///
Expression *interpret(Expression *e, InterState *istate, CtfeGoal goal)
{
UnionExp ue;
Expression *result = interpret(&ue, e, istate, goal);
if (result == ue.exp())
result = ue.copy();
return result;
}
/***********************************
* Interpret the statement.
* Params:
* pue = non-null pointer to temporary storage that can be used to store the return value
* s = Statement to interpret
* istate = context
* Returns:
* NULL continue to next statement
* TOKcantexp cannot interpret statement at compile time
* !NULL expression from return statement, or thrown exception
*/
static Expression *interpret(UnionExp *pue, Statement *s, InterState *istate)
{
if (!s)
return NULL;
Interpreter v(pue, istate, ctfeNeedNothing);
s->accept(&v);
return v.result;
}
Expression *interpret(Statement *s, InterState *istate)
{
UnionExp ue;
Expression *result = interpret(&ue, s, istate);
if (result == ue.exp())
result = ue.copy();
return result;
}
/**
* All results destined for use outside of CTFE need to have their CTFE-specific
* features removed.
* In particular,
* 1. all slices must be resolved.
* 2. all .ownedByCtfe set to OWNEDcode
*/
Expression *scrubReturnValue(Loc loc, Expression *e)
{
if (e->op == TOKclassreference)
{
StructLiteralExp *sle = ((ClassReferenceExp*)e)->value;
if (Expression *ex = scrubStructLiteral(loc, sle))
return ex;
}
else if (e->op == TOKvoid)
{
error(loc, "uninitialized variable `%s` cannot be returned from CTFE", ((VoidInitExp *)e)->var->toChars());
return new ErrorExp();
}
e = resolveSlice(e);
if (e->op == TOKstructliteral)
{
StructLiteralExp *sle = (StructLiteralExp *)e;
if (Expression *ex = scrubStructLiteral(loc, sle))
return ex;
}
else if (e->op == TOKstring)
{
((StringExp *)e)->ownedByCtfe = OWNEDcode;
}
else if (e->op == TOKarrayliteral)
{
ArrayLiteralExp *ale = (ArrayLiteralExp *)e;
ale->ownedByCtfe = OWNEDcode;
if (Expression *ex = scrubArray(loc, ale->elements))
return ex;
}
else if (e->op == TOKassocarrayliteral)
{
AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e;
aae->ownedByCtfe = OWNEDcode;
if (Expression *ex = scrubArray(loc, aae->keys))
return ex;
if (Expression *ex = scrubArray(loc, aae->values))
return ex;
aae->type = toBuiltinAAType(aae->type);
}
else if (e->op == TOKvector)
{
VectorExp *ve = (VectorExp *)e;
ve->ownedByCtfe = OWNEDcode;
if (ve->e1->op == TOKarrayliteral)
{
ArrayLiteralExp *ale = (ArrayLiteralExp *)ve->e1;
ale->ownedByCtfe = OWNEDcode;
if (Expression *ex = scrubArray(loc, ale->elements))
return ex;
}
}
return e;
}
/* Returns: true if e is void,
* or is an array literal or struct literal of void elements.
*/
static bool isVoid(Expression *e, bool checkArray = false)
{
if (e->op == TOKvoid)
return true;
if (checkArray && e->type->ty != Tsarray)
return false;
if (e->op == TOKarrayliteral)
return isEntirelyVoid(((ArrayLiteralExp *)e)->elements);
if (e->op == TOKstructliteral)
return isEntirelyVoid(((StructLiteralExp *)e)->elements);
return false;
}
// Return true if every element is either void,
// or is an array literal or struct literal of void elements.
bool isEntirelyVoid(Expressions *elems)
{
for (size_t i = 0; i < elems->length; i++)
{
Expression *e = (*elems)[i];
// It can be NULL for performance reasons,
// see StructLiteralExp::interpret().
if (e && !isVoid(e))
return false;
}
return true;
}
// Scrub all members of an array. Return false if error
Expression *scrubArray(Loc loc, Expressions *elems, bool structlit)
{
for (size_t i = 0; i < elems->length; i++)
{
Expression *e = (*elems)[i];
// It can be NULL for performance reasons,
// see StructLiteralExp::interpret().
if (!e)
continue;
// A struct .init may contain void members.
// Static array members are a weird special case (bug 10994).
if (structlit && isVoid(e, true))
{
e = NULL;
}
else
{
e = scrubReturnValue(loc, e);
if (CTFEExp::isCantExp(e) || e->op == TOKerror)
return e;
}
(*elems)[i] = e;
}
return NULL;
}
Expression *scrubStructLiteral(Loc loc, StructLiteralExp *sle)
{
sle->ownedByCtfe = OWNEDcode;
if (!(sle->stageflags & stageScrub))
{
const int old = sle->stageflags;
sle->stageflags |= stageScrub; // prevent infinite recursion
if (Expression *ex = scrubArray(loc, sle->elements, true))
return ex;
sle->stageflags = old;
}
return NULL;
}
/**************************************
* Transitively set all .ownedByCtfe to OWNEDcache
*/
Expression *scrubCacheValue(Expression *e)
{
if (!e)
return e;
if (e->op == TOKclassreference)
{
StructLiteralExp *sle = ((ClassReferenceExp*)e)->value;
if (Expression *ex = scrubStructLiteralCache(sle))
return ex;
}
else if (e->op == TOKstructliteral)
{
StructLiteralExp *sle = (StructLiteralExp *)e;
if (Expression *ex = scrubStructLiteralCache(sle))
return ex;
}
else if (e->op == TOKstring)
{
((StringExp *)e)->ownedByCtfe = OWNEDcache;
}
else if (e->op == TOKarrayliteral)
{
ArrayLiteralExp *ale = (ArrayLiteralExp *)e;
ale->ownedByCtfe = OWNEDcache;
if (Expression *ex = scrubArrayCache(ale->elements))
return ex;
}
else if (e->op == TOKassocarrayliteral)
{
AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e;
aae->ownedByCtfe = OWNEDcache;
if (Expression *ex = scrubArrayCache(aae->keys))
return ex;
if (Expression *ex = scrubArrayCache(aae->values))
return ex;
}
else if (e->op == TOKvector)
{
VectorExp *ve = (VectorExp *)e;
ve->ownedByCtfe = OWNEDcache;
if (ve->e1->op == TOKarrayliteral)
{
ArrayLiteralExp *ale = (ArrayLiteralExp *)ve->e1;
ale->ownedByCtfe = OWNEDcache;
if (Expression *ex = scrubArrayCache(ale->elements))
return ex;
}
}
return e;
}
Expression *scrubArrayCache(Expressions *elems)
{
for (size_t i = 0; i < elems->length; i++)
{
Expression *e = (*elems)[i];
(*elems)[i] = scrubCacheValue(e);
}
return NULL;
}
Expression *scrubStructLiteralCache(StructLiteralExp *sle)
{
sle->ownedByCtfe = OWNEDcache;
if (!(sle->stageflags & stageScrub))
{
const int old = sle->stageflags;
sle->stageflags |= stageScrub; // prevent infinite recursion
if (Expression *ex = scrubArrayCache(sle->elements))
return ex;
sle->stageflags = old;
}
return NULL;
}
/******************************* Special Functions ***************************/
static Expression *interpret_length(UnionExp *pue, InterState *istate, Expression *earg)
{
//printf("interpret_length()\n");
earg = interpret(pue, earg, istate);
if (exceptionOrCantInterpret(earg))
return earg;
dinteger_t len = 0;
if (earg->op == TOKassocarrayliteral)
len = ((AssocArrayLiteralExp *)earg)->keys->length;
else
assert(earg->op == TOKnull);
new(pue) IntegerExp(earg->loc, len, Type::tsize_t);
return pue->exp();
}
static Expression *interpret_keys(UnionExp *pue, InterState *istate, Expression *earg, Type *returnType)
{
earg = interpret(pue, earg, istate);
if (exceptionOrCantInterpret(earg))
return earg;
if (earg->op == TOKnull)
{
new(pue) NullExp(earg->loc, earg->type);
return pue->exp();
}
if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray)
return NULL;
assert(earg->op == TOKassocarrayliteral);
AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg;
ArrayLiteralExp *ae = new ArrayLiteralExp(aae->loc, returnType, aae->keys);
ae->ownedByCtfe = aae->ownedByCtfe;
*pue = copyLiteral(ae);
return pue->exp();
}
static Expression *interpret_values(UnionExp *pue, InterState *istate, Expression *earg, Type *returnType)
{
earg = interpret(pue, earg, istate);
if (exceptionOrCantInterpret(earg))
return earg;
if (earg->op == TOKnull)
{
new(pue) NullExp(earg->loc, earg->type);
return pue->exp();
}
if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray)
return NULL;
assert(earg->op == TOKassocarrayliteral);
AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg;
ArrayLiteralExp *ae = new ArrayLiteralExp(aae->loc, returnType, aae->values);
ae->ownedByCtfe = aae->ownedByCtfe;
//printf("result is %s\n", e->toChars());
*pue = copyLiteral(ae);
return pue->exp();
}
Expression *interpret_dup(UnionExp *pue, InterState *istate, Expression *earg)
{
earg = interpret(pue, earg, istate);
if (exceptionOrCantInterpret(earg))
return earg;
if (earg->op == TOKnull)
{
new(pue) NullExp(earg->loc, earg->type);
return pue->exp();
}
if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray)
return NULL;
assert(earg->op == TOKassocarrayliteral);
AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)copyLiteral(earg).copy();
for (size_t i = 0; i < aae->keys->length; i++)
{
if (Expression *e = evaluatePostblit(istate, (*aae->keys)[i]))
return e;
if (Expression *e = evaluatePostblit(istate, (*aae->values)[i]))
return e;
}
aae->type = earg->type->mutableOf(); // repaint type from const(int[int]) to const(int)[int]
//printf("result is %s\n", aae->toChars());
return aae;
}
// signature is int delegate(ref Value) OR int delegate(ref Key, ref Value)
Expression *interpret_aaApply(UnionExp *pue, InterState *istate, Expression *aa, Expression *deleg)
{
aa = interpret(aa, istate);
if (exceptionOrCantInterpret(aa))
return aa;
if (aa->op != TOKassocarrayliteral)
{
new(pue) IntegerExp(deleg->loc, 0, Type::tsize_t);
return pue->exp();
}
FuncDeclaration *fd = NULL;
Expression *pthis = NULL;
if (deleg->op == TOKdelegate)
{
fd = ((DelegateExp *)deleg)->func;
pthis = ((DelegateExp *)deleg)->e1;
}
else if (deleg->op == TOKfunction)
fd = ((FuncExp*)deleg)->fd;
assert(fd && fd->fbody);
assert(fd->parameters);
size_t numParams = fd->parameters->length;
assert(numParams == 1 || numParams == 2);
Parameter *fparam = ((TypeFunction *)fd->type)->parameterList[numParams - 1];
bool wantRefValue = 0 != (fparam->storageClass & (STCout | STCref));
Expressions args;
args.setDim(numParams);
AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)aa;
if (!ae->keys || ae->keys->length == 0)
return new IntegerExp(deleg->loc, 0, Type::tsize_t);
Expression *eresult;
for (size_t i = 0; i < ae->keys->length; ++i)
{
Expression *ekey = (*ae->keys)[i];
Expression *evalue = (*ae->values)[i];
if (wantRefValue)
{
Type *t = evalue->type;
evalue = new IndexExp(deleg->loc, ae, ekey);
evalue->type = t;
}
args[numParams - 1] = evalue;
if (numParams == 2)
args[0] = ekey;
UnionExp ue;
eresult = interpretFunction(&ue, fd, istate, &args, pthis);
if (eresult == ue.exp())
eresult = ue.copy();
if (exceptionOrCantInterpret(eresult))
return eresult;
assert(eresult->op == TOKint64);
if (((IntegerExp *)eresult)->getInteger() != 0)
return eresult;
}
return eresult;
}
/* Decoding UTF strings for foreach loops. Duplicates the functionality of
* the twelve _aApplyXXn functions in aApply.d in the runtime.
*/
static Expression *foreachApplyUtf(UnionExp *pue, InterState *istate, Expression *str, Expression *deleg, bool rvs)
{
FuncDeclaration *fd = NULL;
Expression *pthis = NULL;
if (deleg->op == TOKdelegate)
{
fd = ((DelegateExp *)deleg)->func;
pthis = ((DelegateExp *)deleg)->e1;
}
else if (deleg->op == TOKfunction)
fd = ((FuncExp*)deleg)->fd;
assert(fd && fd->fbody);
assert(fd->parameters);
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;
size_t len = (size_t)resolveArrayLength(str);
if (len == 0)
{
new(pue) IntegerExp(deleg->loc, 0, indexType);
return pue->exp();
}
str = resolveSlice(str);
StringExp *se = NULL;
ArrayLiteralExp *ale = NULL;
if (str->op == TOKstring)
se = (StringExp *) str;
else if (str->op == TOKarrayliteral)
ale = (ArrayLiteralExp *)str;
else
{
str->error("CTFE internal error: cannot foreach %s", str->toChars());
return CTFEExp::cantexp;
}
Expressions args;
args.setDim(numParams);
Expression *eresult = NULL; // ded-store to prevent spurious warning
// Buffers for encoding; also used for decoding array literals
utf8_t utf8buf[4];
unsigned short utf16buf[2];
size_t start = rvs ? len : 0;
size_t end = rvs ? 0: len;
for (size_t indx = start; indx != end;)
{
// Step 1: Decode the next dchar from the string.
const char *errmsg = NULL; // Used for reporting decoding errors
dchar_t rawvalue; // Holds the decoded dchar
size_t currentIndex = indx; // The index of the decoded character
if (ale)
{
// If it is an array literal, copy the code points into the buffer
size_t buflen = 1; // #code points in the buffer
size_t n = 1; // #code points in this char
size_t sz = (size_t)ale->type->nextOf()->size();
switch (sz)
{
case 1:
if (rvs)
{
// find the start of the string
--indx;
buflen = 1;
while (indx > 0 && buflen < 4)
{
Expression * r = (*ale->elements)[indx];
assert(r->op == TOKint64);
utf8_t x = (utf8_t)(((IntegerExp *)r)->getInteger());
if ((x & 0xC0) != 0x80)
break;
++buflen;
}
}
else
buflen = (indx + 4 > len) ? len - indx : 4;
for (size_t i = 0; i < buflen; ++i)
{
Expression * r = (*ale->elements)[indx + i];
assert(r->op == TOKint64);
utf8buf[i] = (utf8_t)(((IntegerExp *)r)->getInteger());
}
n = 0;
errmsg = utf_decodeChar(&utf8buf[0], buflen, &n, &rawvalue);
break;
case 2:
if (rvs)
{
// find the start of the string
--indx;
buflen = 1;
Expression * r = (*ale->elements)[indx];
assert(r->op == TOKint64);
unsigned short x = (unsigned short)(((IntegerExp *)r)->getInteger());
if (indx > 0 && x >= 0xDC00 && x <= 0xDFFF)
{
--indx;
++buflen;
}
}
else
buflen = (indx + 2 > len) ? len - indx : 2;
for (size_t i=0; i < buflen; ++i)
{
Expression * r = (*ale->elements)[indx + i];
assert(r->op == TOKint64);
utf16buf[i] = (unsigned short)(((IntegerExp *)r)->getInteger());
}
n = 0;
errmsg = utf_decodeWchar(&utf16buf[0], buflen, &n, &rawvalue);
break;
case 4:
{
if (rvs)
--indx;
Expression * r = (*ale->elements)[indx];
assert(r->op == TOKint64);
rawvalue = (dchar_t)((IntegerExp *)r)->getInteger();
n = 1;
}
break;
default:
assert(0);
}
if (!rvs)
indx += n;
}
else
{
// String literals
size_t saveindx; // used for reverse iteration
switch (se->sz)
{
case 1:
if (rvs)
{
// find the start of the string
utf8_t *s = (utf8_t *)se->string;
--indx;
while (indx > 0 && ((s[indx]&0xC0) == 0x80))
--indx;
saveindx = indx;
}
errmsg = utf_decodeChar((utf8_t *)se->string, se->len, &indx, &rawvalue);
if (rvs)
indx = saveindx;
break;
case 2:
if (rvs)
{
// find the start
unsigned short *s = (unsigned short *)se->string;
--indx;
if (s[indx] >= 0xDC00 && s[indx]<= 0xDFFF)
--indx;
saveindx = indx;
}
errmsg = utf_decodeWchar((unsigned short *)se->string, se->len, &indx, &rawvalue);
if (rvs)
indx = saveindx;
break;
case 4:
if (rvs)
--indx;
rawvalue = ((unsigned *)(se->string))[indx];
if (!rvs)
++indx;
break;
default:
assert(0);
}
}
if (errmsg)
{
deleg->error("%s", errmsg);
return CTFEExp::cantexp;
}
// Step 2: encode the dchar in the target encoding
int charlen = 1; // How many codepoints are involved?
switch (charType->size())
{
case 1:
charlen = utf_codeLengthChar(rawvalue);
utf_encodeChar(&utf8buf[0], rawvalue);
break;
case 2:
charlen = utf_codeLengthWchar(rawvalue);
utf_encodeWchar(&utf16buf[0], rawvalue);
break;
case 4:
break;
default:
assert(0);
}
if (rvs)
currentIndex = indx;
// Step 3: call the delegate once for each code point
// The index only needs to be set once
if (numParams == 2)
args[0] = new IntegerExp(deleg->loc, currentIndex, indexType);
Expression *val = NULL;
for (int k= 0; k < charlen; ++k)
{
dchar_t codepoint;
switch (charType->size())
{
case 1:
codepoint = utf8buf[k];
break;
case 2:
codepoint = utf16buf[k];
break;
case 4:
codepoint = rawvalue;
break;
default:
assert(0);
}
val = new IntegerExp(str->loc, codepoint, charType);
args[numParams - 1] = val;
UnionExp ue;
eresult = interpretFunction(&ue, fd, istate, &args, pthis);
if (eresult == ue.exp())
eresult = ue.copy();
if (exceptionOrCantInterpret(eresult))
return eresult;
assert(eresult->op == TOKint64);
if (((IntegerExp *)eresult)->getInteger() != 0)
return eresult;
}
}
return eresult;
}
/* If this is a built-in function, return the interpreted result,
* Otherwise, return NULL.
*/
Expression *evaluateIfBuiltin(UnionExp *pue, InterState *istate, Loc loc,
FuncDeclaration *fd, Expressions *arguments, Expression *pthis)
{
Expression *e = NULL;
size_t nargs = arguments ? arguments->length : 0;
if (!pthis)
{
if (isBuiltin(fd) != BUILTINunimp)
{
Expressions args;
args.setDim(nargs);
for (size_t i = 0; i < args.length; i++)
{
Expression *earg = (*arguments)[i];
earg = interpret(earg, istate);
if (exceptionOrCantInterpret(earg))
return earg;
args[i] = earg;
}
e = eval_builtin(loc, fd, &args);
if (!e)
{
error(loc, "cannot evaluate unimplemented builtin %s at compile time", fd->toChars());
e = CTFEExp::cantexp;
}
}
}
if (!pthis)
{
Expression *firstarg = nargs > 0 ? (*arguments)[0] : NULL;
if (firstarg && firstarg->type->toBasetype()->ty == Taarray)
{
TypeAArray *firstAAtype = (TypeAArray *)firstarg->type;
const Identifier *id = fd->ident;
if (nargs == 1)
{
if (fd->ident == Id::aaLen)
return interpret_length(pue, istate, firstarg);
if (fd->toParent2()->ident == Id::object)
{
if (id == Id::keys)
return interpret_keys(pue, istate, firstarg, firstAAtype->index->arrayOf());
if (id == Id::values)
return interpret_values(pue, istate, firstarg, firstAAtype->nextOf()->arrayOf());
if (id == Id::rehash)
return interpret(pue, firstarg, istate);
if (id == Id::dup)
return interpret_dup(pue, istate, firstarg);
}
}
else // (nargs == 3)
{
if (id == Id::_aaApply)
return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]);
if (id == Id::_aaApply2)
return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]);
}
}
}
if (pthis && !fd->fbody && fd->isCtorDeclaration() && fd->parent && fd->parent->parent && fd->parent->parent->ident == Id::object)
{
if (pthis->op == TOKclassreference && 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 = ((ClassReferenceExp *)pthis)->value;
assert(arguments->length <= se->elements->length);
for (size_t i = 0; i < arguments->length; ++i)
{
e = interpret((*arguments)[i], istate);
if (exceptionOrCantInterpret(e))
return e;
(*se->elements)[i] = e;
}
return CTFEExp::voidexp;
}
}
if (nargs == 1 && !pthis &&
(fd->ident == Id::criticalenter || fd->ident == Id::criticalexit))
{
// Support synchronized{} as a no-op
return CTFEExp::voidexp;
}
if (!pthis)
{
const char *id = fd->ident->toChars();
size_t idlen = strlen(id);
if (nargs == 2 && (idlen == 10 || idlen == 11) &&
!strncmp(id, "_aApply", 7))
{
// Functions from aApply.d and aApplyR.d in the runtime
bool rvs = (idlen == 11); // true if foreach_reverse
char c = id[idlen-3]; // char width: 'c', 'w', or 'd'
char s = id[idlen-2]; // string width: 'c', 'w', or 'd'
char n = id[idlen-1]; // numParams: 1 or 2.
// There are 12 combinations
if ((n == '1' || n == '2') &&
(c == 'c' || c == 'w' || c == 'd') &&
(s == 'c' || s == 'w' || s == 'd') && c != s)
{
Expression *str = (*arguments)[0];
str = interpret(str, istate);
if (exceptionOrCantInterpret(str))
return str;
return foreachApplyUtf(pue, istate, str, (*arguments)[1], rvs);
}
}
}
return e;
}
Expression *evaluatePostblit(InterState *istate, Expression *e)
{
Type *tb = e->type->baseElemOf();
if (tb->ty != Tstruct)
return NULL;
StructDeclaration *sd = ((TypeStruct *)tb)->sym;
if (!sd->postblit)
return NULL;
if (e->op == TOKarrayliteral)
{
ArrayLiteralExp *ale = (ArrayLiteralExp *)e;
for (size_t i = 0; i < ale->elements->length; i++)
{
e = evaluatePostblit(istate, (*ale->elements)[i]);
if (e)
return e;
}
return NULL;
}
if (e->op == TOKstructliteral)
{
// e.__postblit()
UnionExp ue;
e = interpretFunction(&ue, sd->postblit, istate, NULL, e);
if (e == ue.exp())
e = ue.copy();
if (exceptionOrCantInterpret(e))
return e;
return NULL;
}
assert(0);
return NULL;
}
Expression *evaluateDtor(InterState *istate, Expression *e)
{
Type *tb = e->type->baseElemOf();
if (tb->ty != Tstruct)
return NULL;
StructDeclaration *sd = ((TypeStruct *)tb)->sym;
if (!sd->dtor)
return NULL;
UnionExp ue;
if (e->op == TOKarrayliteral)
{
ArrayLiteralExp *alex = (ArrayLiteralExp *)e;
for (size_t i = alex->elements->length; 0 < i--; )
e = evaluateDtor(istate, (*alex->elements)[i]);
}
else if (e->op == TOKstructliteral)
{
// e.__dtor()
e = interpretFunction(&ue, sd->dtor, istate, NULL, e);
}
else
assert(0);
if (exceptionOrCantInterpret(e))
{
if (e == ue.exp())
e = ue.copy();
return e;
}
return NULL;
}
/*************************** CTFE Sanity Checks ***************************/
/* Setter functions for CTFE variable values.
* These functions exist to check for compiler CTFE bugs.
*/
bool hasValue(VarDeclaration *vd)
{
if (vd->ctfeAdrOnStack == -1)
return false;
return NULL != getValue(vd);
}
Expression *getValue(VarDeclaration *vd)
{
return ctfeStack.getValue(vd);
}
void setValueNull(VarDeclaration *vd)
{
ctfeStack.setValue(vd, NULL);
}
// Don't check for validity
void setValueWithoutChecking(VarDeclaration *vd, Expression *newval)
{
ctfeStack.setValue(vd, newval);
}
void setValue(VarDeclaration *vd, Expression *newval)
{
assert((vd->storage_class & (STCout | STCref))
? isCtfeReferenceValid(newval)
: isCtfeValueValid(newval));
ctfeStack.setValue(vd, newval);
}