From http://beta.forum.dlang.org/post/[email protected]
class SomeRangeType
{
int a;
this(T)(T...) { }
~this() { a = -1; }
@property int front() { assert(a>=0); return a; }
void popFront() { ++a; }
@property bool empty() { return a < 10; }
}
void main(string[] args)
{
import std.typecons;
foreach(e; scoped!SomeRangeType(args)) {}
}
$ dmd -g -debug -run ~/testforeach.d
--- killed by signal 11
Comment #1 by schuetzm — 2015-06-05T09:13:26Z
I can confirm this for DMD git master.
Comment #2 by schuetzm — 2015-06-05T14:33:29Z
@Lionello: Why ICE?
Comment #3 by lio+bugzilla — 2015-06-05T23:40:33Z
(In reply to Marc Schütz from comment #2)
> @Lionello: Why ICE?
My bad.
Comment #4 by ketmar — 2015-06-17T13:10:20Z
a simplified sample:
===================
import std.stdio : writeln;
struct SomeRangeType {
int a;
alias front = a;
void popFront () { ++a; }
@property bool empty () { return (a >= 2); }
}
auto wrapit () {
static struct Wrapper {
SomeRangeType rng;
alias rng this;
@disable this (this);
~this () { writeln("wrapper dtor"); }
}
return Wrapper(SomeRangeType());
}
void main () {
writeln("before foreach");
foreach (auto e; wrapit()) {
writeln("in foreach; e=", e);
}
writeln("after foreach");
}
===================
this prints:
---
before foreach
wrapper dtor
in foreach; e=0
in foreach; e=1
after foreach
--
what i expected it to print:
---
before foreach
in foreach; e=0
in foreach; e=1
wrapper dtor
after foreach
---
Comment #5 by ketmar — 2015-06-17T13:20:47Z
p.s. seems that what triggers the bug is `alias this` in wrapper. removing `alias this` and rewriting wrapper to do simple redirections emits the correct output.
i.e. changing `Wrapper` struct to the following one makes the bug go away:
static struct Wrapper {
SomeRangeType rng;
//alias rng this;
@property int front () { return rng.front; }
void popFront () { rng.popFront; }
@property bool empty () { return rng.empty; }
@disable this (this);
~this () { writeln("wrapper dtor"); }
}
Comment #6 by ketmar — 2015-06-17T13:26:38Z
generated initializer for `alias this` variant:
init: __r87 = (Wrapper __tmpfordtor86 = wrapit(); , __tmpfordtor86).rng;
generated initializer for proxy variant:
init: __r86 = wrapit();
clearly, compiler tries to pull `rng` away of wrapper struct with `alias this` redirection, and then destroying created wrapper. this may work for other cases, but completely wrong for `scoped`.
seems that `foreach` conversion code shouldn't try to pull off aliased entity.
Comment #7 by ketmar — 2015-06-17T13:58:39Z
that's `inferAggregate()` which pulls out aliased type from wrapper, rewriting it from `wrapit()` to `wrapit().rng`, and semantic then rewrites it to `(Wrapper __tmpfordtor3 = wrapit(); , __tmpfordtor3).rng`. it does this:
if (ad->aliasthis)
{
if (!att && tab->checkAliasThisRec())
att = tab;
fes->aggr = new DotIdExp(fes->aggr->loc, fes->aggr, ad->aliasthis->ident);
continue;
}
i.e. literally pulling away aliased struct with `DotIdExp` (and dtoring result of `wrapit()` by the way, as it thinks that it's not required anymore.
seems that `inferAggregate()` must postpone dtor calling for such cases.