Comment #0 by thomas.bockman — 2021-03-24T22:14:54Z
Since 2.092, the program below calls B's destructor, but not C's:
///////////////////////////////////////
module app;
import core.stdc.stdlib : free, malloc;
import std.stdio : writeln;
struct A(T) {
T* address;
this(const(bool) create) {
if(create)
this.address = cast(T*) malloc(T.sizeof);
}
~this() {
if(address !is null) {
destroy(*address);
free(address);
address = null;
writeln("A destroyed");
}
}
}
void main() {
static struct B {
@property ref A!B next()();
~this() {
writeln("B destroyed"); }
}
static struct C {
@property ref A!C next();
~this() {
writeln("C destroyed"); }
}
A!B b = true;
A!C c = true;
}
///////////////////////////////////////
Apparently this is hard to get right:
///////////////////////////////////////
Up to 2.067.1: Success with output:
-----
C destroyed
A destroyed
B destroyed
A destroyed
-----
2.068.2 to 2.083.1: Success with output:
-----
A destroyed
B destroyed
A destroyed
-----
2.084.1 to 2.091.1: Failure with output similar to:
-----
Error: union `object.destroy!(true, C).destroy.UntypedInit` has forward references
onlineapp.d(14): Error: template instance `object.destroy!(true, C)` error instantiating
onlineapp.d(29): instantiated from here: `A!(C)`
-----
Since 2.092.1: Success with output:
-----
A destroyed
B destroyed
A destroyed
-----
Comment #1 by moonlightsentinel — 2021-03-24T22:44:19Z
Reduced example:
----------------------------------------------
import core.stdc.stdio : puts;
struct A(T)
{
T* address;
~this() {
destroy(*address);
}
}
void main() {
static struct B {
A!B next()();
~this() {
puts("B destroyed");
}
}
static struct C {
A!C next();
~this() {
puts("C destroyed");
}
}
B b;
destroy(b);
C c;
destroy(c);
}
----------------------------------------------
Introduced by https://github.com/dlang/druntime/pull/1312.
But it's probably a DMD issue because the PR introduced __traits(hasMember, S, "__xdtor") which is true for B but false for C
Comment #2 by moonlightsentinel — 2021-03-24T22:59:52Z
This is probably caused by the forward reference of the destructors.
The non-templated next() instantiates A!C before the semantic of C is done and before __xdtor is generated. A!C's destructor then instantiates destroy which omits the dtor call because it's not yet available.
Comment #3 by moonlightsentinel — 2021-03-24T23:04:47Z
Test case without dependencies:
struct A(T)
{
~this()
{
static assert(__traits(hasMember, T, "__xdtor"));
}
}
void main() {
static struct B {
A!B next();
~this() {}
}
static struct C {
A!C next()();
~this() {}
}
C().next();
}
B fails while C compiles just fine.
Comment #4 by moonlightsentinel — 2021-03-24T23:09:10Z
Interestingly, the test case works if B and C are declared outside of main.
Comment #5 by moonlightsentinel — 2021-03-24T23:38:16Z
(In reply to moonlightsentinel from comment #4)
> Interestingly, the test case works if B and C are declared outside of main.
Seems like this was enabled by https://github.com/dlang/dmd/pull/5075
Comment #6 by moonlightsentinel — 2021-03-24T23:39:25Z
Comment #7 by thomas.bockman — 2021-07-01T01:37:34Z
I ran into this again in a different context. One workaround for the compiler bug is to change the definition of object.destroy to do the work of __xdtor itself when the type has a destructor, but no __xdtor is found:
//////////////////////////////////////////////////////////////////
import core.internal.lifetime : emplaceInitializer;
import core.internal.traits : hasElaborateDestructor;
void destroy(bool initialize = true, T)(ref T obj)
if(is(T == struct))
{
static if(initialize) {
destroy!false(obj);
emplaceInitializer(obj);
} else
static if(hasElaborateDestructor!T) {
static if(__traits(hasMember, T, `__xdtor`)
&& __traits(isSame, T, __traits(parent, obj.__xdtor)))
obj.__xdtor();
else {
static if(__traits(hasMember, T, `__dtor`)
&& __traits(isSame, T, __traits(parent, obj.__dtor)))
obj.__dtor();
foreach_reverse(ref objField; obj.tupleof)
destroy!false(objField);
}
}
}
//////////////////////////////////////////////////////////////////
Should I submit a druntime pull request for this?
Comment #8 by robert.schadek — 2024-12-13T19:15:25Z