www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Destructors and static array assignment

reply "David Nadlinger" <code klickverbot.at> writes:
Hi all,

I was about to fix an issue with postblits/destructors in LDC 
when I stumbled upon the following vexing behaviour in DMD (both 
2.067.1 and master):

---
uint dtorCount;

struct S {
     uint x;
     void opAssign(const ref S rhs) { assert(false, "Not called"); 
}
     ~this() { ++dtorCount; }
}


void main() {
     S[2] a;
     a[0].x = 42;

     a = a.init;

     assert(a[0].x == 0); // as expected the value has been reset
     assert(dtorCount == 0); // Passes?!?
}
---

I would have expected this to either call opAssign or to destruct 
the instance, blit the init value, and call any postblits. 
However, as you can see neither the dtor nor opAssign are 
executed. If I add a postblit to S, then suddenly the dtor is 
invoked too.

Am I missing something here, or is this a major bug in struct 
lifetime handling?

I understand why this happens from the DMD source, but it almost 
seems like it was deliberately written this way; thus the 
question.

  — David
Jul 20 2015
next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, 20 July 2015 at 14:18:33 UTC, David Nadlinger wrote:
 Am I missing something here, or is this a major bug in struct 
 lifetime handling?

 I understand why this happens from the DMD source, but it 
 almost seems like it was deliberately written this way; thus 
 the question.
Purposeful or not, I don't see how it could be anything other than a bug. It fundamentally breaks your ability to control what's going on with the construction or destruction of an object. And a quick check seems to indicate that out parameters have the same problem. If I add this function void foo(out S s) { } and then this to the end of main { S s; foo(s); } it fails to hit the assertion. The dtorCount in 1, which I think is correct, since the out param is a reference to s and thus shouldn't destroy it, but it _should_ assign to it, and it looks like it's just bitblitting S.init rather than assigning it S.init. - Jonathan M Davis
Jul 20 2015
parent reply "Benjamin Thaut" <code benjamin-thaut.de> writes:
On Monday, 20 July 2015 at 14:51:57 UTC, Jonathan M Davis wrote:
 On Monday, 20 July 2015 at 14:18:33 UTC, David Nadlinger wrote:
 Am I missing something here, or is this a major bug in struct 
 lifetime handling?

 I understand why this happens from the DMD source, but it 
 almost seems like it was deliberately written this way; thus 
 the question.
Purposeful or not, I don't see how it could be anything other than a bug. It fundamentally breaks your ability to control what's going on with the construction or destruction of an object. And a quick check seems to indicate that out parameters have the same problem. If I add this function void foo(out S s) { } and then this to the end of main { S s; foo(s); } it fails to hit the assertion. The dtorCount in 1, which I think is correct, since the out param is a reference to s and thus shouldn't destroy it, but it _should_ assign to it, and it looks like it's just bitblitting S.init rather than assigning it S.init. - Jonathan M Davis
This bug with "out" and structs is very old: https://issues.dlang.org/show_bug.cgi?id=6186 It even has 26 votes, but it doesn't seem to be important enough to be fixed. Kind Regards Benjamin Thaut
Jul 20 2015
parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, 20 July 2015 at 14:56:45 UTC, Benjamin Thaut wrote:
 This bug with "out" and structs is very old:

 https://issues.dlang.org/show_bug.cgi?id=6186

 It even has 26 votes, but it doesn't seem to be important 
 enough to be fixed.
That bug report certainly paints a more complicated and controversial picture on the issue than I would have expected. But I think that the issue is pretty much the same as what David's bringing up. Certainly, having S[2] a; a = S.init; and void foo(out S s) {} S s; foo(a); not be consistent seems like a bad idea. In both cases, we're dealing with setting the variable to its init value, and having that not be the same in all circumstances just seems like it's begging for trouble... The only way that I can see that they would reasonably be different would be if out were simply disallowing the variable to be anything other than its init value, which might make sense, but it wouldn't make sense when explicitly assigning the init value to a variable. So, making it so that out requires that the variable be its init value is really just sidestepping the issue with out while still leaving it elsewhere, so sidestepping it with out doesn't really save us from having to solve the problem. - Jonathan M Davis
Jul 20 2015
prev sibling next sibling parent Kenji Hara via Digitalmars-d <digitalmars-d puremagic.com> writes:
At least there's a dmd bug.

For such static array assignment, dmd should use druntime function, but
instead plain memory copy operation used.
https://github.com/D-Programming-Language/dmd/blob/master/src/e2ir.c#L2920
https://github.com/D-Programming-Language/dmd/blob/master/src/e2ir.c#L2945

And, even after the bug is fixed, I think it's debatable behavior whether
an element-wise assignment should call opAssign on each elements, because
it can be a kind of optimization.

Related issue: https://issues.dlang.org/show_bug.cgi?id=3D8931

Kenji Hara


2015-07-20 23:18 GMT+09:00 David Nadlinger via Digitalmars-d <
digitalmars-d puremagic.com>:

 Hi all,

 I was about to fix an issue with postblits/destructors in LDC when I
 stumbled upon the following vexing behaviour in DMD (both 2.067.1 and
 master):

 ---
 uint dtorCount;

 struct S {
     uint x;
     void opAssign(const ref S rhs) { assert(false, "Not called"); }
     ~this() { ++dtorCount; }
 }


 void main() {
     S[2] a;
     a[0].x =3D 42;

     a =3D a.init;

     assert(a[0].x =3D=3D 0); // as expected the value has been reset
     assert(dtorCount =3D=3D 0); // Passes?!?
 }
 ---

 I would have expected this to either call opAssign or to destruct the
 instance, blit the init value, and call any postblits. However, as you ca=
n
 see neither the dtor nor opAssign are executed. If I add a postblit to S,
 then suddenly the dtor is invoked too.

 Am I missing something here, or is this a major bug in struct lifetime
 handling?

 I understand why this happens from the DMD source, but it almost seems
 like it was deliberately written this way; thus the question.

  =E2=80=94 David
Jul 20 2015
prev sibling parent Kenji Hara via Digitalmars-d <digitalmars-d puremagic.com> writes:
2015-07-20 23:52 GMT+09:00 Kenji Hara <k.hara.pg gmail.com>:

 At least there's a dmd bug.

 For such static array assignment, dmd should use druntime function, but
 instead plain memory copy operation used.
 https://github.com/D-Programming-Language/dmd/blob/master/src/e2ir.c#L2920
 https://github.com/D-Programming-Language/dmd/blob/master/src/e2ir.c#L2945

 And, even after the bug is fixed, I think it's debatable behavior whether
 an element-wise assignment should call opAssign on each elements, because
 it can be a kind of optimization.
I filed a new wrong-code issue. https://issues.dlang.org/show_bug.cgi?id=14815 Kenji Hara
Jul 20 2015