You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I feel like there has to be a more reduced example of this, but I've failed thus far. In any case, this code (which has the extra strings so that it can print out what's happening even when the GC calls the destructor):
---
void main()
{
import std.format;
import std.stdio;
import std.variant;
static struct S{ Variant v; string copyMsg; string destroyMsg; this(T)(T t) if(!is(immutable T == immutable S)) { v = t; copyMsg = format("copy: %s", v); destroyMsg = format("destroy: %s", v); } this(this) { writeln(copyMsg); } ~this() { writeln(destroyMsg); } string toString() { return format("S(%s)", v); }}writeln(__LINE__);S[] expected = [ ["x": S(1), "y": S(2)], ["x": S(3), "y": S(4)], ["x": S(5), "y": S(6)],];writeln(__LINE__);
}
---
prints
---
46
destroy: ["y":S(6), "x":S(5)]
destroy: ["y":S(4), "x":S(3)]
destroy: ["y":S(2), "x":S(1)]
54
destroy: ["y":S(6), "x":S(5)]
destroy: ["y":S(4), "x":S(3)]
destroy: ["y":S(2), "x":S(1)]
destroy: 1
destroy: 2
destroy: 3
destroy: 4
destroy: 5
destroy: 6
---
So,
---S[] expected = [ ["x": S(1), "y": S(2)], ["x": S(3), "y": S(4)], ["x": S(5), "y": S(6)],];---
somehow ends up destroying the implicitly constructed S's in the array - and then they get destroyed again when the program exits. If there were a copy made in between, then it could be the copies getting destroyed, but there are no calls to the postblit constructor. So, either copies are being made without calling the postblit constructor, or the destructor is simply managing to be called multiple times somehow.
However, if we change that piece of the code to
---S[] expected = [ S(["x": S(1), "y": S(2)]), S(["x": S(3), "y": S(4)]), S(["x": S(5), "y": S(6)]),];---
then we get
---
46
54
destroy: ["y":S(6), "x":S(5)]
destroy: ["y":S(4), "x":S(3)]
destroy: ["y":S(2), "x":S(1)]
destroy: 1
destroy: 2
destroy: 3
destroy: 4
destroy: 5
destroy: 6
---
This would be correct behavior. The number of copies and destructions match. It also indicates that no copies or destructions occurred when constructing the array, which is what I would have expected, but the important thing is that we have the same number of copies of the objects and destructions.
However, the big thing to note here is that this means that using the implicit construction syntax results in different behavior from using the explicit construction syntax when the two are supposed to be semantically identical. So, it would appear that something is going wrong when the compiler generates the code for the implicit construction.
I have yet to be able to reproduce this without a variant type (though I originally ran into it with a variant type at work rather than std.variant.Variant, so it's not specific to std.variant.Variant). So, I suspect that it has something to do with how S's are constructed inside the AA literals and then used to implicitly construct S's from those AA literals, but i don't know. I also haven't been able to reproduce it using just array literals.
That being said, I can say that if I change the constructor to
--- this(int i) { v = i; copyMsg = format("copy: %s", v); destroyMsg = format("destroy: %s", v); } this(S[string] aa) { v = aa; copyMsg = format("copy: %s", v); destroyMsg = format("destroy: %s", v); }---
it doesn't change the behavior, so the fact that S's constructor is templated doesn't seem to be affecting things (though maybe Variant's constructor is somehow).
All in all, this is just weird, but it does mean that we have a subtle footgun for folks who decide to use the implicit construction syntax with types that have a destructor.
The text was updated successfully, but these errors were encountered:
Jonathan M Davis (@jmdavis) reported this on 2024-10-30T18:23:56Z
Transferred from https://issues.dlang.org/show_bug.cgi?id=24840
Description
The text was updated successfully, but these errors were encountered: