Bug 24840 – Implicit construction with associative array literals can result in more destructor calls than constructor calls

Status
NEW
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2024-10-30T18:23:56Z
Last change time
2024-12-13T19:38:24Z
Assigned to
No Owner
Creator
Jonathan M Davis
Moved to GitHub: dmd#20542 →

Comments

Comment #0 by issues.dlang — 2024-10-30T18:23:56Z
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.
Comment #1 by robert.schadek — 2024-12-13T19:38:24Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/20542 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB