Skip to content

Commit

Permalink
fix Issue 14653 - scoped!range in foreach crashes
Browse files Browse the repository at this point in the history
  • Loading branch information
9rnsr committed Jul 3, 2015
1 parent fd976fa commit 0fea843
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 46 deletions.
1 change: 1 addition & 0 deletions src/magicport.json
Expand Up @@ -2891,6 +2891,7 @@
"imports" :
[
"aggregate",
"aliasthis",
"arraytypes",
"core.stdc.stdio",
"core.stdc.string",
Expand Down
28 changes: 11 additions & 17 deletions src/opover.c
Expand Up @@ -1471,23 +1471,15 @@ bool inferAggregate(ForeachStatement *fes, Scope *sc, Dsymbol *&sapply)
int sliced = 0;
Type *tab;
Type *att = NULL;
Expression *org_aggr = fes->aggr;
Expression *aggr = fes->aggr;
AggregateDeclaration *ad;

while (1)
{
fes->aggr = fes->aggr->semantic(sc);
fes->aggr = resolveProperties(sc, fes->aggr);
fes->aggr = fes->aggr->optimize(WANTvalue);
if (!fes->aggr->type)
if (!aggr->type)
goto Lerr;

tab = fes->aggr->type->toBasetype();
if (att == tab)
{
fes->aggr = org_aggr;
goto Lerr;
}
tab = aggr->type->toBasetype();
switch (tab->ty)
{
case Tarray:
Expand Down Expand Up @@ -1516,11 +1508,11 @@ bool inferAggregate(ForeachStatement *fes, Scope *sc, Dsymbol *&sapply)
Dsymbol *s = search_function(ad, Id::slice);
if (s)
{
Expression *rinit = new SliceExp(fes->aggr->loc, fes->aggr, NULL, NULL);
Expression *rinit = new SliceExp(aggr->loc, aggr, NULL, NULL);
rinit = rinit->trySemantic(sc);
if (rinit) // if application of [] succeeded
{
fes->aggr = rinit;
aggr = rinit;
sliced = 1;
continue;
}
Expand All @@ -1535,18 +1527,19 @@ bool inferAggregate(ForeachStatement *fes, Scope *sc, Dsymbol *&sapply)

if (ad->aliasthis)
{
if (att == tab)
goto Lerr;
if (!att && tab->checkAliasThisRec())
att = tab;
fes->aggr = new DotIdExp(fes->aggr->loc, fes->aggr, ad->aliasthis->ident);
aggr = resolveAliasThis(sc, aggr);
continue;
}
goto Lerr;

case Tdelegate:
if (fes->aggr->op == TOKdelegate)
if (aggr->op == TOKdelegate)
{
DelegateExp *de = (DelegateExp *)fes->aggr;
sapply = de->func->isFuncDeclaration();
sapply = ((DelegateExp *)aggr)->func;
}
break;

Expand All @@ -1558,6 +1551,7 @@ bool inferAggregate(ForeachStatement *fes, Scope *sc, Dsymbol *&sapply)
}
break;
}
fes->aggr = aggr;
return true;

Lerr:
Expand Down
101 changes: 72 additions & 29 deletions src/statement.c
Expand Up @@ -1727,14 +1727,32 @@ Statement *ForeachStatement::semantic(Scope *sc)
if (func->fes)
func = func->fes->func;

VarDeclaration *vinit = NULL;
aggr = aggr->semantic(sc);
aggr = resolveProperties(sc, aggr);
aggr = aggr->optimize(WANTvalue);
if (aggr->op == TOKerror)
return new ErrorStatement();

Expression *oaggr = aggr;
if (aggr->type && aggr->type->toBasetype()->ty == Tstruct &&
aggr->op != TOKtype && !aggr->isLvalue())
{
// Bugzilla 14653: Extend the life of rvalue aggregate till the end of foreach.
vinit = new VarDeclaration(loc, aggr->type,
Identifier::generateId("__aggr"), new ExpInitializer(loc, aggr));
vinit->storage_class |= STCtemp;
vinit->semantic(sc);
aggr = new VarExp(aggr->loc, vinit);
}
if (!inferAggregate(this, sc, sapply))
{
const char *msg = "";
if (aggr->type && isAggregate(aggr->type))
{
msg = ", define opApply(), range primitives, or use .tupleof";
}
error("invalid foreach aggregate %s%s", aggr->toChars(), msg);
error("invalid foreach aggregate %s%s", oaggr->toChars(), msg);
return new ErrorStatement();
}

Expand Down Expand Up @@ -1954,8 +1972,9 @@ Statement *ForeachStatement::semantic(Scope *sc)
if (LabelStatement *ls = checkLabeledLoop(sc, this))
ls->gotoTarget = s;
if (te && te->e0)
s = new CompoundStatement(loc,
new ExpStatement(te->e0->loc, te->e0), s);
s = new CompoundStatement(loc, new ExpStatement(te->e0->loc, te->e0), s);
if (vinit)
s = new CompoundStatement(loc, new ExpStatement(loc, vinit), s);
s = s->semantic(sc);
return s;
}
Expand Down Expand Up @@ -2083,7 +2102,7 @@ Statement *ForeachStatement::semantic(Scope *sc)
* for (T[] tmp = a[], size_t key = tmp.length; key--; )
* { T value = tmp[k]; body }
*/
Identifier *id = Identifier::generateId("__aggr");
Identifier *id = Identifier::generateId("__r");
ExpInitializer *ie = new ExpInitializer(loc, new SliceExp(loc, aggr, NULL, NULL));
VarDeclaration *tmp;
if (aggr->op == TOKarrayliteral &&
Expand Down Expand Up @@ -2114,6 +2133,8 @@ Statement *ForeachStatement::semantic(Scope *sc)
key->init = new ExpInitializer(loc, new IntegerExp(loc, 0, key->type));

Statements *cs = new Statements();
if (vinit)
cs->push(new ExpStatement(loc, vinit));
cs->push(new ExpStatement(loc, tmp));
cs->push(new ExpStatement(loc, key));
Statement *forinit = new CompoundDeclarationStatement(loc, cs);
Expand Down Expand Up @@ -2199,7 +2220,7 @@ Statement *ForeachStatement::semantic(Scope *sc)
* .empty, .popFront, .popBack, .front and .back
* foreach (e; aggr) { ... }
* translates to:
* for (auto __r = aggr[]; !__r.empty; __r.popFront) {
* for (auto __r = aggr[]; !__r.empty; __r.popFront()) {
* auto e = __r.front;
* ...
* }
Expand All @@ -2225,17 +2246,29 @@ Statement *ForeachStatement::semantic(Scope *sc)

/* Generate a temporary __r and initialize it with the aggregate.
*/
Identifier *rid = Identifier::generateId("__r");
VarDeclaration *r = new VarDeclaration(loc, NULL, rid, new ExpInitializer(loc, aggr));
r->storage_class |= STCtemp;
Statement *init = new ExpStatement(loc, r);
VarDeclaration *r;
Statement *init;
if (vinit && aggr->op == TOKvar && ((VarExp *)aggr)->var == vinit)
{
r = vinit;
init = new ExpStatement(loc, vinit);
}
else
{
Identifier *rid = Identifier::generateId("__r");
r = new VarDeclaration(loc, NULL, rid, new ExpInitializer(loc, aggr));
r->storage_class |= STCtemp;
init = new ExpStatement(loc, r);
if (vinit)
init = new CompoundStatement(loc, new ExpStatement(loc, vinit), init);
}

// !__r.empty
Expression *e = new VarExp(loc, r);
e = new DotIdExp(loc, e, Id::Fempty);
Expression *condition = new NotExp(loc, e);

// __r.next
// __r.idpopFront()
e = new VarExp(loc, r);
Expression *increment = new CallExp(loc, new DotIdExp(loc, e, idpopFront));

Expand Down Expand Up @@ -2342,9 +2375,6 @@ Statement *ForeachStatement::semantic(Scope *sc)
deprecation("cannot use foreach_reverse with a delegate");
Lapply:
{
Expression *ec;
Expression *e;

if (checkForArgTypes())
{
body = body->semanticNoScope(sc);
Expand Down Expand Up @@ -2452,6 +2482,15 @@ Statement *ForeachStatement::semantic(Scope *sc)
}
}

Expression *e = NULL;
Expression *ec;
if (vinit)
{
e = new DeclarationExp(loc, vinit);
e = e->semantic(sc);
if (e->op == TOKerror)
goto Lerror2;
}
if (taa)
{
// Check types
Expand Down Expand Up @@ -2491,7 +2530,7 @@ Statement *ForeachStatement::semantic(Scope *sc)
unsigned char i = (dim == 2 ? 1 : 0);
if (!fdapply[i])
{
params = new Parameters;
params = new Parameters();
params->push(new Parameter(0, Type::tvoid->pointerTo(), NULL, NULL));
params->push(new Parameter(STCin, Type::tsize_t, NULL, NULL));
Parameters* dgparams = new Parameters;
Expand All @@ -2503,7 +2542,6 @@ Statement *ForeachStatement::semantic(Scope *sc)
fdapply[i] = FuncDeclaration::genCfunc(params, Type::tint32, name[i]);
}

ec = new VarExp(Loc(), fdapply[i]);
Expressions *exps = new Expressions();
exps->push(aggr);
size_t keysize = (size_t)taa->index->size();
Expand All @@ -2516,8 +2554,10 @@ Statement *ForeachStatement::semantic(Scope *sc)
}
exps->push(new IntegerExp(Loc(), keysize, Type::tsize_t));
exps->push(flde);
e = new CallExp(loc, ec, exps);
e->type = Type::tint32; // don't run semantic() on e

ec = new VarExp(Loc(), fdapply[i]);
ec = new CallExp(loc, ec, exps);
ec->type = Type::tint32; // don't run semantic() on ec
}
else if (tab->ty == Tarray || tab->ty == Tsarray)
{
Expand Down Expand Up @@ -2553,7 +2593,7 @@ Statement *ForeachStatement::semantic(Scope *sc)

FuncDeclaration *fdapply;
TypeDelegate *dgty;
params = new Parameters;
params = new Parameters();
params->push(new Parameter(STCin, tn->arrayOf(), NULL, NULL));
Parameters* dgparams = new Parameters;
dgparams->push(new Parameter(0, Type::tvoidptr, NULL, NULL));
Expand All @@ -2563,16 +2603,18 @@ Statement *ForeachStatement::semantic(Scope *sc)
params->push(new Parameter(0, dgty, NULL, NULL));
fdapply = FuncDeclaration::genCfunc(params, Type::tint32, fdname);

ec = new VarExp(Loc(), fdapply);
if (tab->ty == Tsarray)
aggr = aggr->castTo(sc, tn->arrayOf());

// paint delegate argument to the type runtime expects
if (!dgty->equals(flde->type)) {
flde = new CastExp(loc, flde, flde->type);
flde->type = dgty;
}
e = new CallExp(loc, ec, aggr, flde);
e->type = Type::tint32; // don't run semantic() on e

ec = new VarExp(Loc(), fdapply);
ec = new CallExp(loc, ec, aggr, flde);
ec->type = Type::tint32; // don't run semantic() on ec
}
else if (tab->ty == Tdelegate)
{
Expand All @@ -2585,11 +2627,11 @@ Statement *ForeachStatement::semantic(Scope *sc)
// See Bugzilla 3560
aggr = ((DelegateExp *)aggr)->e1;
}
e = new CallExp(loc, aggr, flde);
e = e->semantic(sc);
if (e->op == TOKerror)
ec = new CallExp(loc, aggr, flde);
ec = ec->semantic(sc);
if (ec->op == TOKerror)
goto Lerror2;
if (e->type != Type::tint32)
if (ec->type != Type::tint32)
{
error("opApply() function for %s must return an int", tab->toChars());
goto Lerror2;
Expand All @@ -2603,16 +2645,17 @@ Statement *ForeachStatement::semantic(Scope *sc)
* aggr.apply(flde)
*/
ec = new DotIdExp(loc, aggr, sapply->ident);
e = new CallExp(loc, ec, flde);
e = e->semantic(sc);
if (e->op == TOKerror)
ec = new CallExp(loc, ec, flde);
ec = ec->semantic(sc);
if (ec->op == TOKerror)
goto Lerror2;
if (e->type != Type::tint32)
if (ec->type != Type::tint32)
{
error("opApply() function for %s must return an int", tab->toChars());
goto Lerror2;
}
}
e = Expression::combine(e, ec);

if (!cases->dim)
{
Expand Down
52 changes: 52 additions & 0 deletions test/runnable/foreach5.d
Expand Up @@ -1031,6 +1031,57 @@ void test13756()
}
}

/***************************************/
// 14653

static string result14653;

class RangeClass14653
{
int a;

this(T)(T...) { result14653 ~= "c"; }
~this() { result14653 ~= "d"; a = -1; }

@property bool empty() { result14653 ~= "e"; return a >= 2; }
@property int front() { result14653 ~= "f"; assert(a >= 0); return a; }
void popFront() { result14653 ~= "p"; ++a; }
}

auto scoped14653(T, A...)(A args)
{
static struct Scoped(T)
{
void[__traits(classInstanceSize, T)] store;
T payload() { return cast(T)cast(void*)store.ptr; }
alias payload this;

~this()
{
//.destroy(payload);
payload.__dtor();
(cast(byte[])store)[] = 0;
}
}

Scoped!T result = void;

//emplace!T(result.store[], args);
result.store[] = typeid(T).init[];
result.payload.__ctor(args);

return result;
}

void test14653()
{
foreach (e; scoped14653!RangeClass14653(1))
{
result14653 ~= "b";
}
assert(result14653 == "cefbpefbped", result14653);
}

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

int main()
Expand Down Expand Up @@ -1061,6 +1112,7 @@ int main()
test12739();
test12932();
test13756();
test14653();

printf("Success\n");
return 0;
Expand Down

0 comments on commit 0fea843

Please sign in to comment.