blob: acca4e8097db7414038fa9873630ec9d9592f299 [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2019 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);
Expression *semantic(Expression *e, Scope *sc);
Initializer *semantic(Initializer *init, Scope *sc, Type *t, NeedInterpret needInterpret);
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.dim;
}
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.dim-1]);
localThis = savedThis[savedThis.dim-1];
popAll(framepointer);
framepointer = oldframe;
frames.setDim(frames.dim - 1);
savedThis.setDim(savedThis.dim -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.dim);
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.dim;
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.dim - 1)
{
values.pop();
vars.pop();
savedId.pop();
}
}
void CtfeStack::popAll(size_t stackpointer)
{
if (stackPointer() > maxStackPointer)
maxStackPointer = stackPointer();
assert(values.dim >= stackpointer);
for (size_t i = stackpointer; i < values.dim; ++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.dim;
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->dim; ++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->dim; i++)
{
Statement *sx = (*s->statements)[i];
if (sx)
ctfeCompile(sx);
}
}
void visit(UnrolledLoopStatement *s)
{
for (size_t i = 0; i < s->statements->dim; 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(OnScopeStatement *)
{
// 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->dim; 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->dim; 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->dim; 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 TOKstring:
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->dim; ++i)
{
Expression *g = (*tup->exps)[i];
Expression *h = g;
h = ctfeInterpretForPragmaMsg(g);
if (h != g)
{
if (!expsx)
{
expsx = new Expressions();
expsx->setDim(tup->exps->dim);
for (size_t j = 0; j < tup->exps->dim; 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->varargs && arguments &&
((fd->parameters && arguments->dim != fd->parameters->dim) || (!fd->parameters && arguments->dim)))
{
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->dim : 0;
assert((fd->parameters ? fd->parameters->dim : 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 = Parameter::getNth(tf->parameters, 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 = Parameter::getNth(tf->parameters, 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->dim : 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->dim : 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->dim; 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.dim > 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->dim : 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->dim; 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->dim; 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;
assert((*boss->value->elements)[4]->type->ty == Tclass); // Throwable.next
ClassReferenceExp *collateral = newest->thrown;
if ( isAnErrorException(collateral->originalClass()) &&
!isAnErrorException(boss->originalClass()))
{
// The new exception bypass the existing chain
assert((*collateral->value->elements)[5]->type->ty == Tclass);
(*collateral->value->elements)[5] = boss;
return newest;
}
while ((*boss->value->elements)[4]->op == TOKclassreference)
{
boss = (ClassReferenceExp *)(*boss->value->elements)[4];
}
(*boss->value->elements)[4] = 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(OnScopeStatement *)
{
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 dynamic array, eg &int[3] to int[]*
if (val->type->ty == Tsarray && pointee->ty == Tarray &&
elemsize == pointee->nextOf()->size())
{
new(pue) AddrExp(e->loc, val, e->type);
result = pue->exp();
return;
}
// 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->_scope) // semantic() not yet run
{
v->semantic (v->_scope);
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 = ::semantic(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 = ::semantic(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->dim; ++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->dim == 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->dim; 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->dim : 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->dim != 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->dim; 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->dim != valuesx->dim)
{
e->error("CTFE internal error: invalid AA");
result = CTFEExp::cantexp;
return;
}
/* Remove duplicate keys
*/
for (size_t i = 1; i < keysx->dim; i++)
{
Expression *ekey = (*keysx)[i - 1];
for (size_t j = i; j < keysx->dim; 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->dim : 0;
Expressions *expsx = e->elements;
if (dim != e->sd->fields.dim)
{
// guaranteed by AggregateDeclaration.fill and TypeStruct.defaultInitLiteral
assert(e->sd->isNested() && dim == e->sd->fields.dim - 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.dim);
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->dim != e->sd->fields.dim)
{
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->dim - 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->dim - 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.dim);
if (e->arguments)
{
exps->setDim(e->arguments->dim);
for (size_t i = 0; i < exps->dim; 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.dim;
Expressions *elems = new Expressions;
elems->setDim(totalFieldCount);
size_t fieldsSoFar = totalFieldCount;
for (ClassDeclaration *c = cd; c; c = c->baseClass)
{
fieldsSoFar -= c->fields.dim;
for (size_t i = 0; i < c->fields.dim; 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;
}