diff options
Diffstat (limited to 'gcc/d/dmd/ctfeexpr.c')
-rw-r--r-- | gcc/d/dmd/ctfeexpr.c | 2127 |
1 files changed, 0 insertions, 2127 deletions
diff --git a/gcc/d/dmd/ctfeexpr.c b/gcc/d/dmd/ctfeexpr.c deleted file mode 100644 index a8e9783..0000000 --- a/gcc/d/dmd/ctfeexpr.c +++ /dev/null @@ -1,2127 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved - * written by Walter Bright - * http://www.digitalmars.com - * Distributed under the Boost Software License, Version 1.0. - * http://www.boost.org/LICENSE_1_0.txt - * https://github.com/D-Programming-Language/dmd/blob/master/src/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->length; j++) - { - while (j - fieldsSoFar >= cd->fields.length) - { - fieldsSoFar += cd->fields.length; - cd = cd->baseClass; - } - VarDeclaration *v2 = cd->fields[j - fieldsSoFar]; - if (fieldoffset == v2->offset && - fieldtype->size() == v2->type->size()) - { - return (int)(value->elements->length - fieldsSoFar - cd->fields.length + (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->length; j++) - { - while (j - fieldsSoFar >= cd->fields.length) - { - fieldsSoFar += cd->fields.length; - cd = cd->baseClass; - } - VarDeclaration *v2 = cd->fields[j - fieldsSoFar]; - if (v == v2) - { - return (int)(value->elements->length - fieldsSoFar - cd->fields.length + (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.length; ++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 "<cant>"; - case TOKvoidexp: return "cast(void)0"; - case TOKbreak: return "<break>"; - case TOKcontinue: return "<continue>"; - case TOKgoto: return "<goto>"; - 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->length); - for (size_t i = 0; i < oldelems->length; 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->length); - for (size_t i = 0; i < newelems->length; 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->length : 0; - } - if (e->op == TOKassocarrayliteral) - { - AssocArrayLiteralExp *ale = (AssocArrayLiteralExp *)e; - return ale->keys->length; - } - 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 <typename N> -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); - } - return false; -} - -/// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1 -int specificCmp(TOK op, int rawCmp) -{ - return numCmp<int>(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<dinteger_t>(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<sinteger_t>(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: - break; - - default: - assert(0); - } - return 0; - } - else - { - return numCmp<real_t>(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->length) && - (!es2->elements || !es2->elements->length)) - return 0; // both arrays are empty - else if (!es1->elements || !es2->elements) - return 1; - else if (es1->elements->length != es2->elements->length) - return 1; - else - { - for (size_t i = 0; i < es1->elements->length; 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->length; - if (es2->keys->length != 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->length; - unsigned char sz = es1->sz; - - void *s = mem.xmalloc((len + 1) * sz); - memcpy((char *)s + sz * es2->elements->length, es1->string, es1->len * sz); - for (size_t i = 0; i < es2->elements->length; 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->length; - 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->length; 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->length, 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->length; 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->length) - { - error(loc, "array index %llu is out of bounds %s[0 .. %llu]", (ulonglong)indx, e1->toChars(), (ulonglong)ale->elements->length); - 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->length == newelems->length - 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->length == newelems->length); - - for (size_t i= 0; i < oldelems->length; ++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->length); - for (size_t j = 0; j < expsx->length; 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->length; 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->length; i++) - { - Expression *z = NULL; - VarDeclaration *v = NULL; - if (i > 15) - { - printf("...(total %d elements)\n", (int)elements->length); - return; - } - if (sd) - { - v = sd->fields[i]; - z = (*elements)[i]; - } - else if (cd) - { - while (i - fieldsSoFar >= cd->fields.length) - { - fieldsSoFar += cd->fields.length; - 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->length + i) >= (fieldsSoFar + cd->fields.length)); - size_t indx = (elements->length - fieldsSoFar)- cd->fields.length + i; - assert(indx < elements->length); - 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.length); - for (size_t i = 0; i < ts->sym->fields.length; 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; -} |