Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

New destroy implementation for value types and instinsic types #2124

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 74 additions & 44 deletions src/object.d
Original file line number Diff line number Diff line change
Expand Up @@ -2620,6 +2620,12 @@ unittest
static assert(is(typeof(caa.byValue().front) == const int));
}

void _destructRecurse(T)(ref T obj)
if (!is(T == struct) && !is(T == interface) && !is(T == class) && !__traits(isStaticArray, T))
{
obj = T.init;
}

private void _destructRecurse(S)(ref S s)
if (is(S == struct))
{
Expand Down Expand Up @@ -2923,10 +2929,16 @@ unittest
}

/********
Destroys the given object and sets it back to its initial state. It's used to
_destroy an object, calling its destructor or finalizer so it no longer
references any other objects. It does $(I not) initiate a GC cycle or free
any GC memory.
Destroys the given instance by calling its destructor and, if necessary, reinitializing
it to leave it in a destructible state such that its destructor can be called with
well-defined behavior. It does $(I not) initiate a GC cycle or free any GC memory.

Value types with a defined destructor and reference types are reinitialzed.
Value types without a defined destructor and intrinsic types are not reinitialized.

To destroy an instance through a pointer, the pointer must be dereferenced.
Otherwise, `destroy` will only destroy the pointer itself (a no-op), not the entity being
referenced.
*/
void destroy(T)(T obj) if (is(T == class))
{
Expand All @@ -2953,24 +2965,12 @@ unittest
C c = new C();
assert(c.dtorCount == 0); // destructor not yet called
assert(c.s == "S"); // initial state `c.s` is `"S"`
c.s = "T";
assert(c.s == "T"); // `c.s` is `"T"`
c.s = "T"; // `c.s` is `"T"`
destroy(c);
assert(c.dtorCount == 1); // `c`'s destructor was called
assert(c.s == "S"); // `c.s` is back to its inital state, `"S"`
}

/// Value type demonstration
unittest
{
int i;
assert(i == 0); // `i`'s initial state is `0`
i = 1;
assert(i == 1); // `i` changed to `1`
destroy(i);
assert(i == 0); // `i` is back to its initial state `0`
}

unittest
{
interface I { }
Expand Down Expand Up @@ -3027,7 +3027,7 @@ unittest
}

/// ditto
void destroy(T)(ref T obj) if (is(T == struct))
void destroy(T)(ref T obj) if ((is(T == struct) && __traits(hasMember, T, "__xdtor"))) // struct with defined destructor
{
_destructRecurse(obj);
() @trusted {
Expand All @@ -3040,14 +3040,46 @@ void destroy(T)(ref T obj) if (is(T == struct))
} ();
}

/// Value type demonstration
unittest
{
{
static struct S1
{
string s = "S";
}

S1* s = new S1();
assert(s.s == "S"); // `s`'s initial state is ``"S"`
s.s = "T"; // `s.s` changed to `"T"`
destroy(*s);
assert(s.s == "T"); // `s.s` is still `"T"` because types without a defined destructor are
// not reinitialized
}
{
static struct S2
{
string s = "S";
~this() { }
}

S2* s = new S2();
assert(s.s == "S"); // `s`'s initial state is ``"S"`
s.s = "T"; // `s.s` changed to `"T"`
destroy(*s);
assert(s.s == "S"); // `s.s` is back to `"S"` because types with a defined destructor are
// reinitialized
}
}

nothrow @safe @nogc unittest
{
{
struct A { string s = "A"; }
A a;
a.s = "asd";
destroy(a);
assert(a.s == "A");
assert(a.s == "asd"); // struct does not have defined destructor, so it is not reinitialized
}
{
static int destroyed = 0;
Expand Down Expand Up @@ -3083,7 +3115,7 @@ nothrow @safe @nogc unittest
void destroy(T : U[n], U, size_t n)(ref T obj) if (!is(T == struct))
{
foreach_reverse (ref e; obj[])
destroy(e);
_destructRecurse(e);
}

unittest
Expand Down Expand Up @@ -3145,40 +3177,38 @@ unittest

/// ditto
void destroy(T)(ref T obj)
if (!is(T == struct) && !is(T == interface) && !is(T == class) && !_isStaticArray!T)
if (!(is(T == struct) && __traits(hasMember, T, "__xdtor")) // If not a struct with a defined destructor
&& !is(T == interface) && !is(T == class) && !__traits(isStaticArray, T))
{
obj = T.init;
// no-op. Intrinsic types and value types without a defined destructor do not reinitialize
}

template _isStaticArray(T : U[N], U, size_t N)
{
enum bool _isStaticArray = true;
}

template _isStaticArray(T)
/// Intrinsic type demonstration
unittest
{
enum bool _isStaticArray = false;
int i;
assert(i == 0); // `i`'s initial state is `0`
i = 1; // `i` changed to `1`
destroy(i);
assert(i == 1); // `i` is still `1` because types without a defined destructor are
// not reinitialized
}

unittest
{
{
int a = 42;
destroy(a);
assert(a == 0);
}
{
float a = 42;
destroy(a);
assert(isnan(a));
}
}
template AliasSeq(TList...)
{
alias AliasSeq = TList;
}

version (unittest)
{
private bool isnan(float x)
static foreach (T; AliasSeq!(bool, ubyte, byte, ushort, short, uint, int, ulong, long, float, double, real))
{
return x != x;
{
T t;
t = cast(T)1;
destroy(t);
assert(t == 1); // intrinsic types don't have defined destructors, so they are not reinitialized
}
}
}

Expand Down