object.destroy cannot be instantiated for a self-referential type defined inside a function, even though the equivalent top-level type compiles successfully.
The failure occurs during semantic analysis with:
Error: no size because of forward references
The error occurs while instantiating core.internal.destruction.destructRecurse.
Example:
The following top-level definition compiles successfully:
struct Recursive(T)
{
T* _inst;
void _release() {
object.destroy(*_inst);
}
}
struct Node
{
int id;
Recursive!Node next;
~this() {}
}
void main() {}
However, moving the same type into a function causes a compilation error:
struct Recursive(T)
{
T* _inst;
void _release() {
object.destroy(*_inst);
}
}
void main() {
struct Node
{
int id;
Recursive!Node next;
~this() {}
}
}
Actual result:
onlineapp.d(9): Error: no size because of forward references
struct Node
^
/dlang/dmd/linux/bin64/../../src/druntime/import/object.d(4119): Error: template instance `core.internal.destruction.destructRecurse!(Node)` error instantiating
destructRecurse(obj);
^
onlineapp.d(5): instantiated from here: `destroy!(true, Node)`
object.destroy(*_inst);
^
onlineapp.d(12): instantiated from here: `Recursive!(Node)`
Recursive!Node next;
^
onlineapp.d(9): Error: no size because of forward references
struct Node
^
onlineapp.d(9): Error: no size because of forward references
struct Node
^
onlineapp.d(9): Error: no size because of forward references
struct Node
^
onlineapp.d(9): Error: struct `onlineapp.main.Node` no size because of forward reference
struct Node
^
/dlang/dmd/linux/bin64/../../src/druntime/import/object.d(4124): Error: template instance `core.internal.lifetime.emplaceInitializer!(Node)` error instantiating
emplaceInitializer(obj); // emplace T.init
^
onlineapp.d(5): instantiated from here: `destroy!(true, Node)`
object.destroy(*_inst);
^
onlineapp.d(12): instantiated from here: `Recursive!(Node)`
Recursive!Node next;
^
Expected result
Both programs should compile successfully, since the equivalent module-scope definition compiles successfully.
The behavior of object.destroy should not depend on whether a self-referential type is declared at module scope or local scope.
Additional notes:
object.destroy instantiates core.internal.destruction.destructRecurse, which internally checks for the compiler-generated __xdtor. The issue may therefore be related to handling incomplete types during semantic analysis.
This may be related to the inconsistent behavior of hasElaborateDestructor for incomplete self-referential types. A separate issue has been filed for that behavior.
#23352
Although both issues involve incomplete self-referential types, they concern different problems. hasElaborateDestructor is a compiler trait whose semantics may legitimately depend on whether a type is complete. In contrast, object.destroy is a public API and should remain usable regardless of how destructor availability is internally determined. Therefore these are reported as separate issues.
Additional note 2:
The issue is not limited to compilation failures. In the following example, the program compiles successfully, but object.destroy fails to invoke the destructor of the referenced object.
static int x = 0;
struct Recursive(T)
{
T* _inst;
~this() {
if (_inst)
object.destroy(_inst);
}
}
struct Node
{
int id;
Recursive!Node next;
~this() {
x++;
}
}
void main() {
{
Node n1;
n1.next._inst = new Node;
}
assert(x == 2); // Fails: object.destroy does not invoke `new Node`'s destructor.
}
The assertion fails because only the stack-allocated Node is destroyed. The destructor of the heap-allocated Node, which should be invoked through object.destroy, is never called.
This appears to occur because destructRecurse!Node is instantiated while Node is still incomplete, causing the static if (__traits(hasMember, S, "__xdtor")) branch to be permanently evaluated as false.
object.destroycannot be instantiated for a self-referential type defined inside a function, even though the equivalent top-level type compiles successfully.The failure occurs during semantic analysis with:
The error occurs while instantiating
core.internal.destruction.destructRecurse.Example:
The following top-level definition compiles successfully:
However, moving the same type into a function causes a compilation error:
Actual result:
Expected result
Both programs should compile successfully, since the equivalent module-scope definition compiles successfully.
The behavior of object.destroy should not depend on whether a self-referential type is declared at module scope or local scope.
Additional notes:
object.destroyinstantiates core.internal.destruction.destructRecurse, which internally checks for the compiler-generated__xdtor. The issue may therefore be related to handling incomplete types during semantic analysis.This may be related to the inconsistent behavior of
hasElaborateDestructorfor incomplete self-referential types. A separate issue has been filed for that behavior.#23352
Although both issues involve incomplete self-referential types, they concern different problems.
hasElaborateDestructoris a compiler trait whose semantics may legitimately depend on whether a type is complete. In contrast,object.destroyis a public API and should remain usable regardless of how destructor availability is internally determined. Therefore these are reported as separate issues.Additional note 2:
The issue is not limited to compilation failures. In the following example, the program compiles successfully, but
object.destroyfails to invoke the destructor of the referenced object.The assertion fails because only the stack-allocated Node is destroyed. The destructor of the heap-allocated Node, which should be invoked through
object.destroy, is never called.This appears to occur because destructRecurse!Node is instantiated while Node is still incomplete, causing the
static if (__traits(hasMember, S, "__xdtor"))branch to be permanently evaluated as false.