aboutsummaryrefslogtreecommitdiff
path: root/gcc/d/dmd/ctfeexpr.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/d/dmd/ctfeexpr.c')
-rw-r--r--gcc/d/dmd/ctfeexpr.c2127
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;
-}