Comment #0 by thomas.bockman — 2021-05-26T21:59:50Z
The purpose of destructors is to describe cleanup work to be performed when the lifetime of an object is over. Allowing @safe code to call a destructor manually before that point and then continue to use the destroyed object afterwards breaks RAII memory management:
/////////////////////////////////////////////
module app;
import core.stdc.stdlib : malloc, free;
struct UniqueInt {
private int* target;
this(const(bool) doCreate) scope @trusted nothrow @nogc {
target = cast(int*) malloc(int.sizeof);
*target = 5;
}
@disable this(this);
@disable this(ref typeof(this));
@disable ref typeof(this) opAssign(ref typeof(this));
void withBorrow(void delegate(scope int*) @safe action) @safe {
action(target); }
~this() scope @trusted nothrow @nogc {
if(target !is null) {
free(target);
target = null;
}
}
}
UniqueInt unique;
shared static this() {
unique = true; }
void main() @safe {
import std.stdio: writeln;
unique.withBorrow((scope int* borrowed) @safe {
writeln(*borrowed);
destroy(unique);
writeln(*borrowed); // Use after free.
});
}
/////////////////////////////////////////////
I think the only reasonable solution to this is to make calling __dtor, __xdtor, or destroy manually an @system operation, regardless of the attributes of ~this().
(I suspect there is some way to violate memory safety or break the type system with this even in 100% @safe code, but I couldn't figure out what it is so I've marked this as an enhancement request.)
Forum thread:
https://forum.dlang.org/post/[email protected]
Comment #1 by dkorpel — 2021-11-12T16:51:21Z
*** Issue 22507 has been marked as a duplicate of this issue. ***
Comment #2 by robert.schadek — 2024-12-13T19:16:41Z