Skip to content

Commit

Permalink
fix Issue 9023 - CTFE: cannot use ~= on an empty AA.
Browse files Browse the repository at this point in the history
  • Loading branch information
9rnsr committed Mar 25, 2015
1 parent 8ba4999 commit 76e74bd
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 123 deletions.
10 changes: 6 additions & 4 deletions src/ctfeexpr.c
Original file line number Diff line number Diff line change
Expand Up @@ -2012,7 +2012,7 @@ Expression *modifyStructField(Type *type, StructLiteralExp *se, size_t offset, E
return ee;
}

// Given an AA literal aae, set arr[index] = newval and return the new array.
// Given an AA literal aae, set aae[index] = newval and return newval.
Expression *assignAssocArrayElement(Loc loc, AssocArrayLiteralExp *aae,
Expression *index, Expression *newval)
{
Expand Down Expand Up @@ -2088,10 +2088,12 @@ UnionExp changeArrayLiteralLength(Loc loc, TypeArray *arrayType,
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];
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,
Expand Down
280 changes: 161 additions & 119 deletions src/interpret.c
Original file line number Diff line number Diff line change
Expand Up @@ -3368,12 +3368,139 @@ class Interpreter : public Visitor
// ---------------------------------------
// Interpret left hand side
// ---------------------------------------
AssocArrayLiteralExp *existingAA = NULL;
Expression *lastIndex = NULL;
Expression *oldval = NULL;
if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)
{
assert(((IndexExp *)e1)->modifiable);
// ---------------------------------------
// Deal with AA index assignment
// ---------------------------------------
/* This needs special treatment if the AA doesn't exist yet.
* There are two special cases:
* (1) If the AA is itself an index of another AA, we may need to create
* multiple nested AA literals before we can insert the new value.
* (2) If the ultimate AA is null, no insertion happens at all. Instead,
* we create nested AA literals, and change it into a assignment.
*/
IndexExp *ie = (IndexExp *)e1;
int depth = 0; // how many nested AA indices are there?
while (ie->e1->op == TOKindex &&
((IndexExp *)ie->e1)->e1->type->toBasetype()->ty == Taarray)
{
assert(ie->modifiable);
ie = (IndexExp *)ie->e1;
++depth;
}

// Get the AA value to be modified.
Expression *aggregate = interpret(ie->e1, istate);
if (exceptionOrCant(aggregate))
return;
if (aggregate->op == TOKassocarrayliteral)
{
existingAA = (AssocArrayLiteralExp *)aggregate;
if (existingAA->ownedByCtfe != 1)
{
e->error("cannot modify read-only constant %s", existingAA->toChars());
result = CTFEExp::cantexp;
return;
}

// Normal case, ultimate parent AA already exists
// We need to walk from the deepest index up, checking that an AA literal
// already exists on each level.
lastIndex = interpret(((IndexExp *)e1)->e2, istate);
lastIndex = resolveSlice(lastIndex); // only happens with AA assignment
if (exceptionOrCant(lastIndex))
return;

while (depth > 0)
{
// Walk the syntax tree to find the indexExp at this depth
IndexExp *xe = (IndexExp *)e1;
for (int d= 0; d < depth; ++d)
xe = (IndexExp *)xe->e1;

Expression *ekey = interpret(xe->e2, istate);
if (exceptionOrCant(ekey))
return;
ekey = resolveSlice(ekey); // only happens with AA assignment

// Look up this index in it up in the existing AA, to get the next level of AA.
AssocArrayLiteralExp *newAA = (AssocArrayLiteralExp *)findKeyInAA(e->loc, existingAA, ekey);
if (exceptionOrCant(newAA))
return;
if (!newAA)
{
// Doesn't exist yet, create an empty AA...
Expressions *keysx = new Expressions();
Expressions *valuesx = new Expressions();
newAA = new AssocArrayLiteralExp(e->loc, keysx, valuesx);
newAA->type = xe->type;
newAA->ownedByCtfe = 1;
//... and insert it into the existing AA.
existingAA->keys->push(ekey);
existingAA->values->push(newAA);
}
existingAA = newAA;
--depth;
}

if (fp)
oldval = findKeyInAA(e->loc, existingAA, lastIndex);
}
else
{
/* The AA is currently null. 'aggregate' is actually a reference to
* whatever contains it. It could be anything: var, dotvarexp, ...
* We rewrite the assignment from:
* aa[i][j] op= newval;
* into:
* aa = [i:[j:T.init]];
* aa[j] op= newval;
*/
oldval = copyLiteral(e->e1->type->defaultInitLiteral(e->loc)).copy();

Expression *newaae = oldval;
while (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)
{
Expression *ekey = interpret(((IndexExp *)e1)->e2, istate);
if (exceptionOrCant(ekey))
return;
ekey = resolveSlice(ekey); // only happens with AA assignment
Expressions *keysx = new Expressions();
Expressions *valuesx = new Expressions();
keysx->push(ekey);
valuesx->push(newaae);
AssocArrayLiteralExp *aae = new AssocArrayLiteralExp(e->loc, keysx, valuesx);
aae->type = ((IndexExp *)e1)->e1->type;
aae->ownedByCtfe = 1;
if (!existingAA)
{
existingAA = aae;
lastIndex = ekey;
}
newaae = aae;
e1 = ((IndexExp *)e1)->e1;
}

// We must set to aggregate with newaae
e1 = interpret(e1, istate, ctfeNeedLvalue);
if (exceptionOrCant(e1))
return;
e1 = assignToLvalue(e, e1, newaae);
if (exceptionOrCant(e1))
return;
}
assert(existingAA && lastIndex);
e1 = NULL; // stomp
}
else if (e1->op == TOKarraylength)
{
oldval = interpret(e1, istate);
if (exceptionOrCant(oldval))
return;
}
else if (e->op == TOKconstruct || e->op == TOKblit)
{
Expand Down Expand Up @@ -3404,6 +3531,22 @@ class Interpreter : public Visitor
e1 = interpret(e1, istate, ctfeNeedLvalue);
if (exceptionOrCant(e1))
return;

if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)
{
IndexExp *ie = (IndexExp *)e1;
assert(ie->e1->op == TOKassocarrayliteral);
existingAA = (AssocArrayLiteralExp *)ie->e1;
lastIndex = ie->e2;
}
}

// If it isn't a simple assignment, we need the existing value
if (fp && !oldval)
{
oldval = interpret(e1, istate);
if (exceptionOrCant(oldval))
return;
}

// ---------------------------------------
Expand Down Expand Up @@ -3435,16 +3578,9 @@ class Interpreter : public Visitor
// Also determine the return value (except for slice
// assignments, which are more complicated)
// ----------------------------------------------------
Expression *oldval = NULL;
if (fp || e1->op == TOKarraylength)
{
// If it isn't a simple assignment, we need the existing value
oldval = interpret(e1, istate);
if (exceptionOrCant(oldval))
return;
}
if (fp)
{
assert(oldval);
if (e->e1->type->ty != Tpointer)
{
// ~= can create new values (see bug 6052)
Expand Down Expand Up @@ -3489,118 +3625,17 @@ class Interpreter : public Visitor
}
}

// ---------------------------------------
// Deal with AA index assignment
// ---------------------------------------
/* This needs special treatment if the AA doesn't exist yet.
* There are two special cases:
* (1) If the AA is itself an index of another AA, we may need to create
* multiple nested AA literals before we can insert the new value.
* (2) If the ultimate AA is null, no insertion happens at all. Instead,
* we create nested AA literals, and change it into a assignment.
*/
if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)
if (existingAA)
{
IndexExp *ie = (IndexExp *)e1;
int depth = 0; // how many nested AA indices are there?
while (ie->e1->op == TOKindex && ((IndexExp *)ie->e1)->e1->type->toBasetype()->ty == Taarray)
{
ie = (IndexExp *)ie->e1;
++depth;
}

// Get the AA value to be modified.
Expression *aggregate = interpret(ie->e1, istate);
if (exceptionOrCant(aggregate))
return;
if (aggregate->op == TOKassocarrayliteral)
{
AssocArrayLiteralExp *existingAA = (AssocArrayLiteralExp *)aggregate;
if (existingAA->ownedByCtfe != 1)
{
e->error("cannot modify read-only constant %s", existingAA->toChars());
result = CTFEExp::cantexp;
return;
}

// Normal case, ultimate parent AA already exists
// We need to walk from the deepest index up, checking that an AA literal
// already exists on each level.
Expression *index = interpret(((IndexExp *)e1)->e2, istate);
if (exceptionOrCant(index))
return;
index = resolveSlice(index); // only happens with AA assignment
while (depth > 0)
{
// Walk the syntax tree to find the indexExp at this depth
IndexExp *xe = (IndexExp *)e1;
for (int d= 0; d < depth; ++d)
xe = (IndexExp *)xe->e1;

Expression *indx = interpret(xe->e2, istate);
if (exceptionOrCant(indx))
return;
indx = resolveSlice(indx); // only happens with AA assignment

// Look up this index in it up in the existing AA, to get the next level of AA.
AssocArrayLiteralExp *newAA = (AssocArrayLiteralExp *)findKeyInAA(e->loc, existingAA, indx);
if (exceptionOrCant(newAA))
return;
if (!newAA)
{
// Doesn't exist yet, create an empty AA...
Expressions *valuesx = new Expressions();
Expressions *keysx = new Expressions();
newAA = new AssocArrayLiteralExp(e->loc, keysx, valuesx);
newAA->type = xe->type;
newAA->ownedByCtfe = 1;
//... and insert it into the existing AA.
existingAA->keys->push(indx);
existingAA->values->push(newAA);
}
existingAA = newAA;
--depth;
}
result = assignAssocArrayElement(e->loc, existingAA, index, newval);
return;
}
else
{
/* The AA is currently null. 'aggregate' is actually a reference to
* whatever contains it. It could be anything: var, dotvarexp, ...
* We rewrite the assignment from: aggregate[i][j] = newval;
* into: aggregate = [i:[j: newval]];
*/

// Determine the return value
result = ctfeCast(e->loc, e->type, e->type, fp && post ? oldval : newval);
if (exceptionOrCant(result))
return;
//printf("\t+L%d existingAA = %s, lastIndex = %s, oldval = %s, newval = %s\n",
// __LINE__, existingAA->toChars(), lastIndex->toChars(), oldval ? oldval->toChars() : NULL, newval->toChars());
assignAssocArrayElement(e->loc, existingAA, lastIndex, newval);

while (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)
{
Expression *index = interpret(((IndexExp *)e1)->e2, istate);
if (exceptionOrCant(index))
return;
index = resolveSlice(index); // only happens with AA assignment
Expressions *valuesx = new Expressions();
Expressions *keysx = new Expressions();
valuesx->push(newval);
keysx->push(index);
AssocArrayLiteralExp *newaae = new AssocArrayLiteralExp(e->loc, keysx, valuesx);
newaae->ownedByCtfe = 1;
newaae->type = ((IndexExp *)e1)->e1->type;
newval = newaae;
e1 = ((IndexExp *)e1)->e1;
}

// We must return to the original aggregate, in case it was a reference
e1 = interpret(ie->e1, istate, ctfeNeedLvalue);
if (exceptionOrCant(e1))
return;
}
// Determine the return value
result = ctfeCast(e->loc, e->type, e->type, fp && post ? oldval : newval);
return;
}
else if (e1->op == TOKarraylength)
if (e1->op == TOKarraylength)
{
/* Change the assignment from:
* arr.length = n;
Expand Down Expand Up @@ -3636,8 +3671,15 @@ class Interpreter : public Visitor
oldval = interpret(e1, istate);
newval = changeArrayLiteralLength(e->loc, (TypeArray *)t, oldval,
oldlen, newlen).copy();

e1 = assignToLvalue(e, e1, newval);
if (exceptionOrCant(e1))
return;

return;
}
else if (!isBlockAssignment)

if (!isBlockAssignment)
{
newval = ctfeCast(e->loc, e->type, e->type, newval);
if (exceptionOrCant(newval))
Expand Down
26 changes: 26 additions & 0 deletions test/runnable/interpret.d
Original file line number Diff line number Diff line change
Expand Up @@ -3167,6 +3167,31 @@ struct Test110f { int f1; Test110s f2; }
struct Test110s { this(int, int, int){} }
auto test110 = [Test110f(1, Test110s(1, 2, 3))];

/************************************************/
// 9023

bool test9023()
{
string[][string] aas;
assert(aas.length == 0);
aas["a"] ~= "anything";
assert(aas.length == 1);
assert(aas["a"] == ["anything"]);
aas["a"] ~= "more";
assert(aas.length == 1);
assert(aas["a"] == ["anything", "more"]);

int[int] aan;
assert(aan.length == 0);
auto x = aan[0]++;
assert(x == 0);
assert(aan.length == 1);
assert(aan[0] == 1);

return true;
}
static assert(test9023());

/************************************************/

interface IBug9954
Expand Down Expand Up @@ -3416,6 +3441,7 @@ int main()
test6439();
test6504();
test8818();
test9023();
test9954();

printf("Success\n");
Expand Down

0 comments on commit 76e74bd

Please sign in to comment.