Skip to content

Commit

Permalink
Merge pull request #4100 from 9rnsr/fix13661
Browse files Browse the repository at this point in the history
Issue 13661 - static array init does not call destructors
  • Loading branch information
WalterBright committed Nov 23, 2014
2 parents 81d5a43 + 36ef681 commit 4aeb771
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 30 deletions.
2 changes: 2 additions & 0 deletions src/backend/rtlsym.h
Expand Up @@ -115,6 +115,8 @@ SYMBOL_MARS(ARRAYSETLENGTHT,FLfunc,FREGSAVED,"_d_arraysetlengthT", 0, t) \
SYMBOL_MARS(ARRAYSETLENGTHIT,FLfunc,FREGSAVED,"_d_arraysetlengthiT", 0, t) \
SYMBOL_MARS(ARRAYCOPY, FLfunc,FREGSAVED,"_d_arraycopy", 0, t) \
SYMBOL_MARS(ARRAYASSIGN, FLfunc,FREGSAVED,"_d_arrayassign", 0, t) \
SYMBOL_MARS(ARRAYASSIGN_R, FLfunc,FREGSAVED,"_d_arrayassign_r", 0, t) \
SYMBOL_MARS(ARRAYASSIGN_L, FLfunc,FREGSAVED,"_d_arrayassign_l", 0, t) \
SYMBOL_MARS(ARRAYCTOR, FLfunc,FREGSAVED,"_d_arrayctor", 0, t) \
SYMBOL_MARS(ARRAYSETASSIGN, FLfunc,FREGSAVED,"_d_arraysetassign", 0, t) \
SYMBOL_MARS(ARRAYSETCTOR, FLfunc,FREGSAVED,"_d_arraysetctor", 0, t) \
Expand Down
54 changes: 37 additions & 17 deletions src/e2ir.c
Expand Up @@ -2800,46 +2800,66 @@ elem *toElem(Expression *e, IRState *irs)
goto Lret;
}

/* Determine if we need to do postblit
/* Bugzilla 13661: Even if the elements in rhs are all rvalues and
* don't have to call postblits, this assignment should call
* destructors on old assigned elements.
*/
if (postblit &&
!(ae->e2->op == TOKslice && ((UnaExp *)ae->e2)->e1->isLvalue() ||
ae->e2->op == TOKcast && ((UnaExp *)ae->e2)->e1->isLvalue() ||
ae->e2->op != TOKslice && ae->e2->isLvalue()))
bool lvalueElem = false;
if (ae->e2->op == TOKslice && ((UnaExp *)ae->e2)->e1->isLvalue() ||
ae->e2->op == TOKcast && ((UnaExp *)ae->e2)->e1->isLvalue() ||
ae->e2->op != TOKslice && ae->e2->isLvalue())
{
postblit = false;
lvalueElem = true;
}

elem *e2 = toElem(ae->e2, irs);

