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 }