mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-11 10:16:36 +01:00
2110 lines
65 KiB
C
2110 lines
65 KiB
C
// Compiler implementation of the D programming language
|
|
// Copyright (c) 1999-2012 by Digital Mars
|
|
// All Rights Reserved
|
|
// written by Walter Bright
|
|
// http://www.digitalmars.com
|
|
// License for redistribution is by either the Artistic License
|
|
// in artistic.txt, or the GNU General Public License in gnu.txt.
|
|
// See the included readme.txt for details.
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h> // mem{cpy|set}()
|
|
|
|
#include "rmem.h"
|
|
|
|
#include "expression.h"
|
|
#include "declaration.h"
|
|
#include "aggregate.h"
|
|
// for AssocArray
|
|
#include "id.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;
|
|
}
|
|
|
|
Expression *ClassReferenceExp::interpret(InterState *istate, CtfeGoal goal)
|
|
{
|
|
//printf("ClassReferenceExp::interpret() %s\n", value->toChars());
|
|
return this;
|
|
}
|
|
|
|
char *ClassReferenceExp::toChars()
|
|
{
|
|
return value->toChars();
|
|
}
|
|
|
|
ClassDeclaration *ClassReferenceExp::originalClass()
|
|
{
|
|
return value->sd->isClassDeclaration();
|
|
}
|
|
|
|
VarDeclaration *ClassReferenceExp::getFieldAt(unsigned index)
|
|
{
|
|
ClassDeclaration *cd = originalClass();
|
|
unsigned fieldsSoFar = 0;
|
|
while (index - fieldsSoFar >= cd->fields.dim)
|
|
{ fieldsSoFar += cd->fields.dim;
|
|
cd = cd->baseClass;
|
|
}
|
|
return cd->fields[index - fieldsSoFar];
|
|
}
|
|
|
|
// Return index of the field, or -1 if not found
|
|
int ClassReferenceExp::getFieldIndex(Type *fieldtype, unsigned fieldoffset)
|
|
{
|
|
ClassDeclaration *cd = originalClass();
|
|
unsigned fieldsSoFar = 0;
|
|
for (size_t j = 0; j < value->elements->dim; j++)
|
|
{ while (j - fieldsSoFar >= cd->fields.dim)
|
|
{ fieldsSoFar += cd->fields.dim;
|
|
cd = cd->baseClass;
|
|
}
|
|
Dsymbol *s = cd->fields[j - fieldsSoFar];
|
|
VarDeclaration *v2 = s->isVarDeclaration();
|
|
if (fieldoffset == v2->offset &&
|
|
fieldtype->size() == v2->type->size())
|
|
{ return value->elements->dim - fieldsSoFar - cd->fields.dim + (j-fieldsSoFar);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Return index of the field, or -1 if not found
|
|
// Same as getFieldIndex, but checks for a direct match with the VarDeclaration
|
|
int ClassReferenceExp::findFieldIndexByName(VarDeclaration *v)
|
|
{
|
|
ClassDeclaration *cd = originalClass();
|
|
size_t fieldsSoFar = 0;
|
|
for (size_t j = 0; j < value->elements->dim; j++)
|
|
{ while (j - fieldsSoFar >= cd->fields.dim)
|
|
{ fieldsSoFar += cd->fields.dim;
|
|
cd = cd->baseClass;
|
|
}
|
|
Dsymbol *s = cd->fields[j - fieldsSoFar];
|
|
VarDeclaration *v2 = s->isVarDeclaration();
|
|
if (v == v2)
|
|
{ return value->elements->dim - fieldsSoFar - cd->fields.dim + (j-fieldsSoFar);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/************** VoidInitExp ********************************************/
|
|
|
|
VoidInitExp::VoidInitExp(VarDeclaration *var, Type *type)
|
|
: Expression(var->loc, TOKvoid, sizeof(VoidInitExp))
|
|
{
|
|
this->var = var;
|
|
this->type = var->type;
|
|
}
|
|
|
|
char *VoidInitExp::toChars()
|
|
{
|
|
return (char *)"void";
|
|
}
|
|
|
|
Expression *VoidInitExp::interpret(InterState *istate, CtfeGoal goal)
|
|
{
|
|
error("CTFE internal error: trying to read uninitialized variable");
|
|
assert(0);
|
|
return EXP_CANT_INTERPRET;
|
|
}
|
|
|
|
// 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 (int i = 0; i < sd->fields.dim; ++i)
|
|
{
|
|
if (sd->fields[i] == v)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/************** ThrownExceptionExp ********************************************/
|
|
|
|
ThrownExceptionExp::ThrownExceptionExp(Loc loc, ClassReferenceExp *victim) : Expression(loc, TOKthrownexception, sizeof(ThrownExceptionExp))
|
|
{
|
|
this->thrown = victim;
|
|
this->type = victim->type;
|
|
}
|
|
|
|
Expression *ThrownExceptionExp::interpret(InterState *istate, CtfeGoal)
|
|
{
|
|
assert(0); // This should never be interpreted
|
|
return this;
|
|
}
|
|
|
|
char *ThrownExceptionExp::toChars()
|
|
{
|
|
return (char *)"CTFE ThrownException";
|
|
}
|
|
|
|
// Generate an error message when this exception is not caught
|
|
void ThrownExceptionExp::generateUncaughtError()
|
|
{
|
|
thrown->error("Uncaught CTFE exception %s(%s)", thrown->type->toChars(),
|
|
thrown->value->elements->tdata()[0]->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 EXP_CANT_INTERPRET, or an exception
|
|
bool exceptionOrCantInterpret(Expression *e)
|
|
{
|
|
if (e == EXP_CANT_INTERPRET) return true;
|
|
if (!e || e == EXP_GOTO_INTERPRET || e == EXP_VOID_INTERPRET
|
|
|| e == EXP_BREAK_INTERPRET || e == EXP_CONTINUE_INTERPRET)
|
|
return false;
|
|
return e->op == TOKthrownexception;
|
|
}
|
|
|
|
/************** 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;
|
|
case TOKassocarrayliteral:
|
|
return !((AssocArrayLiteralExp *)expr)->ownedByCtfe;
|
|
case TOKstructliteral:
|
|
return !((StructLiteralExp *)expr)->ownedByCtfe;
|
|
case TOKstring:
|
|
case TOKthis:
|
|
case TOKvar:
|
|
return false;
|
|
case TOKassign:
|
|
return false;
|
|
case TOKindex:
|
|
case TOKdotvar:
|
|
case TOKslice:
|
|
case TOKcast:
|
|
expr = ((UnaExp *)expr)->e1;
|
|
continue;
|
|
case TOKcat:
|
|
return needToCopyLiteral(((BinExp *)expr)->e1) ||
|
|
needToCopyLiteral(((BinExp *)expr)->e2);
|
|
case TOKcatass:
|
|
expr = ((BinExp *)expr)->e2;
|
|
continue;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
Expressions *copyLiteralArray(Expressions *oldelems)
|
|
{
|
|
if (!oldelems)
|
|
return oldelems;
|
|
CtfeStatus::numArrayAllocs++;
|
|
Expressions *newelems = new Expressions();
|
|
newelems->setDim(oldelems->dim);
|
|
for (size_t i = 0; i < oldelems->dim; i++)
|
|
newelems->tdata()[i] = copyLiteral(oldelems->tdata()[i]);
|
|
return newelems;
|
|
}
|
|
|
|
// Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral.
|
|
// This value will be used for in-place modification.
|
|
Expression *copyLiteral(Expression *e)
|
|
{
|
|
if (e->op == TOKstring) // syntaxCopy doesn't make a copy for StringExp!
|
|
{
|
|
StringExp *se = (StringExp *)e;
|
|
unsigned char *s;
|
|
s = (unsigned char *)mem.calloc(se->len + 1, se->sz);
|
|
memcpy(s, se->string, se->len * se->sz);
|
|
StringExp *se2 = new StringExp(se->loc, s, se->len);
|
|
se2->committed = se->committed;
|
|
se2->postfix = se->postfix;
|
|
se2->type = se->type;
|
|
se2->sz = se->sz;
|
|
se2->ownedByCtfe = true;
|
|
return se2;
|
|
}
|
|
else if (e->op == TOKarrayliteral)
|
|
{
|
|
ArrayLiteralExp *ae = (ArrayLiteralExp *)e;
|
|
ArrayLiteralExp *r = new ArrayLiteralExp(e->loc,
|
|
copyLiteralArray(ae->elements));
|
|
r->type = e->type;
|
|
r->ownedByCtfe = true;
|
|
return r;
|
|
}
|
|
else if (e->op == TOKassocarrayliteral)
|
|
{
|
|
AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e;
|
|
AssocArrayLiteralExp *r = new AssocArrayLiteralExp(e->loc,
|
|
copyLiteralArray(aae->keys), copyLiteralArray(aae->values));
|
|
r->type = e->type;
|
|
r->ownedByCtfe = true;
|
|
return r;
|
|
}
|
|
/* syntaxCopy doesn't work for struct literals, because of a nasty special
|
|
* case: block assignment is permitted inside struct literals, eg,
|
|
* an int[4] array can be initialized with a single int.
|
|
*/
|
|
else if (e->op == TOKstructliteral)
|
|
{
|
|
StructLiteralExp *se = (StructLiteralExp *)e;
|
|
Expressions *oldelems = se->elements;
|
|
Expressions * newelems = new Expressions();
|
|
newelems->setDim(oldelems->dim);
|
|
for (size_t i = 0; i < newelems->dim; i++)
|
|
{
|
|
Expression *m = oldelems->tdata()[i];
|
|
// We need the struct definition to detect block assignment
|
|
AggregateDeclaration *sd = se->sd;
|
|
Dsymbol *s = sd->fields[i];
|
|
VarDeclaration *v = s->isVarDeclaration();
|
|
assert(v);
|
|
// If it is a void assignment, use the default initializer
|
|
if (!m)
|
|
m = v->type->voidInitLiteral(v);
|
|
if (m->op == TOKslice)
|
|
m = resolveSlice(m);
|
|
if ((v->type->ty != m->type->ty) && v->type->ty == Tsarray)
|
|
{
|
|
// Block assignment from inside struct literals
|
|
TypeSArray *tsa = (TypeSArray *)v->type;
|
|
uinteger_t length = tsa->dim->toInteger();
|
|
m = createBlockDuplicatedArrayLiteral(e->loc, v->type, m, (size_t)length);
|
|
}
|
|
else if (v->type->ty != Tarray && v->type->ty!=Taarray) // NOTE: do not copy array references
|
|
m = copyLiteral(m);
|
|
newelems->tdata()[i] = m;
|
|
}
|
|
#if DMDV2
|
|
StructLiteralExp *r = new StructLiteralExp(e->loc, se->sd, newelems, se->stype);
|
|
#else
|
|
StructLiteralExp *r = new StructLiteralExp(e->loc, se->sd, newelems);
|
|
#endif
|
|
r->type = e->type;
|
|
r->ownedByCtfe = true;
|
|
return r;
|
|
}
|
|
else if (e->op == TOKfunction || e->op == TOKdelegate
|
|
|| e->op == TOKsymoff || e->op == TOKnull
|
|
|| e->op == TOKvar
|
|
|| e->op == TOKint64 || e->op == TOKfloat64
|
|
|| e->op == TOKchar || e->op == TOKcomplex80
|
|
|| e->op == TOKvoid)
|
|
{ // Simple value types
|
|
Expression *r = e->syntaxCopy();
|
|
r->type = e->type;
|
|
return r;
|
|
}
|
|
else if ( isPointer(e->type) )
|
|
{ // For pointers, we only do a shallow copy.
|
|
Expression *r;
|
|
if (e->op == TOKaddress)
|
|
r = new AddrExp(e->loc, ((AddrExp *)e)->e1);
|
|
else if (e->op == TOKindex)
|
|
r = new IndexExp(e->loc, ((IndexExp *)e)->e1, ((IndexExp *)e)->e2);
|
|
else if (e->op == TOKdotvar)
|
|
r = new DotVarExp(e->loc, ((DotVarExp *)e)->e1,
|
|
((DotVarExp *)e)->var
|
|
#if DMDV2
|
|
, ((DotVarExp *)e)->hasOverloads
|
|
#endif
|
|
);
|
|
else
|
|
assert(0);
|
|
r->type = e->type;
|
|
return r;
|
|
}
|
|
else if (e->op == TOKslice)
|
|
{ // Array slices only do a shallow copy
|
|
Expression *r = new SliceExp(e->loc, ((SliceExp *)e)->e1,
|
|
((SliceExp *)e)->lwr, ((SliceExp *)e)->upr);
|
|
r->type = e->type;
|
|
return r;
|
|
}
|
|
else if (e->op == TOKclassreference)
|
|
return new ClassReferenceExp(e->loc, ((ClassReferenceExp *)e)->value, e->type);
|
|
else
|
|
{
|
|
e->error("Internal Compiler Error: CTFE literal %s", e->toChars());
|
|
assert(0);
|
|
return e;
|
|
}
|
|
}
|
|
|
|
/* Deal with type painting.
|
|
* Type painting is a major nuisance: we can't just set
|
|
* e->type = type, because that would change the original literal.
|
|
* But, we can't simply copy the literal either, because that would change
|
|
* the values of any pointers.
|
|
*/
|
|
Expression *paintTypeOntoLiteral(Type *type, Expression *lit)
|
|
{
|
|
if (lit->type == type)
|
|
return lit;
|
|
Expression *e;
|
|
if (lit->op == TOKslice)
|
|
{
|
|
SliceExp *se = (SliceExp *)lit;
|
|
e = new SliceExp(lit->loc, se->e1, se->lwr, se->upr);
|
|
}
|
|
else if (lit->op == TOKindex)
|
|
{
|
|
IndexExp *ie = (IndexExp *)lit;
|
|
e = new IndexExp(lit->loc, ie->e1, ie->e2);
|
|
}
|
|
else if (lit->op == TOKarrayliteral)
|
|
{
|
|
e = new SliceExp(lit->loc, lit,
|
|
new IntegerExp(0, 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit));
|
|
}
|
|
else if (lit->op == TOKstring)
|
|
{
|
|
// For strings, we need to introduce another level of indirection
|
|
e = new SliceExp(lit->loc, lit,
|
|
new IntegerExp(0, 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit));
|
|
}
|
|
else if (lit->op == TOKassocarrayliteral)
|
|
{
|
|
AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)lit;
|
|
// TODO: we should be creating a reference to this AAExp, not
|
|
// just a ref to the keys and values.
|
|
bool wasOwned = aae->ownedByCtfe;
|
|
aae = new AssocArrayLiteralExp(lit->loc, aae->keys, aae->values);
|
|
aae->ownedByCtfe = wasOwned;
|
|
e = aae;
|
|
}
|
|
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());
|
|
e = copyLiteral(lit);
|
|
}
|
|
e->type = type;
|
|
return e;
|
|
}
|
|
|
|
Expression *resolveSlice(Expression *e)
|
|
{
|
|
if ( ((SliceExp *)e)->e1->op == TOKnull)
|
|
return ((SliceExp *)e)->e1;
|
|
return Slice(e->type, ((SliceExp *)e)->e1,
|
|
((SliceExp *)e)->lwr, ((SliceExp *)e)->upr);
|
|
}
|
|
|
|
/* Determine the array length, without interpreting it.
|
|
* e must be an array literal, or a slice
|
|
* It's very wasteful to resolve the slice when we only
|
|
* need the length.
|
|
*/
|
|
uinteger_t resolveArrayLength(Expression *e)
|
|
{
|
|
if (e->op == TOKnull)
|
|
return 0;
|
|
if (e->op == TOKslice)
|
|
{ uinteger_t ilo = ((SliceExp *)e)->lwr->toInteger();
|
|
uinteger_t iup = ((SliceExp *)e)->upr->toInteger();
|
|
return iup - ilo;
|
|
}
|
|
if (e->op == TOKstring)
|
|
{ return ((StringExp *)e)->len;
|
|
}
|
|
if (e->op == TOKarrayliteral)
|
|
{ ArrayLiteralExp *ale = (ArrayLiteralExp *)e;
|
|
return ale->elements ? ale->elements->dim : 0;
|
|
}
|
|
if (e->op == TOKassocarrayliteral)
|
|
{ AssocArrayLiteralExp *ale = (AssocArrayLiteralExp *)e;
|
|
return ale->keys->dim;
|
|
}
|
|
assert(0);
|
|
return 0;
|
|
}
|
|
|
|
/******************************
|
|
* Helper for NewExp
|
|
* Create an array literal consisting of 'elem' duplicated 'dim' times.
|
|
*/
|
|
ArrayLiteralExp *createBlockDuplicatedArrayLiteral(Loc loc, Type *type,
|
|
Expression *elem, size_t dim)
|
|
{
|
|
Expressions *elements = new Expressions();
|
|
elements->setDim(dim);
|
|
bool mustCopy = needToCopyLiteral(elem);
|
|
for (size_t i = 0; i < dim; i++)
|
|
{ if (mustCopy)
|
|
elem = copyLiteral(elem);
|
|
(*elements)[i] = elem;
|
|
}
|
|
ArrayLiteralExp *ae = new ArrayLiteralExp(loc, elements);
|
|
ae->type = type;
|
|
ae->ownedByCtfe = true;
|
|
return ae;
|
|
}
|
|
|
|
/******************************
|
|
* Helper for NewExp
|
|
* Create a string literal consisting of 'value' duplicated 'dim' times.
|
|
*/
|
|
StringExp *createBlockDuplicatedStringLiteral(Loc loc, Type *type,
|
|
unsigned value, size_t dim, int sz)
|
|
{
|
|
unsigned char *s;
|
|
s = (unsigned char *)mem.calloc(dim + 1, sz);
|
|
for (size_t elemi = 0; elemi < dim; ++elemi)
|
|
{
|
|
switch (sz)
|
|
{
|
|
case 1: s[elemi] = value; break;
|
|
case 2: ((unsigned short *)s)[elemi] = value; break;
|
|
case 4: ((unsigned *)s)[elemi] = value; break;
|
|
default: assert(0);
|
|
}
|
|
}
|
|
StringExp *se = new StringExp(loc, s, dim);
|
|
se->type = type;
|
|
se->sz = sz;
|
|
se->committed = true;
|
|
se->ownedByCtfe = true;
|
|
return se;
|
|
}
|
|
|
|
// Return true if t is an AA, or AssociativeArray!(key, value)
|
|
bool isAssocArray(Type *t)
|
|
{
|
|
t = t->toBasetype();
|
|
if (t->ty == Taarray)
|
|
return true;
|
|
#if DMDV2
|
|
if (t->ty != Tstruct)
|
|
return false;
|
|
StructDeclaration *sym = ((TypeStruct *)t)->sym;
|
|
if (sym->ident == Id::AssociativeArray && sym->parent &&
|
|
sym->parent->parent &&
|
|
sym->parent->parent->ident == Id::object)
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
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;
|
|
#if DMDV2
|
|
assert(t->ty == Tstruct);
|
|
StructDeclaration *sym = ((TypeStruct *)t)->sym;
|
|
assert(sym->ident == Id::AssociativeArray);
|
|
TemplateInstance *tinst = sym->parent->isTemplateInstance();
|
|
assert(tinst);
|
|
return new TypeAArray((Type *)(tinst->tiargs->tdata()[1]), (Type *)(tinst->tiargs->tdata()[0]));
|
|
#else
|
|
assert(0);
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
/************** 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.
|
|
int 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 OK if both are the same (modulo const)
|
|
#if DMDV2
|
|
if (srcPointee->castMod(0) == destPointee->castMod(0))
|
|
return true;
|
|
#else
|
|
if (srcPointee == destPointee)
|
|
return true;
|
|
#endif
|
|
// it's OK to cast to void*
|
|
if (destPointee->ty == Tvoid)
|
|
return true;
|
|
// It's OK if they are the same size integers, eg int* and uint*
|
|
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 == 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->tdata()[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;
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/** Return true if agg1 and agg2 are pointers to the same memory block
|
|
*/
|
|
bool pointToSameMemoryBlock(Expression *agg1, Expression *agg2)
|
|
{
|
|
// Note that type painting can occur with VarExp, so we
|
|
// must compare the variables being pointed to.
|
|
return agg1 == agg2 ||
|
|
(agg1->op == TOKvar && agg2->op == TOKvar &&
|
|
((VarExp *)agg1)->var == ((VarExp *)agg2)->var);
|
|
}
|
|
|
|
// return e1 - e2 as an integer, or error if not possible
|
|
Expression *pointerDifference(Loc loc, Type *type, Expression *e1, Expression *e2)
|
|
{
|
|
dinteger_t ofs1, ofs2;
|
|
Expression *agg1 = getAggregateFromPointer(e1, &ofs1);
|
|
Expression *agg2 = getAggregateFromPointer(e2, &ofs2);
|
|
if (agg1 == agg2)
|
|
{
|
|
Type *pointee = ((TypePointer *)agg1->type)->next;
|
|
dinteger_t sz = pointee->size();
|
|
return new IntegerExp(loc, (ofs1-ofs2)*sz, type);
|
|
}
|
|
else if (agg1->op == TOKstring && agg2->op == TOKstring)
|
|
{
|
|
if (((StringExp *)agg1)->string == ((StringExp *)agg2)->string)
|
|
{
|
|
Type *pointee = ((TypePointer *)agg1->type)->next;
|
|
dinteger_t sz = pointee->size();
|
|
return new IntegerExp(loc, (ofs1-ofs2)*sz, type);
|
|
}
|
|
}
|
|
error(loc, "%s - %s cannot be interpreted at compile time: cannot subtract "
|
|
"pointers to two different memory blocks",
|
|
e1->toChars(), e2->toChars());
|
|
return EXP_CANT_INTERPRET;
|
|
}
|
|
|
|
// Return eptr op e2, where eptr is a pointer, e2 is an integer,
|
|
// and op is TOKadd or TOKmin
|
|
Expression *pointerArithmetic(Loc loc, enum TOK op, Type *type,
|
|
Expression *eptr, Expression *e2)
|
|
{
|
|
if (eptr->type->nextOf()->ty == Tvoid)
|
|
{
|
|
error(loc, "cannot perform arithmetic on void* pointers at compile time");
|
|
return EXP_CANT_INTERPRET;
|
|
}
|
|
dinteger_t ofs1, ofs2;
|
|
if (eptr->op == TOKaddress)
|
|
eptr = ((AddrExp *)eptr)->e1;
|
|
Expression *agg1 = getAggregateFromPointer(eptr, &ofs1);
|
|
if (agg1->op != TOKstring && agg1->op != TOKarrayliteral)
|
|
{
|
|
error(loc, "cannot perform pointer arithmetic on non-arrays at compile time");
|
|
return EXP_CANT_INTERPRET;
|
|
}
|
|
ofs2 = e2->toInteger();
|
|
Type *pointee = ((TypePointer *)agg1->type)->next;
|
|
dinteger_t sz = pointee->size();
|
|
Expression *dollar = ArrayLength(Type::tsize_t, agg1);
|
|
assert(dollar != EXP_CANT_INTERPRET);
|
|
dinteger_t len = dollar->toInteger();
|
|
|
|
Expression *val = agg1;
|
|
TypeArray *tar = (TypeArray *)val->type;
|
|
sinteger_t indx = ofs1;
|
|
#if IN_LLVM // LDC: llvm uses typesafe pointer arithmetic
|
|
if (op == TOKadd || op == TOKaddass || op == TOKplusplus)
|
|
indx += ofs2;
|
|
else if (op == TOKmin || op == TOKminass || op == TOKminusminus)
|
|
indx -= ofs2;
|
|
#else
|
|
if (op == TOKadd || op == TOKaddass || op == TOKplusplus)
|
|
indx = indx + ofs2/sz;
|
|
else if (op == TOKmin || op == TOKminass || op == TOKminusminus)
|
|
indx -= ofs2/sz;
|
|
#endif
|
|
else
|
|
{
|
|
error(loc, "CTFE Internal compiler error: bad pointer operation");
|
|
return EXP_CANT_INTERPRET;
|
|
}
|
|
if (val->op != TOKarrayliteral && val->op != TOKstring)
|
|
{
|
|
error(loc, "CTFE Internal compiler error: pointer arithmetic %s", val->toChars());
|
|
return EXP_CANT_INTERPRET;
|
|
}
|
|
if (indx < 0 || indx > len)
|
|
{
|
|
error(loc, "cannot assign pointer to index %lld inside memory block [0..%lld]", indx, len);
|
|
return EXP_CANT_INTERPRET;
|
|
}
|
|
|
|
IntegerExp *ofs = new IntegerExp(loc, indx, Type::tsize_t);
|
|
IndexExp *ie = new IndexExp(loc, val, ofs);
|
|
ie->type = type;
|
|
return ie;
|
|
}
|
|
|
|
// Return 1 if true, 0 if false
|
|
// -1 if comparison is illegal because they point to non-comparable memory blocks
|
|
int comparePointers(Loc loc, enum TOK op, Type *type, Expression *agg1, dinteger_t ofs1,
|
|
Expression *agg2, dinteger_t ofs2)
|
|
{
|
|
if ( pointToSameMemoryBlock(agg1, agg2) )
|
|
{
|
|
dinteger_t cm = ofs1 - ofs2;
|
|
dinteger_t n;
|
|
dinteger_t zero = 0;
|
|
switch(op)
|
|
{
|
|
case TOKlt: n = (ofs1 < ofs2); break;
|
|
case TOKle: n = (ofs1 <= ofs2); break;
|
|
case TOKgt: n = (ofs1 > ofs2); break;
|
|
case TOKge: n = (ofs1 >= ofs2); break;
|
|
case TOKidentity:
|
|
case TOKequal: n = (ofs1 == ofs2); break;
|
|
case TOKnotidentity:
|
|
case TOKnotequal: n = (ofs1 != ofs2); break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
return 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;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
union UnionFloatInt
|
|
{
|
|
float f;
|
|
d_int32 x;
|
|
};
|
|
|
|
union UnionDoubleLong
|
|
{
|
|
double f;
|
|
d_int64 x;
|
|
};
|
|
|
|
// 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(Expression *fromVal, Type *to)
|
|
{
|
|
if (exceptionOrCantInterpret(fromVal))
|
|
return fromVal;
|
|
|
|
if (to->size() == 4)
|
|
{
|
|
UnionFloatInt u;
|
|
if (to->isintegral())
|
|
{
|
|
u.f = fromVal->toReal();
|
|
return new IntegerExp(fromVal->loc, ldouble(u.x), to);
|
|
}
|
|
else
|
|
{
|
|
u.x = fromVal->toInteger();
|
|
return new RealExp(fromVal->loc, ldouble(u.f), to);
|
|
}
|
|
}
|
|
else if (to->size() == 8)
|
|
{
|
|
UnionDoubleLong v;
|
|
if (to->isintegral())
|
|
{
|
|
v.f = fromVal->toReal();
|
|
return new IntegerExp(fromVal->loc, v.x, to);
|
|
}
|
|
else
|
|
{
|
|
v.x = fromVal->toInteger();
|
|
return new RealExp(fromVal->loc, ldouble(v.f), to);
|
|
}
|
|
}
|
|
assert(0);
|
|
return NULL; // avoid warning
|
|
}
|
|
|
|
|
|
/***********************************************
|
|
Primitive integer operations
|
|
***********************************************/
|
|
|
|
/** e = OP e
|
|
*/
|
|
void intUnary(TOK op, IntegerExp *e)
|
|
{
|
|
switch (op)
|
|
{
|
|
case TOKneg:
|
|
e->value = -e->value;
|
|
break;
|
|
case TOKtilde:
|
|
e->value = ~e->value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/** dest = e1 OP e2;
|
|
*/
|
|
void intBinary(TOK op, IntegerExp *dest, Type *type, IntegerExp *e1, IntegerExp *e2)
|
|
{
|
|
dinteger_t result;
|
|
switch (op)
|
|
{
|
|
case TOKand:
|
|
result = e1->value & e2->value;
|
|
break;
|
|
case TOKor:
|
|
result = e1->value | e2->value;
|
|
break;
|
|
case TOKxor:
|
|
result = e1->value ^ e2->value;
|
|
break;
|
|
case TOKadd:
|
|
result = e1->value + e2->value;
|
|
break;
|
|
case TOKmin:
|
|
result = e1->value - e2->value;
|
|
break;
|
|
case TOKmul:
|
|
result = e1->value * e2->value;
|
|
break;
|
|
case TOKdiv:
|
|
{ sinteger_t n1 = e1->value;
|
|
sinteger_t n2 = e2->value;
|
|
|
|
if (n2 == 0)
|
|
{ e2->error("divide by 0");
|
|
result = 1;
|
|
}
|
|
else if (e1->type->isunsigned() || e2->type->isunsigned())
|
|
result = ((d_uns64) n1) / ((d_uns64) n2);
|
|
else
|
|
result = n1 / n2;
|
|
}
|
|
break;
|
|
case TOKmod:
|
|
{ sinteger_t n1 = e1->value;
|
|
sinteger_t n2 = e2->value;
|
|
|
|
if (n2 == 0)
|
|
{ e2->error("divide by 0");
|
|
n2 = 1;
|
|
}
|
|
if (n2 == -1 && !type->isunsigned())
|
|
{ // Check for int.min % -1
|
|
if (n1 == 0xFFFFFFFF80000000ULL && type->toBasetype()->ty != Tint64)
|
|
{
|
|
e2->error("integer overflow: int.min % -1");
|
|
n2 = 1;
|
|
}
|
|
else if (n1 == 0x8000000000000000LL) // long.min % -1
|
|
{
|
|
e2->error("integer overflow: long.min % -1");
|
|
n2 = 1;
|
|
}
|
|
}
|
|
if (e1->type->isunsigned() || e2->type->isunsigned())
|
|
result = ((d_uns64) n1) % ((d_uns64) n2);
|
|
else
|
|
result = n1 % n2;
|
|
}
|
|
break;
|
|
case TOKpow:
|
|
{ dinteger_t n = e2->value;
|
|
if (!e2->type->isunsigned() && (sinteger_t)n < 0)
|
|
{
|
|
e2->error("integer ^^ -integer: total loss of precision");
|
|
n = 1;
|
|
}
|
|
uinteger_t r = e1->value;
|
|
result = 1;
|
|
while (n != 0)
|
|
{
|
|
if (n & 1)
|
|
result = result * r;
|
|
n >>= 1;
|
|
r = r * r;
|
|
}
|
|
}
|
|
break;
|
|
case TOKshl:
|
|
result = e1->value << e2->value;
|
|
break;
|
|
case TOKshr:
|
|
{ dinteger_t value = e1->value;
|
|
dinteger_t dcount = e2->value;
|
|
assert(dcount <= 0xFFFFFFFF);
|
|
unsigned count = (unsigned)dcount;
|
|
switch (e1->type->toBasetype()->ty)
|
|
{
|
|
case Tint8:
|
|
result = (d_int8)(value) >> count;
|
|
break;
|
|
|
|
case Tuns8:
|
|
case Tchar:
|
|
result = (d_uns8)(value) >> count;
|
|
break;
|
|
|
|
case Tint16:
|
|
result = (d_int16)(value) >> count;
|
|
break;
|
|
|
|
case Tuns16:
|
|
case Twchar:
|
|
result = (d_uns16)(value) >> count;
|
|
break;
|
|
|
|
case Tint32:
|
|
result = (d_int32)(value) >> count;
|
|
break;
|
|
|
|
case Tuns32:
|
|
case Tdchar:
|
|
result = (d_uns32)(value) >> count;
|
|
break;
|
|
|
|
case Tint64:
|
|
result = (d_int64)(value) >> count;
|
|
break;
|
|
|
|
case Tuns64:
|
|
result = (d_uns64)(value) >> count;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
break;
|
|
case TOKushr:
|
|
{ dinteger_t value = e1->value;
|
|
dinteger_t dcount = e2->value;
|
|
assert(dcount <= 0xFFFFFFFF);
|
|
unsigned count = (unsigned)dcount;
|
|
switch (e1->type->toBasetype()->ty)
|
|
{
|
|
case Tint8:
|
|
case Tuns8:
|
|
case Tchar:
|
|
// Possible only with >>>=. >>> always gets promoted to int.
|
|
result = (value & 0xFF) >> count;
|
|
break;
|
|
|
|
case Tint16:
|
|
case Tuns16:
|
|
case Twchar:
|
|
// Possible only with >>>=. >>> always gets promoted to int.
|
|
result = (value & 0xFFFF) >> count;
|
|
break;
|
|
|
|
case Tint32:
|
|
case Tuns32:
|
|
case Tdchar:
|
|
result = (value & 0xFFFFFFFF) >> count;
|
|
break;
|
|
|
|
case Tint64:
|
|
case Tuns64:
|
|
result = (d_uns64)(value) >> count;
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
break;
|
|
case TOKequal:
|
|
case TOKidentity:
|
|
result = (e1->value == e2->value);
|
|
break;
|
|
case TOKnotequal:
|
|
case TOKnotidentity:
|
|
result = (e1->value != e2->value);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
dest->value = result;
|
|
dest->type = type;
|
|
}
|
|
|
|
|
|
/******** 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)
|
|
{
|
|
Expression *x = e;
|
|
if (x->op == TOKslice)
|
|
x = ((SliceExp *)e)->e1;
|
|
|
|
if (x->isConst() != 1 &&
|
|
x->op != TOKnull &&
|
|
x->op != TOKstring &&
|
|
x->op != TOKarrayliteral &&
|
|
x->op != TOKstructliteral &&
|
|
x->op != TOKclassreference)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
|
|
int intUnsignedCmp(TOK op, d_uns64 n1, d_uns64 n2)
|
|
{
|
|
int n;
|
|
switch (op)
|
|
{
|
|
case TOKlt: n = n1 < n2; break;
|
|
case TOKle: n = n1 <= n2; break;
|
|
case TOKgt: n = n1 > n2; break;
|
|
case TOKge: n = n1 >= n2; break;
|
|
|
|
case TOKleg: n = 1; break;
|
|
case TOKlg: n = n1 != n2; break;
|
|
case TOKunord: n = 0; break;
|
|
case TOKue: n = n1 == n2; break;
|
|
case TOKug: n = n1 > n2; break;
|
|
case TOKuge: n = n1 >= n2; break;
|
|
case TOKul: n = n1 < n2; break;
|
|
case TOKule: n = n1 <= n2; break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
|
|
int intSignedCmp(TOK op, sinteger_t n1, sinteger_t n2)
|
|
{
|
|
int n;
|
|
switch (op)
|
|
{
|
|
case TOKlt: n = n1 < n2; break;
|
|
case TOKle: n = n1 <= n2; break;
|
|
case TOKgt: n = n1 > n2; break;
|
|
case TOKge: n = n1 >= n2; break;
|
|
|
|
case TOKleg: n = 1; break;
|
|
case TOKlg: n = n1 != n2; break;
|
|
case TOKunord: n = 0; break;
|
|
case TOKue: n = n1 == n2; break;
|
|
case TOKug: n = n1 > n2; break;
|
|
case TOKuge: n = n1 >= n2; break;
|
|
case TOKul: n = n1 < n2; break;
|
|
case TOKule: n = n1 <= n2; break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
|
|
int realCmp(TOK op, real_t r1, real_t r2)
|
|
{
|
|
int n;
|
|
#if __DMC__
|
|
// DMC is the only compiler I know of that handles NAN arguments
|
|
// correctly in comparisons.
|
|
switch (op)
|
|
{
|
|
case TOKlt: n = r1 < r2; break;
|
|
case TOKle: n = r1 <= r2; break;
|
|
case TOKgt: n = r1 > r2; break;
|
|
case TOKge: n = r1 >= r2; break;
|
|
|
|
case TOKleg: n = r1 <>= r2; break;
|
|
case TOKlg: n = r1 <> r2; break;
|
|
case TOKunord: n = r1 !<>= r2; break;
|
|
case TOKue: n = r1 !<> r2; break;
|
|
case TOKug: n = r1 !<= r2; break;
|
|
case TOKuge: n = r1 !< r2; break;
|
|
case TOKul: n = r1 !>= r2; break;
|
|
case TOKule: n = r1 !> r2; break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
#else
|
|
// Don't rely on compiler, handle NAN arguments separately
|
|
if (Port::isNan(r1) || Port::isNan(r2)) // if unordered
|
|
{
|
|
switch (op)
|
|
{
|
|
case TOKlt: n = 0; break;
|
|
case TOKle: n = 0; break;
|
|
case TOKgt: n = 0; break;
|
|
case TOKge: n = 0; break;
|
|
|
|
case TOKleg: n = 0; break;
|
|
case TOKlg: n = 0; break;
|
|
case TOKunord: n = 1; break;
|
|
case TOKue: n = 1; break;
|
|
case TOKug: n = 1; break;
|
|
case TOKuge: n = 1; break;
|
|
case TOKul: n = 1; break;
|
|
case TOKule: n = 1; break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (op)
|
|
{
|
|
case TOKlt: n = r1 < r2; break;
|
|
case TOKle: n = r1 <= r2; break;
|
|
case TOKgt: n = r1 > r2; break;
|
|
case TOKge: n = r1 >= r2; break;
|
|
|
|
case TOKleg: n = 1; break;
|
|
case TOKlg: n = r1 != r2; break;
|
|
case TOKunord: n = 0; break;
|
|
case TOKue: n = r1 == r2; break;
|
|
case TOKug: n = r1 > r2; break;
|
|
case TOKuge: n = r1 >= r2; break;
|
|
case TOKul: n = r1 < r2; break;
|
|
case TOKule: n = r1 <= r2; break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
#endif
|
|
return n;
|
|
}
|
|
|
|
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 : 0;
|
|
ArrayLiteralExp *ae1 = (x->op == TOKarrayliteral) ? (ArrayLiteralExp *)x : 0;
|
|
|
|
x = e2;
|
|
if (x->op == TOKslice)
|
|
{ lo2 = ((SliceExp *)x)->lwr->toInteger();
|
|
x = ((SliceExp*)x)->e1;
|
|
}
|
|
StringExp *se2 = (x->op == TOKstring) ? (StringExp *)x : 0;
|
|
ArrayLiteralExp *ae2 = (x->op == TOKarrayliteral) ? (ArrayLiteralExp *)x : 0;
|
|
|
|
// Now both must be either TOKarrayliteral or TOKstring
|
|
if (se1 && se2)
|
|
return sliceCmpStringWithString(se1, se2, lo1, lo2, len);
|
|
if (se1 && ae2)
|
|
return sliceCmpStringWithArray(se1, ae2, lo1, lo2, len);
|
|
if (se2 && ae1)
|
|
return -sliceCmpStringWithArray(se2, ae1, lo2, lo1, 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 < len; i++)
|
|
{ Expression *ee1 = (*ae1->elements)[lo1 + i];
|
|
Expression *ee2 = (*ae2->elements)[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;
|
|
}
|
|
|
|
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 == TOKnull && e2->op == TOKnull)
|
|
return 0;
|
|
|
|
if (e1->type->ty == Tpointer && e2->type->ty == Tpointer)
|
|
{ // Can only be an equality test.
|
|
if (e1->op == TOKnull && e2->op == TOKnull)
|
|
return 0;
|
|
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 (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 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 __DMC__
|
|
return (r1 != r2);
|
|
#else
|
|
if (Port::isNan(r1) || Port::isNan(r2)) // if unordered
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return (r1 != r2);
|
|
}
|
|
#endif
|
|
}
|
|
else if (e1->type->iscomplex())
|
|
{
|
|
return e1->toComplex() != e2->toComplex();
|
|
}
|
|
|
|
if (e1->op == TOKstructliteral && e2->op == TOKstructliteral)
|
|
{ StructLiteralExp *es1 = (StructLiteralExp *)e1;
|
|
StructLiteralExp *es2 = (StructLiteralExp *)e2;
|
|
// For structs, we only need to return 0 or 1 (< and > aren't legal).
|
|
|
|
if (es1->sd != es2->sd)
|
|
return 1;
|
|
else if ((!es1->elements || !es1->elements->dim) &&
|
|
(!es2->elements || !es2->elements->dim))
|
|
return 0; // both arrays are empty
|
|
else if (!es1->elements || !es2->elements)
|
|
return 1;
|
|
else if (es1->elements->dim != es2->elements->dim)
|
|
return 1;
|
|
else
|
|
{
|
|
for (size_t i = 0; i < es1->elements->dim; i++)
|
|
{ Expression *ee1 = (*es1->elements)[i];
|
|
Expression *ee2 = (*es2->elements)[i];
|
|
|
|
if (ee1 == ee2)
|
|
continue;
|
|
if (!ee1 || !ee2)
|
|
return 1;
|
|
int cmp = ctfeRawCmp(loc, ee1, ee2);
|
|
if (cmp)
|
|
return 1;
|
|
}
|
|
return 0; // All elements are equal
|
|
}
|
|
}
|
|
error(loc, "CTFE internal error: bad compare");
|
|
assert(0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1
|
|
int ctfeEqual(Loc loc, enum 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, enum TOK op, Expression *e1, Expression *e2)
|
|
{
|
|
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, enum TOK op, Expression *e1, Expression *e2)
|
|
{
|
|
int n;
|
|
if (e1->type->isString() && e2->type->isString())
|
|
{
|
|
int cmp = ctfeRawCmp(loc, e1, e2);
|
|
switch (op)
|
|
{
|
|
case TOKlt: n = cmp < 0; break;
|
|
case TOKle: n = cmp <= 0; break;
|
|
case TOKgt: n = cmp > 0; break;
|
|
case TOKge: n = cmp >= 0; break;
|
|
|
|
case TOKleg: n = 1; break;
|
|
case TOKlg: n = cmp != 0; break;
|
|
case TOKunord: n = 0; break;
|
|
case TOKue: n = cmp == 0; break;
|
|
case TOKug: n = cmp > 0; break;
|
|
case TOKuge: n = cmp >= 0; break;
|
|
case TOKul: n = cmp < 0; break;
|
|
case TOKule: n = cmp <= 0; break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
else if (e1->type->isreal())
|
|
{
|
|
n = realCmp(op, e1->toReal(), e2->toReal());
|
|
}
|
|
else if (e1->type->isimaginary())
|
|
{
|
|
n = realCmp(op, e1->toImaginary(), e2->toImaginary());
|
|
}
|
|
else if (e1->type->isunsigned() || e2->type->isunsigned())
|
|
{
|
|
n = intUnsignedCmp(op, e1->toInteger(), e2->toInteger());
|
|
}
|
|
else
|
|
{
|
|
n = intSignedCmp(op, e1->toInteger(), e2->toInteger());
|
|
}
|
|
return n;
|
|
}
|
|
|
|
|
|
Expression *ctfeCat(Type *type, Expression *e1, Expression *e2)
|
|
{
|
|
Loc loc = e1->loc;
|
|
Type *t1 = e1->type->toBasetype();
|
|
Type *t2 = e2->type->toBasetype();
|
|
Expression *e;
|
|
if (e2->op == TOKstring && e1->op == TOKarrayliteral &&
|
|
t1->nextOf()->isintegral())
|
|
{
|
|
// [chars] ~ string => string (only valid for CTFE)
|
|
StringExp *es1 = (StringExp *)e2;
|
|
ArrayLiteralExp *es2 = (ArrayLiteralExp *)e1;
|
|
size_t len = es1->len + es2->elements->dim;
|
|
int sz = es1->sz;
|
|
|
|
void *s = mem.malloc((len + 1) * sz);
|
|
memcpy((char *)s + sz * es2->elements->dim, es1->string, es1->len * sz);
|
|
for (size_t i = 0; i < es2->elements->dim; i++)
|
|
{ Expression *es2e = es2->elements->tdata()[i];
|
|
if (es2e->op != TOKint64)
|
|
return EXP_CANT_INTERPRET;
|
|
dinteger_t v = es2e->toInteger();
|
|
#if IN_LLVM
|
|
#if __LITTLE_ENDIAN__
|
|
memcpy((unsigned char *)s + i * sz, &v, sz);
|
|
#else
|
|
memcpy((unsigned char *)s + i * sz,
|
|
(unsigned char *)&v + (sizeof(dinteger_t) - sz), sz);
|
|
#endif
|
|
#else
|
|
memcpy((unsigned char *)s + i * sz, &v, sz);
|
|
#endif
|
|
}
|
|
|
|
// Add terminating 0
|
|
memset((unsigned char *)s + len * sz, 0, sz);
|
|
|
|
StringExp *es = new StringExp(loc, s, len);
|
|
es->sz = sz;
|
|
es->committed = 0;
|
|
es->type = type;
|
|
e = es;
|
|
return e;
|
|
}
|
|
else if (e1->op == TOKstring && e2->op == TOKarrayliteral &&
|
|
t2->nextOf()->isintegral())
|
|
{
|
|
// string ~ [chars] => string (only valid for CTFE)
|
|
// Concatenate the strings
|
|
StringExp *es1 = (StringExp *)e1;
|
|
ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2;
|
|
size_t len = es1->len + es2->elements->dim;
|
|
int sz = es1->sz;
|
|
|
|
void *s = mem.malloc((len + 1) * sz);
|
|
memcpy(s, es1->string, es1->len * sz);
|
|
for (size_t i = 0; i < es2->elements->dim; i++)
|
|
{ Expression *es2e = es2->elements->tdata()[i];
|
|
if (es2e->op != TOKint64)
|
|
return EXP_CANT_INTERPRET;
|
|
dinteger_t v = es2e->toInteger();
|
|
#if IN_LLVM
|
|
#if __LITTLE_ENDIAN__
|
|
memcpy((unsigned char *)s + (es1->len + i) * sz, &v, sz);
|
|
#else
|
|
memcpy((unsigned char *)s + (es1->len + i) * sz,
|
|
(unsigned char *) &v + (sizeof(dinteger_t) - sz), sz);
|
|
#endif
|
|
#else
|
|
memcpy((unsigned char *)s + (es1->len + i) * sz, &v, sz);
|
|
#endif
|
|
}
|
|
|
|
// Add terminating 0
|
|
memset((unsigned char *)s + len * sz, 0, sz);
|
|
|
|
StringExp *es = new StringExp(loc, s, len);
|
|
es->sz = sz;
|
|
es->committed = 0; //es1->committed;
|
|
es->type = type;
|
|
e = es;
|
|
return e;
|
|
}
|
|
return Cat(type, e1, e2);
|
|
}
|
|
|
|
/* Given an AA literal 'ae', and a key 'e2':
|
|
* Return ae[e2] if present, or NULL if not found.
|
|
*/
|
|
Expression *findKeyInAA(Loc loc, AssocArrayLiteralExp *ae, Expression *e2)
|
|
{
|
|
/* Search the keys backwards, in case there are duplicate keys
|
|
*/
|
|
for (size_t i = ae->keys->dim; i;)
|
|
{
|
|
i--;
|
|
Expression *ekey = ae->keys->tdata()[i];
|
|
int eq = ctfeEqual(loc, TOKequal, ekey, e2);
|
|
if (eq)
|
|
{
|
|
return ae->values->tdata()[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 %ju is out of bounds [0 .. %zu]", indx, es1->len);
|
|
return EXP_CANT_INTERPRET;
|
|
}
|
|
else
|
|
return new IntegerExp(loc, es1->charAt(indx), type);
|
|
}
|
|
assert(e1->op == TOKarrayliteral);
|
|
ArrayLiteralExp *ale = (ArrayLiteralExp *)e1;
|
|
if (indx >= ale->elements->dim)
|
|
{
|
|
error(loc, "array index %ju is out of bounds %s[0 .. %u]", indx, e1->toChars(), ale->elements->dim);
|
|
return EXP_CANT_INTERPRET;
|
|
}
|
|
Expression *e = ale->elements->tdata()[indx];
|
|
return paintTypeOntoLiteral(type, e);
|
|
}
|
|
|
|
Expression *ctfeCast(Loc loc, Type *type, Type *to, Expression *e)
|
|
{
|
|
if (e->op == TOKnull)
|
|
return paintTypeOntoLiteral(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))
|
|
return paintTypeOntoLiteral(to, e);
|
|
else
|
|
return new NullExp(loc, to);
|
|
}
|
|
Expression *r = Cast(type, to, e);
|
|
if (r == EXP_CANT_INTERPRET)
|
|
error(loc, "cannot cast %s to %s at compile time", e->toChars(), to->toChars());
|
|
if (e->op == TOKarrayliteral)
|
|
((ArrayLiteralExp *)e)->ownedByCtfe = true;
|
|
if (e->op == TOKstring)
|
|
((StringExp *)e)->ownedByCtfe = true;
|
|
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;
|
|
}
|
|
else if (dest->op == TOKarrayliteral && src->op==TOKarrayliteral)
|
|
{
|
|
oldelems = ((ArrayLiteralExp *)dest)->elements;
|
|
newelems = ((ArrayLiteralExp *)src)->elements;
|
|
}
|
|
else if (dest->op == TOKstring && src->op == TOKstring)
|
|
{
|
|
sliceAssignStringFromString((StringExp *)dest, (StringExp *)src, 0);
|
|
return;
|
|
}
|
|
else if (dest->op == TOKarrayliteral && src->op == TOKstring)
|
|
{
|
|
sliceAssignArrayLiteralFromString((ArrayLiteralExp *)dest, (StringExp *)src, 0);
|
|
return;
|
|
}
|
|
else if (src->op == TOKarrayliteral && dest->op == TOKstring)
|
|
{
|
|
sliceAssignStringFromArrayLiteral((StringExp *)dest, (ArrayLiteralExp *)src, 0);
|
|
return;
|
|
}
|
|
else assert(0);
|
|
|
|
assert(oldelems->dim == newelems->dim);
|
|
|
|
for (size_t i= 0; i < oldelems->dim; ++i)
|
|
{
|
|
Expression *e = newelems->tdata()[i];
|
|
Expression *o = oldelems->tdata()[i];
|
|
if (e->op == TOKstructliteral)
|
|
{
|
|
assert(o->op == e->op);
|
|
assignInPlace(o, e);
|
|
}
|
|
else if (e->type->ty == Tsarray && o->type->ty == Tsarray && e->op != TOKvoid)
|
|
{
|
|
assignInPlace(o, e);
|
|
}
|
|
else
|
|
{
|
|
oldelems->tdata()[i] = newelems->tdata()[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void recursiveBlockAssign(ArrayLiteralExp *ae, Expression *val, bool wantRef)
|
|
{
|
|
assert( ae->type->ty == Tsarray || ae->type->ty == Tarray);
|
|
#if DMDV2
|
|
Type *desttype = ((TypeArray *)ae->type)->next->castMod(0);
|
|
bool directblk = (val->type->toBasetype()->castMod(0)) == desttype;
|
|
#else
|
|
Type *desttype = ((TypeArray *)ae->type)->next;
|
|
bool directblk = (val->type->toBasetype()) == desttype;
|
|
#endif
|
|
|
|
bool cow = !(val->op == TOKstructliteral || val->op == TOKarrayliteral
|
|
|| val->op == TOKstring);
|
|
|
|
for (size_t k = 0; k < ae->elements->dim; k++)
|
|
{
|
|
if (!directblk && ae->elements->tdata()[k]->op == TOKarrayliteral)
|
|
{
|
|
recursiveBlockAssign((ArrayLiteralExp *)ae->elements->tdata()[k], val, wantRef);
|
|
}
|
|
else
|
|
{
|
|
if (wantRef || cow)
|
|
ae->elements->tdata()[k] = val;
|
|
else
|
|
assignInPlace(ae->elements->tdata()[k], val);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Duplicate the elements array, then set field 'indexToChange' = newelem.
|
|
Expressions *changeOneElement(Expressions *oldelems, size_t indexToChange, Expression *newelem)
|
|
{
|
|
Expressions *expsx = new Expressions();
|
|
++CtfeStatus::numArrayAllocs;
|
|
expsx->setDim(oldelems->dim);
|
|
for (size_t j = 0; j < expsx->dim; j++)
|
|
{
|
|
if (j == indexToChange)
|
|
(*expsx)[j] = newelem;
|
|
else
|
|
(*expsx)[j] = oldelems->tdata()[j];
|
|
}
|
|
return expsx;
|
|
}
|
|
|
|
// Create a new struct literal, which is the same as se except that se.field[offset] = elem
|
|
Expression * modifyStructField(Type *type, StructLiteralExp *se, size_t offset, Expression *newval)
|
|
{
|
|
int fieldi = se->getFieldIndex(newval->type, offset);
|
|
if (fieldi == -1)
|
|
return EXP_CANT_INTERPRET;
|
|
/* Create new struct literal reflecting updated fieldi
|
|
*/
|
|
Expressions *expsx = changeOneElement(se->elements, fieldi, newval);
|
|
StructLiteralExp * ee = new StructLiteralExp(se->loc, se->sd, expsx);
|
|
ee->type = se->type;
|
|
ee->ownedByCtfe = 1;
|
|
return ee;
|
|
}
|
|
|
|
// Given an AA literal aae, set arr[index] = newval and return the new array.
|
|
Expression *assignAssocArrayElement(Loc loc, AssocArrayLiteralExp *aae,
|
|
Expression *index, Expression *newval)
|
|
{
|
|
/* Create new associative array literal reflecting updated key/value
|
|
*/
|
|
Expressions *keysx = aae->keys;
|
|
Expressions *valuesx = aae->values;
|
|
int updated = 0;
|
|
for (size_t j = valuesx->dim; j; )
|
|
{ j--;
|
|
Expression *ekey = aae->keys->tdata()[j];
|
|
int eq = ctfeEqual(loc, TOKequal, ekey, index);
|
|
if (eq)
|
|
{ valuesx->tdata()[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.
|
|
Expression *changeArrayLiteralLength(Loc loc, TypeArray *arrayType,
|
|
Expression *oldval, size_t oldlen, size_t newlen)
|
|
{
|
|
Type *elemType = 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 = ((SliceExp *)oldval)->lwr->toInteger();
|
|
oldval = ((SliceExp *)oldval)->e1;
|
|
}
|
|
size_t copylen = oldlen < newlen ? oldlen : newlen;
|
|
if (oldval->op == TOKstring)
|
|
{
|
|
StringExp *oldse = (StringExp *)oldval;
|
|
unsigned char *s = (unsigned char *)mem.calloc(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: s[indxlo + elemi] = defaultValue; break;
|
|
case 2: ((unsigned short *)s)[indxlo + elemi] = defaultValue; break;
|
|
case 4: ((unsigned *)s)[indxlo + elemi] = defaultValue; break;
|
|
default: assert(0);
|
|
}
|
|
}
|
|
StringExp *se = new StringExp(loc, s, newlen);
|
|
se->type = arrayType;
|
|
se->sz = oldse->sz;
|
|
se->committed = oldse->committed;
|
|
se->ownedByCtfe = true;
|
|
return se;
|
|
}
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
for (size_t i = copylen; i < newlen; i++)
|
|
(*elements)[i] = defaultElem;
|
|
}
|
|
ArrayLiteralExp *aae = new ArrayLiteralExp(loc, elements);
|
|
aae->type = arrayType;
|
|
aae->ownedByCtfe = true;
|
|
return aae;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************** CTFE Sanity Checks ***************************/
|
|
|
|
|
|
bool isCtfeValueValid(Expression *newval)
|
|
{
|
|
if (
|
|
#if DMDV2
|
|
newval->type->ty == Tnull ||
|
|
#endif
|
|
isPointer(newval->type) )
|
|
{
|
|
if (newval->op == TOKaddress || newval->op == TOKnull ||
|
|
newval->op == TOKstring)
|
|
return true;
|
|
if (newval->op == TOKindex)
|
|
{
|
|
Expression *g = ((IndexExp *)newval)->e1;
|
|
if (g->op == TOKarrayliteral || g->op == TOKstring ||
|
|
g->op == TOKassocarrayliteral)
|
|
return true;
|
|
}
|
|
if (newval->op == TOKvar)
|
|
return true;
|
|
if (newval->type->nextOf()->ty == Tarray && newval->op == TOKslice)
|
|
return true;
|
|
if (newval->op == TOKint64)
|
|
return true; // Result of a cast, but cannot be dereferenced
|
|
// else it must be a reference
|
|
}
|
|
if (newval->op == TOKclassreference || (newval->op == TOKnull && newval->type->ty == Tclass))
|
|
return true;
|
|
if (newval->op == TOKvar)
|
|
{
|
|
VarExp *ve = (VarExp *)newval;
|
|
VarDeclaration *vv = ve->var->isVarDeclaration();
|
|
// Must not be a reference to a reference
|
|
if (!(vv && vv->getValue() && vv->getValue()->op == TOKvar))
|
|
return true;
|
|
}
|
|
if (newval->op == TOKdotvar)
|
|
{
|
|
if (((DotVarExp *)newval)->e1->op == TOKstructliteral)
|
|
{
|
|
assert(((StructLiteralExp *)((DotVarExp *)newval)->e1)->ownedByCtfe);
|
|
return true;
|
|
}
|
|
}
|
|
if (newval->op == TOKindex)
|
|
{
|
|
IndexExp *ie = (IndexExp *)newval;
|
|
if (ie->e2->op == TOKint64)
|
|
{
|
|
if (ie->e1->op == TOKarrayliteral || ie->e1->op == TOKstring)
|
|
return true;
|
|
}
|
|
if (ie->e1->op == TOKassocarrayliteral)
|
|
return true;
|
|
// BUG: Happens ONLY in ref foreach. Should tighten this.
|
|
if (ie->e2->op == TOKvar)
|
|
return true;
|
|
}
|
|
if (newval->op == TOKfunction) return true; // function/delegate literal
|
|
if (newval->op == TOKdelegate) return true;
|
|
if (newval->op == TOKsymoff) // function pointer
|
|
{
|
|
if (((SymOffExp *)newval)->var->isFuncDeclaration())
|
|
return true;
|
|
}
|
|
#if IN_LLVM
|
|
if (newval->op == TOKaddress) { // function pointer
|
|
AddrExp *ae = (AddrExp *)newval;
|
|
if (ae->e1->op == TOKvar) {
|
|
if (((VarExp *)ae->e1)->var->isFuncDeclaration())
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (newval->op == TOKint64 || newval->op == TOKfloat64 ||
|
|
newval->op == TOKchar || newval->op == TOKcomplex80)
|
|
return true;
|
|
|
|
// References
|
|
|
|
if (newval->op == TOKstructliteral)
|
|
assert(((StructLiteralExp *)newval)->ownedByCtfe);
|
|
if (newval->op == TOKarrayliteral)
|
|
assert(((ArrayLiteralExp *)newval)->ownedByCtfe);
|
|
if (newval->op == TOKassocarrayliteral)
|
|
assert(((AssocArrayLiteralExp *)newval)->ownedByCtfe);
|
|
|
|
if ((newval->op ==TOKarrayliteral) || ( newval->op==TOKstructliteral) ||
|
|
(newval->op==TOKstring) || (newval->op == TOKassocarrayliteral) ||
|
|
(newval->op == TOKnull))
|
|
{ return true;
|
|
}
|
|
// Dynamic arrays passed by ref may be null. When this happens
|
|
// they may originate from an index or dotvar expression.
|
|
if (newval->type->ty == Tarray || newval->type->ty == Taarray)
|
|
if (newval->op == TOKdotvar || newval->op == TOKindex)
|
|
return true; // actually must be null
|
|
|
|
if (newval->op == TOKslice)
|
|
{
|
|
SliceExp *se = (SliceExp *)newval;
|
|
assert(se->lwr && se->lwr != EXP_CANT_INTERPRET && se->lwr->op == TOKint64);
|
|
assert(se->upr && se->upr != EXP_CANT_INTERPRET && se->upr->op == TOKint64);
|
|
assert(se->e1->op == TOKarrayliteral || se->e1->op == TOKstring);
|
|
return true;
|
|
}
|
|
if (newval->op == TOKvoid)
|
|
{
|
|
return true;
|
|
}
|
|
newval->error("CTFE internal error: illegal value %s", newval->toChars());
|
|
return false;
|
|
}
|
|
|
|
// 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 && v->getValue())
|
|
showCtfeExpr(v->getValue(), level + 1);
|
|
}
|
|
else if (isPointer(e->type))
|
|
{
|
|
// This is potentially recursive. We mustn't try to print the thing we're pointing to.
|
|
if (e->op == TOKindex)
|
|
printf("POINTER %p into %p [%s]\n", e, ((IndexExp *)e)->e1, ((IndexExp *)e)->e2->toChars());
|
|
else if (e->op == TOKdotvar)
|
|
printf("POINTER %p to %p .%s\n", e, ((DotVarExp *)e)->e1, ((DotVarExp *)e)->var->toChars());
|
|
else
|
|
printf("POINTER %p: %s\n", e, e->toChars());
|
|
}
|
|
else
|
|
printf("VALUE %p: %s\n", e, e->toChars());
|
|
|
|
if (elements)
|
|
{
|
|
size_t fieldsSoFar = 0;
|
|
for (size_t i = 0; i < elements->dim; i++)
|
|
{ Expression *z = NULL;
|
|
Dsymbol *s = NULL;
|
|
if (i > 15) {
|
|
int nelements = elements->dim;
|
|
printf("...(total %d elements)\n", nelements);
|
|
return;
|
|
}
|
|
if (sd)
|
|
{ s = sd->fields[i];
|
|
z = (*elements)[i];
|
|
}
|
|
else if (cd)
|
|
{ while (i - fieldsSoFar >= cd->fields.dim)
|
|
{ fieldsSoFar += cd->fields.dim;
|
|
cd = cd->baseClass;
|
|
for (int j = level; j>0; --j) printf(" ");
|
|
printf(" BASE CLASS: %s\n", cd->toChars());
|
|
}
|
|
s = cd->fields[i - fieldsSoFar];
|
|
assert((elements->dim + i) >= (fieldsSoFar + cd->fields.dim));
|
|
size_t indx = (elements->dim - fieldsSoFar)- cd->fields.dim + i;
|
|
assert(indx < elements->dim);
|
|
z = (*elements)[indx];
|
|
}
|
|
if (!z) {
|
|
for (int j = level; j>0; --j) printf(" ");
|
|
printf(" void\n");
|
|
continue;
|
|
}
|
|
|
|
if (s)
|
|
{
|
|
VarDeclaration *v = s->isVarDeclaration();
|
|
assert(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 ***************************/
|
|
|
|
Expression *Type::voidInitLiteral(VarDeclaration *var)
|
|
{
|
|
return new VoidInitExp(var, this);
|
|
}
|
|
|
|
Expression *TypeSArray::voidInitLiteral(VarDeclaration *var)
|
|
{
|
|
Expression *elem = next->voidInitLiteral(var);
|
|
|
|
// 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 = dim->toInteger();
|
|
elements->setDim(d);
|
|
for (size_t i = 0; i < d; i++)
|
|
{ if (mustCopy && i > 0)
|
|
elem = copyLiteral(elem);
|
|
(*elements)[i] = elem;
|
|
}
|
|
ArrayLiteralExp *ae = new ArrayLiteralExp(var->loc, elements);
|
|
ae->type = this;
|
|
ae->ownedByCtfe = true;
|
|
return ae;
|
|
}
|
|
|
|
Expression *TypeStruct::voidInitLiteral(VarDeclaration *var)
|
|
{
|
|
Expressions *exps = new Expressions();
|
|
exps->setDim(sym->fields.dim);
|
|
for (size_t i = 0; i < sym->fields.dim; i++)
|
|
{
|
|
(*exps)[i] = sym->fields[i]->type->voidInitLiteral(var);
|
|
}
|
|
StructLiteralExp *se = new StructLiteralExp(var->loc, sym, exps);
|
|
se->type = this;
|
|
se->ownedByCtfe = true;
|
|
return se;
|
|
}
|