[Issue 2043] Closure outer variables in nested blocks are not allocated/instantiated correctly.

d-bugmail at puremagic.com d-bugmail at puremagic.com
Sat Jan 1 18:23:01 PST 2011


http://d.puremagic.com/issues/show_bug.cgi?id=2043


Witold Baryluk <baryluk at smp.if.uj.edu.pl> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |baryluk at smp.if.uj.edu.pl


--- Comment #6 from Witold Baryluk <baryluk at smp.if.uj.edu.pl> 2011-01-01 18:20:46 PST ---
I today hit this bug. It really isn't obvious how to workaround this problem.
It is not possible to easly just allocate variable or struct with variable's
value manually, as it still will just by single identifier, and it will at the
end still point to single entity.

Simplified case, which returns array of delegates each printing next integer.

  import std.stdio;
  alias void delegate() D;
  /* do not work, */
  auto f(int n) {
        auto tab = new D[n];
        for (int i = 0; i < n; i++) {
                // invariant ii = i; // will not help
                // struct S { int i_; }; S x = new S; x.i_ = i;
                // will also not help, as x is on stack
                tab[i] = delegate void() { writefln("i=%d", i); };
                // also using nested function, and taking address do not work
        }
        return tab;
  }

  void main() {
        auto tab = f(10);
        foreach (k, ref x; tab) {
                writef("%d : ", k);
                x();
        }
  }

It currently will print in all cases:

  0 : i=10
  1 : i=10
  2 : i=10
  3 : i=10
  4 : i=10
  5 : i=10
  6 : i=10
  7 : i=10
  8 : i=10
  9 : i=10


My current workaround involves looping using recursion (and be sure that
compiler do not make it tail-recursive probably):

/* recursive loop version will work */
auto f2(int n) {
    auto tab = new D[n];
    void f2r(int i) {
        tab[i] = delegate void() { writefln("i=%d", i); };
        if (i < n-1) {
            f2r(i+1); /* recursion */
        }
    }
    f2r(0); /* enter recursion */
    return tab;
}

Which will make our constructed delegates work as desired:

  0 : i=0
  1 : i=1
  2 : i=2
  3 : i=3
  4 : i=4
  5 : i=5
  6 : i=6
  7 : i=7
  8 : i=8
  9 : i=9

(I tested if this is not just a a coincidence, but overwriting stack by random
values, and it works still correctly. BTW. switch for compiler which will show
what local variables are/will automatically allocated on heap will be usefull).

As of definitive solution, i think "invariant" which is referenced by escaping
delegate should be allocated on heap, and its addresses should be remembered
immediately in constructed delegate. Other possibility is separate syntax, like
"new delegate void() ... ", which will make all variables referenced by
delegate a copies of current values of variables with the same names
(considering scoping rules).


Hope this will be helpful for somebody.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------


More information about the Digitalmars-d-bugs mailing list