// Compiler implementation of the D programming language // Copyright (c) 1999-2011 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com // License for redistribution is by either the Artistic License // in artistic.txt, or the GNU General Public License in gnu.txt. // See the included readme.txt for details. #include #include #include #include "rmem.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" #define LOG 0 #define LOGASSIGN 0 struct InterState { InterState *caller; // calling function's InterState FuncDeclaration *fd; // function being interpreted VarDeclarations vars; // variables used in this function Statement *start; // if !=NULL, start execution at this statement Statement *gotoTarget; /* target of EXP_GOTO_INTERPRET result; also * target of labelled EXP_BREAK_INTERPRET or * EXP_CONTINUE_INTERPRET. (NULL if no label). */ Expression *localThis; // value of 'this', or NULL if none bool awaitingLvalueReturn; // Support for ref return values: // Any return to this function should return an lvalue. InterState(); }; InterState::InterState() { memset(this, 0, sizeof(InterState)); } Expression * resolveReferences(Expression *e, Expression *thisval, bool *isReference = NULL); Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal); VarDeclaration *findParentVar(Expression *e, Expression *thisval); void addVarToInterstate(InterState *istate, VarDeclaration *v); bool needToCopyLiteral(Expression *expr); Expression *copyLiteral(Expression *e); Expression *paintTypeOntoLiteral(Type *type, Expression *lit); bool evaluateIfBuiltin(Expression **result, InterState *istate, FuncDeclaration *fd, Expressions *arguments, Expression *pthis); // Used for debugging only void showCtfeExpr(Expression *e, int level = 0) { for (int i = level; i>0; --i) printf(" "); Expressions *elements = NULL; // We need the struct definition to detect block assignment StructDeclaration *sd = NULL; if (e->op == TOKstructliteral) { elements = ((StructLiteralExp *)e)->elements; sd = ((StructLiteralExp *)e)->sd; printf("STRUCT type = %s %p :\n", e->type->toChars(), e); } else if (e->op == TOKarrayliteral) { elements = ((ArrayLiteralExp *)e)->elements; printf("ARRAY LITERAL type=%s %p:\n", e->type->toChars(), e); } else if (e->op == TOKassocarrayliteral) { printf("AA LITERAL type=%s %p:\n", e->type->toChars(), e); } else if (e->op == TOKstring) { printf("STRING %s %p\n", e->toChars(), ((StringExp *)e)->string); } else if (e->op == TOKslice) { printf("SLICE %p: %s\n", e, e->toChars()); showCtfeExpr(((SliceExp *)e)->e1, level + 1); } else if (e->op == TOKvar) { printf("VAR %p %s\n", e, e->toChars()); VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); if (v && v->getValue()) showCtfeExpr(v->getValue(), level + 1); } else printf("VALUE %p: %s\n", e, e->toChars()); if (elements) { for (size_t i = 0; i < elements->dim; i++) { Expression *z = elements->tdata()[i]; if (sd) { Dsymbol *s = sd->fields.tdata()[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v); // If it is a void assignment, use the default initializer if (!z) { for (int j = level; j>0; --j) printf(" "); printf(" field:void\n"); continue; } if ((v->type->ty != z->type->ty) && v->type->ty == Tsarray) { for (int j = level; --j;) printf(" "); printf(" field: block initalized static array\n"); continue; } } showCtfeExpr(z, level + 1); } } } /************************************* * Attempt to interpret a function given the arguments. * Input: * 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, NULL if not. */ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *arguments, Expression *thisarg) { #if LOG printf("\n********\nFuncDeclaration::interpret(istate = %p) %s\n", istate, toChars()); printf("cantInterpret = %d, semanticRun = %d\n", cantInterpret, semanticRun); #endif if (global.errors) return NULL; if (cantInterpret || semanticRun == PASSsemantic3) return NULL; if (semanticRun < PASSsemantic3 && scope) { int olderrors = global.errors; semantic3(scope); if (olderrors != global.errors) // if errors compiling this function return NULL; } if (semanticRun < PASSsemantic3done) return NULL; Type *tb = type->toBasetype(); assert(tb->ty == Tfunction); TypeFunction *tf = (TypeFunction *)tb; Type *tret = tf->next->toBasetype(); if (tf->varargs && arguments && ((parameters && arguments->dim != parameters->dim) || (!parameters && arguments->dim))) { cantInterpret = 1; error("C-style variadic functions are not yet implemented in CTFE"); return NULL; } InterState istatex; istatex.caller = istate; istatex.fd = this; istatex.localThis = thisarg; Expressions vsave; // place to save previous parameter values size_t dim = 0; if (needThis() && !thisarg) { // error, no this. Prevent segfault. error("need 'this' to access member %s", toChars()); return NULL; } if (thisarg && !istate) { // Check that 'this' aleady has a value if (thisarg->interpret(istate) == EXP_CANT_INTERPRET) return NULL; } if (arguments) { dim = arguments->dim; assert(!dim || (parameters && (parameters->dim == dim))); vsave.setDim(dim); /* Evaluate all the arguments to the function, * store the results in eargs[] */ Expressions eargs; eargs.setDim(dim); for (size_t i = 0; i < dim; i++) { Expression *earg = arguments->tdata()[i]; Parameter *arg = Parameter::getNth(tf->parameters, i); if (arg->storageClass & (STCout | STCref)) { if (!istate && (arg->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 NULL; } // Convert all reference arguments into lvalue references earg = earg->interpret(istate, ctfeNeedLvalueRef); if (earg == EXP_CANT_INTERPRET) return NULL; } else if (arg->storageClass & STClazy) { } else { /* Value parameters */ Type *ta = arg->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 = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; } eargs.tdata()[i] = earg; } for (size_t i = 0; i < dim; i++) { Expression *earg = eargs.tdata()[i]; Parameter *arg = Parameter::getNth(tf->parameters, i); VarDeclaration *v = parameters->tdata()[i]; vsave.tdata()[i] = v->getValue(); #if LOG printf("arg[%d] = %s\n", i, earg->toChars()); #endif if (arg->storageClass & (STCout | STCref) && earg->op==TOKvar) { VarExp *ve = (VarExp *)earg; VarDeclaration *v2 = ve->var->isVarDeclaration(); if (!v2) { error("cannot interpret %s as a ref parameter", ve->toChars()); return NULL; } v->setValueWithoutChecking(earg); /* Don't restore the value of v2 upon function return */ for (size_t j = 0; j < (istate ? istate->vars.dim : 0); j++) { VarDeclaration *vx = istate->vars.tdata()[j]; if (vx == v2) { istate->vars.tdata()[j] = NULL; break; } } } else { // Value parameters and non-trivial references v->setValueWithoutChecking(earg); } #if LOG || LOGASSIGN printf("interpreted arg[%d] = %s\n", i, earg->toChars()); showCtfeExpr(earg); #endif } } // Don't restore the value of 'this' upon function return if (needThis() && istate) { VarDeclaration *thisvar = findParentVar(thisarg, istate->localThis); if (!thisvar) // it's a reference. Find which variable it refers to. thisvar = findParentVar(thisarg->interpret(istate), istate->localThis); for (size_t i = 0; i < istate->vars.dim; i++) { VarDeclaration *v = istate->vars.tdata()[i]; if (v == thisvar) { istate->vars.tdata()[i] = NULL; break; } } } /* Save the values of the local variables used */ Expressions valueSaves; if (istate) { //printf("saving local variables...\n"); valueSaves.setDim(istate->vars.dim); for (size_t i = 0; i < istate->vars.dim; i++) { VarDeclaration *v = istate->vars.tdata()[i]; bool isParentVar = false; /* Nested functions only restore their own local variables * (not variables in the parent function) */ if (v && (!isNested() || v->parent == this)) { //printf("\tsaving [%d] %s = %s\n", i, v->toChars(), v->getValue() ? v->getValue()->toChars() : ""); valueSaves.tdata()[i] = v->getValue(); v->setValueNull(); } } } Expression *e = NULL; while (1) { e = fbody->interpret(&istatex); if (e == EXP_CANT_INTERPRET) { #if LOG printf("function body failed to interpret\n"); #endif e = NULL; } /* 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 (e == EXP_GOTO_INTERPRET) { istatex.start = istatex.gotoTarget; // set starting statement istatex.gotoTarget = NULL; } else break; } assert(e != EXP_CONTINUE_INTERPRET && e != EXP_BREAK_INTERPRET); /* Restore the parameter values */ for (size_t i = 0; i < dim; i++) { VarDeclaration *v = parameters->tdata()[i]; v->setValueWithoutChecking(vsave.tdata()[i]); } /* Clear __result. (Bug 6049). */ if (vresult) vresult->setValueNull(); if (istate) { /* Restore the variable values */ //printf("restoring local variables...\n"); for (size_t i = 0; i < istate->vars.dim; i++) { VarDeclaration *v = istate->vars.tdata()[i]; /* Nested functions only restore their own local variables * (not variables in the parent function) */ if (v && (!isNested() || v->parent == this)) { v->setValueWithoutChecking(valueSaves.tdata()[i]); //printf("\trestoring [%d] %s = %s\n", i, v->toChars(), v->getValue() ? v->getValue()->toChars() : ""); } } } return e; } /******************************** Statement ***************************/ #define START() \ if (istate->start) \ { if (istate->start != this) \ return NULL; \ istate->start = NULL; \ } /*********************************** * Interpret the statement. * Returns: * NULL continue to next statement * EXP_CANT_INTERPRET cannot interpret statement at compile time * !NULL expression from return statement */ Expression *Statement::interpret(InterState *istate) { #if LOG printf("Statement::interpret()\n"); #endif START() error("Statement %s cannot be interpreted at compile time", this->toChars()); return EXP_CANT_INTERPRET; } Expression *ExpStatement::interpret(InterState *istate) { #if LOG printf("ExpStatement::interpret(%s)\n", exp ? exp->toChars() : ""); #endif START() if (exp) { Expression *e = exp->interpret(istate, ctfeNeedNothing); if (e == EXP_CANT_INTERPRET) { //printf("-ExpStatement::interpret(): %p\n", e); return EXP_CANT_INTERPRET; } } return NULL; } Expression *CompoundStatement::interpret(InterState *istate) { Expression *e = NULL; #if LOG printf("CompoundStatement::interpret()\n"); #endif if (istate->start == this) istate->start = NULL; if (statements) { for (size_t i = 0; i < statements->dim; i++) { Statement *s = statements->tdata()[i]; if (s) { e = s->interpret(istate); if (e) break; } } } #if LOG printf("-CompoundStatement::interpret() %p\n", e); #endif return e; } Expression *UnrolledLoopStatement::interpret(InterState *istate) { Expression *e = NULL; #if LOG printf("UnrolledLoopStatement::interpret()\n"); #endif if (istate->start == this) istate->start = NULL; if (statements) { for (size_t i = 0; i < statements->dim; i++) { Statement *s = statements->tdata()[i]; e = s->interpret(istate); if (e == EXP_CANT_INTERPRET) break; if (e == EXP_CONTINUE_INTERPRET) { if (istate->gotoTarget && istate->gotoTarget != this) break; // continue at higher level istate->gotoTarget = NULL; e = NULL; continue; } if (e == EXP_BREAK_INTERPRET) { if (!istate->gotoTarget || istate->gotoTarget == this) { istate->gotoTarget = NULL; e = NULL; } // else break at a higher level break; } if (e) break; } } return e; } // For CTFE only. Returns true if 'e' is TRUE or a non-null pointer. int isTrueBool(Expression *e) { return e->isBool(TRUE) || (e->type->ty == Tpointer && e->op != TOKnull); } Expression *IfStatement::interpret(InterState *istate) { #if LOG printf("IfStatement::interpret(%s)\n", condition->toChars()); #endif if (istate->start == this) istate->start = NULL; if (istate->start) { Expression *e = NULL; if (ifbody) e = ifbody->interpret(istate); if (istate->start && elsebody) e = elsebody->interpret(istate); return e; } Expression *e = condition->interpret(istate); assert(e); //if (e == EXP_CANT_INTERPRET) printf("cannot interpret\n"); if (e != EXP_CANT_INTERPRET) { if (isTrueBool(e)) e = ifbody ? ifbody->interpret(istate) : NULL; else if (e->isBool(FALSE)) e = elsebody ? elsebody->interpret(istate) : NULL; else { e = EXP_CANT_INTERPRET; } } return e; } Expression *ScopeStatement::interpret(InterState *istate) { #if LOG printf("ScopeStatement::interpret()\n"); #endif if (istate->start == this) istate->start = NULL; return statement ? statement->interpret(istate) : NULL; } Expression *resolveSlice(Expression *e) { if ( ((SliceExp *)e)->e1->op == TOKnull) return ((SliceExp *)e)->e1; return Slice(e->type, ((SliceExp *)e)->e1, ((SliceExp *)e)->lwr, ((SliceExp *)e)->upr); } /* Determine the array length, without interpreting it. * e must be an array literal, or a slice * It's very wasteful to resolve the slice when we only * need the length. */ uinteger_t resolveArrayLength(Expression *e) { if (e->op == TOKnull) return 0; if (e->op == TOKslice) { uinteger_t ilo = ((SliceExp *)e)->lwr->toInteger(); uinteger_t iup = ((SliceExp *)e)->upr->toInteger(); return iup - ilo; } if (e->op == TOKstring) { return ((StringExp *)e)->len; } if (e->op == TOKarrayliteral) { ArrayLiteralExp *ale = (ArrayLiteralExp *)e; return ale->elements ? ale->elements->dim : 0; } if (e->op == TOKassocarrayliteral) { AssocArrayLiteralExp *ale = (AssocArrayLiteralExp *)e; return ale->keys->dim; } assert(0); return 0; } // As Equal, but resolves slices before comparing Expression *ctfeEqual(enum TOK op, Type *type, Expression *e1, Expression *e2) { if (e1->op == TOKslice) e1 = resolveSlice(e1); if (e2->op == TOKslice) e2 = resolveSlice(e2); return Equal(op, type, e1, e2); } void scrubArray(Expressions *elems); /* All results destined for use outside of CTFE need to have their CTFE-specific * features removed. * In particular, all slices must be resolved. */ Expression *scrubReturnValue(Expression *e) { if (e->op == TOKslice) { e = resolveSlice(e); } if (e->op == TOKstructliteral) { StructLiteralExp *se = (StructLiteralExp *)e; scrubArray(se->elements); } if (e->op == TOKarrayliteral) { scrubArray(((ArrayLiteralExp *)e)->elements); } if (e->op == TOKassocarrayliteral) { AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; scrubArray(aae->keys); scrubArray(aae->values); } return e; } // Scrub all members of an array void scrubArray(Expressions *elems) { for (size_t i = 0; i < elems->dim; i++) { Expression *m = elems->tdata()[i]; if (!m) continue; m = scrubReturnValue(m); elems->tdata()[i] = m; } } Expression *ReturnStatement::interpret(InterState *istate) { #if LOG printf("ReturnStatement::interpret(%s)\n", exp ? exp->toChars() : ""); #endif START() if (!exp) return EXP_VOID_INTERPRET; assert(istate && istate->fd && istate->fd->type); #if DMDV2 /* 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 (istate->fd->type && istate->fd->type->ty==Tfunction) { TypeFunction *tf = (TypeFunction *)istate->fd->type; if (tf->isref && istate->caller && istate->caller->awaitingLvalueReturn) { // We need to return an lvalue Expression *e = exp->interpret(istate, ctfeNeedLvalue); if (e == EXP_CANT_INTERPRET) error("ref return %s is not yet supported in CTFE", exp->toChars()); return e; } if (tf->next && (tf->next->ty == Tdelegate) && istate->fd->closureVars.dim > 0) { // To support this, we need to copy all the closure vars // into the delegate literal. error("closures are not yet supported in CTFE"); return EXP_CANT_INTERPRET; } } #endif // We need to treat pointers specially, because TOKsymoff can be used to // return a value OR a pointer Expression *e; if ((exp->type->ty == Tpointer && exp->type->nextOf()->ty != Tfunction)) e = exp->interpret(istate, ctfeNeedLvalue); else e = exp->interpret(istate); if (e == EXP_CANT_INTERPRET) return e; if (!istate->caller) { e = scrubReturnValue(e); if (e == EXP_CANT_INTERPRET) return e; } else if (needToCopyLiteral(exp)) e = copyLiteral(e); #if LOGASSIGN printf("RETURN %s\n", loc.toChars()); showCtfeExpr(e); #endif return e; } Expression *BreakStatement::interpret(InterState *istate) { #if LOG printf("BreakStatement::interpret()\n"); #endif START() if (ident) { LabelDsymbol *label = istate->fd->searchLabel(ident); assert(label && label->statement); Statement *s = label->statement; if (s->isLabelStatement()) s = s->isLabelStatement()->statement; if (s->isScopeStatement()) s = s->isScopeStatement()->statement; istate->gotoTarget = s; return EXP_BREAK_INTERPRET; } else { istate->gotoTarget = NULL; return EXP_BREAK_INTERPRET; } } Expression *ContinueStatement::interpret(InterState *istate) { #if LOG printf("ContinueStatement::interpret()\n"); #endif START() if (ident) { LabelDsymbol *label = istate->fd->searchLabel(ident); assert(label && label->statement); Statement *s = label->statement; if (s->isLabelStatement()) s = s->isLabelStatement()->statement; if (s->isScopeStatement()) s = s->isScopeStatement()->statement; istate->gotoTarget = s; return EXP_CONTINUE_INTERPRET; } else return EXP_CONTINUE_INTERPRET; } Expression *WhileStatement::interpret(InterState *istate) { #if LOG printf("WhileStatement::interpret()\n"); #endif assert(0); // rewritten to ForStatement return NULL; } Expression *DoStatement::interpret(InterState *istate) { #if LOG printf("DoStatement::interpret()\n"); #endif if (istate->start == this) istate->start = NULL; Expression *e; if (istate->start) { e = body ? body->interpret(istate) : NULL; if (istate->start) return NULL; if (e == EXP_CANT_INTERPRET) return e; if (e == EXP_BREAK_INTERPRET) { if (!istate->gotoTarget || istate->gotoTarget == this) { istate->gotoTarget = NULL; e = NULL; } // else break at a higher level return e; } if (e == EXP_CONTINUE_INTERPRET) if (!istate->gotoTarget || istate->gotoTarget == this) { goto Lcontinue; } else // else continue at a higher level return e; if (e) return e; } while (1) { e = body ? body->interpret(istate) : NULL; if (e == EXP_CANT_INTERPRET) break; if (e == EXP_BREAK_INTERPRET) { if (!istate->gotoTarget || istate->gotoTarget == this) { istate->gotoTarget = NULL; e = NULL; } // else break at a higher level break; } if (e && e != EXP_CONTINUE_INTERPRET) break; if (istate->gotoTarget && istate->gotoTarget != this) break; // continue at a higher level Lcontinue: istate->gotoTarget = NULL; e = condition->interpret(istate); if (e == EXP_CANT_INTERPRET) break; if (!e->isConst()) { e = EXP_CANT_INTERPRET; break; } if (isTrueBool(e)) { } else if (e->isBool(FALSE)) { e = NULL; break; } else assert(0); } return e; } Expression *ForStatement::interpret(InterState *istate) { #if LOG printf("ForStatement::interpret()\n"); #endif if (istate->start == this) istate->start = NULL; Expression *e; if (init) { e = init->interpret(istate); if (e == EXP_CANT_INTERPRET) return e; assert(!e); } if (istate->start) { e = body ? body->interpret(istate) : NULL; if (istate->start) return NULL; if (e == EXP_CANT_INTERPRET) return e; if (e == EXP_BREAK_INTERPRET) { if (!istate->gotoTarget || istate->gotoTarget == this) { istate->gotoTarget = NULL; return NULL; } // else break at a higher level } if (e == EXP_CONTINUE_INTERPRET) { if (!istate->gotoTarget || istate->gotoTarget == this) { istate->gotoTarget = NULL; goto Lcontinue; } // else continue at a higher level } if (e) return e; } while (1) { if (!condition) goto Lhead; e = condition->interpret(istate); if (e == EXP_CANT_INTERPRET) break; if (!e->isConst()) { e = EXP_CANT_INTERPRET; break; } if (isTrueBool(e)) { Lhead: e = body ? body->interpret(istate) : NULL; if (e == EXP_CANT_INTERPRET) break; if (e == EXP_BREAK_INTERPRET) { if (!istate->gotoTarget || istate->gotoTarget == this) { istate->gotoTarget = NULL; e = NULL; } // else break at a higher level break; } if (e && e != EXP_CONTINUE_INTERPRET) break; if (istate->gotoTarget && istate->gotoTarget != this) break; // continue at a higher level Lcontinue: istate->gotoTarget = NULL; if (increment) { e = increment->interpret(istate); if (e == EXP_CANT_INTERPRET) break; } } else if (e->isBool(FALSE)) { e = NULL; break; } else assert(0); } return e; } Expression *ForeachStatement::interpret(InterState *istate) { #if 1 assert(0); // rewritten to ForStatement return NULL; #else #if LOG printf("ForeachStatement::interpret()\n"); #endif if (istate->start == this) istate->start = NULL; if (istate->start) return NULL; Expression *e = NULL; Expression *eaggr; if (value->isOut() || value->isRef()) return EXP_CANT_INTERPRET; eaggr = aggr->interpret(istate); if (eaggr == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; Expression *dim = ArrayLength(Type::tsize_t, eaggr); if (dim == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; Expression *keysave = key ? key->value : NULL; Expression *valuesave = value->value; uinteger_t d = dim->toUInteger(); uinteger_t index; if (op == TOKforeach) { for (index = 0; index < d; index++) { Expression *ekey = new IntegerExp(loc, index, Type::tsize_t); if (key) key->value = ekey; e = Index(value->type, eaggr, ekey); if (e == EXP_CANT_INTERPRET) break; value->value = e; e = body ? body->interpret(istate) : NULL; if (e == EXP_CANT_INTERPRET) break; if (e == EXP_BREAK_INTERPRET) { if (!istate->gotoTarget || istate->gotoTarget == this) { istate->gotoTarget = NULL; e = NULL; } // else break at a higher level break; } if (e == EXP_CONTINUE_INTERPRET) { if (istate->gotoTarget && istate->gotoTarget != this) break; // continue at higher level istate->gotoTarget = NULL; e = NULL; } else if (e) break; } } else // TOKforeach_reverse { for (index = d; index-- != 0;) { Expression *ekey = new IntegerExp(loc, index, Type::tsize_t); if (key) key->value = ekey; e = Index(value->type, eaggr, ekey); if (e == EXP_CANT_INTERPRET) break; value->value = e; e = body ? body->interpret(istate) : NULL; if (e == EXP_CANT_INTERPRET) break; if (e == EXP_BREAK_INTERPRET) { if (!istate->gotoTarget || istate->gotoTarget == this) { istate->gotoTarget = NULL; e = NULL; } // else break at a higher level break; } if (e == EXP_CONTINUE_INTERPRET) { if (istate->gotoTarget && istate->gotoTarget != this) break; // continue at higher level istate->gotoTarget = NULL; e = NULL; } else if (e) break; } } value->value = valuesave; if (key) key->value = keysave; return e; #endif } #if DMDV2 Expression *ForeachRangeStatement::interpret(InterState *istate) { #if 1 assert(0); // rewritten to ForStatement return NULL; #else #if LOG printf("ForeachRangeStatement::interpret()\n"); #endif if (istate->start == this) istate->start = NULL; if (istate->start) return NULL; Expression *e = NULL; Expression *elwr = lwr->interpret(istate); if (elwr == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; Expression *eupr = upr->interpret(istate); if (eupr == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; Expression *keysave = key->value; if (op == TOKforeach) { key->value = elwr; while (1) { e = Cmp(TOKlt, key->value->type, key->value, eupr); if (e == EXP_CANT_INTERPRET) break; if (e->isBool(TRUE) == FALSE) { e = NULL; break; } e = body ? body->interpret(istate) : NULL; if (e == EXP_CANT_INTERPRET) break; if (e == EXP_BREAK_INTERPRET) { if (!istate->gotoTarget || istate->gotoTarget == this) { istate->gotoTarget = NULL; e = NULL; } // else break at a higher level break; } if (e == EXP_CONTINUE_INTERPRET && istate->gotoTarget && istate->gotoTarget != this) break; // continue at higher level if (e == NULL || e == EXP_CONTINUE_INTERPRET) { e = Add(key->value->type, key->value, new IntegerExp(loc, 1, key->value->type)); istate->gotoTarget = NULL; if (e == EXP_CANT_INTERPRET) break; key->value = e; } else break; } } else // TOKforeach_reverse { key->value = eupr; do { e = Cmp(TOKgt, key->value->type, key->value, elwr); if (e == EXP_CANT_INTERPRET) break; if (e->isBool(TRUE) == FALSE) { e = NULL; break; } e = Min(key->value->type, key->value, new IntegerExp(loc, 1, key->value->type)); if (e == EXP_CANT_INTERPRET) break; key->value = e; e = body ? body->interpret(istate) : NULL; if (e == EXP_CANT_INTERPRET) break; if (e == EXP_BREAK_INTERPRET) { if (!istate->gotoTarget || istate->gotoTarget == this) { istate->gotoTarget = NULL; e = NULL; } // else break at a higher level break; } if (e == EXP_CONTINUE_INTERPRET) { if (istate->gotoTarget && istate->gotoTarget != this) break; // continue at higher level istate->gotoTarget = NULL; } } while (e == NULL || e == EXP_CONTINUE_INTERPRET); } key->value = keysave; return e; #endif } #endif Expression *SwitchStatement::interpret(InterState *istate) { #if LOG printf("SwitchStatement::interpret()\n"); #endif if (istate->start == this) istate->start = NULL; Expression *e = NULL; if (istate->start) { e = body ? body->interpret(istate) : NULL; if (istate->start) return NULL; if (e == EXP_CANT_INTERPRET) return e; if (e == EXP_BREAK_INTERPRET) { if (!istate->gotoTarget || istate->gotoTarget == this) { istate->gotoTarget = NULL; return NULL; } // else break at a higher level } return e; } Expression *econdition = condition->interpret(istate); if (econdition == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; if (econdition->op == TOKslice) econdition = resolveSlice(econdition); Statement *s = NULL; if (cases) { for (size_t i = 0; i < cases->dim; i++) { CaseStatement *cs = cases->tdata()[i]; e = ctfeEqual(TOKequal, Type::tint32, econdition, cs->exp); if (e == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; if (e->isBool(TRUE)) { s = cs; break; } } } if (!s) { if (hasNoDefault) error("no default or case for %s in switch statement", econdition->toChars()); s = sdefault; } #if IN_LLVM if (!s) return EXP_CANT_INTERPRET; #endif assert(s); istate->start = s; e = body ? body->interpret(istate) : NULL; assert(!istate->start); if (e == EXP_BREAK_INTERPRET) { if (!istate->gotoTarget || istate->gotoTarget == this) { istate->gotoTarget = NULL; e = NULL; } // else break at a higher level } return e; } Expression *CaseStatement::interpret(InterState *istate) { #if LOG printf("CaseStatement::interpret(%s) this = %p\n", exp->toChars(), this); #endif if (istate->start == this) istate->start = NULL; if (statement) return statement->interpret(istate); else return NULL; } Expression *DefaultStatement::interpret(InterState *istate) { #if LOG printf("DefaultStatement::interpret()\n"); #endif if (istate->start == this) istate->start = NULL; if (statement) return statement->interpret(istate); else return NULL; } Expression *GotoStatement::interpret(InterState *istate) { #if LOG printf("GotoStatement::interpret()\n"); #endif START() assert(label && label->statement); istate->gotoTarget = label->statement; return EXP_GOTO_INTERPRET; } Expression *GotoCaseStatement::interpret(InterState *istate) { #if LOG printf("GotoCaseStatement::interpret()\n"); #endif START() assert(cs); istate->gotoTarget = cs; return EXP_GOTO_INTERPRET; } Expression *GotoDefaultStatement::interpret(InterState *istate) { #if LOG printf("GotoDefaultStatement::interpret()\n"); #endif START() assert(sw && sw->sdefault); istate->gotoTarget = sw->sdefault; return EXP_GOTO_INTERPRET; } Expression *LabelStatement::interpret(InterState *istate) { #if LOG printf("LabelStatement::interpret()\n"); #endif if (istate->start == this) istate->start = NULL; return statement ? statement->interpret(istate) : NULL; } Expression *TryCatchStatement::interpret(InterState *istate) { #if LOG printf("TryCatchStatement::interpret()\n"); #endif START() error("try-catch statements are not yet supported in CTFE"); return EXP_CANT_INTERPRET; } Expression *TryFinallyStatement::interpret(InterState *istate) { #if LOG printf("TryFinallyStatement::interpret()\n"); #endif START() error("try-finally statements are not yet supported in CTFE"); return EXP_CANT_INTERPRET; } Expression *ThrowStatement::interpret(InterState *istate) { #if LOG printf("ThrowStatement::interpret()\n"); #endif START() error("throw statements are not yet supported in CTFE"); return EXP_CANT_INTERPRET; } Expression *OnScopeStatement::interpret(InterState *istate) { #if LOG printf("OnScopeStatement::interpret()\n"); #endif START() error("scope guard statements are not yet supported in CTFE"); return EXP_CANT_INTERPRET; } Expression *WithStatement::interpret(InterState *istate) { #if LOG printf("WithStatement::interpret()\n"); #endif START() error("with statements are not yet supported in CTFE"); return EXP_CANT_INTERPRET; } Expression *AsmStatement::interpret(InterState *istate) { #if LOG printf("AsmStatement::interpret()\n"); #endif START() error("asm statements cannot be interpreted at compile time"); return EXP_CANT_INTERPRET; } #if DMDV2 Expression *ImportStatement::interpret(InterState *istate) { #if LOG printf("ImportStatement::interpret()\n"); #endif START(); return NULL; } #endif /******************************** Expression ***************************/ Expression *Expression::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("Expression::interpret() %s\n", toChars()); printf("type = %s\n", type->toChars()); dump(0); #endif error("Cannot interpret %s at compile time", toChars()); return EXP_CANT_INTERPRET; } Expression *ThisExp::interpret(InterState *istate, CtfeGoal goal) { if (istate && istate->localThis && istate->localThis->op == TOKstructliteral) return istate->localThis; if (istate && istate->localThis) return istate->localThis->interpret(istate, goal); error("value of 'this' is not known at compile time"); return EXP_CANT_INTERPRET; } Expression *NullExp::interpret(InterState *istate, CtfeGoal goal) { return this; } Expression *IntegerExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("IntegerExp::interpret() %s\n", toChars()); #endif return this; } Expression *RealExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("RealExp::interpret() %s\n", toChars()); #endif return this; } Expression *ComplexExp::interpret(InterState *istate, CtfeGoal goal) { return this; } Expression *StringExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("StringExp::interpret() %s\n", toChars()); #endif /* Since we are using StringExps as reference types for char[] arrays, * we need to dup them if there's any chance they'll be modified. * For efficiency, we try to only dup when necessary. */ // Fixed-length char arrays always get duped later anyway. if (type->ty == Tsarray) return this; /* String literals are normally immutable, so we don't need to dup them * In D2, we can detect attempts to write to read-only literals. * For D1, we could be pessimistic, and always dup. * But since it fails only when there has been an explicit cast, and any * such function would give different results at runtime anyway (eg, it * may crash), it hardly seems worth the massive performance hit. */ #if DMDV2 if (!(((TypeNext *)type)->next->mod & (MODconst | MODimmutable))) { // It seems this happens only when there has been an explicit cast error("cannot cast a read-only string literal to mutable in CTFE"); return EXP_CANT_INTERPRET; } #endif return this; } Expression *FuncExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("FuncExp::interpret() %s\n", toChars()); #endif return this; } Expression *SymOffExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("SymOffExp::interpret() %s\n", toChars()); #endif if (var->isFuncDeclaration() && offset == 0) { return this; } if (type->ty != Tpointer) { // Probably impossible error("Cannot interpret %s at compile time", toChars()); return EXP_CANT_INTERPRET; } Type *pointee = ((TypePointer *)type)->next; Expression *val = getVarExp(loc, istate, var, goal); if (val->type->ty == Tarray || val->type->ty == Tsarray) { // Check for unsupported type painting operations Type *elemtype = ((TypeArray *)(val->type))->next; if ( #if DMDV2 elemtype->castMod(0) != pointee->castMod(0) #else elemtype != pointee #endif && !(elemtype->isintegral() && pointee->isintegral() && elemtype->size() == pointee->size())) { error("reinterpreting cast from %s to %s is not supported in CTFE", val->type->toChars(), type->toChars()); return EXP_CANT_INTERPRET; } TypeArray *tar = (TypeArray *)val->type; dinteger_t sz = pointee->size(); dinteger_t indx = offset/sz; assert(sz * indx == offset); Expression *aggregate = NULL; if (val->op == TOKarrayliteral || val->op == TOKstring) aggregate = val; else if (val->op == TOKslice) { aggregate = ((SliceExp *)val)->e1; Expression *lwr = ((SliceExp *)val)->lwr->interpret(istate); indx += lwr->toInteger(); } if (aggregate) { IntegerExp *ofs = new IntegerExp(loc, indx, Type::tsize_t); IndexExp *ie = new IndexExp(loc, aggregate, ofs); ie->type = type; return ie; } } else if (offset == 0 && #if DMDV2 pointee->castMod(0) == var->type->castMod(0) #else pointee == var->type #endif ) { if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) { VarExp *ve = new VarExp(loc, var); ve->type = type; return ve; } Expression *e = getVarExp(loc, istate, var, goal); e = new AddrExp(loc, e); e->type = type; return e; } error("Cannot convert &%s to %s at compile time", var->type->toChars(), type->toChars()); return EXP_CANT_INTERPRET; } Expression *AddrExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("AddrExp::interpret() %s\n", toChars()); #endif #if IN_LLVM if (e1->op == TOKvar) { VarExp *ve = (VarExp *)e1; if (ve->var->isFuncDeclaration()) return this; if (type->ty != Tpointer) { // Probably impossible error("Cannot interpret %s at compile time", toChars()); return EXP_CANT_INTERPRET; } Type *pointee = ((TypePointer *)type)->next; if (pointee == ve->type) { if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) { ve = (VarExp*)ve->syntaxCopy(); ve->type = type; return ve; } else { Expression *e = getVarExp(loc, istate, ve->var, goal); e = new AddrExp(loc, e); e->type = type; return e; } } } else if (e1->op == TOKindex) { IndexExp *ae = (IndexExp *)e1; if (ae->e2->op == TOKint64 && ae->e1->op == TOKvar) { dinteger_t indx = ae->e2->toInteger(); VarExp *ve = (VarExp *)ae->e1; if (/*ve->type->ty == Tarray || */ve->type->ty == Tsarray) { Expression *val = getVarExp(loc, istate, ve->var, goal); Expression *aggregate = NULL; if (val->op == TOKarrayliteral || val->op == TOKstring) aggregate = val; else if (val->op == TOKslice) { aggregate = ((SliceExp *)val)->e1; Expression *lwr = ((SliceExp *)val)->lwr->interpret(istate); indx += lwr->toInteger(); } if (aggregate) { IntegerExp *ofs = new IntegerExp(loc, indx, Type::tsize_t); IndexExp *ie = new IndexExp(loc, aggregate, ofs); ie->type = type; return ie; } } } } #endif // For reference types, we need to return an lvalue ref. TY tb = e1->type->toBasetype()->ty; bool needRef = (tb == Tarray || tb == Taarray || tb == Tclass); Expression *e = e1->interpret(istate, needRef ? ctfeNeedLvalueRef : ctfeNeedLvalue); if (e == EXP_CANT_INTERPRET) return e; // Return a simplified address expression e = new AddrExp(loc, e); e->type = type; return e; } Expression *DelegateExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("DelegateExp::interpret() %s\n", toChars()); #endif return this; } // ------------------------------------------------------------- // Remove out, ref, and this // ------------------------------------------------------------- // The variable used in a dotvar, index, or slice expression, // after 'out', 'ref', and 'this' have been removed. // *isReference will be set to true if a reference was removed. Expression * resolveReferences(Expression *e, Expression *thisval, bool *isReference /*=NULL */) { if (isReference) *isReference = false; for(;;) { if (e->op == TOKthis) { assert(thisval); assert(e != thisval); e = thisval; continue; } if (e->op == TOKvar) { // Chase down rebinding of out and ref. VarExp *ve = (VarExp *)e; VarDeclaration *v = ve->var->isVarDeclaration(); if (v->type->ty == Tpointer) break; if (v && v->getValue() && v->getValue()->op == TOKvar) // it's probably a reference { // Make sure it's a real reference. // It's not a reference if v is a struct initialized to // 0 using an __initZ StaticStructInitDeclaration from // TypeStruct::defaultInit() VarExp *ve2 = (VarExp *)v->getValue(); if (!ve2->var->isStaticStructInitDeclaration()) { if (isReference) *isReference = true; e = v->getValue(); continue; } } else if (v && v->getValue() && (v->getValue()->op == TOKslice)) { SliceExp *se = (SliceExp *)v->getValue(); if (se->e1->op == TOKarrayliteral || se->e1->op == TOKassocarrayliteral || se->e1->op == TOKstring) break; e = v->getValue(); continue; } else if (v && v->getValue() && (v->getValue()->op==TOKindex || v->getValue()->op == TOKdotvar || v->getValue()->op == TOKthis )) { e = v->getValue(); continue; } } break; } return e; } Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal) { Expression *e = EXP_CANT_INTERPRET; VarDeclaration *v = d->isVarDeclaration(); #if IN_LLVM StaticStructInitDeclaration *s = d->isStaticStructInitDeclaration(); #else SymbolDeclaration *s = d->isSymbolDeclaration(); #endif if (v) { #if DMDV2 /* Magic variable __ctfe always returns true when interpreting */ if (v->ident == Id::ctfe) return new IntegerExp(loc, 1, Type::tbool); if ((v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) && v->init && !v->getValue()) #else if (v->isConst() && v->init) #endif { e = v->init->toExpression(); if (e && (e->op == TOKconstruct || e->op == TOKblit)) { AssignExp *ae = (AssignExp *)e; e = ae->e2; v->inuse++; e = e->interpret(istate, ctfeNeedAnyValue); v->inuse--; if (e == EXP_CANT_INTERPRET) return e; e->type = v->type; } else { if (e && !e->type) e->type = v->type; if (e) e = e->interpret(istate, ctfeNeedAnyValue); } if (e && e != EXP_CANT_INTERPRET) v->setValueWithoutChecking(e); } else if (v->isCTFE() && !v->getValue()) { if (v->init && v->type->size() != 0) { if (v->init->isVoidInitializer()) { error(loc, "variable %s is used before initialization", v->toChars()); return EXP_CANT_INTERPRET; } e = v->init->toExpression(); e = e->interpret(istate); } else e = v->type->defaultInitLiteral(loc); } else if (!v->isDataseg() && !v->isCTFE() && !istate) { error(loc, "variable %s cannot be read at compile time", v->toChars()); return EXP_CANT_INTERPRET; } else { e = v->getValue(); if (!e && !v->isCTFE() && v->isDataseg()) { error(loc, "static variable %s cannot be read at compile time", v->toChars()); e = EXP_CANT_INTERPRET; } else if (!e) error(loc, "variable %s is used before initialization", v->toChars()); else if (e == EXP_CANT_INTERPRET) return e; else if ((goal == ctfeNeedLvalue) || e->op == TOKstring || e->op == TOKstructliteral || e->op == TOKarrayliteral || e->op == TOKassocarrayliteral || e->op == TOKslice || e->type->toBasetype()->ty == Tpointer) return e; // it's already an Lvalue else e = e->interpret(istate, goal); } if (!e) e = EXP_CANT_INTERPRET; } else if (s) { #if !IN_LLVM // Struct static initializers, for example if (s->dsym->toInitializer() == s->sym) { #endif Expressions *exps = new Expressions(); e = new StructLiteralExp(loc, s->dsym, exps); e = e->semantic(NULL); if (e->op == TOKerror) e = EXP_CANT_INTERPRET; #if !IN_LLVM } else error(loc, "cannot interpret symbol %s at compile time", v->toChars()); #endif } else error(loc, "cannot interpret declaration %s at compile time", d->toChars()); return e; } Expression *VarExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("VarExp::interpret() %s\n", toChars()); #endif if (goal == ctfeNeedLvalueRef) { // If it is a reference, return the thing it's pointing to. VarDeclaration *v = var->isVarDeclaration(); if (v && v->getValue() && (v->storage_class & (STCref | STCout))) return v->getValue(); if (v && !v->isDataseg() && !v->isCTFE() && !istate) { error("variable %s cannot be referenced at compile time", v->toChars()); return EXP_CANT_INTERPRET; } else if (v && !v->getValue() && !v->isCTFE() && v->isDataseg()) { error("static variable %s cannot be referenced at compile time", v->toChars()); return EXP_CANT_INTERPRET; } return this; } Expression *e = getVarExp(loc, istate, var, goal); // A VarExp may include an implicit cast. It must be done explicitly. if (e != EXP_CANT_INTERPRET) e = paintTypeOntoLiteral(type, e); return e; } Expression *DeclarationExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("DeclarationExp::interpret() %s\n", toChars()); #endif Expression *e; VarDeclaration *v = declaration->isVarDeclaration(); if (v) { if (v->getValue()) { addVarToInterstate(istate, v); v->setValueNull(); } Dsymbol *s = v->toAlias(); if (s == v && !v->isStatic() && v->init) { ExpInitializer *ie = v->init->isExpInitializer(); if (ie) e = ie->exp->interpret(istate); else if (v->init->isVoidInitializer()) e = NULL; else { error("Declaration %s is not yet implemented in CTFE", toChars()); e = EXP_CANT_INTERPRET; } } else if (s == v && !v->init && v->type->size()==0) { // Zero-length arrays don't need an initializer e = v->type->defaultInitLiteral(loc); } #if DMDV2 else if (s == v && (v->isConst() || v->isImmutable()) && v->init) #else else if (s == v && v->isConst() && v->init) #endif { e = v->init->toExpression(); if (!e) e = EXP_CANT_INTERPRET; else if (!e->type) e->type = v->type; } else if (s->isTupleDeclaration() && !v->init) e = NULL; else { error("Declaration %s is not yet implemented in CTFE", toChars()); e = EXP_CANT_INTERPRET; } } else if (declaration->isAttribDeclaration() || declaration->isTemplateMixin() || declaration->isTupleDeclaration()) { // These can be made to work, too lazy now error("Declaration %s is not yet implemented in CTFE", toChars()); e = EXP_CANT_INTERPRET; } else { // Others should not contain executable code, so are trivial to evaluate e = NULL; } #if LOG printf("-DeclarationExp::interpret(%s): %p\n", toChars(), e); #endif return e; } Expression *TupleExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("TupleExp::interpret() %s\n", toChars()); #endif Expressions *expsx = NULL; for (size_t i = 0; i < exps->dim; i++) { Expression *e = exps->tdata()[i]; Expression *ex; ex = e->interpret(istate); if (ex == EXP_CANT_INTERPRET) { delete expsx; return ex; } // A tuple of assignments can contain void (Bug 5676). if (goal == ctfeNeedNothing) continue; if (ex == EXP_VOID_INTERPRET) { error("ICE: void element %s in tuple", e->toChars()); assert(0); } /* If any changes, do Copy On Write */ if (ex != e) { if (!expsx) { expsx = new Expressions(); expsx->setDim(exps->dim); for (size_t j = 0; j < i; j++) { expsx->tdata()[j] = exps->tdata()[j]; } } expsx->tdata()[i] = ex; } } if (expsx) { TupleExp *te = new TupleExp(loc, expsx); expandTuples(te->exps); te->type = new TypeTuple(te->exps); return te; } return this; } Expression *ArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) { Expressions *expsx = NULL; #if LOG printf("ArrayLiteralExp::interpret() %s\n", toChars()); #endif if (elements) { for (size_t i = 0; i < elements->dim; i++) { Expression *e = elements->tdata()[i]; Expression *ex; if (e->op == TOKindex) // segfault bug 6250 assert( ((IndexExp*)e)->e1 != this); ex = e->interpret(istate); if (ex == EXP_CANT_INTERPRET) goto Lerror; /* If any changes, do Copy On Write */ if (ex != e) { if (!expsx) { expsx = new Expressions(); expsx->setDim(elements->dim); for (size_t j = 0; j < elements->dim; j++) { expsx->tdata()[j] = elements->tdata()[j]; } } expsx->tdata()[i] = ex; } } } if (elements && expsx) { expandTuples(expsx); if (expsx->dim != elements->dim) goto Lerror; ArrayLiteralExp *ae = new ArrayLiteralExp(loc, expsx); ae->type = type; return ae; } #if DMDV2 if (((TypeNext *)type)->next->mod & (MODconst | MODimmutable)) { // If it's immutable, we don't need to dup it return this; } #endif return copyLiteral(this); Lerror: if (expsx) delete expsx; error("cannot interpret array literal"); return EXP_CANT_INTERPRET; } Expression *AssocArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) { Expressions *keysx = keys; Expressions *valuesx = values; #if LOG printf("AssocArrayLiteralExp::interpret() %s\n", toChars()); #endif for (size_t i = 0; i < keys->dim; i++) { Expression *ekey = keys->tdata()[i]; Expression *evalue = values->tdata()[i]; Expression *ex; ex = ekey->interpret(istate); if (ex == EXP_CANT_INTERPRET) goto Lerr; /* If any changes, do Copy On Write */ if (ex != ekey) { if (keysx == keys) keysx = (Expressions *)keys->copy(); keysx->tdata()[i] = ex; } ex = evalue->interpret(istate); if (ex == EXP_CANT_INTERPRET) goto Lerr; /* If any changes, do Copy On Write */ if (ex != evalue) { if (valuesx == values) valuesx = (Expressions *)values->copy(); valuesx->tdata()[i] = ex; } } if (keysx != keys) expandTuples(keysx); if (valuesx != values) expandTuples(valuesx); if (keysx->dim != valuesx->dim) goto Lerr; /* Remove duplicate keys */ for (size_t i = 1; i < keysx->dim; i++) { Expression *ekey = keysx->tdata()[i - 1]; if (ekey->op == TOKslice) ekey = resolveSlice(ekey); for (size_t j = i; j < keysx->dim; j++) { Expression *ekey2 = keysx->tdata()[j]; Expression *ex = ctfeEqual(TOKequal, Type::tbool, ekey, ekey2); if (ex == EXP_CANT_INTERPRET) goto Lerr; if (ex->isBool(TRUE)) // if a match { // Remove ekey if (keysx == keys) keysx = (Expressions *)keys->copy(); if (valuesx == values) valuesx = (Expressions *)values->copy(); keysx->remove(i - 1); valuesx->remove(i - 1); i -= 1; // redo the i'th iteration break; } } } if (keysx != keys || valuesx != values) { AssocArrayLiteralExp *ae; ae = new AssocArrayLiteralExp(loc, keysx, valuesx); ae->type = type; return ae; } return this; Lerr: if (keysx != keys) delete keysx; if (valuesx != values) delete values; return EXP_CANT_INTERPRET; } Expression *StructLiteralExp::interpret(InterState *istate, CtfeGoal goal) { Expressions *expsx = NULL; #if LOG printf("StructLiteralExp::interpret() %s\n", toChars()); #endif /* We don't know how to deal with overlapping fields */ if (sd->hasUnions) { error("Unions with overlapping fields are not yet supported in CTFE"); return EXP_CANT_INTERPRET; } if (elements) { for (size_t i = 0; i < elements->dim; i++) { Expression *e = elements->tdata()[i]; if (!e) continue; Expression *ex = e->interpret(istate); if (ex == EXP_CANT_INTERPRET) { delete expsx; return EXP_CANT_INTERPRET; } /* If any changes, do Copy On Write */ if (ex != e) { if (!expsx) { expsx = new Expressions(); expsx->setDim(elements->dim); for (size_t j = 0; j < elements->dim; j++) { expsx->tdata()[j] = elements->tdata()[j]; } } expsx->tdata()[i] = ex; } } } if (elements && expsx) { expandTuples(expsx); if (expsx->dim != elements->dim) { delete expsx; return EXP_CANT_INTERPRET; } StructLiteralExp *se = new StructLiteralExp(loc, sd, expsx); se->type = type; return se; } return copyLiteral(this); } /****************************** * Helper for NewExp * Create an array literal consisting of 'elem' duplicated 'dim' times. */ ArrayLiteralExp *createBlockDuplicatedArrayLiteral(Type *type, Expression *elem, size_t dim) { Expressions *elements = new Expressions(); elements->setDim(dim); for (size_t i = 0; i < dim; i++) elements->tdata()[i] = elem; ArrayLiteralExp *ae = new ArrayLiteralExp(0, elements); ae->type = type; return ae; } /****************************** * Helper for NewExp * Create a string literal consisting of 'value' duplicated 'dim' times. */ StringExp *createBlockDuplicatedStringLiteral(Type *type, unsigned value, size_t dim, int sz) { unsigned char *s; s = (unsigned char *)mem.calloc(dim + 1, sz); for (size_t elemi=0; elemitype = type; se->sz = sz; se->committed = true; return se; } // Create an array literal of type 'newtype' with dimensions given by // 'arguments'[argnum..$] Expression *recursivelyCreateArrayLiteral(Type *newtype, InterState *istate, Expressions *arguments, int argnum) { Expression *lenExpr = ((arguments->tdata()[argnum]))->interpret(istate); if (lenExpr == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; size_t len = (size_t)(lenExpr->toInteger()); Type *elemType = ((TypeArray *)newtype)->next; if (elemType->ty == Tarray) { assert(argnum < arguments->dim - 1); Expression *elem = recursivelyCreateArrayLiteral(elemType, istate, arguments, argnum + 1); if (elem == EXP_CANT_INTERPRET) return elem; Expressions *elements = new Expressions(); elements->setDim(len); for (size_t i = 0; i < len; i++) elements->tdata()[i] = copyLiteral(elem); ArrayLiteralExp *ae = new ArrayLiteralExp(0, elements); ae->type = newtype; return ae; } assert(argnum == arguments->dim - 1); if (elemType->ty == Tchar || elemType->ty == Twchar || elemType->ty == Tdchar) return createBlockDuplicatedStringLiteral(newtype, (unsigned)(elemType->defaultInitLiteral()->toInteger()), len, elemType->size()); return createBlockDuplicatedArrayLiteral(newtype, elemType->defaultInitLiteral(), len); } Expression *NewExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("NewExp::interpret() %s\n", toChars()); #endif if (newtype->ty == Tarray && arguments) return recursivelyCreateArrayLiteral(newtype, istate, arguments, 0); if (newtype->toBasetype()->ty == Tstruct) { Expression *se = newtype->defaultInitLiteral(); #if DMDV2 if (member) { int olderrors = global.errors; member->interpret(istate, arguments, se); if (olderrors != global.errors) { error("cannot evaluate %s at compile time", toChars()); return EXP_CANT_INTERPRET; } } #else // The above code would fail on D1 because it doesn't use STRUCTTHISREF, // but that's OK because D1 doesn't have struct constructors anyway. assert(!member); #endif Expression *e = new AddrExp(loc, se); e->type = type; return e; } if (newtype->ty == Tclass) { error("classes are not yet supported in CTFE"); return EXP_CANT_INTERPRET; } error("Cannot interpret %s at compile time", toChars()); return EXP_CANT_INTERPRET; } Expression *UnaExp::interpretCommon(InterState *istate, CtfeGoal goal, Expression *(*fp)(Type *, Expression *)) { Expression *e; Expression *e1; #if LOG printf("UnaExp::interpretCommon() %s\n", toChars()); #endif e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) goto Lcant; e = (*fp)(type, e1); return e; Lcant: return EXP_CANT_INTERPRET; } #define UNA_INTERPRET(op) \ Expression *op##Exp::interpret(InterState *istate, CtfeGoal goal) \ { \ return interpretCommon(istate, goal, &op); \ } UNA_INTERPRET(Neg) UNA_INTERPRET(Com) UNA_INTERPRET(Not) UNA_INTERPRET(Bool) Expression *getAggregateFromPointer(Expression *e, dinteger_t *ofs) { *ofs = 0; if (e->op == TOKaddress) e = ((AddrExp *)e)->e1; if (e->op == TOKindex) { IndexExp *ie = (IndexExp *)e; // Note that each AA element is part of its own memory block if ((ie->e1->type->ty == Tarray || ie->e1->type->ty == Tsarray || ie->e1->op == TOKstring || ie->e1->op==TOKarrayliteral) && ie->e2->op == TOKint64) { *ofs = ie->e2->toInteger(); return ie->e1; } } return e; } // return e1 - e2 as an integer, or error if not possible Expression *pointerDifference(Loc loc, Type *type, Expression *e1, Expression *e2) { dinteger_t ofs1, ofs2; Expression *agg1 = getAggregateFromPointer(e1, &ofs1); Expression *agg2 = getAggregateFromPointer(e2, &ofs2); if (agg1 == agg2) { Type *pointee = ((TypePointer *)agg1->type)->next; dinteger_t sz = pointee->size(); return new IntegerExp(loc, (ofs1-ofs2)*sz, type); } else if (agg1->op == TOKstring && agg2->op == TOKstring) { if (((StringExp *)agg1)->string == ((StringExp *)agg2)->string) { Type *pointee = ((TypePointer *)agg1->type)->next; dinteger_t sz = pointee->size(); return new IntegerExp(loc, (ofs1-ofs2)*sz, type); } } #if LOGASSIGN printf("FAILED POINTER DIFF\n"); showCtfeExpr(agg1); showCtfeExpr(agg2); #endif error(loc, "%s - %s cannot be interpreted at compile time: cannot subtract " "pointers to two different memory blocks", e1->toChars(), e2->toChars()); return EXP_CANT_INTERPRET; } // Return eptr op e2, where eptr is a pointer, e2 is an integer, // and op is TOKadd or TOKmin Expression *pointerArithmetic(Loc loc, enum TOK op, Type *type, Expression *eptr, Expression *e2) { dinteger_t ofs1, ofs2; if (eptr->op == TOKaddress) eptr = ((AddrExp *)eptr)->e1; Expression *agg1 = getAggregateFromPointer(eptr, &ofs1); if (agg1->op != TOKstring && agg1->op != TOKarrayliteral) { error(loc, "cannot perform pointer arithmetic on non-arrays at compile time"); return EXP_CANT_INTERPRET; } ofs2 = e2->toInteger(); Type *pointee = ((TypePointer *)agg1->type)->next; dinteger_t sz = pointee->size(); Expression *dollar = ArrayLength(Type::tsize_t, agg1); assert(dollar != EXP_CANT_INTERPRET); dinteger_t len = dollar->toInteger(); Expression *val = agg1; TypeArray *tar = (TypeArray *)val->type; dinteger_t indx = ofs1; #if IN_LLVM // LDC: llvm uses typesafe pointer arithmetic if (op == TOKadd || op == TOKaddass || op == TOKplusplus) indx += ofs2; else if (op == TOKmin || op == TOKminass || op == TOKminusminus) indx -= ofs2; #else if (op == TOKadd || op == TOKaddass || op == TOKplusplus) indx = indx + ofs2/sz; else if (op == TOKmin || op == TOKminass || op == TOKminusminus) indx -= ofs2/sz; #endif else { error(loc, "CTFE Internal compiler error: bad pointer operation"); return EXP_CANT_INTERPRET; } if (val->op != TOKarrayliteral && val->op != TOKstring) { error(loc, "CTFE Internal compiler error: pointer arithmetic %s", val->toChars()); return EXP_CANT_INTERPRET; } if (indx < 0 || indx > len) { error(loc, "cannot assign pointer to index %jd inside memory block [0..%jd]", indx, len); return EXP_CANT_INTERPRET; } IntegerExp *ofs = new IntegerExp(loc, indx, Type::tsize_t); IndexExp *ie = new IndexExp(loc, val, ofs); ie->type = type; return ie; } typedef Expression *(*fp_t)(Type *, Expression *, Expression *); Expression *BinExp::interpretCommon(InterState *istate, CtfeGoal goal, fp_t fp) { Expression *e; Expression *e1; Expression *e2; #if LOG printf("BinExp::interpretCommon() %s\n", toChars()); #endif if (this->e1->type->ty == Tpointer && this->e2->type->ty == Tpointer && op == TOKmin) { e1 = this->e1->interpret(istate, ctfeNeedLvalue); e2 = this->e2->interpret(istate, ctfeNeedLvalue); if (e1 == EXP_CANT_INTERPRET || e2 == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; return pointerDifference(loc, type, e1, e2); } if (this->e1->type->ty == Tpointer && this->e2->type->isintegral()) { e1 = this->e1->interpret(istate, ctfeNeedLvalue); e2 = this->e2->interpret(istate); if (e1 == EXP_CANT_INTERPRET || e2 == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; return pointerArithmetic(loc, op, type, e1, e2); } if (this->e2->type->ty == Tpointer && this->e1->type->isintegral() && op==TOKadd) { e2 = this->e2->interpret(istate, ctfeNeedLvalue); e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET || e2 == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; return pointerArithmetic(loc, op, type, e2, e1); } if (this->e1->type->ty == Tpointer || this->e2->type->ty == Tpointer) { error("pointer expression %s cannot be interpreted at compile time", toChars()); return EXP_CANT_INTERPRET; } e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) goto Lcant; if (e1->isConst() != 1) goto Lcant; e2 = this->e2->interpret(istate); if (e2 == EXP_CANT_INTERPRET) goto Lcant; if (e2->isConst() != 1) goto Lcant; e = (*fp)(type, e1, e2); if (e == EXP_CANT_INTERPRET) error("%s cannot be interpreted at compile time", toChars()); return e; Lcant: return EXP_CANT_INTERPRET; } #define BIN_INTERPRET(op) \ Expression *op##Exp::interpret(InterState *istate, CtfeGoal goal) \ { \ return interpretCommon(istate, goal, &op); \ } BIN_INTERPRET(Add) BIN_INTERPRET(Min) BIN_INTERPRET(Mul) BIN_INTERPRET(Div) BIN_INTERPRET(Mod) BIN_INTERPRET(Shl) BIN_INTERPRET(Shr) BIN_INTERPRET(Ushr) BIN_INTERPRET(And) BIN_INTERPRET(Or) BIN_INTERPRET(Xor) typedef Expression *(*fp2_t)(enum TOK, Type *, Expression *, Expression *); // Return EXP_CANT_INTERPRET if they point to independent memory blocks Expression *comparePointers(Loc loc, enum TOK op, Type *type, Expression *e1, Expression *e2) { dinteger_t ofs1, ofs2; Expression *agg1 = getAggregateFromPointer(e1, &ofs1); Expression *agg2 = getAggregateFromPointer(e2, &ofs2); if (agg1 == agg2 || (agg1->op == TOKstring && agg2->op == TOKstring && ((StringExp *)agg1)->string == ((StringExp *)agg2)->string)) { dinteger_t cm = ofs1 - ofs2; dinteger_t n; dinteger_t zero = 0; switch(op) { case TOKlt: n = (ofs1 < ofs2); break; case TOKle: n = (ofs1 <= ofs2); break; case TOKgt: n = (ofs1 > ofs2); break; case TOKge: n = (ofs1 >= ofs2); break; case TOKidentity: case TOKequal: n = (ofs1 == ofs2); break; case TOKnotidentity: case TOKnotequal: n = (ofs1 != ofs2); break; default: assert(0); } return new IntegerExp(loc, n, type); } int cmp; if (e1->op == TOKnull) { cmp = (e2->op == TOKnull); } else if (e2->op == TOKnull) { cmp = 0; } else { switch(op) { case TOKidentity: case TOKequal: case TOKnotidentity: // 'cmp' gets inverted below case TOKnotequal: cmp = 0; break; default: return EXP_CANT_INTERPRET; } } if (op == TOKnotidentity || op == TOKnotequal) cmp ^= 1; return new IntegerExp(loc, cmp, type); } Expression *BinExp::interpretCommon2(InterState *istate, CtfeGoal goal, fp2_t fp) { Expression *e; Expression *e1; Expression *e2; #if LOG printf("BinExp::interpretCommon2() %s\n", toChars()); #endif if (this->e1->type->ty == Tpointer && this->e2->type->ty == Tpointer) { e1 = this->e1->interpret(istate, ctfeNeedLvalue); e2 = this->e2->interpret(istate, ctfeNeedLvalue); if (e1 == EXP_CANT_INTERPRET || e2 == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; e = comparePointers(loc, op, type, e1, e2); if (e == EXP_CANT_INTERPRET) { error("%s and %s point to independent memory blocks and " "cannot be compared at compile time", this->e1->toChars(), this->e2->toChars()); } return e; } e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) goto Lcant; if (e1->op == TOKslice) e1 = resolveSlice(e1); if (e1->isConst() != 1 && e1->op != TOKnull && e1->op != TOKstring && e1->op != TOKarrayliteral && e1->op != TOKstructliteral) { error("cannot compare %s at compile time", e1->toChars()); goto Lcant; } e2 = this->e2->interpret(istate); if (e2 == EXP_CANT_INTERPRET) goto Lcant; if (e2->op == TOKslice) e2 = resolveSlice(e2); if (e2->isConst() != 1 && e2->op != TOKnull && e2->op != TOKstring && e2->op != TOKarrayliteral && e2->op != TOKstructliteral) { error("cannot compare %s at compile time", e2->toChars()); goto Lcant; } e = (*fp)(op, type, e1, e2); if (e == EXP_CANT_INTERPRET) error("%s cannot be interpreted at compile time", toChars()); return e; Lcant: return EXP_CANT_INTERPRET; } #define BIN_INTERPRET2(op) \ Expression *op##Exp::interpret(InterState *istate, CtfeGoal goal) \ { \ return interpretCommon2(istate, goal, &op); \ } BIN_INTERPRET2(Equal) BIN_INTERPRET2(Identity) BIN_INTERPRET2(Cmp) /* Helper functions for BinExp::interpretAssignCommon */ /*************************************** * Duplicate the elements array, then set field 'indexToChange' = newelem. */ Expressions *changeOneElement(Expressions *oldelems, size_t indexToChange, Expression *newelem) { Expressions *expsx = new Expressions(); expsx->setDim(oldelems->dim); for (size_t j = 0; j < expsx->dim; j++) { if (j == indexToChange) expsx->tdata()[j] = newelem; else expsx->tdata()[j] = oldelems->tdata()[j]; } return expsx; } /******************************** * Add v to the istate list, unless it already exists there. */ void addVarToInterstate(InterState *istate, VarDeclaration *v) { if (!v->isParameter()) { for (size_t i = 0; 1; i++) { if (i == istate->vars.dim) { istate->vars.push(v); //printf("\tadding %s to istate\n", v->toChars()); break; } if (v == istate->vars.tdata()[i]) break; } } } // Create a new struct literal, which is the same as se except that se.field[offset] = elem Expression * modifyStructField(Type *type, StructLiteralExp *se, size_t offset, Expression *newval) { int fieldi = se->getFieldIndex(newval->type, offset); if (fieldi == -1) return EXP_CANT_INTERPRET; /* Create new struct literal reflecting updated fieldi */ Expressions *expsx = changeOneElement(se->elements, fieldi, newval); Expression * ee = new StructLiteralExp(se->loc, se->sd, expsx); ee->type = se->type; return ee; } /******************************** * Given an array literal arr (either arrayliteral, stringliteral, or assocArrayLiteral), * set arr[index] = newval and return the new array. * */ Expression *assignAssocArrayElement(Loc loc, AssocArrayLiteralExp *aae, Expression *index, Expression *newval) { /* Create new associative array literal reflecting updated key/value */ Expressions *keysx = aae->keys; Expressions *valuesx = aae->values; int updated = 0; for (size_t j = valuesx->dim; j; ) { j--; Expression *ekey = aae->keys->tdata()[j]; Expression *ex = ctfeEqual(TOKequal, Type::tbool, ekey, index); if (ex == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; if (ex->isBool(TRUE)) { valuesx->tdata()[j] = newval; updated = 1; } } if (!updated) { // Append index/newval to keysx[]/valuesx[] valuesx->push(newval); keysx->push(index); } return newval; } // Return true if e is derived from UnaryExp. // Consider moving this function into Expression. UnaExp *isUnaExp(Expression *e) { switch (e->op) { case TOKdotvar: case TOKindex: case TOKslice: case TOKcall: case TOKdot: case TOKdotti: case TOKdottype: case TOKcast: return (UnaExp *)e; default: break; } return NULL; } // Returns the variable which is eventually modified, or NULL if an rvalue. // thisval is the current value of 'this'. VarDeclaration * findParentVar(Expression *e, Expression *thisval) { for (;;) { e = resolveReferences(e, thisval); 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; } // Given expr, which evaluates to an array/AA/string literal, // return true if it needs to be copied bool needToCopyLiteral(Expression *expr) { for (;;) { switch (expr->op) { case TOKarrayliteral: case TOKassocarrayliteral: case TOKstructliteral: return true; case TOKstring: case TOKthis: case TOKvar: return false; case TOKassign: return false; case TOKindex: case TOKdotvar: case TOKslice: case TOKcast: expr = ((UnaExp *)expr)->e1; continue; case TOKcat: return needToCopyLiteral(((BinExp *)expr)->e1) || needToCopyLiteral(((BinExp *)expr)->e2); case TOKcatass: expr = ((BinExp *)expr)->e2; continue; default: return false; } } } Expressions *copyLiteralArray(Expressions *oldelems) { if (!oldelems) return oldelems; Expressions *newelems = new Expressions(); newelems->setDim(oldelems->dim); for (size_t i = 0; i < oldelems->dim; i++) newelems->tdata()[i] = copyLiteral(oldelems->tdata()[i]); return newelems; } // Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral. // This value will be used for in-place modification. Expression *copyLiteral(Expression *e) { if (e->op == TOKstring) // syntaxCopy doesn't make a copy for StringExp! { StringExp *se = (StringExp *)e; unsigned char *s; s = (unsigned char *)mem.calloc(se->len + 1, se->sz); memcpy(s, se->string, se->len * se->sz); StringExp *se2 = new StringExp(se->loc, s, se->len); se2->committed = se->committed; se2->postfix = se->postfix; se2->type = se->type; se2->sz = se->sz; return se2; } else if (e->op == TOKarrayliteral) { ArrayLiteralExp *ae = (ArrayLiteralExp *)e; ArrayLiteralExp *r = new ArrayLiteralExp(e->loc, copyLiteralArray(ae->elements)); r->type = e->type; return r; } else if (e->op == TOKassocarrayliteral) { AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; AssocArrayLiteralExp *r = new AssocArrayLiteralExp(e->loc, copyLiteralArray(aae->keys), copyLiteralArray(aae->values)); r->type = e->type; return r; } /* syntaxCopy doesn't work for struct literals, because of a nasty special * case: block assignment is permitted inside struct literals, eg, * an int[4] array can be initialized with a single int. */ else if (e->op == TOKstructliteral) { StructLiteralExp *se = (StructLiteralExp *)e; Expressions *oldelems = se->elements; Expressions * newelems = new Expressions(); newelems->setDim(oldelems->dim); for (size_t i = 0; i < newelems->dim; i++) { Expression *m = oldelems->tdata()[i]; // We need the struct definition to detect block assignment StructDeclaration *sd = se->sd; Dsymbol *s = sd->fields.tdata()[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v); // If it is a void assignment, use the default initializer if (!m) m = v->type->defaultInitLiteral(e->loc); if (m->op == TOKslice) m = resolveSlice(m); if ((v->type->ty != m->type->ty) && v->type->ty == Tsarray) { // Block assignment from inside struct literals TypeSArray *tsa = (TypeSArray *)v->type; uinteger_t length = tsa->dim->toInteger(); m = createBlockDuplicatedArrayLiteral(v->type, m, (size_t)length); } else if (v->type->ty != Tarray) // NOTE: do not copy array references m = copyLiteral(m); newelems->tdata()[i] = m; } #if DMDV2 StructLiteralExp *r = new StructLiteralExp(e->loc, se->sd, newelems, se->stype); #else StructLiteralExp *r = new StructLiteralExp(e->loc, se->sd, newelems); #endif r->type = e->type; return r; } else if (e->op == TOKfunction || e->op == TOKdelegate || e->op == TOKsymoff || e->op == TOKnull || e->op == TOKvar || e->op == TOKint64 || e->op == TOKfloat64 || e->op == TOKchar || e->op == TOKcomplex80) { // Simple value types Expression *r = e->syntaxCopy(); r->type = e->type; return r; } else if (e->type->ty == Tpointer && e->type->nextOf()->ty != Tfunction) { // For pointers, we only do a shallow copy. Expression *r; if (e->op == TOKaddress) r = new AddrExp(e->loc, ((AddrExp *)e)->e1); else if (e->op == TOKindex) r = new IndexExp(e->loc, ((IndexExp *)e)->e1, ((IndexExp *)e)->e2); else if (e->op == TOKdotvar) r = new DotVarExp(e->loc, ((DotVarExp *)e)->e1, ((DotVarExp *)e)->var #if DMDV2 , ((DotVarExp *)e)->hasOverloads #endif ); else assert(0); r->type = e->type; return r; } else if (e->op == TOKslice) { // Array slices only do a shallow copy Expression *r = new SliceExp(e->loc, ((SliceExp *)e)->e1, ((SliceExp *)e)->lwr, ((SliceExp *)e)->upr); r->type = e->type; return r; } else { e->error("Internal Compiler Error: CTFE literal %s", e->toChars()); assert(0); return e; } } /* Deal with type painting. * Type painting is a major nuisance: we can't just set * e->type = type, because that would change the original literal. * But, we can't simply copy the literal either, because that would change * the values of any pointers. */ Expression *paintTypeOntoLiteral(Type *type, Expression *lit) { if (lit->type == type) return lit; Expression *e; if (lit->op == TOKslice) { SliceExp *se = (SliceExp *)lit; e = new SliceExp(lit->loc, se->e1, se->lwr, se->upr); } else if (lit->op == TOKindex) { IndexExp *ie = (IndexExp *)lit; e = new IndexExp(lit->loc, ie->e1, ie->e2); } else if (lit->op == TOKarrayliteral) { ArrayLiteralExp *ae = (ArrayLiteralExp *)lit; e = new ArrayLiteralExp(lit->loc, ae->elements); } else if (lit->op == TOKstring) { // For strings, we need to introduce another level of indirection e = new SliceExp(lit->loc, lit, new IntegerExp(0, 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit)); } else if (lit->op == TOKassocarrayliteral) { AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)lit; e = new AssocArrayLiteralExp(lit->loc, aae->keys, aae->values); } else e = copyLiteral(lit); e->type = type; return e; } /* Set a slice of char array literal 'existingAE' from a string 'newval'. * existingAE[firstIndex..firstIndex+newval.length] = newval. */ void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, int firstIndex) { size_t newlen = newval->len; size_t sz = newval->sz; unsigned char *s = (unsigned char *)newval->string; Type *elemType = existingAE->type->nextOf(); for (size_t j = 0; j < newlen; j++) { dinteger_t val; switch (sz) { case 1: val = s[j]; break; case 2: val = ((unsigned short *)s)[j]; break; case 4: val = ((unsigned *)s)[j]; break; default: assert(0); break; } existingAE->elements->tdata()[j+firstIndex] = new IntegerExp(newval->loc, val, elemType); } } /* Set a slice of string 'existingSE' from a char array literal 'newae'. * existingSE[firstIndex..firstIndex+newae.length] = newae. */ void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *newae, int firstIndex) { unsigned char *s = (unsigned char *)existingSE->string; for (size_t j = 0; j < newae->elements->dim; j++) { unsigned value = (unsigned)(newae->elements->tdata()[j]->toInteger()); switch (existingSE->sz) { case 1: s[j+firstIndex] = value; break; case 2: ((unsigned short *)s)[j+firstIndex] = value; break; case 4: ((unsigned *)s)[j+firstIndex] = value; break; default: assert(0); break; } } } /* Set a slice of string 'existingSE' from a string 'newstr'. * existingSE[firstIndex..firstIndex+newstr.length] = newstr. */ void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, int firstIndex) { unsigned char *s = (unsigned char *)existingSE->string; size_t sz = existingSE->sz; assert(sz == newstr->sz); memcpy(s + firstIndex * sz, newstr->string, sz * newstr->len); } /* Set dest = src, where both dest and src are container value literals * (ie, struct literals, or static arrays (can be an array literal or a string) * Assignment is recursively in-place. * Purpose: any reference to a member of 'dest' will remain valid after the * assignment. */ void assignInPlace(Expression *dest, Expression *src) { assert(dest->op == TOKstructliteral || dest->op == TOKarrayliteral || dest->op == TOKstring); Expressions *oldelems; Expressions *newelems; if (dest->op == TOKstructliteral) { assert(dest->op == src->op); oldelems = ((StructLiteralExp *)dest)->elements; newelems = ((StructLiteralExp *)src)->elements; } else if (dest->op == TOKarrayliteral && src->op==TOKarrayliteral) { oldelems = ((ArrayLiteralExp *)dest)->elements; newelems = ((ArrayLiteralExp *)src)->elements; } else if (dest->op == TOKstring && src->op == TOKstring) { sliceAssignStringFromString((StringExp *)dest, (StringExp *)src, 0); return; } else if (dest->op == TOKarrayliteral && src->op == TOKstring) { sliceAssignArrayLiteralFromString((ArrayLiteralExp *)dest, (StringExp *)src, 0); return; } else if (src->op == TOKarrayliteral && dest->op == TOKstring) { sliceAssignStringFromArrayLiteral((StringExp *)dest, (ArrayLiteralExp *)src, 0); return; } else assert(0); assert(oldelems->dim == newelems->dim); for (size_t i= 0; i < oldelems->dim; ++i) { Expression *e = newelems->tdata()[i]; Expression *o = oldelems->tdata()[i]; if (e->op == TOKstructliteral) { assert(o->op == e->op); assignInPlace(o, e); } else if (e->type->ty == Tsarray && o->type->ty == Tsarray) { assignInPlace(o, e); } else { oldelems->tdata()[i] = newelems->tdata()[i]; } } } void recursiveBlockAssign(ArrayLiteralExp *ae, Expression *val, bool wantRef) { assert( ae->type->ty == Tsarray || ae->type->ty == Tarray); #if DMDV2 Type *desttype = ((TypeArray *)ae->type)->next->castMod(0); bool directblk = (val->type->toBasetype()->castMod(0)) == desttype; #else Type *desttype = ((TypeArray *)ae->type)->next; bool directblk = (val->type->toBasetype()) == desttype; #endif bool cow = !(val->op == TOKstructliteral || val->op == TOKarrayliteral || val->op == TOKstring); for (size_t k = 0; k < ae->elements->dim; k++) { if (!directblk && ae->elements->tdata()[k]->op == TOKarrayliteral) { recursiveBlockAssign((ArrayLiteralExp *)ae->elements->tdata()[k], val, wantRef); } else { if (wantRef || cow) ae->elements->tdata()[k] = val; else assignInPlace(ae->elements->tdata()[k], val); } } } Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_t fp, int post) { #if LOG printf("BinExp::interpretAssignCommon() %s\n", toChars()); #endif Expression *returnValue = EXP_CANT_INTERPRET; Expression *e1 = this->e1; if (!istate) { error("value of %s is not known at compile time", e1->toChars()); return returnValue; } /* 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 *desttype = e1->type->toBasetype(); #if DMDV2 Type *srctype = e2->type->toBasetype()->castMod(0); #else Type *srctype = e2->type->toBasetype(); #endif while ( desttype->ty == Tsarray || desttype->ty == Tarray) { desttype = ((TypeArray *)desttype)->next; #if DMDV2 if (srctype == desttype->castMod(0)) #else if (srctype == desttype) #endif { isBlockAssignment = true; break; } } } bool wantRef = false; if (!fp && this->e1->type->toBasetype() == this->e2->type->toBasetype() && (e1->type->toBasetype()->ty == Tarray || e1->type->toBasetype()->ty == Taarray || e1->type->toBasetype()->ty == Tclass) // e = *x is never a reference, because *x is always a value && this->e2->op != TOKstar ) { #if DMDV2 wantRef = true; #else /* D1 doesn't have const in the type system. But there is still a * vestigal const in the form of static const variables. * Problematic code like: * const int [] x = [1,2,3]; * int [] y = x; * can be dealt with by making this a non-ref assign (y = x.dup). * Otherwise it's a big mess. */ VarDeclaration * targetVar = findParentVar(e2, istate->localThis); if (!(targetVar && targetVar->isConst())) wantRef = true; // slice assignment of static arrays is not reference assignment if ((e1->op==TOKslice) && ((SliceExp *)e1)->e1->type->ty == Tsarray) wantRef = false; #endif } if (isBlockAssignment && (e2->type->toBasetype()->ty == Tarray || e2->type->toBasetype()->ty == Tsarray)) { wantRef = true; } /* This happens inside compiler-generated foreach statements. * It's another case where we need a reference * Note that a similar case, where e2 = 'this', occurs in * construction of a struct with an invariant(). */ if (op==TOKconstruct && this->e1->op==TOKvar && this->e2->op != TOKthis && this->e2->op != TOKcomma && ((VarExp*)this->e1)->var->storage_class & STCref) wantRef = true; if (fp) { if (e1->op == TOKcast) { CastExp *ce = (CastExp *)e1; e1 = ce->e1; } } if (e1 == EXP_CANT_INTERPRET) return e1; // First, deal with this = e; and call() = e; if (e1->op == TOKthis) { e1 = istate->localThis; } if (e1->op == TOKcall) { bool oldWaiting = istate->awaitingLvalueReturn; istate->awaitingLvalueReturn = true; e1 = e1->interpret(istate); istate->awaitingLvalueReturn = oldWaiting; if (e1 == EXP_CANT_INTERPRET) return e1; if (e1->op == TOKarrayliteral || e1->op == TOKstring) { // f() = e2, when f returns an array, is always a slice assignment. // Convert into arr[0..arr.length] = e2 e1 = new SliceExp(loc, e1, new IntegerExp(0, 0, Type::tsize_t), ArrayLength(Type::tsize_t, e1)); e1->type = type; } } if (e1->op == TOKstar) { e1 = e1->interpret(istate, ctfeNeedLvalue); if (e1 == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; if (!(e1->op == TOKvar || e1->op == TOKdotvar || e1->op == TOKindex || e1->op == TOKslice)) { error("cannot dereference invalid pointer %s", this->e1->toChars()); return EXP_CANT_INTERPRET; } } if (!(e1->op == TOKarraylength || e1->op == TOKvar || e1->op == TOKdotvar || e1->op == TOKindex || e1->op == TOKslice)) printf("CTFE internal error: unsupported assignment %s\n", toChars()); assert(e1->op == TOKarraylength || e1->op == TOKvar || e1->op == TOKdotvar || e1->op == TOKindex || e1->op == TOKslice); Expression * newval = NULL; if (!wantRef) { // We need to treat pointers specially, because TOKsymoff can be used to // return a value OR a pointer assert(e1); assert(e1->type); if ((e1->type->ty == Tpointer && e1->type->nextOf()->ty != Tfunction) && (e2->op == TOKsymoff || e2->op==TOKaddress || e2->op==TOKvar)) newval = this->e2->interpret(istate, ctfeNeedLvalue); else newval = this->e2->interpret(istate); } if (newval == EXP_CANT_INTERPRET) return newval; // ---------------------------------------------------- // 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 || e1->op == TOKarraylength) { // If it isn't a simple assignment, we need the existing value Expression * oldval = e1->interpret(istate); if (oldval == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; while (oldval->op == TOKvar) { oldval = resolveReferences(oldval, istate->localThis); oldval = oldval->interpret(istate); if (oldval == EXP_CANT_INTERPRET) return oldval; } if (fp) { // ~= can create new values (see bug 6052) if (op == TOKcatass) { if (needToCopyLiteral(this->e2)) newval = copyLiteral(newval); if (newval->op == TOKslice) newval = resolveSlice(newval); // It becomes a reference assignment wantRef = true; } if (oldval->op == TOKslice) oldval = resolveSlice(oldval); if (this->e1->type->ty == Tpointer && this->e2->type->isintegral() && (op==TOKaddass || op == TOKminass || op == TOKplusplus || op == TOKminusminus)) { oldval = this->e1->interpret(istate, ctfeNeedLvalue); newval = this->e2->interpret(istate); if (oldval == EXP_CANT_INTERPRET || newval == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; newval = pointerArithmetic(loc, op, type, oldval, newval); } else if (this->e1->type->ty == Tpointer) { error("pointer expression %s cannot be interpreted at compile time", toChars()); return EXP_CANT_INTERPRET; } else { newval = (*fp)(type, oldval, newval); } if (newval == EXP_CANT_INTERPRET) { error("Cannot interpret %s at compile time", toChars()); return EXP_CANT_INTERPRET; } // Determine the return value returnValue = Cast(type, type, post ? oldval : newval); if (returnValue == EXP_CANT_INTERPRET) return returnValue; } else returnValue = newval; if (e1->op == TOKarraylength) { size_t oldlen = oldval->toInteger(); size_t newlen = newval->toInteger(); if (oldlen == newlen) // no change required -- we're done! return returnValue; // Now change the assignment from arr.length = n into arr = newval e1 = ((ArrayLengthExp *)e1)->e1; if (oldlen != 0) { // Get the old array literal. oldval = e1->interpret(istate); while (oldval->op == TOKvar) { oldval = resolveReferences(oldval, istate->localThis); oldval = oldval->interpret(istate); } } if (oldval->op == TOKslice) oldval = resolveSlice(oldval); Type *t = e1->type->toBasetype(); if (t->ty == Tarray) { Type *elemType= NULL; elemType = ((TypeArray *)t)->next; assert(elemType); Expression *defaultElem = elemType->defaultInitLiteral(); Expressions *elements = new Expressions(); elements->setDim(newlen); size_t copylen = oldlen < newlen ? oldlen : newlen; if (oldlen !=0) assert(oldval->op == TOKarrayliteral); ArrayLiteralExp *ae = (ArrayLiteralExp *)oldval; for (size_t i = 0; i < copylen; i++) elements->tdata()[i] = ae->elements->tdata()[i]; if (elemType->ty == Tstruct || elemType->ty == Tsarray) { /* If it is an aggregate literal representing a value type, * we need to create a unique copy for each element */ for (size_t i = copylen; i < newlen; i++) elements->tdata()[i] = copyLiteral(defaultElem); } else { for (size_t i = copylen; i < newlen; i++) elements->tdata()[i] = defaultElem; } ArrayLiteralExp *aae = new ArrayLiteralExp(0, elements); aae->type = t; newval = aae; // We have changed it into a reference assignment // Note that returnValue is still the new length. wantRef = true; if (e1->op == TOKstar) { // arr.length+=n becomes (t=&arr, *(t).length=*(t).length+n); e1 = e1->interpret(istate, ctfeNeedLvalue); if (e1 == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; } } else { error("%s is not yet supported at compile time", toChars()); return EXP_CANT_INTERPRET; } } } else if (!wantRef && e1->op != TOKslice) { /* Look for special case of struct being initialized with 0. */ if (type->toBasetype()->ty == Tstruct && newval->op == TOKint64) { newval = type->defaultInitLiteral(loc); } newval = Cast(type, type, newval); if (newval == EXP_CANT_INTERPRET) { error("CTFE error: cannot cast %s to type %s", this->e2->toChars(), type->toChars()); return EXP_CANT_INTERPRET; } returnValue = newval; } if (newval == EXP_CANT_INTERPRET) return newval; // ------------------------------------------------- // Make sure destination can be modified // ------------------------------------------------- // Make sure we're not trying to modify a global or static variable // We do this by locating the ultimate parent variable which gets modified. VarDeclaration * ultimateVar = findParentVar(e1, istate->localThis); if (ultimateVar && ultimateVar->isDataseg() && !ultimateVar->isCTFE()) { // Can't modify global or static data error("%s cannot be modified at compile time", ultimateVar->toChars()); return EXP_CANT_INTERPRET; } // This happens inside compiler-generated foreach statements. if (op==TOKconstruct && this->e1->op==TOKvar && this->e2->op == TOKindex && ((VarExp*)this->e1)->var->storage_class & STCref) { VarDeclaration *v = ((VarExp *)e1)->var->isVarDeclaration(); #if (LOGASSIGN) printf("FOREACH ASSIGN %s=%s\n", v->toChars(), e2->toChars()); #endif v->setValueNull(); v->createStackValue(e2); return e2; } bool destinationIsReference = false; e1 = resolveReferences(e1, istate->localThis, &destinationIsReference); // 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. if (e1->op != TOKvar && ultimateVar && !ultimateVar->getValue()) ultimateVar->createRefValue(copyLiteral(ultimateVar->type->defaultInitLiteral())); // --------------------------------------- // Deal with reference assignment // (We already have 'newval' for arraylength operations) // --------------------------------------- if (wantRef && !fp && this->e1->op != TOKarraylength) { newval = this->e2->interpret(istate, ctfeNeedLvalue); if (newval == EXP_CANT_INTERPRET) return newval; // If it is an assignment from a array function parameter passed by // reference, resolve the reference. (This should NOT happen for // non-reference types). if (newval->op == TOKvar && (newval->type->ty == Tarray || newval->type->ty == Tclass)) { newval = newval->interpret(istate); } if (newval->op == TOKassocarrayliteral || newval->op == TOKstring || newval->op==TOKarrayliteral) { if (needToCopyLiteral(this->e2)) newval = copyLiteral(newval); } returnValue = newval; } // --------------------------------------- // Deal with dotvar expressions // --------------------------------------- // Because structs are not reference types, dotvar expressions can be // collapsed into a single assignment. if (!wantRef && e1->op == TOKdotvar) { // Strip of all of the leading dotvars, unless we started with a call // (in which case, we already have the lvalue). if (this->e1->op != TOKcall) e1 = e1->interpret(istate, ctfeNeedLvalue); if (e1 == EXP_CANT_INTERPRET) return e1; if (e1->op == TOKstructliteral && newval->op == TOKstructliteral) { assignInPlace(e1, newval); return returnValue; } } #if LOGASSIGN if (wantRef) printf("REF ASSIGN: %s=%s\n", e1->toChars(), newval->toChars()); else printf("ASSIGN: %s=%s\n", e1->toChars(), newval->toChars()); showCtfeExpr(newval); #endif /* Assignment to variable of the form: * v = newval */ if (e1->op == TOKvar) { VarExp *ve = (VarExp *)e1; VarDeclaration *v = ve->var->isVarDeclaration(); if (!destinationIsReference) addVarToInterstate(istate, v); if (wantRef) { v->setValueNull(); v->createRefValue(newval); } else if (e1->type->toBasetype()->ty == Tstruct) { // In-place modification if (newval->op != TOKstructliteral) { error("CTFE internal error assigning struct"); return EXP_CANT_INTERPRET; } newval = copyLiteral(newval); if (v->getValue()) assignInPlace(v->getValue(), newval); else v->createRefValue(newval); } else { if (e1->type->toBasetype()->ty == Tarray || e1->type->toBasetype()->ty == Taarray) { // arr op= arr if (!v->getValue()) v->createRefValue(newval); else v->setRefValue(newval); } else { if (!v->getValue()) // creating a new value v->createStackValue(newval); else v->setStackValue(newval); } } } else if (e1->op == TOKdotvar) { /* Assignment to member variable of the form: * e.v = newval */ Expression *exx = ((DotVarExp *)e1)->e1; if (wantRef && exx->op != TOKstructliteral) { exx = exx->interpret(istate); if (exx == EXP_CANT_INTERPRET) return exx; } if (exx->op != TOKstructliteral) { error("CTFE internal error: Dotvar assignment"); return EXP_CANT_INTERPRET; } StructLiteralExp *se = (StructLiteralExp *)exx; VarDeclaration *member = ((DotVarExp *)e1)->var->isVarDeclaration(); if (!member) { error("CTFE internal error: Dotvar assignment"); return EXP_CANT_INTERPRET; } int fieldi = se->getFieldIndex(member->type, member->offset); if (fieldi == -1) return EXP_CANT_INTERPRET; assert(fieldi>=0 && fieldi < se->elements->dim); if (newval->op == TOKstructliteral) assignInPlace(se->elements->tdata()[fieldi], newval); else se->elements->tdata()[fieldi] = newval; if (ultimateVar && !destinationIsReference) addVarToInterstate(istate, ultimateVar); return returnValue; } else if (e1->op == TOKindex) { /* Assignment to array element of the form: * aggregate[i] = newval */ IndexExp *ie = (IndexExp *)e1; uinteger_t destarraylen = 0; // not for AAs // Set the $ variable, and find the array literal to modify if (ie->e1->type->toBasetype()->ty != Taarray && ie->e1->type->toBasetype()->ty != Tpointer) { Expression *oldval = ie->e1->interpret(istate); if (oldval->op == TOKnull) { error("cannot index null array %s", ie->e1->toChars()); return EXP_CANT_INTERPRET; } if (oldval->op != TOKarrayliteral && oldval->op != TOKstring && oldval->op != TOKslice) { error("cannot determine length of %s at compile time", ie->e1->toChars()); return EXP_CANT_INTERPRET; } destarraylen = resolveArrayLength(oldval); if (ie->lengthVar) { IntegerExp *dollarExp = new IntegerExp(loc, destarraylen, Type::tsize_t); ie->lengthVar->createStackValue(dollarExp); } } Expression *index = ie->e2->interpret(istate); if (ie->lengthVar) ie->lengthVar->setValueNull(); // $ is defined only inside [] if (index == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; if (index->op == TOKslice) // only happens with AA assignment index = resolveSlice(index); ArrayLiteralExp *existingAE = NULL; StringExp *existingSE = NULL; AssocArrayLiteralExp *existingAA = NULL; Expression *aggregate = resolveReferences(ie->e1, istate->localThis); // Set the index to modify (for non-AAs), and check that it is in range dinteger_t indexToModify = 0; if (ie->e1->type->toBasetype()->ty != Taarray) { indexToModify = index->toInteger(); if (ie->e1->type->toBasetype()->ty == Tpointer) { dinteger_t ofs; aggregate = aggregate->interpret(istate, ctfeNeedLvalue); if (aggregate == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; if (aggregate->op == TOKnull) { error("cannot index through null pointer %s", ie->e1->toChars()); return EXP_CANT_INTERPRET; } if (aggregate->op == TOKint64) { error("cannot index through invalid pointer %s of value %s", ie->e1->toChars(), aggregate->toChars()); return EXP_CANT_INTERPRET; } aggregate = getAggregateFromPointer(aggregate, &ofs); indexToModify += ofs; destarraylen = resolveArrayLength(aggregate); } if (indexToModify >= destarraylen) { error("array index %d is out of bounds [0..%d]", indexToModify, destarraylen); return EXP_CANT_INTERPRET; } } /* The only possible indexable LValue aggregates are array literals, * slices of array literals, and AA literals. */ if (aggregate->op == TOKindex || aggregate->op == TOKdotvar || aggregate->op == TOKslice || aggregate->op == TOKcall || aggregate->op == TOKstar) { aggregate = aggregate->interpret(istate, ctfeNeedLvalue); if (aggregate == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; // The array could be an index of an AA. Resolve it if so. if (aggregate->op == TOKindex) { IndexExp *ix = (IndexExp *)aggregate; aggregate = Index(ix->type, ix->e1, ix->e2); } } if (aggregate->op == TOKvar) { VarExp *ve = (VarExp *)aggregate; VarDeclaration *v = ve->var->isVarDeclaration(); aggregate = v->getValue(); if (aggregate->op == TOKnull) { if (v->type->ty == Taarray) { // Assign to empty associative array Expressions *valuesx = new Expressions(); Expressions *keysx = new Expressions(); Expression *indx = ie->e2->interpret(istate); if (indx == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; valuesx->push(newval); keysx->push(indx); Expression *aae2 = new AssocArrayLiteralExp(loc, keysx, valuesx); aae2->type = v->type; newval = aae2; v->setRefValue(newval); return returnValue; } // This would be a runtime segfault error("cannot index null array %s", v->toChars()); return EXP_CANT_INTERPRET; } } if (aggregate->op == TOKslice) { SliceExp *sexp = (SliceExp *)aggregate; aggregate = sexp->e1; Expression *lwr = sexp->lwr->interpret(istate); indexToModify += lwr->toInteger(); } if (aggregate->op == TOKarrayliteral) existingAE = (ArrayLiteralExp *)aggregate; else if (aggregate->op == TOKstring) existingSE = (StringExp *)aggregate; else if (aggregate->op == TOKassocarrayliteral) existingAA = (AssocArrayLiteralExp *)aggregate; else { error("CTFE internal compiler error %s", aggregate->toChars()); return EXP_CANT_INTERPRET; } if (!wantRef && newval->op == TOKslice) { newval = resolveSlice(newval); if (newval == EXP_CANT_INTERPRET) { error("Compiler error: CTFE index assign %s", toChars()); assert(0); } } if (existingAE) { if (newval->op == TOKstructliteral) assignInPlace((Expression *)(existingAE->elements->tdata()[indexToModify]), newval); else existingAE->elements->tdata()[indexToModify] = newval; return returnValue; } if (existingSE) { unsigned char *s = (unsigned char *)existingSE->string; unsigned value = newval->toInteger(); switch (existingSE->sz) { case 1: s[indexToModify] = value; break; case 2: ((unsigned short *)s)[indexToModify] = value; break; case 4: ((unsigned *)s)[indexToModify] = value; break; default: assert(0); break; } return returnValue; } else if (existingAA) { if (assignAssocArrayElement(loc, existingAA, index, newval) == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; return returnValue; } else { error("Index assignment %s is not yet supported in CTFE ", toChars()); return EXP_CANT_INTERPRET; } return returnValue; } else if (e1->op == TOKslice) { // ------------------------------ // aggregate[] = newval // aggregate[low..upp] = newval // ------------------------------ SliceExp * sexp = (SliceExp *)e1; // Set the $ variable Expression *oldval = sexp->e1; bool assignmentToSlicedPointer = false; if (oldval->type->toBasetype()->ty == Tpointer && oldval->type->toBasetype()->nextOf()->ty != Tfunction) { // Slicing a pointer oldval = oldval->interpret(istate, ctfeNeedLvalue); dinteger_t ofs; oldval = getAggregateFromPointer(oldval, &ofs); assignmentToSlicedPointer = true; } else oldval = oldval->interpret(istate); if (oldval->op != TOKarrayliteral && oldval->op != TOKstring && oldval->op != TOKslice && oldval->op != TOKnull) { error("CTFE ICE: cannot resolve array length"); return EXP_CANT_INTERPRET; } uinteger_t dollar = resolveArrayLength(oldval); if (sexp->lengthVar) { Expression *arraylen = new IntegerExp(loc, dollar, Type::tsize_t); sexp->lengthVar->createStackValue(arraylen); } Expression *upper = NULL; Expression *lower = NULL; if (sexp->upr) upper = sexp->upr->interpret(istate); if (sexp->lwr) lower = sexp->lwr->interpret(istate); if (sexp->lengthVar) sexp->lengthVar->setValueNull(); // $ is defined only in [L..U] if (upper == EXP_CANT_INTERPRET || lower == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; size_t dim = dollar; size_t upperbound = upper ? upper->toInteger() : dim; int lowerbound = lower ? lower->toInteger() : 0; if (!assignmentToSlicedPointer && (((int)lowerbound < 0) || (upperbound > dim))) { error("Array bounds [0..%d] exceeded in slice [%d..%d]", dim, lowerbound, upperbound); return EXP_CANT_INTERPRET; } if (upperbound == lowerbound) return newval; Expression *aggregate = resolveReferences(((SliceExp *)e1)->e1, istate->localThis); dinteger_t firstIndex = lowerbound; ArrayLiteralExp *existingAE = NULL; StringExp *existingSE = NULL; /* The only possible slicable LValue aggregates are array literals, * and slices of array literals. */ if (aggregate->op == TOKindex || aggregate->op == TOKdotvar || aggregate->op == TOKslice || aggregate->op == TOKstar || aggregate->op == TOKcall) { aggregate = aggregate->interpret(istate, ctfeNeedLvalue); if (aggregate == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; // The array could be an index of an AA. Resolve it if so. if (aggregate->op == TOKindex) { IndexExp *ie = (IndexExp *)aggregate; aggregate = Index(ie->type, ie->e1, ie->e2); } } if (aggregate->op == TOKvar) { VarExp *ve = (VarExp *)(aggregate); VarDeclaration *v = ve->var->isVarDeclaration(); aggregate = v->getValue(); } if (aggregate->op == TOKslice) { // Slice of a slice --> change the bounds SliceExp *sexpold = (SliceExp *)aggregate; dinteger_t hi = upperbound + sexpold->lwr->toInteger(); firstIndex = lowerbound + sexpold->lwr->toInteger(); if (hi > sexpold->upr->toInteger()) { error("slice [%d..%d] exceeds array bounds [0..%jd]", lowerbound, upperbound, sexpold->upr->toInteger() - sexpold->lwr->toInteger()); return EXP_CANT_INTERPRET; } aggregate = sexpold->e1; } if (aggregate->type->toBasetype()->ty == Tpointer && aggregate->type->toBasetype()->nextOf()->ty != Tfunction) { // Slicing a pointer --> change the bounds aggregate = sexp->e1->interpret(istate, ctfeNeedLvalue); dinteger_t ofs; aggregate = getAggregateFromPointer(aggregate, &ofs); dinteger_t hi = upperbound + ofs; firstIndex = lowerbound + ofs; if (firstIndex < 0 || hi > dim) { error("slice [%d..%jd] exceeds memory block bounds [0..%jd]", firstIndex, hi, dim); return EXP_CANT_INTERPRET; } } if (aggregate->op==TOKarrayliteral) existingAE = (ArrayLiteralExp *)aggregate; else if (aggregate->op==TOKstring) existingSE = (StringExp *)aggregate; if (!wantRef && newval->op == TOKslice) { newval = resolveSlice(newval); if (newval == EXP_CANT_INTERPRET) { error("Compiler error: CTFE slice %s", toChars()); assert(0); } } // For slice assignment, we check that the lengths match. size_t srclen = 0; if (newval->op == TOKarrayliteral) srclen = ((ArrayLiteralExp *)newval)->elements->dim; else if (newval->op == TOKstring) srclen = ((StringExp *)newval)->len; if (!isBlockAssignment && srclen != (upperbound - lowerbound)) { error("Array length mismatch assigning [0..%d] to [%d..%d]", srclen, lowerbound, upperbound); return EXP_CANT_INTERPRET; } if (!isBlockAssignment && newval->op == TOKarrayliteral && existingAE) { Expressions *oldelems = existingAE->elements; Expressions *newelems = ((ArrayLiteralExp *)newval)->elements; for (size_t j = 0; j < newelems->dim; j++) { oldelems->tdata()[j + firstIndex] = newelems->tdata()[j]; } return newval; } else if (newval->op == TOKstring && existingSE) { sliceAssignStringFromString((StringExp *)existingSE, (StringExp *)newval, firstIndex); return newval; } else if (newval->op == TOKstring && existingAE) { /* Mixed slice: it was initialized as an array literal of chars. * Now a slice of it is being set with a string. */ sliceAssignArrayLiteralFromString(existingAE, (StringExp *)newval, firstIndex); return newval; } else if (newval->op == TOKarrayliteral && existingSE) { /* 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, firstIndex); return newval; } else if (existingSE) { // String literal block slice assign unsigned value = newval->toInteger(); unsigned char *s = (unsigned char *)existingSE->string; for (size_t j = 0; j < upperbound-lowerbound; j++) { switch (existingSE->sz) { case 1: s[j+firstIndex] = value; break; case 2: ((unsigned short *)s)[j+firstIndex] = value; break; case 4: ((unsigned *)s)[j+firstIndex] = value; break; default: assert(0); break; } } if (goal == ctfeNeedNothing) return NULL; // avoid creating an unused literal SliceExp *retslice = new SliceExp(loc, existingSE, new IntegerExp(loc, firstIndex, Type::tsize_t), new IntegerExp(loc, firstIndex + upperbound-lowerbound, Type::tsize_t)); retslice->type = this->type; return retslice->interpret(istate); } else if (existingAE) { /* Block assignment, initialization of static arrays * x[] = e * x may be a multidimensional static array. (Note that this * only happens with array literals, never with strings). */ Expressions * w = existingAE->elements; assert( existingAE->type->ty == Tsarray || existingAE->type->ty == Tarray); #if DMDV2 Type *desttype = ((TypeArray *)existingAE->type)->next->castMod(0); bool directblk = (e2->type->toBasetype()->castMod(0)) == desttype; #else Type *desttype = ((TypeArray *)existingAE->type)->next; bool directblk = (e2->type->toBasetype()) == desttype; #endif bool cow = !(newval->op == TOKstructliteral || newval->op == TOKarrayliteral || newval->op == TOKstring); for (size_t j = 0; j < upperbound-lowerbound; j++) { if (!directblk) // Multidimensional array block assign recursiveBlockAssign((ArrayLiteralExp *)w->tdata()[j+firstIndex], newval, wantRef); else { if (wantRef || cow) existingAE->elements->tdata()[j+firstIndex] = newval; else assignInPlace(existingAE->elements->tdata()[j+firstIndex], newval); } } if (goal == ctfeNeedNothing) return NULL; // avoid creating an unused literal SliceExp *retslice = new SliceExp(loc, existingAE, new IntegerExp(loc, firstIndex, Type::tsize_t), new IntegerExp(loc, firstIndex + upperbound-lowerbound, Type::tsize_t)); retslice->type = this->type; return retslice->interpret(istate); } else error("Slice operation %s cannot be evaluated at compile time", toChars()); } else { error("%s cannot be evaluated at compile time", toChars()); #ifdef DEBUG dump(0); #endif } return returnValue; } Expression *AssignExp::interpret(InterState *istate, CtfeGoal goal) { return interpretAssignCommon(istate, goal, NULL); } #define BIN_ASSIGN_INTERPRET(op) \ Expression *op##AssignExp::interpret(InterState *istate, CtfeGoal goal) \ { \ return interpretAssignCommon(istate, goal, &op); \ } BIN_ASSIGN_INTERPRET(Add) BIN_ASSIGN_INTERPRET(Min) BIN_ASSIGN_INTERPRET(Cat) BIN_ASSIGN_INTERPRET(Mul) BIN_ASSIGN_INTERPRET(Div) BIN_ASSIGN_INTERPRET(Mod) BIN_ASSIGN_INTERPRET(Shl) BIN_ASSIGN_INTERPRET(Shr) BIN_ASSIGN_INTERPRET(Ushr) BIN_ASSIGN_INTERPRET(And) BIN_ASSIGN_INTERPRET(Or) BIN_ASSIGN_INTERPRET(Xor) Expression *PostExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("PostExp::interpret() %s\n", toChars()); #endif Expression *e; if (op == TOKplusplus) e = interpretAssignCommon(istate, goal, &Add, 1); else e = interpretAssignCommon(istate, goal, &Min, 1); #if LOG if (e == EXP_CANT_INTERPRET) printf("PostExp::interpret() CANT\n"); #endif return e; } Expression *AndAndExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("AndAndExp::interpret() %s\n", toChars()); #endif Expression *e = e1->interpret(istate); if (e != EXP_CANT_INTERPRET) { if (e->isBool(FALSE)) e = new IntegerExp(e1->loc, 0, type); else if (isTrueBool(e)) { e = e2->interpret(istate); if (e != EXP_CANT_INTERPRET) { if (e->isBool(FALSE)) e = new IntegerExp(e1->loc, 0, type); else if (isTrueBool(e)) e = new IntegerExp(e1->loc, 1, type); else e = EXP_CANT_INTERPRET; } } else e = EXP_CANT_INTERPRET; } return e; } Expression *OrOrExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("OrOrExp::interpret() %s\n", toChars()); #endif Expression *e = e1->interpret(istate); if (e != EXP_CANT_INTERPRET) { if (isTrueBool(e)) e = new IntegerExp(e1->loc, 1, type); else if (e->isBool(FALSE)) { e = e2->interpret(istate); if (e != EXP_CANT_INTERPRET) { if (e->isBool(FALSE)) e = new IntegerExp(e1->loc, 0, type); else if (isTrueBool(e)) e = new IntegerExp(e1->loc, 1, type); else e = EXP_CANT_INTERPRET; } } else e = EXP_CANT_INTERPRET; } return e; } Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e = EXP_CANT_INTERPRET; #if LOG printf("CallExp::interpret() %s\n", toChars()); #endif Expression * pthis = NULL; FuncDeclaration *fd = NULL; Expression *ecall = e1; if (ecall->op == TOKcall) { ecall = e1->interpret(istate); if (ecall == EXP_CANT_INTERPRET) return ecall; } if (ecall->op == TOKstar) { // Calling a function pointer Expression * pe = ((PtrExp*)ecall)->e1; if (pe->op == TOKvar) { VarDeclaration *vd = ((VarExp *)((PtrExp*)ecall)->e1)->var->isVarDeclaration(); if (vd && vd->getValue() && vd->getValue()->op == TOKsymoff) fd = ((SymOffExp *)vd->getValue())->var->isFuncDeclaration(); else { ecall = getVarExp(loc, istate, vd, goal); if (ecall == EXP_CANT_INTERPRET) return ecall; #if IN_LLVM if (ecall->op == TOKaddress) { AddrExp *e = (AddrExp*)ecall; if (e->e1->op == TOKvar) fd = ((VarExp *)e->e1)->var->isFuncDeclaration(); } #else if (ecall->op == TOKsymoff) fd = ((SymOffExp *)ecall)->var->isFuncDeclaration(); #endif } } else ecall = ((PtrExp*)ecall)->e1->interpret(istate); } if (ecall == EXP_CANT_INTERPRET) return ecall; if (ecall->op == TOKindex) { ecall = e1->interpret(istate); if (ecall == EXP_CANT_INTERPRET) return ecall; } if (ecall->op == TOKdotvar && !((DotVarExp*)ecall)->var->isFuncDeclaration()) { ecall = e1->interpret(istate); if (ecall == EXP_CANT_INTERPRET) return ecall; } if (ecall->op == TOKdotvar) { // Calling a member function pthis = ((DotVarExp*)e1)->e1; fd = ((DotVarExp*)e1)->var->isFuncDeclaration(); } else if (ecall->op == TOKvar) { VarDeclaration *vd = ((VarExp *)ecall)->var->isVarDeclaration(); if (vd && vd->getValue()) ecall = vd->getValue(); else // Calling a function fd = ((VarExp *)e1)->var->isFuncDeclaration(); } if (ecall->op == TOKdelegate) { // Calling a delegate fd = ((DelegateExp *)ecall)->func; pthis = ((DelegateExp *)ecall)->e1; } else if (ecall->op == TOKfunction) { // Calling a delegate literal fd = ((FuncExp*)ecall)->fd; } else if (ecall->op == TOKstar && ((PtrExp*)ecall)->e1->op==TOKfunction) { // Calling a function literal fd = ((FuncExp*)((PtrExp*)ecall)->e1)->fd; } TypeFunction *tf = fd ? (TypeFunction *)(fd->type) : NULL; if (!tf) { // DAC: I'm not sure if this ever happens //printf("ecall=%s %d %d\n", ecall->toChars(), ecall->op, TOKcall); error("cannot evaluate %s at compile time", toChars()); return EXP_CANT_INTERPRET; } if (!fd) { error("cannot evaluate %s at compile time", toChars()); return EXP_CANT_INTERPRET; } if (pthis) { // Member function call if (pthis->op == TOKthis) pthis = istate ? istate->localThis : NULL; else { if (pthis->op == TOKcomma) pthis = pthis->interpret(istate); if (pthis == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; // Evaluate 'this' if (pthis->op != TOKvar) pthis = pthis->interpret(istate, ctfeNeedLvalue); if (pthis == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; } } // Check for built-in functions Expression *eresult; if (evaluateIfBuiltin(&eresult, istate, fd, arguments, pthis)) return eresult; // Inline .dup. Special case because it needs the return type. if (!pthis && fd->ident == Id::adDup && arguments && arguments->dim == 2) { e = arguments->tdata()[1]; e = e->interpret(istate); if (e != EXP_CANT_INTERPRET) { if (e->op == TOKslice) e= resolveSlice(e); e = expType(type, e); e = copyLiteral(e); } return e; } if (!fd->fbody) { error("%s cannot be interpreted at compile time," " because it has no available source code", fd->toChars()); return EXP_CANT_INTERPRET; } eresult = fd->interpret(istate, arguments, pthis); if (eresult) e = eresult; else if (fd->type->toBasetype()->nextOf()->ty == Tvoid && !global.errors) e = EXP_VOID_INTERPRET; else error("cannot evaluate %s at compile time", toChars()); return e; } Expression *CommaExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("CommaExp::interpret() %s\n", toChars()); #endif CommaExp * firstComma = this; 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) istate = &istateComma; // If the comma returns a temporary variable, it needs to be an lvalue // (this is particularly important for struct constructors) if (e1->op == TOKdeclaration && e2->op == TOKvar && ((DeclarationExp *)e1)->declaration == ((VarExp*)e2)->var && ((VarExp*)e2)->var->storage_class & STCctfe) // same as Expression::isTemp { VarExp* ve = (VarExp *)e2; VarDeclaration *v = ve->var->isVarDeclaration(); if (!v->init && !v->getValue()) { v->createRefValue(copyLiteral(v->type->defaultInitLiteral())); } if (!v->getValue()) { Expression *newval = v->init->toExpression(); // v->setRefValue(v->init->toExpression()); // 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 = newval->interpret(istate); if (newval == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; if (newval != EXP_VOID_INTERPRET) { // v isn't necessarily null. v->setValueWithoutChecking(copyLiteral(newval)); } } if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) return e2; return e2->interpret(istate, goal); } Expression *e = e1->interpret(istate, ctfeNeedNothing); if (e != EXP_CANT_INTERPRET) e = e2->interpret(istate, goal); return e; } Expression *CondExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG printf("CondExp::interpret() %s\n", toChars()); #endif Expression *e; if (econd->type->ty == Tpointer && econd->type->nextOf()->ty != Tfunction) { e = econd->interpret(istate, ctfeNeedLvalue); if (e == EXP_CANT_INTERPRET) return e; if (e->op != TOKnull) e = new IntegerExp(loc, 1, Type::tbool); } else e = econd->interpret(istate); if (e != EXP_CANT_INTERPRET) { if (isTrueBool(e)) e = e1->interpret(istate, goal); else if (e->isBool(FALSE)) e = e2->interpret(istate, goal); else { error("%s does not evaluate to boolean result at compile time", econd->toChars()); e = EXP_CANT_INTERPRET; } } return e; } Expression *ArrayLengthExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e; Expression *e1; #if LOG printf("ArrayLengthExp::interpret() %s\n", toChars()); #endif e1 = this->e1->interpret(istate); assert(e1); if (e1 == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; if (e1->op == TOKstring || e1->op == TOKarrayliteral || e1->op == TOKslice || e1->op == TOKassocarrayliteral || e1->op == TOKnull) { e = new IntegerExp(loc, resolveArrayLength(e1), type); } else { error("%s cannot be evaluated at compile time", toChars()); return EXP_CANT_INTERPRET; } return e; } /* Given an AA literal 'ae', and a key 'e2': * Return ae[e2] if present, or NULL if not found. * Return EXP_CANT_INTERPRET on error. */ Expression *findKeyInAA(AssocArrayLiteralExp *ae, Expression *e2) { /* Search the keys backwards, in case there are duplicate keys */ for (size_t i = ae->keys->dim; i;) { i--; Expression *ekey = ae->keys->tdata()[i]; Expression *ex = ctfeEqual(TOKequal, Type::tbool, ekey, e2); if (ex == EXP_CANT_INTERPRET) { error("cannot evaluate %s==%s at compile time", ekey->toChars(), e2->toChars()); return ex; } if (ex->isBool(TRUE)) { return ae->values->tdata()[i]; } } return NULL; } Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e1 = NULL; Expression *e2; #if LOG printf("IndexExp::interpret() %s\n", toChars()); #endif if (this->e1->type->toBasetype()->ty == Tpointer) { // Indexing a pointer. Note that there is no $ in this case. e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; e2 = this->e2->interpret(istate); if (e2 == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; dinteger_t indx = e2->toInteger(); dinteger_t ofs; Expression *agg = getAggregateFromPointer(e1, &ofs); if (agg->op == TOKnull) { error("cannot index null pointer %s", this->e1->toChars()); return EXP_CANT_INTERPRET; } assert(agg->op == TOKarrayliteral || agg->op == TOKstring); dinteger_t len = ArrayLength(Type::tsize_t, agg)->toInteger(); Type *pointee = ((TypePointer *)agg->type)->next; if ((indx + ofs) < 0 || (indx+ofs) > len) { error("pointer index [%jd] exceeds allocated memory block [0..%jd]", indx+ofs, len); return EXP_CANT_INTERPRET; } return Index(type, agg, new IntegerExp(loc, indx+ofs, Type::tsize_t)); } e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; if (e1->op == TOKnull) { error("cannot index null array %s", this->e1->toChars()); return EXP_CANT_INTERPRET; } /* Set the $ variable. * Note that foreach uses indexing but doesn't need $ */ if (lengthVar && (e1->op == TOKstring || e1->op == TOKarrayliteral || e1->op == TOKslice)) { uinteger_t dollar = resolveArrayLength(e1); Expression *dollarExp = new IntegerExp(loc, dollar, Type::tsize_t); lengthVar->createStackValue(dollarExp); } e2 = this->e2->interpret(istate); if (lengthVar) lengthVar->setValueNull(); // $ is defined only inside [] if (e2 == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; if (e1->op == TOKslice && e2->op == TOKint64) { // Simplify index of slice: agg[lwr..upr][indx] --> agg[indx'] uinteger_t indx = e2->toInteger(); uinteger_t ilo = ((SliceExp *)e1)->lwr->toInteger(); uinteger_t iup = ((SliceExp *)e1)->upr->toInteger(); if (indx > iup - ilo) { error("index %ju exceeds array length %ju", indx, iup - ilo); return EXP_CANT_INTERPRET; } indx += ilo; e1 = ((SliceExp *)e1)->e1; e2 = new IntegerExp(e2->loc, indx, e2->type); } Expression *e = NULL; if ((goal == ctfeNeedLvalue && type->ty != Taarray && type->ty != Tarray && type->ty != Tsarray && type->ty != Tstruct && type->ty != Tclass) || (goal == ctfeNeedLvalueRef && type->ty != Tsarray && type->ty != Tstruct) ) { // Pointer or reference of a scalar type e = new IndexExp(loc, e1, e2); e->type = type; return e; } if (e1->op == TOKassocarrayliteral) { if (e2->op == TOKslice) e2 = resolveSlice(e2); e = findKeyInAA((AssocArrayLiteralExp *)e1, e2); if (!e) { error("key %s not found in associative array %s", e2->toChars(), this->e1->toChars()); return EXP_CANT_INTERPRET; } if (e == EXP_CANT_INTERPRET) return e; assert(!e->checkSideEffect(2)); e = paintTypeOntoLiteral(type, e); } else { e = Index(type, e1, e2); } if (e == EXP_CANT_INTERPRET) { error("%s cannot be interpreted at compile time", toChars()); return e; } if (goal == ctfeNeedRvalue && (e->op == TOKslice || e->op == TOKdotvar)) e = e->interpret(istate); return e; } Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e1; Expression *lwr; Expression *upr; #if LOG printf("SliceExp::interpret() %s\n", toChars()); #endif if (this->e1->type->toBasetype()->ty == Tpointer) { // Slicing a pointer. Note that there is no $ in this case. e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; if (e1->op == TOKint64) { error("cannot slice invalid pointer %s of value %s", this->e1->toChars(), e1->toChars()); return EXP_CANT_INTERPRET; } /* Evaluate lower and upper bounds of slice */ lwr = this->lwr->interpret(istate); if (lwr == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; upr = this->upr->interpret(istate); if (upr == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; uinteger_t ilwr; uinteger_t iupr; ilwr = lwr->toInteger(); iupr = upr->toInteger(); Expression *e; dinteger_t ofs; Expression *agg = getAggregateFromPointer(e1, &ofs); if (agg->op == TOKnull) { if (iupr == ilwr) { e = new NullExp(loc); e->type = type; return e; } error("cannot slice null pointer %s", this->e1->toChars()); return EXP_CANT_INTERPRET; } assert(agg->op == TOKarrayliteral || agg->op == TOKstring); dinteger_t len = ArrayLength(Type::tsize_t, agg)->toInteger(); Type *pointee = ((TypePointer *)agg->type)->next; if ((ilwr + ofs) < 0 || (iupr+ofs) > (len + 1) || iupr < ilwr) { error("pointer slice [%jd..%jd] exceeds allocated memory block [0..%jd]", ilwr+ofs, iupr+ofs, len); return EXP_CANT_INTERPRET; } e = new SliceExp(loc, agg, lwr, upr); e->type = type; return e; } if (goal == ctfeNeedRvalue && this->e1->op == TOKstring) e1 = this->e1; // Will get duplicated anyway else e1 = this->e1->interpret(istate, goal); if (e1 == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; if (e1->op == TOKvar) e1 = e1->interpret(istate); if (!this->lwr) { if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) return e1; return paintTypeOntoLiteral(type, e1); } /* Set the $ variable */ if (e1->op != TOKarrayliteral && e1->op != TOKstring && e1->op != TOKnull && e1->op != TOKslice) { error("Cannot determine length of %s at compile time\n", e1->toChars()); return EXP_CANT_INTERPRET; } uinteger_t dollar = resolveArrayLength(e1); if (lengthVar) { IntegerExp *dollarExp = new IntegerExp(loc, dollar, Type::tsize_t); lengthVar->createStackValue(dollarExp); } /* Evaluate lower and upper bounds of slice */ lwr = this->lwr->interpret(istate); if (lwr != EXP_CANT_INTERPRET) upr = this->upr->interpret(istate); if (lengthVar) lengthVar->setValueNull(); // $ is defined only inside [L..U] if (lwr == EXP_CANT_INTERPRET || upr == EXP_CANT_INTERPRET) { return EXP_CANT_INTERPRET; } Expression *e; uinteger_t ilwr; uinteger_t iupr; ilwr = lwr->toInteger(); iupr = upr->toInteger(); if (e1->op == TOKnull) { if (ilwr== 0 && iupr == 0) return e1; e1->error("slice [%ju..%ju] is out of bounds", ilwr, iupr); return EXP_CANT_INTERPRET; } 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) { error("slice[%ju..%ju] exceeds array bounds[%ju..%ju]", ilwr, iupr, lo1, up1); return EXP_CANT_INTERPRET; } ilwr += lo1; iupr += lo1; e = new SliceExp(loc, se->e1, new IntegerExp(loc, ilwr, lwr->type), new IntegerExp(loc, iupr, upr->type)); e->type = type; return e; } if (e1->op == TOKarrayliteral || e1->op == TOKstring) { if (iupr < ilwr || ilwr < 0 || iupr > dollar) { error("slice [%jd..%jd] exceeds array bounds [0..%jd]", ilwr, iupr, dollar); return EXP_CANT_INTERPRET; } } e = new SliceExp(loc, e1, lwr, upr); e->type = type; return e; } Expression *InExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e = EXP_CANT_INTERPRET; #if LOG printf("InExp::interpret() %s\n", toChars()); #endif Expression *e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) return e1; Expression *e2 = this->e2->interpret(istate); if (e2 == EXP_CANT_INTERPRET) return e2; if (e2->op == TOKnull) return new NullExp(loc, type); if (e2->op != TOKassocarrayliteral) { error(" %s cannot be interpreted at compile time", toChars()); return EXP_CANT_INTERPRET; } if (e1->op == TOKslice) e1 = resolveSlice(e1); e = findKeyInAA((AssocArrayLiteralExp *)e2, e1); if (e == EXP_CANT_INTERPRET) return e; if (!e) return new NullExp(loc, type); e = new IndexExp(loc, e2, e1); e->type = type; return e; } Expression *CatExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e; Expression *e1; Expression *e2; #if LOG printf("CatExp::interpret() %s\n", toChars()); #endif e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) { goto Lcant; } if (e1->op == TOKslice) { e1 = resolveSlice(e1); } e2 = this->e2->interpret(istate); if (e2 == EXP_CANT_INTERPRET) goto Lcant; if (e2->op == TOKslice) e2 = resolveSlice(e2); e = Cat(type, e1, e2); if (e == EXP_CANT_INTERPRET) error("%s cannot be interpreted at compile time", toChars()); return e; Lcant: #if LOG printf("CatExp::interpret() %s CANT\n", toChars()); #endif return EXP_CANT_INTERPRET; } Expression *CastExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e; Expression *e1; #if LOG printf("CastExp::interpret() %s\n", toChars()); #endif e1 = this->e1->interpret(istate, goal); if (e1 == EXP_CANT_INTERPRET) goto Lcant; if (to->ty == Tpointer && e1->op != TOKnull) { // Deal with casts from char[] to char * if (e1->type->ty == Tarray || e1->type->ty == Tsarray) { // Check for unsupported type painting operations Type *elemtype = ((TypeArray *)(e1->type))->next; Type *pointee = ((TypePointer *)type)->next; if ( #if DMDV2 e1->type->nextOf()->castMod(0) != to->nextOf()->castMod(0) #else e1->type->nextOf() != to->nextOf() #endif && !(elemtype->isintegral() && pointee->isintegral() && elemtype->size() == pointee->size())) { error("reinterpreting cast from %s to %s is not supported in CTFE", e1->type->toChars(), type->toChars()); return EXP_CANT_INTERPRET; } } if (e1->op == TOKslice) { if ( ((SliceExp *)e1)->e1->op == TOKnull) { return paintTypeOntoLiteral(type, ((SliceExp *)e1)->e1); } e = new IndexExp(loc, ((SliceExp *)e1)->e1, ((SliceExp *)e1)->lwr); e->type = type; return e; } if (e1->op == TOKarrayliteral) { e = new IndexExp(loc, e1, new IntegerExp(loc, 0, Type::tsize_t)); e->type = type; return e; } if (e1->op == TOKstring) { return e1; } if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type != e1->type) { // type painting operation IndexExp *ie = (IndexExp *)e1; e = new IndexExp(e1->loc, ie->e1, ie->e2); e->type = type; return e; } if (e1->op == TOKint64) { // Happens with Windows HANDLEs, for example. return paintTypeOntoLiteral(to, e1); } error("pointer cast from %s to %s is not supported at compile time", e1->type->toChars(), to->toChars()); return EXP_CANT_INTERPRET; } if (to->ty == Tarray && e1->op == TOKslice) { e1 = new SliceExp(e1->loc, ((SliceExp *)e1)->e1, ((SliceExp *)e1)->lwr, ((SliceExp *)e1)->upr); e1->type = to; return e1; } // Disallow array type painting, except for conversions between built-in // types of identical size. if ((to->ty == Tsarray || to->ty == Tarray) && (e1->type->ty == Tsarray || e1->type->ty == Tarray) && #if DMDV2 e1->type->nextOf()->castMod(0) != to->nextOf()->castMod(0) #else e1->type->nextOf() != to->nextOf() #endif && !(to->nextOf()->isTypeBasic() && e1->type->nextOf()->isTypeBasic() && to->nextOf()->size() == e1->type->nextOf()->size()) ) { error("array cast from %s to %s is not supported at compile time", e1->type->toChars(), to->toChars()); return EXP_CANT_INTERPRET; } if (to->ty == Tsarray && e1->op == TOKslice) e1 = resolveSlice(e1); if (to->toBasetype()->ty == Tbool && e1->type->ty==Tpointer) { return new IntegerExp(loc, e1->op != TOKnull, to); } if (e1->op == TOKnull) return paintTypeOntoLiteral(to, e1); e = Cast(type, to, e1); if (e == EXP_CANT_INTERPRET) error("%s cannot be interpreted at compile time", toChars()); return e; Lcant: #if LOG printf("CastExp::interpret() %s CANT\n", toChars()); #endif return EXP_CANT_INTERPRET; } Expression *AssertExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e; Expression *e1; #if LOG printf("AssertExp::interpret() %s\n", toChars()); #endif if (this->e1->op == TOKthis) { if (istate->localThis) { if (istate->localThis->op == TOKdotvar && ((DotVarExp *)(istate->localThis))->e1->op == TOKthis) return getVarExp(loc, istate, ((DotVarExp*)(istate->localThis))->var, ctfeNeedRvalue); else return istate->localThis->interpret(istate); } } // Deal with pointers (including compiler-inserted assert(&this, "null this")) if (this->e1->type->ty == Tpointer && this->e1->type->nextOf()->ty != Tfunction) { e1 = this->e1->interpret(istate, ctfeNeedLvalue); if (e1 == EXP_CANT_INTERPRET) goto Lcant; if (e1->op != TOKnull) return new IntegerExp(loc, 1, Type::tbool); } else e1 = this->e1->interpret(istate); if (e1 == EXP_CANT_INTERPRET) goto Lcant; if (isTrueBool(e1)) { } else if (e1->isBool(FALSE)) { if (msg) { e = msg->interpret(istate); if (e == EXP_CANT_INTERPRET) goto Lcant; error("%s", e->toChars()); } else error("%s failed", toChars()); goto Lcant; } else { error("%s is not a compile-time boolean expression", e1->toChars()); goto Lcant; } return e1; Lcant: return EXP_CANT_INTERPRET; } Expression *PtrExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e = EXP_CANT_INTERPRET; #if LOG printf("PtrExp::interpret() %s\n", toChars()); #endif // Constant fold *(&structliteral + offset) if (e1->op == TOKadd) { AddExp *ae = (AddExp *)e1; if (ae->e1->op == TOKaddress && ae->e2->op == TOKint64) { AddrExp *ade = (AddrExp *)ae->e1; Expression *ex = ade->e1; ex = ex->interpret(istate); if (ex != EXP_CANT_INTERPRET) { if (ex->op == TOKstructliteral) { StructLiteralExp *se = (StructLiteralExp *)ex; dinteger_t offset = ae->e2->toInteger(); e = se->getField(type, offset); if (!e) e = EXP_CANT_INTERPRET; return e; } } } e = Ptr(type, e1); } #if DMDV2 #else // this is required for D1, where structs return *this instead of 'this'. else if (e1->op == TOKthis) { if(istate->localThis) return istate->localThis->interpret(istate); } #endif else { // 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. e = e1->interpret(istate, ctfeNeedLvalue); if (e == EXP_CANT_INTERPRET) return e; if (!(e->op == TOKvar || e->op == TOKdotvar || e->op == TOKindex || e->op == TOKslice || e->op == TOKaddress)) { error("dereference of invalid pointer '%s'", e->toChars()); return EXP_CANT_INTERPRET; } if (goal != ctfeNeedLvalue) { if (e->op == TOKindex && e->type->ty == Tpointer) { IndexExp *ie = (IndexExp *)e; if ((ie->e1->op == TOKarrayliteral || ie->e1->op == TOKstring) && ie->e2->op == TOKint64) { Expression *dollar = ArrayLength(Type::tsize_t, ie->e1); dinteger_t len = dollar->toInteger(); dinteger_t indx = ie->e2->toInteger(); assert(indx >=0 && indx <= len); // invalid pointer if (indx == len) { error("dereference of pointer %s one past end of memory block limits [0..%jd]", toChars(), len); return EXP_CANT_INTERPRET; } return Index(type, ie->e1, ie->e2); } if (ie->e1->op == TOKassocarrayliteral) return Index(type, ie->e1, ie->e2); } if (e->op == TOKstructliteral) return e; e = e1->interpret(istate, goal); if (e->op == TOKaddress) { e = ((AddrExp*)e)->e1; if (e->op == TOKdotvar || e->op == TOKindex) e = e->interpret(istate, goal); } if (e == EXP_CANT_INTERPRET) return e; } else if (e->op == TOKaddress) e = ((AddrExp*)e)->e1; // *(&x) ==> x if (e->op == TOKnull) { error("dereference of null pointer '%s'", e1->toChars()); return EXP_CANT_INTERPRET; } e->type = type; } #if LOG if (e == EXP_CANT_INTERPRET) printf("PtrExp::interpret() %s = EXP_CANT_INTERPRET\n", toChars()); #endif return e; } Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e = EXP_CANT_INTERPRET; #if LOG printf("DotVarExp::interpret() %s\n", toChars()); #endif Expression *ex = e1->interpret(istate); if (ex != EXP_CANT_INTERPRET) { if (ex->op == TOKaddress) ex = ((AddrExp *)ex)->e1; if (ex->op == TOKstructliteral) { StructLiteralExp *se = (StructLiteralExp *)ex; VarDeclaration *v = var->isVarDeclaration(); if (v) { if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) { // We can't use getField, because it makes a copy int i = se->getFieldIndex(type, v->offset); if (i == -1) { error("couldn't find field %s in %s", v->toChars(), type->toChars()); return EXP_CANT_INTERPRET; } e = se->elements->tdata()[i]; // If it is an lvalue literal, return it... if (e->op == TOKstructliteral) return e; if ((type->ty == Tsarray || goal == ctfeNeedLvalue) && ( e->op == TOKarrayliteral || e->op == TOKassocarrayliteral || e->op == TOKstring || e->op == TOKslice)) return e; /* Element is an allocated pointer, which was created in * CastExp. */ if (goal == ctfeNeedLvalue && e->op == TOKindex && e->type == type && (type->ty == Tpointer && type->nextOf()->ty != Tfunction)) return e; // ...Otherwise, just return the (simplified) dotvar expression e = new DotVarExp(loc, ex, v); e->type = type; return e; } e = se->getField(type, v->offset); if (!e) { error("couldn't find field %s in %s", v->toChars(), type->toChars()); e = EXP_CANT_INTERPRET; } // If it is an rvalue literal, return it... if (e->op == TOKstructliteral || e->op == TOKarrayliteral || e->op == TOKassocarrayliteral || e->op == TOKstring) return e; if (type->ty == Tpointer && type->nextOf()->ty != Tfunction) { assert(e->type == type); return e; } return e->interpret(istate, goal); } } else error("%s.%s is not yet implemented at compile time", e1->toChars(), var->toChars()); } #if LOG if (e == EXP_CANT_INTERPRET) printf("DotVarExp::interpret() %s = EXP_CANT_INTERPRET\n", toChars()); #endif return e; } /******************************* Special Functions ***************************/ #if DMDV1 Expression *interpret_aaLen(InterState *istate, Expressions *arguments) { if (!arguments || arguments->dim != 1) return NULL; Expression *earg = arguments->tdata()[0]; earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; if (earg->op != TOKassocarrayliteral) return NULL; AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; Expression *e = new IntegerExp(aae->loc, aae->keys->dim, Type::tsize_t); return e; } Expression *interpret_aaKeys(InterState *istate, Expressions *arguments) { #if LOG printf("interpret_aaKeys()\n"); #endif if (!arguments || arguments->dim != 2) return NULL; Expression *earg = arguments->tdata()[0]; earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; if (earg->op == TOKnull) return new NullExp(earg->loc); if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) return NULL; AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; Expression *e = new ArrayLiteralExp(aae->loc, aae->keys); Type *elemType = ((TypeAArray *)aae->type)->index; e->type = new TypeSArray(elemType, new IntegerExp(arguments ? arguments->dim : 0)); return copyLiteral(e); } Expression *interpret_aaValues(InterState *istate, Expressions *arguments) { #if LOG printf("interpret_aaValues()\n"); #endif if (!arguments || arguments->dim != 3) return NULL; Expression *earg = arguments->tdata()[0]; earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; if (earg->op == TOKnull) return new NullExp(earg->loc); if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) return NULL; AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; Expression *e = new ArrayLiteralExp(aae->loc, aae->values); Type *elemType = ((TypeAArray *)aae->type)->next; e->type = new TypeSArray(elemType, new IntegerExp(arguments ? arguments->dim : 0)); //printf("result is %s\n", e->toChars()); return copyLiteral(e); } #endif #if DMDV2 Expression *interpret_length(InterState *istate, Expression *earg) { //printf("interpret_length()\n"); earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; if (earg->op != TOKassocarrayliteral) return NULL; AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; Expression *e = new IntegerExp(aae->loc, aae->keys->dim, Type::tsize_t); return e; } Expression *interpret_keys(InterState *istate, Expression *earg, FuncDeclaration *fd) { #if LOG printf("interpret_keys()\n"); #endif earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; if (earg->op == TOKnull) return new NullExp(earg->loc); if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) return NULL; assert(earg->op == TOKassocarrayliteral); AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; Expression *e = new ArrayLiteralExp(aae->loc, aae->keys); assert(fd->type->ty == Tfunction); assert(fd->type->nextOf()->ty == Tarray); Type *elemType = ((TypeFunction *)fd->type)->nextOf()->nextOf(); e->type = new TypeSArray(elemType, new IntegerExp(aae->keys->dim)); return copyLiteral(e); } Expression *interpret_values(InterState *istate, Expression *earg, FuncDeclaration *fd) { #if LOG printf("interpret_values()\n"); #endif earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; if (earg->op == TOKnull) return new NullExp(earg->loc); if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) return NULL; assert(earg->op == TOKassocarrayliteral); AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; Expression *e = new ArrayLiteralExp(aae->loc, aae->values); assert(fd->type->ty == Tfunction); assert(fd->type->nextOf()->ty == Tarray); Type *elemType = ((TypeFunction *)fd->type)->nextOf()->nextOf(); e->type = new TypeSArray(elemType, new IntegerExp(aae->values->dim)); //printf("result is %s\n", e->toChars()); return copyLiteral(e); } #endif #if DMDV2 // Return true if t is an AA, or AssociativeArray!(key, value) bool isAssocArray(Type *t) { t = t->toBasetype(); if (t->ty == Taarray) return true; if (t->ty != Tstruct) return false; StructDeclaration *sym = ((TypeStruct *)t)->sym; if (sym->ident == Id::AssociativeArray) return true; return false; } #endif /* Decoding UTF strings for foreach loops. Duplicates the functionality of * the twelve _aApplyXXn functions in aApply.d in the runtime. */ Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *deleg, bool rvs) { #if LOG printf("foreachApplyUtf(%s, %s)\n", str->toChars(), deleg->toChars()); #endif 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); int numParams = fd->parameters->dim; assert(numParams == 1 || numParams==2); Type *charType = fd->parameters->tdata()[numParams-1]->type; Type *indexType = numParams == 2 ? fd->parameters->tdata()[0]->type : Type::tsize_t; uinteger_t len = resolveArrayLength(str); if (len == 0) return new IntegerExp(deleg->loc, 0, indexType); if (str->op == TOKslice) 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 { error("CTFE internal error: cannot foreach %s", str->toChars()); return EXP_CANT_INTERPRET; } Expressions args; args.setDim(numParams); Expression *eresult; // Buffers for encoding; also used for decoding array literals unsigned char 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 int buflen = 1; // #code points in the buffer size_t n = 1; // #code points in this char size_t sz = 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->tdata()[indx]; assert(r->op == TOKint64); unsigned char x = (unsigned char)(((IntegerExp *)r)->value); if ( (x & 0xC0) != 0x80) break; ++buflen; } } else buflen = (indx + 4 > len) ? len - indx : 4; for (int i=0; i < buflen; ++i) { Expression * r = ale->elements->tdata()[indx + i]; assert(r->op == TOKint64); utf8buf[i] = (unsigned char)(((IntegerExp *)r)->value); } 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->tdata()[indx]; assert(r->op == TOKint64); unsigned short x = (unsigned short)(((IntegerExp *)r)->value); if (indx > 0 && x >= 0xDC00 && x <= 0xDFFF) { --indx; ++buflen; } } else buflen = (indx + 2 > len) ? len - indx : 2; for (int i=0; i < buflen; ++i) { Expression * r = ale->elements->tdata()[indx + i]; assert(r->op == TOKint64); utf16buf[i] = (unsigned short)(((IntegerExp *)r)->value); } n = 0; errmsg = utf_decodeWchar(&utf16buf[0], buflen, &n, &rawvalue); break; case 4: { if (rvs) --indx; Expression * r = ale->elements->tdata()[indx]; assert(r->op == TOKint64); rawvalue = ((IntegerExp *)r)->value; 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 unsigned char *s = (unsigned char *)se->string; --indx; while (indx > 0 && ((s[indx]&0xC0)==0x80)) --indx; saveindx = indx; } errmsg = utf_decodeChar((unsigned char *)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 EXP_CANT_INTERPRET; } // 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.tdata()[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.tdata()[numParams - 1] = val; eresult = fd->interpret(istate, &args, pthis); if (eresult == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; assert(eresult->op == TOKint64); if (((IntegerExp *)eresult)->value != 0) return eresult; } } return eresult; } /* If this is a built-in function, set 'result' to the interpreted result, * and return true. * Otherwise, return false */ bool evaluateIfBuiltin(Expression **result, InterState *istate, FuncDeclaration *fd, Expressions *arguments, Expression *pthis) { Expression *e = NULL; int nargs = arguments ? arguments->dim : 0; #if DMDV2 if (pthis && isAssocArray(pthis->type) && nargs==0) { if (fd->ident == Id::length) e = interpret_length(istate, pthis); else if (fd->ident == Id::keys) e = interpret_keys(istate, pthis, fd); else if (fd->ident == Id::values) e = interpret_values(istate, pthis, fd); else if (fd->ident == Id::rehash) e = pthis; // rehash is a no-op } if (!pthis) { enum BUILTIN b = fd->isBuiltin(); if (b) { Expressions args; args.setDim(nargs); for (size_t i = 0; i < args.dim; i++) { Expression *earg = arguments->tdata()[i]; earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) { *result = EXP_CANT_INTERPRET; return true; } args.tdata()[i] = earg; } e = eval_builtin(b, &args); if (!e) e = EXP_CANT_INTERPRET; } } #endif #if DMDV1 if (!pthis) { if (fd->ident == Id::aaLen) e = interpret_aaLen(istate, arguments); else if (fd->ident == Id::aaKeys) e = interpret_aaKeys(istate, arguments); else if (fd->ident == Id::aaValues) e = interpret_aaValues(istate, arguments); else if (fd->ident == Id::aaRehash && nargs == 2) { // rehash is a no-op Expression *earg = (Expression *)(arguments->data[0]); return earg->interpret(istate, ctfeNeedLvalue); } } #endif if (!pthis) { size_t idlen = strlen(fd->ident->string); if (nargs == 2 && (idlen == 10 || idlen == 11) && !strncmp(fd->ident->string, "_aApply", 7)) { // Functions from aApply.d and aApplyR.d in the runtime bool rvs = (idlen == 11); // true if foreach_reverse char c = fd->ident->string[idlen-3]; // char width: 'c', 'w', or 'd' char s = fd->ident->string[idlen-2]; // string width: 'c', 'w', or 'd' char n = fd->ident->string[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->tdata()[0]; str = str->interpret(istate); if (str == EXP_CANT_INTERPRET) { *result = EXP_CANT_INTERPRET; return true; } *result = foreachApplyUtf(istate, str, arguments->tdata()[1], rvs); return true; } } } if (!e) return false; *result = e; return true; } /*************************** CTFE Sanity Checks ***************************/ /* Setter functions for CTFE variable values. * These functions exist to check for compiler CTFE bugs. */ bool isStackValueValid(Expression *newval) { if (newval->type->ty == Tpointer && newval->type->nextOf()->ty != Tfunction) { if (newval->op == TOKaddress || newval->op == TOKnull || newval->op == TOKstring) return true; if (newval->op == TOKindex) { Expression *g = ((IndexExp *)newval)->e1; if (g->op == TOKarrayliteral || g->op == TOKstring || g->op == TOKassocarrayliteral) return true; } if (newval->op == TOKvar) return true; if (newval->type->nextOf()->ty == Tarray && newval->op == TOKslice) return true; if (newval->op == TOKint64) return true; // Result of a cast, but cannot be dereferenced newval->error("CTFE internal error: illegal pointer value %s\n", newval->toChars()); return false; } if ((newval->op ==TOKarrayliteral) || ( newval->op==TOKstructliteral) || (newval->op==TOKstring) || (newval->op == TOKassocarrayliteral) || (newval->op == TOKnull) || (newval->op == TOKslice)) { return false; } if (newval->op == TOKvar) { VarExp *ve = (VarExp *)newval; VarDeclaration *vv = ve->var->isVarDeclaration(); // Must not be a reference to a reference if (!(vv && vv->getValue() && vv->getValue()->op == TOKvar)) return true; } if (newval->op == TOKdotvar) { if (((DotVarExp *)newval)->e1->op == TOKstructliteral) return true; } if (newval->op == TOKindex) { IndexExp *ie = (IndexExp *)newval; if (ie->e2->op == TOKint64) { if (ie->e1->op == TOKarrayliteral || ie->e1->op == TOKstring) return true; } if (ie->e1->op == TOKassocarrayliteral) return true; // BUG: Happens ONLY in ref foreach. Should tighten this. if (ie->e2->op == TOKvar) return true; } if (newval->op == TOKfunction) return true; // function/delegate literal if (newval->op == TOKdelegate) return true; if (newval->op == TOKsymoff) // function pointer { if (((SymOffExp *)newval)->var->isFuncDeclaration()) return true; } #if IN_LLVM if (newval->op == TOKaddress) { // function pointer AddrExp *ae = (AddrExp *)newval; if (ae->e1->op == TOKvar) { if (((VarExp *)ae->e1)->var->isFuncDeclaration()) return true; } } #endif if (newval->op == TOKint64 || newval->op == TOKfloat64 || newval->op == TOKchar || newval->op == TOKcomplex80) return true; newval->error("CTFE internal error: illegal stack value %s\n", newval->toChars()); return false; } bool isRefValueValid(Expression *newval) { assert(newval); if ((newval->op ==TOKarrayliteral) || ( newval->op==TOKstructliteral) || (newval->op==TOKstring) || (newval->op == TOKassocarrayliteral) || (newval->op == TOKnull)) { return true; } // Dynamic arrays passed by ref may be null. When this happens // they may originate from an index or dotvar expression. if (newval->type->ty == Tarray || newval->type->ty == Taarray || newval->type->ty == Tclass) if (newval->op == TOKdotvar || newval->op == TOKindex) return isStackValueValid(newval); // actually must be null if (newval->op == TOKslice) { SliceExp *se = (SliceExp *)newval; assert(se->lwr && se->lwr != EXP_CANT_INTERPRET && se->lwr->op == TOKint64); assert(se->upr && se->upr != EXP_CANT_INTERPRET && se->upr->op == TOKint64); assert(se->e1->op == TOKarrayliteral || se->e1->op == TOKstring); return true; } newval->error("CTFE internal error: illegal reference value %s\n", newval->toChars()); return false; } void VarDeclaration::setValueNull() { literalvalue = NULL; } // Don't check for validity void VarDeclaration::setValueWithoutChecking(Expression *newval) { assert(!newval || isStackValueValid(newval) || isRefValueValid(newval)); literalvalue = newval; } void VarDeclaration::createRefValue(Expression *newval) { assert(!literalvalue); assert(isRefValueValid(newval)); literalvalue = newval; } void VarDeclaration::setRefValue(Expression *newval) { assert(literalvalue); assert(isRefValueValid(newval)); literalvalue = newval; } void VarDeclaration::setStackValue(Expression *newval) { assert(literalvalue); assert(isStackValueValid(newval)); literalvalue = newval; } void VarDeclaration::createStackValue(Expression *newval) { assert(!literalvalue); assert(isStackValueValid(newval)); literalvalue = newval; }