the following code failing the assertion:
===
int rc = 0;
struct S {
int n;
this (int na) { ++rc; n = na; import core.stdc.stdio; printf("ctor for (n=%d)\n", n); }
this (this) { if (n) ++rc; import core.stdc.stdio; printf("postblit for (n=%d)\n", n); }
~this () { if (n) --rc; import core.stdc.stdio; printf("dtor for (n=%d)\n", n); n = 0; }
}
void test0 (S[] vals...) {
vals[0] = S.init; //(1) this causes the bug
}
void test1 () {
auto a0 = S(1);
test0(a0);
}
void main () {
test1();
assert(rc == 0);
}
===
`dtor for (n=1)` is called twice, but only if (1) is not commented. comment out (1), and the bug is gone.
*technically* calling dtor on already destructed struct is not a bug. but IRL it will ruin any reference counting scheme.
tbh, i don't know how to fix this... let's say "corner case" without loosing performance.
also, i dont' know why `a0` isn't cleared by `vals[0] = S.init;`. i thought that typed vararg just creates a slice of program stack, and assigning `.init` to `vals` member should clear `a0`. either that, or compiler should call postblit for each typed vararg member (please, no! ;-).
so, this can be "missing postblit call" case too. i put both in subj until we'll decide what should be done in this case.
please note that i'm clearing `n` in dtor, so calling dtor on already "destroyed" struct should not be disasterous. but somehow `a0` is not cleared.
Comment #1 by ilya.yanok — 2024-11-20T14:28:22Z
The arguments are most definitely copied, one can easily see that by taking the addresses of `vals[0]` and `a0` respectively. The spec is also pretty clear that the contents of the array is invalid after the function exits, see 5. here: https://docarchives.dlang.io/v2.076.0/spec/function.html#typesafe_variadic_functions
So, that a0 remains unchanged is not really a mystery.
It is weird though, that neither copy constructor nor postblit is called (I tried adding a copy constructor), probably some kind of optimization? But should we require typed varargs to always be const then?
Then, the default opAssign calls the destructor on the lvalue being assigned. That seems to be correct, but since the value was never constructed, this causes a disparity in constructor/destructor calls you observe.
Comment #2 by robert.schadek — 2024-12-13T18:55:13Z