if (!postblit || ae->op == TOKblit || type_size(e1->ET) == 0)
if (!postblit || (!lvalueElem && ae->op == TOKconstruct) ||
ae->op == TOKblit || type_size(e1->ET) == 0)
{
e = el_bin(OPstreq, tym, e1, e2);
e->ET = Type_toCtype(ae->e1->type);
if (type_size(e->ET) == 0)
e->Eoper = OPcomma;
}
else if (ae->op == TOKconstruct)
{
e1 = sarray_toDarray(ae->e1->loc, ae->e1->type, NULL, e1);
e2 = sarray_toDarray(ae->e2->loc, ae->e2->type, NULL, e2);

/* Generate:
* _d_arrayctor(ti, e2, e1)
*/
Expression *ti = t1b->nextOf()->toBasetype()->getTypeInfo(NULL);
if (config.exe == EX_WIN64)
{
e1 = addressElem(e1, Type::tvoid->arrayOf());
e2 = addressElem(e2, Type::tvoid->arrayOf());
}
elem *ep = el_params(e1, e2, toElem(ti, irs), NULL);
e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYCTOR]), ep);
}
else
{
elem *eto = e1;
elem *efrom = e2;
e1 = sarray_toDarray(ae->e1->loc, ae->e1->type, NULL, e1);
e2 = sarray_toDarray(ae->e2->loc, ae->e2->type, NULL, e2);

eto = sarray_toDarray(ae->e1->loc, ae->e1->type, NULL, eto);
efrom = sarray_toDarray(ae->e2->loc, ae->e2->type, NULL, efrom);
symbol *stmp = symbol_genauto(Type_toCtype(t1b->nextOf()));
elem *etmp = el_una(OPaddr, TYnptr, el_var(stmp));

/* Generate:
* _d_arrayassign(ti, efrom, eto)
* _d_arrayassign_r(ti, e2, e1, etmp)
* or:
* _d_arrayctor(ti, efrom, eto)
* _d_arrayassign_r(ti, e2, e1, etmp)
*/
Expression *ti = t1b->nextOf()->toBasetype()->getTypeInfo(NULL);
if (config.exe == EX_WIN64)
{
eto = addressElem(eto, Type::tvoid->arrayOf());
efrom = addressElem(efrom, Type::tvoid->arrayOf());
e1 = addressElem(e1, Type::tvoid->arrayOf());
e2 = addressElem(e2, Type::tvoid->arrayOf());
}
elem *ep = el_params(eto, efrom, toElem(ti, irs), NULL);
int rtl = (ae->op == TOKconstruct) ? RTLSYM_ARRAYCTOR : RTLSYM_ARRAYASSIGN;
elem *ep = el_params(etmp, e1, e2, toElem(ti, irs), NULL);
int rtl = lvalueElem ? RTLSYM_ARRAYASSIGN_L : RTLSYM_ARRAYASSIGN_R;
e = el_bin(OPcall, TYdarray, el_var(rtlsym[rtl]), ep);
}
}
Expand Down
118 changes: 105 additions & 13 deletions src/interpret.c
Expand Up @@ -263,6 +263,7 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc,
FuncDeclaration *fd, Expressions *arguments, Expression *pthis);
Expression *evaluatePostblits(InterState *istate, ArrayLiteralExp *ale, size_t lwr, size_t upr);
Expression *evaluatePostblit(InterState *istate, Expression *e);
Expression *evaluateDtor(InterState *istate, Expression *e);
Expression *scrubReturnValue(Loc loc, Expression *e);


Expand Down Expand Up @@ -4011,12 +4012,13 @@ class Interpreter : public Visitor
{
VarExp *ve = (VarExp *)e1;
VarDeclaration *v = ve->var->isVarDeclaration();
Type *t1b = e1->type->toBasetype();
if (wantRef)
{
setValueNull(v);
setValue(v, newval);
}
else if (e1->type->toBasetype()->ty == Tstruct)
else if (t1b->ty == Tstruct)
{
// In-place modification
if (newval->op != TOKstructliteral)
Expand All @@ -4031,35 +4033,72 @@ class Interpreter : public Visitor
else
setValue(v, newval);
}
else
else if (t1b->ty == Tsarray)
{
TY tyE1 = e1->type->toBasetype()->ty;
if (tyE1 == Tsarray && newval->op == TOKslice)
if (newval->op == TOKslice)
{
// Newly set value is non-ref static array,
// so making new ArrayLiteralExp is legitimate.
newval = resolveSlice(newval);
assert(newval->op == TOKarrayliteral);
((ArrayLiteralExp *)newval)->ownedByCtfe = true;
}
if (tyE1 == Tarray || tyE1 == Taarray)
if (e->op == TOKassign)
{
// arr op= arr
setValue(v, newval);
Expression *oldval = getValue(v);
assert(oldval->op == TOKarrayliteral);
assert(newval->op == TOKarrayliteral);

Expressions *oldelems = ((ArrayLiteralExp *)oldval)->elements;
Expressions *newelems = ((ArrayLiteralExp *)newval)->elements;
assert(oldelems->dim == newelems->dim);

Type *elemtype = oldval->type->nextOf();
for (size_t j = 0; j < newelems->dim; j++)
{
Expression *newelem = paintTypeOntoLiteral(elemtype, (*newelems)[j]);
// Bugzilla 9245
if (Expression *x = evaluatePostblit(istate, newelem))
{
result = x;
return;
}
// Bugzilla 13661
if (Expression *x = evaluateDtor(istate, (*oldelems)[j]))
{
result = x;
return;
}
(*oldelems)[j] = newelem;
}
}
else
{
setValue(v, newval);
if (e->op != TOKblit && tyE1 == Tsarray && e->e2->isLvalue())

if (e->op == TOKconstruct && e->e2->isLvalue())
{
assert(newval->op == TOKarrayliteral);
ArrayLiteralExp *ale = (ArrayLiteralExp *)newval;
if (Expression *x = evaluatePostblits(istate, ale, 0, ale->elements->dim))
// Bugzilla 9245
if (Expression *x = evaluatePostblit(istate, newval))
{
result = x;
return;
}
}
}
return;
}
else
{
if (t1b->ty == Tarray || t1b->ty == Taarray)
{
// arr op= arr
setValue(v, newval);
}
else
{
setValue(v, newval);
}
}
}
else if (e1->op == TOKstructliteral && newval->op == TOKstructliteral)
Expand Down Expand Up @@ -5165,9 +5204,35 @@ class Interpreter : public Visitor

if (ecall->op == TOKdotvar)
{
DotVarExp *dve = (DotVarExp *)e->e1;

// Calling a member function
pthis = ((DotVarExp *)e->e1)->e1;
fd = ((DotVarExp *)e->e1)->var->isFuncDeclaration();
pthis = dve->e1;
fd = dve->var->isFuncDeclaration();

// Special handling for: typeid(T[n]).destroy(cast(void*)&v)
TypeInfoDeclaration *tid;
if (pthis->op == TOKsymoff &&
(tid = ((SymOffExp *)pthis)->var->isTypeInfoDeclaration()) != NULL &&
tid->tinfo->toBasetype()->ty == Tsarray &&
fd->ident == Id::destroy &&
e->arguments->dim == 1 &&
(*e->arguments)[0]->op == TOKsymoff)
{
Type *tb = tid->tinfo->baseElemOf();
if (tb->ty == Tstruct && ((TypeStruct *)tb)->sym->dtor)
{
Declaration *v = ((SymOffExp *)(*e->arguments)[0])->var;
Expression *arg = getVarExp(e->loc, istate, v, ctfeNeedRvalue);

result = evaluateDtor(istate, arg);
if (result)
return;
result = CTFEExp::voidexp;
return;

}
}
}
else if (ecall->op == TOKvar)
{
Expand Down Expand Up @@ -7265,6 +7330,33 @@ Expression *evaluatePostblit(InterState *istate, Expression *e)
return NULL;
}

Expression *evaluateDtor(InterState *istate, Expression *e)
{
Type *tb = e->type->baseElemOf();
if (tb->ty != Tstruct)
return NULL;
StructDeclaration *sd = ((TypeStruct *)tb)->sym;
if (!sd->dtor)
return NULL;

if (e->op == TOKarrayliteral)
{
ArrayLiteralExp *alex = (ArrayLiteralExp *)e;
for (size_t i = 0; i < alex->elements->dim; i++)
e = evaluateDtor(istate, (*alex->elements)[i]);
}
else if (e->op == TOKstructliteral)
{
// e.__dtor()
e = interpret(sd->dtor, istate, NULL, e);
}
else
assert(0);
if (exceptionOrCantInterpret(e))
return e;
return NULL;
}

/*************************** CTFE Sanity Checks ***************************/

/* Setter functions for CTFE variable values.
Expand Down
25 changes: 25 additions & 0 deletions test/compilable/interpret3.d
Expand Up @@ -6708,3 +6708,28 @@ struct S13630(T)
}

enum s13630 = S13630!float(1);

/**************************************************
13669 - dtor call on static array variable
**************************************************/

bool test13669()
{
string dtor;

struct S
{
char x = 'x';
~this() { dtor ~= x; }
}

{ S[2] a; }
assert(dtor == "xx");
dtor = "";

{ S[2] a = [S('a'), S('b')]; }
assert(dtor == "ab");

return true;
}
static assert(test13669());
47 changes: 47 additions & 0 deletions test/runnable/sdtor.d
Expand Up @@ -2300,6 +2300,7 @@ void g8335(lazy S8335[3] arr)
{
assert(S8335.postblit == 0);
auto x = arr;
assert(S8335.postblit == 3);
}

void h8335(lazy S8335 s)
Expand Down Expand Up @@ -3448,6 +3449,51 @@ void test13586()
}
}

/**********************************/
// 13661

bool test13661()
{
string postblit;
string dtor;

struct S
{
char x = 'x';

this(this) { postblit ~= x; }
~this() { dtor ~= x; }

ref auto opAssign(T)(T arg)
{
assert(0);
return this;
}
}

{
S[2] a;

a[0].x = 'a';
a[1].x = 'b';

a = a.init;
assert(dtor == "ab");
assert(a[0].x == 'x' && a[1].x == 'x');

a[0].x = 'c';
a[1].x = 'd';

a = [S(), S()]; // equivalent a = a.init
assert(dtor == "abcd");
assert(a[0].x == 'x' && a[1].x == 'x');
}
assert(dtor == "abcdxx");

return true;
}
static assert(test13661()); // CTFE

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

__gshared bool b13095 = false;
Expand Down Expand Up @@ -3576,6 +3622,7 @@ int main()
test13303();
test13673();
test13586();
test13661();
test13095();

printf("Success\n");
Expand Down

0 comments on commit 4aeb771

Please sign in to comment.