Skip to content

Commit

Permalink
Merge pull request #65 from donc/ctfe5966
Browse files Browse the repository at this point in the history
Fix for bug 5972: CTFE: Can't assign to elements of arrays of slices
  • Loading branch information
WalterBright committed May 10, 2011
2 parents fb3c67b + 808105a commit 223c47f
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 55 deletions.
145 changes: 90 additions & 55 deletions src/interpret.c
Expand Up @@ -2594,7 +2594,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_
newval->type = sexp->type;
}
else
newval = this->e2->interpret(istate);
newval = this->e2->interpret(istate, ctfeNeedLvalue);
if (newval == EXP_CANT_INTERPRET)
return newval;

Expand Down Expand Up @@ -2806,31 +2806,18 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_
}

Expression *aggregate = resolveReferences(ie->e1, istate->localThis);
if (aggregate->op == TOKdotvar)
{
DotVarExp *dve = (DotVarExp *)aggregate;
aggregate = dve->e1->interpret(istate);
if (aggregate->op == TOKstructliteral)
{
StructLiteralExp *se = (StructLiteralExp *)aggregate;
VarDeclaration *v = dve->var->isVarDeclaration();
if (v)
{
int i = se->getFieldIndex(dve->type, v->offset);
aggregate = (Expression *)se->elements->data[i];
}
}
else
aggregate = dve; // unsupported, generate an error
}
if (aggregate->op == TOKindex)

/* The only possible indexable LValue aggregates are array literals,
* slices of array literals, and AA literals.
*/

if (aggregate->op == TOKindex || aggregate->op == TOKdotvar ||
aggregate->op == TOKslice)
{
// If it returns an array, it might be a slice.
// We want to preserve the slice.
if (aggregate->type->ty != Tarray)
aggregate = aggregate->interpret(istate);
aggregate = aggregate->interpret(istate, ctfeNeedLvalue);
if (aggregate == EXP_CANT_INTERPRET)
return EXP_CANT_INTERPRET;
}

