www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - foreach: rvalue aggregate expression not finalized!

reply Martin Kinkelin <noone spam.com> writes:
Hi again,

I just came across something odd - if the aggregate expression in a
foreach statement constructs a new struct (returning an rvalue), it
isn't finalized (well, to be precise, its implicit copy isn't).

Test:
----------
import std.stdio;

struct A
{
    int[3] _data;
    string _name;

    this(string name) { writeln("A.__ctor() for ", name); _name = name; }
    ~this() { writeln("A.__dtor() for ", _name); }
    this(this) { _name ~= "2"; writeln("Postblit constructor for ", _name); }

    A dup()
    {
        A r = A(_name ~ ".dup");
        r._data[] = _data[];
        return r;
    }

    int opApply(int delegate(ref int) dg)
    {
        int r = 0;
        for (int i = 0; i < _data.length; i++)
        {
            r = dg(_data[i]);
            if (r)
                break;
        }
        return r;
    }
}

unittest
{
    A a = A("a");
    a._data = [ 1, 2, 3 ];

    writeln("Iterating through a:");
    foreach (ref e; a)
        writeln(e);

    writeln("\nIterating through a.dup:");
    {
        foreach (ref e; a.dup)
            writeln(e);
        writeln("ending inner scope");
    }
    writeln("inner scope ended");
}
----------

Output:
----------
A.__ctor() for a
Iterating through a:
1
2
3

Iterating through a.dup:
A.__ctor() for a.dup
Postblit constructor for a.dup2
A.__dtor() for a.dup
1
2
3
ending inner scope
inner scope ended
A.__dtor() for a
----------

The problem is remedied by assigning a.dup manually to an lvalue and iterating
over that:
----------
...
        A dup = a.dup;
        foreach (ref e; dup)
...
----------

Output:
----------
A.__ctor() for a
Iterating through a:
1
2
3

Iterating through a.dup:
A.__ctor() for a.dup
Postblit constructor for a.dup2
A.__dtor() for a.dup
1
2
3
ending inner scope
A.__dtor() for a.dup2
inner scope ended
A.__dtor() for a
----------

I just figured this out and think it should be considered as bug as it is far
from obvious, at least from my point of view.
Feb 20 2011
parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 20.02.2011 23:34, Martin Kinkelin wrote:
 Hi again,

 I just came across something odd - if the aggregate expression in a
 foreach statement constructs a new struct (returning an rvalue), it
 isn't finalized (well, to be precise, its implicit copy isn't).

 Test:
 ----------
 import std.stdio;

 struct A
 {
      int[3] _data;
      string _name;

      this(string name) { writeln("A.__ctor() for ", name); _name = name; }
      ~this() { writeln("A.__dtor() for ", _name); }
      this(this) { _name ~= "2"; writeln("Postblit constructor for ", _name); }

      A dup()
      {
          A r = A(_name ~ ".dup");
          r._data[] = _data[];
          return r;
      }

      int opApply(int delegate(ref int) dg)
      {
          int r = 0;
          for (int i = 0; i<  _data.length; i++)
          {
              r = dg(_data[i]);
              if (r)
                  break;
          }
          return r;
      }
 }

 unittest
 {
      A a = A("a");
      a._data = [ 1, 2, 3 ];

      writeln("Iterating through a:");
      foreach (ref e; a)
          writeln(e);

      writeln("\nIterating through a.dup:");
      {
          foreach (ref e; a.dup)
              writeln(e);
          writeln("ending inner scope");
      }
      writeln("inner scope ended");
 }
 ----------

 Output:
 ----------
 A.__ctor() for a
 Iterating through a:
 1
 2
 3

 Iterating through a.dup:
 A.__ctor() for a.dup
 Postblit constructor for a.dup2
 A.__dtor() for a.dup
 1
 2
 3
 ending inner scope
 inner scope ended
 A.__dtor() for a
 ----------

 The problem is remedied by assigning a.dup manually to an lvalue and iterating
 over that:
 ----------
 ...
          A dup = a.dup;
          foreach (ref e; dup)
 ...
 ----------

 Output:
 ----------
 A.__ctor() for a
 Iterating through a:
 1
 2
 3

 Iterating through a.dup:
 A.__ctor() for a.dup
 Postblit constructor for a.dup2
 A.__dtor() for a.dup
 1
 2
 3
 ending inner scope
 A.__dtor() for a.dup2
 inner scope ended
 A.__dtor() for a
 ----------

 I just figured this out and think it should be considered as bug as it is far
 from obvious, at least from my point of view.
It looks like the manifestation of http://d.puremagic.com/issues/show_bug.cgi?id=3516 vote up ! ;) -- Dmitry Olshansky
Feb 20 2011