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 @safe @nogc pure 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 @system @nogc pure unittest {
45     import std.traits: functionAttributes, FunctionAttribute;
46     import std.conv: text;
47 
48     struct A { ~this() @nogc {} }
49     struct B { ~this() {} }
50     class CA { A a; ~this() @nogc {} }
51     class CB { B b; ~this() @nogc {} }
52 
53     static assert( __traits(compiles, () @nogc { CA a; destruct(a); }));
54     static assert(!__traits(compiles, () @system @nogc { CB b; destruct(b); }));
55 }
56 
57 private:
58 
59 extern(C) void rt_finalize(void* p, bool det = true);
60 
61 // A slightly better hack than the one presented by
62 // https://www.auburnsounds.com/blog/2016-11-10_Running-D-without-its-runtime.html
63 //
64 // This template infers destruction attributes from the given
65 // class hierarchy. It actually may be incorrect, as by
66 // the current language rules derived class can still
67 // have weaker set of destruction attributes.
68 extern(C)
69 template _finalizeType(T) {
70     static if (is(T == Object)) {
71         alias _finalizeType = typeof(&rt_finalize);
72     } else {
73         import std.traits : BaseClassesTuple;
74         import std.meta : AliasSeq;
75         alias _finalizeType = typeof((void* p, bool det = true) {
76             // generate a body that calls all the destructors in the chain,
77             // compiler should infer the intersection of attributes
78             foreach (B; AliasSeq!(T, BaseClassesTuple!T)) {
79                 // __dtor, i.e. B.~this
80                 static if (__traits(hasMember, B, "__dtor"))
81                     () { B obj; obj.__dtor; } ();
82                 // __xdtor, i.e. dtors for all RAII members
83                 static if (__traits(hasMember, B, "__xdtor"))
84                     () { B obj; obj.__xdtor; } ();
85             }
86         });
87     }
88 }