if (aggregate->op == TOKvar)
{
VarExp *ve = (VarExp *)aggregate;
Expand Down Expand Up @@ -2957,30 +2944,17 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_
ArrayLiteralExp *existingAE = NULL;
StringExp *existingSE = NULL;

if (aggregate->op == TOKdotvar)
{
DotVarExp *dve = (DotVarExp *)aggregate;
aggregate = dve->e1->interpret(istate);
if (aggregate->op == TOKstructliteral)
{
StructLiteralExp *se = (StructLiteralExp *)aggregate;
VarDeclaration *v = dve->var->isVarDeclaration();
if (v)
{
int i = se->getFieldIndex(dve->type, v->offset);
aggregate = (Expression *)se->elements->data[i];
}
}
}
if (aggregate->op == TOKindex)
/* The only possible slicable LValue aggregates are array literals,
* and slices of array literals.
*/

if (aggregate->op == TOKindex || aggregate->op == TOKdotvar ||
aggregate->op == TOKslice)
{
IndexExp *ie = (IndexExp *)aggregate;
// If it returns an array, it might be a slice.
// We want to preserve the slice.
if (ie->type->ty != Tarray)
aggregate = aggregate->interpret(istate);
aggregate = aggregate->interpret(istate, ctfeNeedLvalue);
if (aggregate == EXP_CANT_INTERPRET)
return EXP_CANT_INTERPRET;
}

if (aggregate->op == TOKvar)
{
VarExp *ve = (VarExp *)(aggregate);
Expand Down Expand Up @@ -3561,7 +3535,7 @@ Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal)
#if LOG
printf("IndexExp::interpret() %s\n", toChars());
#endif
e1 = this->e1->interpret(istate);
e1 = this->e1->interpret(istate, goal);
if (e1 == EXP_CANT_INTERPRET)
goto Lcant;

Expand Down Expand Up @@ -3589,6 +3563,22 @@ Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal)
lengthVar->setValueNull(); // $ is defined only inside []
if (e2 == EXP_CANT_INTERPRET)
goto Lcant;
if (e1->op == TOKslice && e2->op == TOKint64)
{
// Simplify index of slice: agg[lwr..upr][indx] --> agg[indx']
uinteger_t indx = e2->toInteger();
uinteger_t ilo = ((SliceExp *)e1)->lwr->toInteger();
uinteger_t iup = ((SliceExp *)e1)->upr->toInteger();

if (indx > iup - ilo)
{
error("index %ju exceeds array length %ju", indx, iup - ilo);
goto Lcant;
}
indx += ilo;
e1 = ((SliceExp *)e1)->e1;
e2 = new IntegerExp(e2->loc, indx, e2->type);
}
e = Index(type, e1, e2);
if (e == EXP_CANT_INTERPRET)
error("%s cannot be interpreted at compile time", toChars());
Expand Down Expand Up @@ -3624,6 +3614,12 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal)
*/
if (e1->op == TOKnull)
e = new IntegerExp(0, 0, Type::tsize_t);
else if (e1->op == TOKslice)
{
// For lvalue slices, slice ends have already been calculated
e = new IntegerExp(0, ((SliceExp *)e1)->upr->toInteger()
- ((SliceExp *)e1)->lwr->toInteger(), Type::tsize_t);
}
else
e = ArrayLength(Type::tsize_t, e1);
if (e == EXP_CANT_INTERPRET)
Expand All @@ -3644,13 +3640,6 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal)
goto Lcant;
if (lengthVar)
lengthVar->setValueNull(); // $ is defined only inside [L..U]
if (goal == ctfeNeedLvalue)
{
assert(e1->op != TOKslice);
e = new SliceExp(loc, e1, lwr, upr);
e->type = type;
return e;
}
{
uinteger_t ilwr = lwr->toInteger();
uinteger_t iupr = upr->toInteger();
Expand All @@ -3661,6 +3650,33 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal)
e1->error("slice [%ju..%ju] is out of bounds", ilwr, iupr);
return EXP_CANT_INTERPRET;
}
if (goal == ctfeNeedLvalue)
{
if (e1->op == TOKslice)
{
SliceExp *se = (SliceExp *)e1;
// Simplify slice of slice:
// aggregate[lo1..up1][lwr..upr] ---> aggregate[lwr'..upr']
uinteger_t lo1 = se->lwr->toInteger();
uinteger_t up1 = se->upr->toInteger();
if (ilwr > iupr || iupr > up1 - lo1)
{
error("slice[%ju..%ju] exceeds array bounds[%ju..%ju]",
ilwr, iupr, lo1, up1);
goto Lcant;
}
ilwr += lo1;
iupr += lo1;
e = new SliceExp(loc, se->e1,
new IntegerExp(loc, ilwr, lwr->type),
new IntegerExp(loc, iupr, upr->type));
e->type = type;
return e;
}
e = new SliceExp(loc, e1, lwr, upr);
e->type = type;
return e;
}
e = Slice(type, e1, lwr, upr);
if (e == EXP_CANT_INTERPRET)
error("%s cannot be interpreted at compile time", toChars());
Expand Down Expand Up @@ -3854,7 +3870,26 @@ Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal)
{ StructLiteralExp *se = (StructLiteralExp *)ex;
VarDeclaration *v = var->isVarDeclaration();
if (v)
{ e = se->getField(type, v->offset);
{
if (goal == ctfeNeedLvalue)
{
// We can't use getField, because it makes a copy
int i = se->getFieldIndex(type, v->offset);
if (i == -1)
{
error("couldn't find field %s in %s", v->toChars(), type->toChars());
return EXP_CANT_INTERPRET;
}
e = (Expression *)se->elements->data[i];
// If it is an lvalue literal, return it...
if (e->op == TOKstructliteral || e->op == TOKarrayliteral ||
e->op == TOKassocarrayliteral || e->op == TOKstring ||
e->op == TOKslice)
return e;
// ...Otherwise, just return the (simplified) dotvar expression
return new DotVarExp(loc, ex, v);
}
e = se->getField(type, v->offset);
if (!e)
{
error("couldn't find field %s in %s", v->toChars(), type->toChars());
Expand Down
52 changes: 52 additions & 0 deletions test/compilable/interpret3.d
Expand Up @@ -741,3 +741,55 @@ public:
invariant() { }
}
const testTODsThrownZ = TimeOfDayZ(0);

/*******************************************
Bug 5972
*******************************************/

int bug5972()
{
char [] z = "abc".dup;
char[] [] a = [null, null];
a[0] = z[0..2];
char[] b = a[0];
assert(b == "ab");
a[0][1] = 'q';
assert( a[0] == "aq");
assert(b == "aq");
assert(b[1]=='q');
a[0][0..$-1][0..$] = a[0][0..$-1][0..$];
return 56;
}
static assert(bug5972()==56);

/*******************************************
2.053beta [CTFE]ICE 'global errors'
*******************************************/

int wconcat(wstring replace)
{
wstring value;
value = "A"w;
value = value ~ replace;
return 1;
}
static assert(wconcat("X"w));

/*******************************************
Bug 4001: A Space Oddity
*******************************************/

int space() { return 4001; }

void oddity4001(int q)
{
const int bowie = space();
static assert(space() == 4001);
static assert(bowie == 4001);
}

/*******************************************
Bug 3779
*******************************************/

static const bug3779 = ["123"][0][$-1];

0 comments on commit 223c47f

Please sign in to comment.