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