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)(() @trusted { return 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         obj.__xdtor;
19 }
20 
21 void destruct(T : U[n], U, size_t n)(ref T obj) if (!is(T == struct)) {
22     foreach_reverse (ref e; obj[])
23         destruct(e);
24 }
25 
26 void destruct(T)(ref T obj)
27 if(!is(T == struct) && !is(T == class) && !is(T == interface) && !isStaticArray!T) {
28     obj = T.init;
29 }
30 
31 @("class dtor inference")
32 @safe @nogc pure unittest {
33     class A { ~this() @nogc {} }
34     class B : A { ~this() {} }
35     class C : B { ~this() @nogc {} }
36 
37     static assert( __traits(compiles, () @nogc { A a; destruct(a); }));
38     static assert(!__traits(compiles, () @nogc { B a; destruct(b); }));
39     static assert(!__traits(compiles, () @nogc { C a; destruct(c); }));
40 }
41 
42 @("class dtor inference with struct members")
43 @system @nogc pure unittest {
44     import std.traits: functionAttributes, FunctionAttribute;
45     import std.conv: text;
46 
47     struct A { ~this() @nogc {} }
48     struct B { ~this() { new int; } }
49     class CA { A a; ~this() @nogc {} }
50     class CB { B b; ~this() @nogc {} }
51 
52     static assert( __traits(compiles, () @nogc { CA a; destruct(a); }));
53     static assert(!__traits(compiles, () @system @nogc { CB b; destruct(b); }));
54 }
55 
56 private:
57 
58 extern(C) void rt_finalize(void* p, bool det = true) @trusted @nogc pure;
59 
60 // A slightly better hack than the one presented by
61 // https://www.auburnsounds.com/blog/2016-11-10_Running-D-without-its-runtime.html
62 //
63 // This template infers destruction attributes from the given
64 // class hierarchy. It actually may be incorrect, as by
65 // the current language rules derived class can still
66 // have weaker set of destruction attributes.
67 extern(C)
68 template _finalizeType(T) {
69     import std.traits: Unqual;
70     static if (is(Unqual!T == Object)) {
71         alias _finalizeType = typeof(&rt_finalize);
72     } else {
73         import std.traits : BaseClassesTuple;
74         import std.meta : AliasSeq;
75         alias _finalizeType = typeof(function void(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 }