/* Compiler implementation of the D programming language * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved * written by Walter Bright * http://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * http://www.boost.org/LICENSE_1_0.txt * https://github.com/D-Programming-Language/dmd/blob/master/src/ctfeexpr.c */ #include "root/dsystem.h" // mem{cpy|set}() #include "root/rmem.h" #include "mars.h" #include "expression.h" #include "declaration.h" #include "aggregate.h" // for AssocArray #include "id.h" #include "utf.h" #include "template.h" #include "ctfe.h" int RealEquals(real_t x1, real_t x2); /************** ClassReferenceExp ********************************************/ ClassReferenceExp::ClassReferenceExp(Loc loc, StructLiteralExp *lit, Type *type) : Expression(loc, TOKclassreference, sizeof(ClassReferenceExp)) { assert(lit && lit->sd && lit->sd->isClassDeclaration()); this->value = lit; this->type = type; } ClassDeclaration *ClassReferenceExp::originalClass() { return value->sd->isClassDeclaration(); } // Return index of the field, or -1 if not found int ClassReferenceExp::getFieldIndex(Type *fieldtype, unsigned fieldoffset) { ClassDeclaration *cd = originalClass(); unsigned fieldsSoFar = 0; for (size_t j = 0; j < value->elements->dim; j++) { while (j - fieldsSoFar >= cd->fields.dim) { fieldsSoFar += cd->fields.dim; cd = cd->baseClass; } VarDeclaration *v2 = cd->fields[j - fieldsSoFar]; if (fieldoffset == v2->offset && fieldtype->size() == v2->type->size()) { return (int)(value->elements->dim - fieldsSoFar - cd->fields.dim + (j-fieldsSoFar)); } } return -1; } // Return index of the field, or -1 if not found // Same as getFieldIndex, but checks for a direct match with the VarDeclaration int ClassReferenceExp::findFieldIndexByName(VarDeclaration *v) { ClassDeclaration *cd = originalClass(); size_t fieldsSoFar = 0; for (size_t j = 0; j < value->elements->dim; j++) { while (j - fieldsSoFar >= cd->fields.dim) { fieldsSoFar += cd->fields.dim; cd = cd->baseClass; } VarDeclaration *v2 = cd->fields[j - fieldsSoFar]; if (v == v2) { return (int)(value->elements->dim - fieldsSoFar - cd->fields.dim + (j-fieldsSoFar)); } } return -1; } /************** VoidInitExp ********************************************/ VoidInitExp::VoidInitExp(VarDeclaration *var, Type *) : Expression(var->loc, TOKvoid, sizeof(VoidInitExp)) { this->var = var; this->type = var->type; } const char *VoidInitExp::toChars() { return "void"; } // Return index of the field, or -1 if not found // Same as getFieldIndex, but checks for a direct match with the VarDeclaration int findFieldIndexByName(StructDeclaration *sd, VarDeclaration *v) { for (size_t i = 0; i < sd->fields.dim; ++i) { if (sd->fields[i] == v) return (int)i; } return -1; } /************** ThrownExceptionExp ********************************************/ ThrownExceptionExp::ThrownExceptionExp(Loc loc, ClassReferenceExp *victim) : Expression(loc, TOKthrownexception, sizeof(ThrownExceptionExp)) { this->thrown = victim; this->type = victim->type; } const char *ThrownExceptionExp::toChars() { return "CTFE ThrownException"; } // Generate an error message when this exception is not caught void ThrownExceptionExp::generateUncaughtError() { UnionExp ue; Expression *e = resolveSlice((*thrown->value->elements)[0], &ue); StringExp *se = e->toStringExp(); thrown->error("uncaught CTFE exception %s(%s)", thrown->type->toChars(), se ? se->toChars() : e->toChars()); /* Also give the line where the throw statement was. We won't have it * in the case where the ThrowStatement is generated internally * (eg, in ScopeStatement) */ if (loc.filename && !loc.equals(thrown->loc)) errorSupplemental(loc, "thrown from here"); } // True if 'e' is CTFEExp::cantexp, or an exception bool exceptionOrCantInterpret(Expression *e) { return e && (e->op == TOKcantexp || e->op == TOKthrownexception); } /********************** CTFEExp ******************************************/ CTFEExp *CTFEExp::cantexp; CTFEExp *CTFEExp::voidexp; CTFEExp *CTFEExp::breakexp; CTFEExp *CTFEExp::continueexp; CTFEExp *CTFEExp::gotoexp; CTFEExp::CTFEExp(TOK tok) : Expression(Loc(), tok, sizeof(CTFEExp)) { type = Type::tvoid; } const char *CTFEExp::toChars() { switch (op) { case TOKcantexp: return ""; case TOKvoidexp: return ""; case TOKbreak: return ""; case TOKcontinue: return ""; case TOKgoto: return ""; default: assert(0); return NULL; } } Expression *UnionExp::copy() { Expression *e = exp(); //if (e->size > sizeof(u)) printf("%s\n", Token::toChars(e->op)); assert(e->size <= sizeof(u)); if (e->op == TOKcantexp) return CTFEExp::cantexp; if (e->op == TOKvoidexp) return CTFEExp::voidexp; if (e->op == TOKbreak) return CTFEExp::breakexp; if (e->op == TOKcontinue) return CTFEExp::continueexp; if (e->op == TOKgoto) return CTFEExp::gotoexp; return e->copy(); } /************** Aggregate literals (AA/string/array/struct) ******************/ // 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: return ((ArrayLiteralExp *)expr)->ownedByCtfe == OWNEDcode; case TOKassocarrayliteral: return ((AssocArrayLiteralExp *)expr)->ownedByCtfe == OWNEDcode; case TOKstructliteral: return ((StructLiteralExp *)expr)->ownedByCtfe == OWNEDcode; 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, Expression *basis = NULL) { if (!oldelems) return oldelems; CtfeStatus::numArrayAllocs++; Expressions *newelems = new Expressions(); newelems->setDim(oldelems->dim); for (size_t i = 0; i < oldelems->dim; i++) { Expression *el = (*oldelems)[i]; if (!el) el = basis; (*newelems)[i] = copyLiteral(el).copy(); } return newelems; } // Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral. // This value will be used for in-place modification. UnionExp copyLiteral(Expression *e) { UnionExp ue; if (e->op == TOKstring) // syntaxCopy doesn't make a copy for StringExp! { StringExp *se = (StringExp *)e; utf8_t *s = (utf8_t *)mem.xcalloc(se->len + 1, se->sz); memcpy(s, se->string, se->len * se->sz); new(&ue) StringExp(se->loc, s, se->len); StringExp *se2 = (StringExp *)ue.exp(); se2->committed = se->committed; se2->postfix = se->postfix; se2->type = se->type; se2->sz = se->sz; se2->ownedByCtfe = OWNEDctfe; return ue; } if (e->op == TOKarrayliteral) { ArrayLiteralExp *ale = (ArrayLiteralExp *)e; Expressions *elements = copyLiteralArray(ale->elements, ale->basis); new(&ue) ArrayLiteralExp(e->loc, e->type, elements); ArrayLiteralExp *r = (ArrayLiteralExp *)ue.exp(); r->ownedByCtfe = OWNEDctfe; return ue; } if (e->op == TOKassocarrayliteral) { AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; new(&ue) AssocArrayLiteralExp(e->loc, copyLiteralArray(aae->keys), copyLiteralArray(aae->values)); AssocArrayLiteralExp *r = (AssocArrayLiteralExp *)ue.exp(); r->type = e->type; r->ownedByCtfe = OWNEDctfe; return ue; } if (e->op == TOKstructliteral) { /* 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. */ StructLiteralExp *sle = (StructLiteralExp *)e; Expressions *oldelems = sle->elements; Expressions * newelems = new Expressions(); newelems->setDim(oldelems->dim); for (size_t i = 0; i < newelems->dim; i++) { // We need the struct definition to detect block assignment VarDeclaration *v = sle->sd->fields[i]; Expression *m = (*oldelems)[i]; // If it is a void assignment, use the default initializer if (!m) m = voidInitLiteral(v->type, v).copy(); if (v->type->ty == Tarray || v->type->ty == Taarray) { // Don't have to copy array references } else { // Buzilla 15681: Copy the source element always. m = copyLiteral(m).copy(); // Block assignment from inside struct literals if (v->type->ty != m->type->ty && v->type->ty == Tsarray) { TypeSArray *tsa = (TypeSArray *)v->type; size_t len = (size_t)tsa->dim->toInteger(); UnionExp uex; m = createBlockDuplicatedArrayLiteral(&uex, e->loc, v->type, m, len); if (m == uex.exp()) m = uex.copy(); } } (*newelems)[i] = m; } new(&ue) StructLiteralExp(e->loc, sle->sd, newelems, sle->stype); StructLiteralExp *r = (StructLiteralExp *)ue.exp(); r->type = e->type; r->ownedByCtfe = OWNEDctfe; r->origin = ((StructLiteralExp *)e)->origin; return ue; } if (e->op == TOKfunction || e->op == TOKdelegate || e->op == TOKsymoff || e->op == TOKnull || e->op == TOKvar || e->op == TOKdotvar || e->op == TOKint64 || e->op == TOKfloat64 || e->op == TOKchar || e->op == TOKcomplex80 || e->op == TOKvoid || e->op == TOKvector || e->op == TOKtypeid) { // Simple value types // Keep e1 for DelegateExp and DotVarExp new(&ue) UnionExp(e); Expression *r = ue.exp(); r->type = e->type; return ue; } if (e->op == TOKslice) { SliceExp *se = (SliceExp *)e; if (se->type->toBasetype()->ty == Tsarray) { // same with resolveSlice() if (se->e1->op == TOKnull) { new(&ue) NullExp(se->loc, se->type); return ue; } ue = Slice(se->type, se->e1, se->lwr, se->upr); assert(ue.exp()->op == TOKarrayliteral); ArrayLiteralExp *r = (ArrayLiteralExp *)ue.exp(); r->elements = copyLiteralArray(r->elements); r->ownedByCtfe = OWNEDctfe; return ue; } else { // Array slices only do a shallow copy new(&ue) SliceExp(e->loc, se->e1, se->lwr, se->upr); Expression *r = ue.exp(); r->type = e->type; return ue; } } if (isPointer(e->type)) { // For pointers, we only do a shallow copy. if (e->op == TOKaddress) new(&ue) AddrExp(e->loc, ((AddrExp *)e)->e1); else if (e->op == TOKindex) new(&ue) IndexExp(e->loc, ((IndexExp *)e)->e1, ((IndexExp *)e)->e2); else if (e->op == TOKdotvar) { new(&ue) DotVarExp(e->loc, ((DotVarExp *)e)->e1, ((DotVarExp *)e)->var, ((DotVarExp *)e)->hasOverloads); } else assert(0); Expression *r = ue.exp(); r->type = e->type; return ue; } if (e->op == TOKclassreference) { new(&ue) ClassReferenceExp(e->loc, ((ClassReferenceExp *)e)->value, e->type); return ue; } if (e->op == TOKerror) { new(&ue) UnionExp(e); return ue; } e->error("CTFE internal error: literal %s", e->toChars()); assert(0); return ue; } /* 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->equals(type)) return lit; return paintTypeOntoLiteralCopy(type, lit).copy(); } Expression *paintTypeOntoLiteral(UnionExp *pue, Type *type, Expression *lit) { if (lit->type->equals(type)) return lit; *pue = paintTypeOntoLiteralCopy(type, lit); return pue->exp(); } UnionExp paintTypeOntoLiteralCopy(Type *type, Expression *lit) { UnionExp ue; if (lit->type->equals(type)) { new(&ue) UnionExp(lit); return ue; } // If it is a cast to inout, retain the original type of the referenced part. if (type->hasWild() && type->hasPointers()) { new(&ue) UnionExp(lit); ue.exp()->type = type; return ue; } if (lit->op == TOKslice) { SliceExp *se = (SliceExp *)lit; new(&ue) SliceExp(lit->loc, se->e1, se->lwr, se->upr); } else if (lit->op == TOKindex) { IndexExp *ie = (IndexExp *)lit; new(&ue) IndexExp(lit->loc, ie->e1, ie->e2); } else if (lit->op == TOKarrayliteral) { new(&ue) SliceExp(lit->loc, lit, new IntegerExp(Loc(), 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit).copy()); } else if (lit->op == TOKstring) { // For strings, we need to introduce another level of indirection new(&ue) SliceExp(lit->loc, lit, new IntegerExp(Loc(), 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit).copy()); } else if (lit->op == TOKassocarrayliteral) { AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)lit; // TODO: we should be creating a reference to this AAExp, not // just a ref to the keys and values. OwnedBy wasOwned = aae->ownedByCtfe; new(&ue) AssocArrayLiteralExp(lit->loc, aae->keys, aae->values); aae = (AssocArrayLiteralExp *)ue.exp(); aae->ownedByCtfe = wasOwned; } else { // Can't type paint from struct to struct*; this needs another // level of indirection if (lit->op == TOKstructliteral && isPointer(type)) lit->error("CTFE internal error: painting %s", type->toChars()); ue = copyLiteral(lit); } ue.exp()->type = type; return ue; } /************************************* * If e is a SliceExp, constant fold it. * Params: * e = expression to resolve * pue = if not null, store resulting expression here * Returns: * resulting expression */ Expression *resolveSlice(Expression *e, UnionExp *pue) { if (e->op != TOKslice) return e; SliceExp *se = (SliceExp *)e; if (se->e1->op == TOKnull) return se->e1; if (pue) { *pue = Slice(e->type, se->e1, se->lwr, se->upr); return pue->exp(); } else return Slice(e->type, se->e1, se->lwr, se->upr).copy(); } /* 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 == TOKvector) return ((VectorExp *)e)->dim; 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; } /****************************** * Helper for NewExp * Create an array literal consisting of 'elem' duplicated 'dim' times. * Params: * pue = where to store result * loc = source location where the interpretation occurs * type = target type of the result * elem = the source of array element, it will be owned by the result * dim = element number of the result * Returns: * Constructed ArrayLiteralExp */ ArrayLiteralExp *createBlockDuplicatedArrayLiteral(UnionExp *pue, Loc loc, Type *type, Expression *elem, size_t dim) { if (type->ty == Tsarray && type->nextOf()->ty == Tsarray && elem->type->ty != Tsarray) { // If it is a multidimensional array literal, do it recursively TypeSArray *tsa = (TypeSArray *)type->nextOf(); size_t len = (size_t)tsa->dim->toInteger(); UnionExp ue; elem = createBlockDuplicatedArrayLiteral(&ue, loc, type->nextOf(), elem, len); if (elem == ue.exp()) elem = ue.copy(); } // Buzilla 15681 Type *tb = elem->type->toBasetype(); const bool mustCopy = tb->ty == Tstruct || tb->ty == Tsarray; Expressions *elements = new Expressions(); elements->setDim(dim); for (size_t i = 0; i < dim; i++) { (*elements)[i] = mustCopy ? copyLiteral(elem).copy() : elem; } new(pue) ArrayLiteralExp(loc, type, elements); ArrayLiteralExp *ale = (ArrayLiteralExp *)pue->exp(); ale->ownedByCtfe = OWNEDctfe; return ale; } /****************************** * Helper for NewExp * Create a string literal consisting of 'value' duplicated 'dim' times. */ StringExp *createBlockDuplicatedStringLiteral(UnionExp *pue, Loc loc, Type *type, unsigned value, size_t dim, unsigned char sz) { utf8_t *s = (utf8_t *)mem.xcalloc(dim + 1, sz); for (size_t elemi = 0; elemi < dim; ++elemi) { switch (sz) { case 1: s[elemi] = (utf8_t)value; break; case 2: ((unsigned short *)s)[elemi] = (unsigned short)value; break; case 4: ((unsigned *)s)[elemi] = value; break; default: assert(0); } } new(pue) StringExp(loc, s, dim); StringExp *se = (StringExp *)pue->exp(); se->type = type; se->sz = sz; se->committed = true; se->ownedByCtfe = OWNEDctfe; return se; } // Return true if t is an AA bool isAssocArray(Type *t) { t = t->toBasetype(); if (t->ty == Taarray) return true; return false; } // Given a template AA type, extract the corresponding built-in AA type TypeAArray *toBuiltinAAType(Type *t) { t = t->toBasetype(); if (t->ty == Taarray) return (TypeAArray *)t; assert(0); return NULL; } /************** TypeInfo operations ************************************/ // Return true if type is TypeInfo_Class bool isTypeInfo_Class(Type *type) { return type->ty == Tclass && (Type::dtypeinfo == ((TypeClass *)type)->sym || Type::dtypeinfo->isBaseOf(((TypeClass *)type)->sym, NULL)); } /************** Pointer operations ************************************/ // Return true if t is a pointer (not a function pointer) bool isPointer(Type *t) { Type * tb = t->toBasetype(); return tb->ty == Tpointer && tb->nextOf()->ty != Tfunction; } // For CTFE only. Returns true if 'e' is true or a non-null pointer. bool isTrueBool(Expression *e) { return e->isBool(true) || ((e->type->ty == Tpointer || e->type->ty == Tclass) && e->op != TOKnull); } /* Is it safe to convert from srcPointee* to destPointee* ? * srcPointee is the genuine type (never void). * destPointee may be void. */ bool isSafePointerCast(Type *srcPointee, Type *destPointee) { // It's safe to cast S** to D** if it's OK to cast S* to D* while (srcPointee->ty == Tpointer && destPointee->ty == Tpointer) { srcPointee = srcPointee->nextOf(); destPointee = destPointee->nextOf(); } // It's OK if both are the same (modulo const) if (srcPointee->constConv(destPointee)) return true; // It's OK if function pointers differ only in safe/pure/nothrow if (srcPointee->ty == Tfunction && destPointee->ty == Tfunction) return srcPointee->covariant(destPointee) == 1; // it's OK to cast to void* if (destPointee->ty == Tvoid) return true; // It's OK to cast from V[K] to void* if (srcPointee->ty == Taarray && destPointee == Type::tvoidptr) return true; // It's OK if they are the same size (static array of) integers, eg: // int* --> uint* // int[5][] --> uint[5][] if (srcPointee->ty == Tsarray && destPointee->ty == Tsarray) { if (srcPointee->size() != destPointee->size()) return false; srcPointee = srcPointee->baseElemOf(); destPointee = destPointee->baseElemOf(); } return srcPointee->isintegral() && destPointee->isintegral() && srcPointee->size() == destPointee->size(); } Expression *getAggregateFromPointer(Expression *e, dinteger_t *ofs) { *ofs = 0; if (e->op == TOKaddress) e = ((AddrExp *)e)->e1; if (e->op == TOKsymoff) *ofs = ((SymOffExp *)e)->offset; if (e->op == TOKdotvar) { Expression *ex = ((DotVarExp *)e)->e1; VarDeclaration *v = ((DotVarExp *)e)->var->isVarDeclaration(); assert(v); StructLiteralExp *se = ex->op == TOKclassreference ? ((ClassReferenceExp *)ex)->value : (StructLiteralExp *)ex; // We can't use getField, because it makes a copy unsigned i; if (ex->op == TOKclassreference) i = ((ClassReferenceExp *)ex)->getFieldIndex(e->type, v->offset); else i = se->getFieldIndex(e->type, v->offset); e = (*se->elements)[i]; } 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; } } if (e->op == TOKslice && e->type->toBasetype()->ty == Tsarray) { SliceExp *se = (SliceExp *)e; if ((se->e1->type->ty == Tarray || se->e1->type->ty == Tsarray || se->e1->op == TOKstring || se->e1->op == TOKarrayliteral) && se->lwr->op == TOKint64) { *ofs = se->lwr->toInteger(); return se->e1; } } return e; } /** Return true if agg1 and agg2 are pointers to the same memory block */ bool pointToSameMemoryBlock(Expression *agg1, Expression *agg2) { if (agg1 == agg2) return true; // For integers cast to pointers, we regard them as non-comparable // unless they are identical. (This may be overly strict). if (agg1->op == TOKint64 && agg2->op == TOKint64 && agg1->toInteger() == agg2->toInteger()) { return true; } // Note that type painting can occur with VarExp, so we // must compare the variables being pointed to. if (agg1->op == TOKvar && agg2->op == TOKvar && ((VarExp *)agg1)->var == ((VarExp *)agg2)->var) { return true; } if (agg1->op == TOKsymoff && agg2->op == TOKsymoff && ((SymOffExp *)agg1)->var == ((SymOffExp *)agg2)->var) { return true; } return false; } // return e1 - e2 as an integer, or error if not possible UnionExp pointerDifference(Loc loc, Type *type, Expression *e1, Expression *e2) { UnionExp ue; 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(); new(&ue) 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(); new(&ue) IntegerExp(loc, (ofs1 - ofs2) * sz, type); } } else if (agg1->op == TOKsymoff && agg2->op == TOKsymoff && ((SymOffExp *)agg1)->var == ((SymOffExp *)agg2)->var) { new(&ue) IntegerExp(loc, ofs1 - ofs2, type); } else { error(loc, "%s - %s cannot be interpreted at compile time: cannot subtract " "pointers to two different memory blocks", e1->toChars(), e2->toChars()); new(&ue) CTFEExp(TOKcantexp); } return ue; } // Return eptr op e2, where eptr is a pointer, e2 is an integer, // and op is TOKadd or TOKmin UnionExp pointerArithmetic(Loc loc, TOK op, Type *type, Expression *eptr, Expression *e2) { UnionExp ue; if (eptr->type->nextOf()->ty == Tvoid) { error(loc, "cannot perform arithmetic on void* pointers at compile time"); Lcant: new(&ue) CTFEExp(TOKcantexp); return ue; } dinteger_t ofs1; if (eptr->op == TOKaddress) eptr = ((AddrExp *)eptr)->e1; Expression *agg1 = getAggregateFromPointer(eptr, &ofs1); if (agg1->op == TOKsymoff) { if (((SymOffExp *)agg1)->var->type->ty != Tsarray) { error(loc, "cannot perform pointer arithmetic on arrays of unknown length at compile time"); goto Lcant; } } else if (agg1->op != TOKstring && agg1->op != TOKarrayliteral) { error(loc, "cannot perform pointer arithmetic on non-arrays at compile time"); goto Lcant; } dinteger_t ofs2 = e2->toInteger(); Type *pointee = ((TypeNext *)agg1->type->toBasetype())->next; dinteger_t sz = pointee->size(); sinteger_t indx; dinteger_t len; if (agg1->op == TOKsymoff) { indx = ofs1 / sz; len = ((TypeSArray *)((SymOffExp *)agg1)->var->type)->dim->toInteger(); } else { Expression *dollar = ArrayLength(Type::tsize_t, agg1).copy(); assert(!CTFEExp::isCantExp(dollar)); indx = ofs1; len = dollar->toInteger(); } if (op == TOKadd || op == TOKaddass || op == TOKplusplus) indx += ofs2 / sz; else if (op == TOKmin || op == TOKminass || op == TOKminusminus) indx -= ofs2 / sz; else { error(loc, "CTFE internal error: bad pointer operation"); goto Lcant; } if (indx < 0 || len < (dinteger_t)indx) { error(loc, "cannot assign pointer to index %lld inside memory block [0..%lld]", (ulonglong)indx, (ulonglong)len); goto Lcant; } if (agg1->op == TOKsymoff) { new(&ue) SymOffExp(loc, ((SymOffExp *)agg1)->var, indx * sz); SymOffExp *se = (SymOffExp *)ue.exp(); se->type = type; return ue; } if (agg1->op != TOKarrayliteral && agg1->op != TOKstring) { error(loc, "CTFE internal error: pointer arithmetic %s", agg1->toChars()); goto Lcant; } if (eptr->type->toBasetype()->ty == Tsarray) { dinteger_t dim = ((TypeSArray *)eptr->type->toBasetype())->dim->toInteger(); // Create a CTFE pointer &agg1[indx .. indx+dim] SliceExp *se = new SliceExp(loc, agg1, new IntegerExp(loc, indx, Type::tsize_t), new IntegerExp(loc, indx + dim, Type::tsize_t)); se->type = type->toBasetype()->nextOf(); new(&ue) AddrExp(loc, se); ue.exp()->type = type; return ue; } // Create a CTFE pointer &agg1[indx] IntegerExp *ofs = new IntegerExp(loc, indx, Type::tsize_t); Expression *ie = new IndexExp(loc, agg1, ofs); ie->type = type->toBasetype()->nextOf(); // Bugzilla 13992 new(&ue) AddrExp(loc, ie); ue.exp()->type = type; return ue; } // Return 1 if true, 0 if false // -1 if comparison is illegal because they point to non-comparable memory blocks int comparePointers(TOK op, Expression *agg1, dinteger_t ofs1, Expression *agg2, dinteger_t ofs2) { if (pointToSameMemoryBlock(agg1, agg2)) { int n; 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 n; } bool null1 = (agg1->op == TOKnull); bool null2 = (agg2->op == TOKnull); int cmp; if (null1 || null2) { switch (op) { case TOKlt: cmp = null1 && !null2; break; case TOKgt: cmp = !null1 && null2; break; case TOKle: cmp = null1; break; case TOKge: cmp = null2; break; case TOKidentity: case TOKequal: case TOKnotidentity: // 'cmp' gets inverted below case TOKnotequal: cmp = (null1 == null2); break; default: assert(0); } } else { switch (op) { case TOKidentity: case TOKequal: case TOKnotidentity: // 'cmp' gets inverted below case TOKnotequal: cmp = 0; break; default: return -1; // memory blocks are different } } if (op == TOKnotidentity || op == TOKnotequal) cmp ^= 1; return cmp; } // True if conversion from type 'from' to 'to' involves a reinterpret_cast // floating point -> integer or integer -> floating point bool isFloatIntPaint(Type *to, Type *from) { return from->size() == to->size() && ((from->isintegral() && to->isfloating()) || (from->isfloating() && to->isintegral())); } // Reinterpret float/int value 'fromVal' as a float/integer of type 'to'. Expression *paintFloatInt(UnionExp *pue, Expression *fromVal, Type *to) { if (exceptionOrCantInterpret(fromVal)) return fromVal; assert(to->size() == 4 || to->size() == 8); return Compiler::paintAsType(pue, fromVal, to); } /******** Constant folding, with support for CTFE ***************************/ /// Return true if non-pointer expression e can be compared /// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity bool isCtfeComparable(Expression *e) { if (e->op == TOKslice) e = ((SliceExp *)e)->e1; if (e->isConst() != 1) { if (e->op == TOKnull || e->op == TOKstring || e->op == TOKfunction || e->op == TOKdelegate || e->op == TOKarrayliteral || e->op == TOKstructliteral || e->op == TOKassocarrayliteral || e->op == TOKclassreference) { return true; } // Bugzilla 14123: TypeInfo object is comparable in CTFE if (e->op == TOKtypeid) return true; return false; } return true; } /// Map TOK comparison ops template static bool numCmp(TOK op, N n1, N n2) { switch (op) { case TOKlt: return n1 < n2; case TOKle: return n1 <= n2; case TOKgt: return n1 > n2; case TOKge: return n1 >= n2; default: assert(0); } } /// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1 int specificCmp(TOK op, int rawCmp) { return numCmp(op, rawCmp, 0); } /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1 int intUnsignedCmp(TOK op, dinteger_t n1, dinteger_t n2) { return numCmp(op, n1, n2); } /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1 int intSignedCmp(TOK op, sinteger_t n1, sinteger_t n2) { return numCmp(op, n1, n2); } /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1 int realCmp(TOK op, real_t r1, real_t r2) { // Don't rely on compiler, handle NAN arguments separately if (CTFloat::isNaN(r1) || CTFloat::isNaN(r2)) // if unordered { switch (op) { case TOKlt: case TOKle: case TOKgt: case TOKge: return 0; default: assert(0); } } else { return numCmp(op, r1, r2); } } int ctfeRawCmp(Loc loc, Expression *e1, Expression *e2); /* Conceptually the same as memcmp(e1, e2). * e1 and e2 may be strings, arrayliterals, or slices. * For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2. * For all other types, return 0 if e1 == e2, !=0 if e1 != e2. */ int ctfeCmpArrays(Loc loc, Expression *e1, Expression *e2, uinteger_t len) { // Resolve slices, if necessary uinteger_t lo1 = 0; uinteger_t lo2 = 0; Expression *x = e1; if (x->op == TOKslice) { lo1 = ((SliceExp *)x)->lwr->toInteger(); x = ((SliceExp *)x)->e1; } StringExp *se1 = (x->op == TOKstring) ? (StringExp *)x : NULL; ArrayLiteralExp *ae1 = (x->op == TOKarrayliteral) ? (ArrayLiteralExp *)x : NULL; x = e2; if (x->op == TOKslice) { lo2 = ((SliceExp *)x)->lwr->toInteger(); x = ((SliceExp *)x)->e1; } StringExp *se2 = (x->op == TOKstring) ? (StringExp *)x : NULL; ArrayLiteralExp *ae2 = (x->op == TOKarrayliteral) ? (ArrayLiteralExp *)x : NULL; // Now both must be either TOKarrayliteral or TOKstring if (se1 && se2) return sliceCmpStringWithString(se1, se2, (size_t)lo1, (size_t)lo2, (size_t)len); if (se1 && ae2) return sliceCmpStringWithArray(se1, ae2, (size_t)lo1, (size_t)lo2, (size_t)len); if (se2 && ae1) return -sliceCmpStringWithArray(se2, ae1, (size_t)lo2, (size_t)lo1, (size_t)len); assert (ae1 && ae2); // Comparing two array literals. This case is potentially recursive. // If they aren't strings, we just need an equality check rather than // a full cmp. bool needCmp = ae1->type->nextOf()->isintegral(); for (size_t i = 0; i < (size_t)len; i++) { Expression *ee1 = (*ae1->elements)[(size_t)(lo1 + i)]; Expression *ee2 = (*ae2->elements)[(size_t)(lo2 + i)]; if (needCmp) { sinteger_t c = ee1->toInteger() - ee2->toInteger(); if (c > 0) return 1; if (c < 0) return -1; } else { if (ctfeRawCmp(loc, ee1, ee2)) return 1; } } return 0; } /* Given a delegate expression e, return .funcptr. * If e is NullExp, return NULL. */ FuncDeclaration *funcptrOf(Expression *e) { assert(e->type->ty == Tdelegate); if (e->op == TOKdelegate) return ((DelegateExp *)e)->func; if (e->op == TOKfunction) return ((FuncExp *)e)->fd; assert(e->op == TOKnull); return NULL; } bool isArray(Expression *e) { return e->op == TOKarrayliteral || e->op == TOKstring || e->op == TOKslice || e->op == TOKnull; } /* For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2. * For all other types, return 0 if e1 == e2, !=0 if e1 != e2. */ int ctfeRawCmp(Loc loc, Expression *e1, Expression *e2) { if (e1->op == TOKclassreference || e2->op == TOKclassreference) { if (e1->op == TOKclassreference && e2->op == TOKclassreference && ((ClassReferenceExp *)e1)->value == ((ClassReferenceExp *)e2)->value) return 0; return 1; } if (e1->op == TOKtypeid && e2->op == TOKtypeid) { // printf("e1: %s\n", e1->toChars()); // printf("e2: %s\n", e2->toChars()); Type *t1 = isType(((TypeidExp *)e1)->obj); Type *t2 = isType(((TypeidExp *)e2)->obj); assert(t1); assert(t2); return t1 != t2; } // null == null, regardless of type if (e1->op == TOKnull && e2->op == TOKnull) return 0; if (e1->type->ty == Tpointer && e2->type->ty == Tpointer) { // Can only be an equality test. dinteger_t ofs1, ofs2; Expression *agg1 = getAggregateFromPointer(e1, &ofs1); Expression *agg2 = getAggregateFromPointer(e2, &ofs2); if ((agg1 == agg2) || (agg1->op == TOKvar && agg2->op == TOKvar && ((VarExp *)agg1)->var == ((VarExp *)agg2)->var)) { if (ofs1 == ofs2) return 0; } return 1; } if (e1->type->ty == Tdelegate && e2->type->ty == Tdelegate) { // If .funcptr isn't the same, they are not equal if (funcptrOf(e1) != funcptrOf(e2)) return 1; // If both are delegate literals, assume they have the // same closure pointer. TODO: We don't support closures yet! if (e1->op == TOKfunction && e2->op == TOKfunction) return 0; assert(e1->op == TOKdelegate && e2->op == TOKdelegate); // Same .funcptr. Do they have the same .ptr? Expression * ptr1 = ((DelegateExp *)e1)->e1; Expression * ptr2 = ((DelegateExp *)e2)->e1; dinteger_t ofs1, ofs2; Expression *agg1 = getAggregateFromPointer(ptr1, &ofs1); Expression *agg2 = getAggregateFromPointer(ptr2, &ofs2); // If they are TOKvar, it means they are FuncDeclarations if ((agg1 == agg2 && ofs1 == ofs2) || (agg1->op == TOKvar && agg2->op == TOKvar && ((VarExp *)agg1)->var == ((VarExp *)agg2)->var)) { return 0; } return 1; } if (isArray(e1) && isArray(e2)) { uinteger_t len1 = resolveArrayLength(e1); uinteger_t len2 = resolveArrayLength(e2); // workaround for dmc optimizer bug calculating wrong len for // uinteger_t len = (len1 < len2 ? len1 : len2); // if (len == 0) ... if (len1 > 0 && len2 > 0) { uinteger_t len = (len1 < len2 ? len1 : len2); int res = ctfeCmpArrays(loc, e1, e2, len); if (res != 0) return res; } return (int)(len1 - len2); } if (e1->type->isintegral()) { return e1->toInteger() != e2->toInteger(); } real_t r1; real_t r2; if (e1->type->isreal()) { r1 = e1->toReal(); r2 = e2->toReal(); goto L1; } else if (e1->type->isimaginary()) { r1 = e1->toImaginary(); r2 = e2->toImaginary(); L1: if (CTFloat::isNaN(r1) || CTFloat::isNaN(r2)) // if unordered { return 1; } else { return (r1 != r2); } } else if (e1->type->iscomplex()) { return e1->toComplex() != e2->toComplex(); } if (e1->op == TOKstructliteral && e2->op == TOKstructliteral) { StructLiteralExp *es1 = (StructLiteralExp *)e1; StructLiteralExp *es2 = (StructLiteralExp *)e2; // For structs, we only need to return 0 or 1 (< and > aren't legal). if (es1->sd != es2->sd) return 1; else if ((!es1->elements || !es1->elements->dim) && (!es2->elements || !es2->elements->dim)) return 0; // both arrays are empty else if (!es1->elements || !es2->elements) return 1; else if (es1->elements->dim != es2->elements->dim) return 1; else { for (size_t i = 0; i < es1->elements->dim; i++) { Expression *ee1 = (*es1->elements)[i]; Expression *ee2 = (*es2->elements)[i]; if (ee1 == ee2) continue; if (!ee1 || !ee2) return 1; int cmp = ctfeRawCmp(loc, ee1, ee2); if (cmp) return 1; } return 0; // All elements are equal } } if (e1->op == TOKassocarrayliteral && e2->op == TOKassocarrayliteral) { AssocArrayLiteralExp *es1 = (AssocArrayLiteralExp *)e1; AssocArrayLiteralExp *es2 = (AssocArrayLiteralExp *)e2; size_t dim = es1->keys->dim; if (es2->keys->dim != dim) return 1; bool *used = (bool *)mem.xmalloc(sizeof(bool) * dim); memset(used, 0, sizeof(bool) * dim); for (size_t i = 0; i < dim; ++i) { Expression *k1 = (*es1->keys)[i]; Expression *v1 = (*es1->values)[i]; Expression *v2 = NULL; for (size_t j = 0; j < dim; ++j) { if (used[j]) continue; Expression *k2 = (*es2->keys)[j]; if (ctfeRawCmp(loc, k1, k2)) continue; used[j] = true; v2 = (*es2->values)[j]; break; } if (!v2 || ctfeRawCmp(loc, v1, v2)) { mem.xfree(used); return 1; } } mem.xfree(used); return 0; } error(loc, "CTFE internal error: bad compare of `%s` and `%s`", e1->toChars(), e2->toChars()); assert(0); return 0; } /// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1 int ctfeEqual(Loc loc, TOK op, Expression *e1, Expression *e2) { int cmp = !ctfeRawCmp(loc, e1, e2); if (op == TOKnotequal) cmp ^= 1; return cmp; } /// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1 int ctfeIdentity(Loc loc, TOK op, Expression *e1, Expression *e2) { //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", Token::toChars(op), // Token::toChars(e1->op), e1->toChars(), Token::toChars(e2->op), e1->toChars()); int cmp; if (e1->op == TOKnull) { cmp = (e2->op == TOKnull); } else if (e2->op == TOKnull) { cmp = 0; } else if (e1->op == TOKsymoff && e2->op == TOKsymoff) { SymOffExp *es1 = (SymOffExp *)e1; SymOffExp *es2 = (SymOffExp *)e2; cmp = (es1->var == es2->var && es1->offset == es2->offset); } else if (e1->type->isreal()) cmp = RealEquals(e1->toReal(), e2->toReal()); else if (e1->type->isimaginary()) cmp = RealEquals(e1->toImaginary(), e2->toImaginary()); else if (e1->type->iscomplex()) { complex_t v1 = e1->toComplex(); complex_t v2 = e2->toComplex(); cmp = RealEquals(creall(v1), creall(v2)) && RealEquals(cimagl(v1), cimagl(v1)); } else cmp = !ctfeRawCmp(loc, e1, e2); if (op == TOKnotidentity || op == TOKnotequal) cmp ^= 1; return cmp; } /// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1 int ctfeCmp(Loc loc, TOK op, Expression *e1, Expression *e2) { Type *t1 = e1->type->toBasetype(); Type *t2 = e2->type->toBasetype(); if (t1->isString() && t2->isString()) return specificCmp(op, ctfeRawCmp(loc, e1, e2)); else if (t1->isreal()) return realCmp(op, e1->toReal(), e2->toReal()); else if (t1->isimaginary()) return realCmp(op, e1->toImaginary(), e2->toImaginary()); else if (t1->isunsigned() || t2->isunsigned()) return intUnsignedCmp(op, e1->toInteger(), e2->toInteger()); else return intSignedCmp(op, e1->toInteger(), e2->toInteger()); } UnionExp ctfeCat(Loc loc, Type *type, Expression *e1, Expression *e2) { Type *t1 = e1->type->toBasetype(); Type *t2 = e2->type->toBasetype(); UnionExp ue; if (e2->op == TOKstring && e1->op == TOKarrayliteral && t1->nextOf()->isintegral()) { // [chars] ~ string => string (only valid for CTFE) StringExp *es1 = (StringExp *)e2; ArrayLiteralExp *es2 = (ArrayLiteralExp *)e1; size_t len = es1->len + es2->elements->dim; unsigned char sz = es1->sz; void *s = mem.xmalloc((len + 1) * sz); memcpy((char *)s + sz * es2->elements->dim, es1->string, es1->len * sz); for (size_t i = 0; i < es2->elements->dim; i++) { Expression *es2e = (*es2->elements)[i]; if (es2e->op != TOKint64) { new(&ue) CTFEExp(TOKcantexp); return ue; } dinteger_t v = es2e->toInteger(); Port::valcpy((utf8_t *)s + i * sz, v, sz); } // Add terminating 0 memset((utf8_t *)s + len * sz, 0, sz); new(&ue) StringExp(loc, s, len); StringExp *es = (StringExp *)ue.exp(); es->sz = sz; es->committed = 0; es->type = type; return ue; } if (e1->op == TOKstring && e2->op == TOKarrayliteral && t2->nextOf()->isintegral()) { // string ~ [chars] => string (only valid for CTFE) // Concatenate the strings StringExp *es1 = (StringExp *)e1; ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; size_t len = es1->len + es2->elements->dim; unsigned char sz = es1->sz; void *s = mem.xmalloc((len + 1) * sz); memcpy(s, es1->string, es1->len * sz); for (size_t i = 0; i < es2->elements->dim; i++) { Expression *es2e = (*es2->elements)[i]; if (es2e->op != TOKint64) { new(&ue) CTFEExp(TOKcantexp); return ue; } dinteger_t v = es2e->toInteger(); Port::valcpy((utf8_t *)s + (es1->len + i) * sz, v, sz); } // Add terminating 0 memset((utf8_t *)s + len * sz, 0, sz); new(&ue) StringExp(loc, s, len); StringExp *es = (StringExp *)ue.exp(); es->sz = sz; es->committed = 0; //es1->committed; es->type = type; return ue; } if (e1->op == TOKarrayliteral && e2->op == TOKarrayliteral && t1->nextOf()->equals(t2->nextOf())) { // [ e1 ] ~ [ e2 ] ---> [ e1, e2 ] ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1; ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; new(&ue) ArrayLiteralExp(es1->loc, type, copyLiteralArray(es1->elements)); es1 = (ArrayLiteralExp *)ue.exp(); es1->elements->insert(es1->elements->dim, copyLiteralArray(es2->elements)); return ue; } if (e1->op == TOKarrayliteral && e2->op == TOKnull && t1->nextOf()->equals(t2->nextOf())) { // [ e1 ] ~ null ----> [ e1 ].dup ue = paintTypeOntoLiteralCopy(type, copyLiteral(e1).copy()); return ue; } if (e1->op == TOKnull && e2->op == TOKarrayliteral && t1->nextOf()->equals(t2->nextOf())) { // null ~ [ e2 ] ----> [ e2 ].dup ue = paintTypeOntoLiteralCopy(type, copyLiteral(e2).copy()); return ue; } ue = Cat(type, e1, e2); return ue; } /* Given an AA literal 'ae', and a key 'e2': * Return ae[e2] if present, or NULL if not found. */ Expression *findKeyInAA(Loc loc, 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)[i]; int eq = ctfeEqual(loc, TOKequal, ekey, e2); if (eq) { return (*ae->values)[i]; } } return NULL; } /* Same as for constfold.Index, except that it only works for static arrays, * dynamic arrays, and strings. We know that e1 is an * interpreted CTFE expression, so it cannot have side-effects. */ Expression *ctfeIndex(Loc loc, Type *type, Expression *e1, uinteger_t indx) { //printf("ctfeIndex(e1 = %s)\n", e1->toChars()); assert(e1->type); if (e1->op == TOKstring) { StringExp *es1 = (StringExp *)e1; if (indx >= es1->len) { error(loc, "string index %llu is out of bounds [0 .. %llu]", (ulonglong)indx, (ulonglong)es1->len); return CTFEExp::cantexp; } return new IntegerExp(loc, es1->charAt(indx), type); } assert(e1->op == TOKarrayliteral); { ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; if (indx >= ale->elements->dim) { error(loc, "array index %llu is out of bounds %s[0 .. %llu]", (ulonglong)indx, e1->toChars(), (ulonglong)ale->elements->dim); return CTFEExp::cantexp; } Expression *e = (*ale->elements)[(size_t)indx]; return paintTypeOntoLiteral(type, e); } } Expression *ctfeCast(UnionExp *pue, Loc loc, Type *type, Type *to, Expression *e) { if (e->op == TOKnull) return paintTypeOntoLiteral(pue, to, e); if (e->op == TOKclassreference) { // Disallow reinterpreting class casts. Do this by ensuring that // the original class can implicitly convert to the target class ClassDeclaration *originalClass = ((ClassReferenceExp *)e)->originalClass(); if (originalClass->type->implicitConvTo(to->mutableOf())) return paintTypeOntoLiteral(pue, to, e); else { new(pue) NullExp(loc, to); return pue->exp(); } } // Allow TypeInfo type painting if (isTypeInfo_Class(e->type) && e->type->implicitConvTo(to)) return paintTypeOntoLiteral(pue, to, e); // Allow casting away const for struct literals if (e->op == TOKstructliteral && e->type->toBasetype()->castMod(0) == to->toBasetype()->castMod(0)) return paintTypeOntoLiteral(pue, to, e); Expression *r; if (e->type->equals(type) && type->equals(to)) { // necessary not to change e's address for pointer comparisons r = e; } else if (to->toBasetype()->ty == Tarray && type->toBasetype()->ty == Tarray && to->toBasetype()->nextOf()->size() == type->toBasetype()->nextOf()->size()) { // Bugzilla 12495: Array reinterpret casts: eg. string to immutable(ubyte)[] return paintTypeOntoLiteral(pue, to, e); } else { *pue = Cast(loc, type, to, e); r = pue->exp(); } if (CTFEExp::isCantExp(r)) error(loc, "cannot cast %s to %s at compile time", e->toChars(), to->toChars()); if (e->op == TOKarrayliteral) ((ArrayLiteralExp *)e)->ownedByCtfe = OWNEDctfe; if (e->op == TOKstring) ((StringExp *)e)->ownedByCtfe = OWNEDctfe; return r; } /******** Assignment helper functions ***************************/ /* 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; if (((StructLiteralExp *)dest)->sd->isNested() && oldelems->dim == newelems->dim - 1) oldelems->push(NULL); } 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)[i]; Expression *o = (*oldelems)[i]; if (e->op == TOKstructliteral) { assert(o->op == e->op); assignInPlace(o, e); } else if (e->type->ty == Tsarray && e->op != TOKvoid && o->type->ty == Tsarray) { assignInPlace(o, e); } else { (*oldelems)[i] = (*newelems)[i]; } } } // Duplicate the elements array, then set field 'indexToChange' = newelem. Expressions *changeOneElement(Expressions *oldelems, size_t indexToChange, Expression *newelem) { Expressions *expsx = new Expressions(); ++CtfeStatus::numArrayAllocs; expsx->setDim(oldelems->dim); for (size_t j = 0; j < expsx->dim; j++) { if (j == indexToChange) (*expsx)[j] = newelem; else (*expsx)[j] = (*oldelems)[j]; } return expsx; } // Given an AA literal aae, set aae[index] = newval and return newval. 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)[j]; int eq = ctfeEqual(loc, TOKequal, ekey, index); if (eq) { (*valuesx)[j] = newval; updated = 1; } } if (!updated) { // Append index/newval to keysx[]/valuesx[] valuesx->push(newval); keysx->push(index); } return newval; } /// Given array literal oldval of type ArrayLiteralExp or StringExp, of length /// oldlen, change its length to newlen. If the newlen is longer than oldlen, /// all new elements will be set to the default initializer for the element type. UnionExp changeArrayLiteralLength(Loc loc, TypeArray *arrayType, Expression *oldval, size_t oldlen, size_t newlen) { UnionExp ue; Type *elemType = arrayType->next; assert(elemType); Expression *defaultElem = elemType->defaultInitLiteral(loc); Expressions *elements = new Expressions(); elements->setDim(newlen); // Resolve slices size_t indxlo = 0; if (oldval->op == TOKslice) { indxlo = (size_t)((SliceExp *)oldval)->lwr->toInteger(); oldval = ((SliceExp *)oldval)->e1; } size_t copylen = oldlen < newlen ? oldlen : newlen; if (oldval->op == TOKstring) { StringExp *oldse = (StringExp *)oldval; void *s = mem.xcalloc(newlen + 1, oldse->sz); memcpy(s, oldse->string, copylen * oldse->sz); unsigned defaultValue = (unsigned)(defaultElem->toInteger()); for (size_t elemi = copylen; elemi < newlen; ++elemi) { switch (oldse->sz) { case 1: (( utf8_t *)s)[(size_t)(indxlo + elemi)] = ( utf8_t)defaultValue; break; case 2: ((utf16_t *)s)[(size_t)(indxlo + elemi)] = (utf16_t)defaultValue; break; case 4: ((utf32_t *)s)[(size_t)(indxlo + elemi)] = (utf32_t)defaultValue; break; default: assert(0); } } new(&ue) StringExp(loc, s, newlen); StringExp *se = (StringExp *)ue.exp(); se->type = arrayType; se->sz = oldse->sz; se->committed = oldse->committed; se->ownedByCtfe = OWNEDctfe; } else { if (oldlen != 0) { assert(oldval->op == TOKarrayliteral); ArrayLiteralExp *ae = (ArrayLiteralExp *)oldval; for (size_t i = 0; i < copylen; i++) (*elements)[i] = (*ae->elements)[indxlo + 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)[i] = copyLiteral(defaultElem).copy(); } else { for (size_t i = copylen; i < newlen; i++) (*elements)[i] = defaultElem; } new(&ue) ArrayLiteralExp(loc, arrayType, elements); ArrayLiteralExp *aae = (ArrayLiteralExp *)ue.exp(); aae->ownedByCtfe = OWNEDctfe; } return ue; } /*************************** CTFE Sanity Checks ***************************/ bool isCtfeValueValid(Expression *newval) { Type *tb = newval->type->toBasetype(); if (newval->op == TOKint64 || newval->op == TOKfloat64 || newval->op == TOKchar || newval->op == TOKcomplex80) { return tb->isscalar(); } if (newval->op == TOKnull) { return tb->ty == Tnull || tb->ty == Tpointer || tb->ty == Tarray || tb->ty == Taarray || tb->ty == Tclass || tb->ty == Tdelegate; } if (newval->op == TOKstring) return true; // CTFE would directly use the StringExp in AST. if (newval->op == TOKarrayliteral) return true; //((ArrayLiteralExp *)newval)->ownedByCtfe; if (newval->op == TOKassocarrayliteral) return true; //((AssocArrayLiteralExp *)newval)->ownedByCtfe; if (newval->op == TOKstructliteral) return true; //((StructLiteralExp *)newval)->ownedByCtfe; if (newval->op == TOKclassreference) return true; if (newval->op == TOKvector) return true; // vector literal if (newval->op == TOKfunction) return true; // function literal or delegate literal if (newval->op == TOKdelegate) { // &struct.func or &clasinst.func // &nestedfunc Expression *ethis = ((DelegateExp *)newval)->e1; return (ethis->op == TOKstructliteral || ethis->op == TOKclassreference || (ethis->op == TOKvar && ((VarExp *)ethis)->var == ((DelegateExp *)newval)->func)); } if (newval->op == TOKsymoff) { // function pointer, or pointer to static variable Declaration *d = ((SymOffExp *)newval)->var; return d->isFuncDeclaration() || d->isDataseg(); } if (newval->op == TOKtypeid) { // always valid return true; } if (newval->op == TOKaddress) { // e1 should be a CTFE reference Expression *e1 = ((AddrExp *)newval)->e1; return tb->ty == Tpointer && (((e1->op == TOKstructliteral || e1->op == TOKarrayliteral) && isCtfeValueValid(e1)) || (e1->op == TOKvar) || (e1->op == TOKdotvar && isCtfeReferenceValid(e1)) || (e1->op == TOKindex && isCtfeReferenceValid(e1)) || (e1->op == TOKslice && e1->type->toBasetype()->ty == Tsarray)); } if (newval->op == TOKslice) { // e1 should be an array aggregate SliceExp *se = (SliceExp *)newval; assert(se->lwr && se->lwr->op == TOKint64); assert(se->upr && se->upr->op == TOKint64); return (tb->ty == Tarray || tb->ty == Tsarray) && (se->e1->op == TOKstring || se->e1->op == TOKarrayliteral); } if (newval->op == TOKvoid) return true; // uninitialized value newval->error("CTFE internal error: illegal CTFE value %s", newval->toChars()); return false; } bool isCtfeReferenceValid(Expression *newval) { if (newval->op == TOKthis) return true; if (newval->op == TOKvar) { VarDeclaration *v = ((VarExp *)newval)->var->isVarDeclaration(); assert(v); // Must not be a reference to a reference return true; } if (newval->op == TOKindex) { Expression *eagg = ((IndexExp *)newval)->e1; return eagg->op == TOKstring || eagg->op == TOKarrayliteral || eagg->op == TOKassocarrayliteral; } if (newval->op == TOKdotvar) { Expression *eagg = ((DotVarExp *)newval)->e1; return (eagg->op == TOKstructliteral || eagg->op == TOKclassreference) && isCtfeValueValid(eagg); } // Internally a ref variable may directly point a stack memory. // e.g. ref int v = 1; return isCtfeValueValid(newval); } // Used for debugging only void showCtfeExpr(Expression *e, int level) { for (int i = level; i > 0; --i) printf(" "); Expressions *elements = NULL; // We need the struct definition to detect block assignment StructDeclaration *sd = NULL; ClassDeclaration *cd = 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 == TOKclassreference) { elements = ((ClassReferenceExp *)e)->value->elements; cd = ((ClassReferenceExp *)e)->originalClass(); printf("CLASS type = %s %p:\n", e->type->toChars(), ((ClassReferenceExp *)e)->value); } 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 && getValue(v)) showCtfeExpr(getValue(v), level + 1); } else if (e->op == TOKaddress) { // This is potentially recursive. We mustn't try to print the thing we're pointing to. printf("POINTER %p to %p: %s\n", e, ((AddrExp *)e)->e1, e->toChars()); } else printf("VALUE %p: %s\n", e, e->toChars()); if (elements) { size_t fieldsSoFar = 0; for (size_t i = 0; i < elements->dim; i++) { Expression *z = NULL; VarDeclaration *v = NULL; if (i > 15) { printf("...(total %d elements)\n", (int)elements->dim); return; } if (sd) { v = sd->fields[i]; z = (*elements)[i]; } else if (cd) { while (i - fieldsSoFar >= cd->fields.dim) { fieldsSoFar += cd->fields.dim; cd = cd->baseClass; for (int j = level; j > 0; --j) printf(" "); printf(" BASE CLASS: %s\n", cd->toChars()); } v = cd->fields[i - fieldsSoFar]; assert((elements->dim + i) >= (fieldsSoFar + cd->fields.dim)); size_t indx = (elements->dim - fieldsSoFar)- cd->fields.dim + i; assert(indx < elements->dim); z = (*elements)[indx]; } if (!z) { for (int j = level; j > 0; --j) printf(" "); printf(" void\n"); continue; } if (v) { // If it is a void assignment, use the default initializer 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); } } } /*************************** Void initialization ***************************/ UnionExp voidInitLiteral(Type *t, VarDeclaration *var) { UnionExp ue; if (t->ty == Tsarray) { TypeSArray *tsa = (TypeSArray *)t; Expression *elem = voidInitLiteral(tsa->next, var).copy(); // For aggregate value types (structs, static arrays) we must // create an a separate copy for each element. bool mustCopy = (elem->op == TOKarrayliteral || elem->op == TOKstructliteral); Expressions *elements = new Expressions(); size_t d = (size_t)tsa->dim->toInteger(); elements->setDim(d); for (size_t i = 0; i < d; i++) { if (mustCopy && i > 0) elem = copyLiteral(elem).copy(); (*elements)[i] = elem; } new(&ue) ArrayLiteralExp(var->loc, tsa, elements); ArrayLiteralExp *ae = (ArrayLiteralExp *)ue.exp(); ae->ownedByCtfe = OWNEDctfe; } else if (t->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)t; Expressions *exps = new Expressions(); exps->setDim(ts->sym->fields.dim); for (size_t i = 0; i < ts->sym->fields.dim; i++) { (*exps)[i] = voidInitLiteral(ts->sym->fields[i]->type, ts->sym->fields[i]).copy(); } new(&ue) StructLiteralExp(var->loc, ts->sym, exps); StructLiteralExp *se = (StructLiteralExp *)ue.exp(); se->type = ts; se->ownedByCtfe = OWNEDctfe; } else new(&ue) VoidInitExp(var, t); return ue; }