1 module automem.utils;
2 
3 import std.traits : isStaticArray;
4 
5 // This is a destroy() copied and modified from
6 // druntime, to allow for destruction attribute inference
7 
8 void destruct(T)(T obj) if (is(T == class)) {
9     (cast(_finalizeType!T)&rt_finalize)(cast(void*)obj);
10 }
11 
12 void destruct(T)(T obj) if (is(T == interface)) {
13     destruct(cast(Object)obj);
14 }
15 
16 void destruct(T)(ref T obj) if (is(T == struct)) {
17     static if (__traits(hasMember, T, "__xdtor") &&
18                __traits(isSame, T, __traits(parent, obj.__xdtor)))
19         obj.__xdtor;
20 }
21 
22 void destruct(T : U[n], U, size_t n)(ref T obj) if (!is(T == struct)) {
23     foreach_reverse (ref e; obj[])
24         destruct(e);
25 }
26 
27 void destruct(T)(ref T obj)
28 if(!is(T == struct) && !is(T == class) && !is(T == interface) && !isStaticArray!T) {
29     obj = T.init;
30 }
31 
32 @("class dtor inference")
33 unittest {
34     class A { ~this() @nogc {} }
35     class B : A { ~this() {} }
36     class C : B { ~this() @nogc {} }
37 
38     static assert( __traits(compiles, () @nogc { A a; destruct(a); }));
39     static assert(!__traits(compiles, () @nogc { B a; destruct(b); }));
40     static assert(!__traits(compiles, () @nogc { C a; destruct(c); }));
41 }
42 
43 @("class dtor inference with struct members")
44 unittest {
45     struct A { ~this() @nogc {} }
46     struct B { ~this() {} }
47     class CA { A a; ~this() @nogc {} }
48     class CB { B b; ~this() @nogc {} }
49 
50     static assert( __traits(compiles, () @nogc { CA a; destruct(a); }));
51     static assert(!__traits(compiles, () @nogc { CB a; destruct(b); }));
52 }
53 
54 private:
55 
56 extern(C) void rt_finalize(void* p, bool det = true);
57 
58 // A slightly better hack than the one presented by
59 // https://www.auburnsounds.com/blog/2016-11-10_Running-D-without-its-runtime.html
60 //
61 // This template infers destruction attributes from the given
62 // class hierarchy. It actually may be incorrect, as by
63 // the current language rules derived class can still
64 // have weaker set of destruction attributes.
65 extern(C)
66 template _finalizeType(T) {
67     static if (is(T == Object)) {
68         alias _finalizeType = typeof(&rt_finalize);
69     } else {
70         import std.traits : BaseClassesTuple;
71         import std.meta : AliasSeq;
72         alias _finalizeType = typeof((void* p, bool det = true) {
73             // generate a body that calls all the destructors in the chain,
74             // compiler should infer the intersection of attributes
75             foreach (B; AliasSeq!(T, BaseClassesTuple!T)) {
76                 // __dtor, i.e. B.~this
77                 static if (__traits(hasMember, B, "__dtor"))
78                     () { B obj; obj.__dtor; } ();
79                 // __xdtor, i.e. dtors for all RAII members
80                 static if (__traits(hasMember, B, "__xdtor"))
81                     () { B obj; obj.__xdtor; } ();
82             }
83         });
84     }
85 }
86