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 }