1 module nxt.gc_traits; 2 3 /** Used as an UDA to mark a variable of a type that looks like GC-managed but 4 * that is actually not GC-managed, because its allocated by `malloc`, `calloc` 5 * or some other non-GC allocator. 6 */ 7 enum NoGc; 8 9 /** 10 * When this enum is used as UDA on aggregate types whose instances are 11 * created with construct() a compile time message indicates if a GC range 12 * will be added for the members. 13 */ 14 enum TellRangeAdded; 15 16 /** Indicates if an aggregate contains members that might be collected by the 17 * garbage collector. This is used in constructors to determine if the content 18 * of a manually allocated aggregate must be declared to the GC. 19 */ 20 template mustAddGCRange(T) { 21 import std.traits : isPointer, isArray, isScalarType, hasUDA; 22 static if (isScalarType!T) 23 enum mustAddGCRange = false; 24 else static if (is(T == struct) || 25 is(T == union)) 26 enum mustAddGCRange = mustAddGCRangeOfStructOrUnion!T; 27 else static if (is(T == U[], U) || // isSlice 28 is(T == class) || // a class is memory-wise 29 isPointer!T) // just a pointer, consistent with opCmp 30 enum mustAddGCRange = !hasUDA!(T, NoGc); 31 else static if (isArray!T) { 32 static if (__traits(isStaticArray, T)) { 33 static if (T.length == 0) 34 enum mustAddGCRange = false; 35 else 36 enum mustAddGCRange = mustAddGCRange!(typeof(T.init[0])); 37 } 38 else 39 enum mustAddGCRange = true; 40 } 41 else 42 static assert(0, "Handle type " ~ T.stringof); 43 } 44 45 /// 46 pure nothrow @safe @nogc unittest { 47 @NoGc int[] slice; 48 import std.traits : hasUDA; 49 static assert(hasUDA!(slice, NoGc)); 50 /+ TODO: static assert(!mustAddGCRange!(typeof(slice))); +/ 51 } 52 53 /// 54 pure nothrow @safe @nogc unittest { 55 static assert(!mustAddGCRange!int); 56 static assert(mustAddGCRange!(int*)); 57 static assert(mustAddGCRange!(int*[1])); 58 static assert(!mustAddGCRange!(int*[0])); 59 static assert(mustAddGCRange!(int[])); 60 } 61 62 /// Helper for `mustAddGCRange`. 63 private template mustAddGCRangeOfStructOrUnion(T) 64 if (is(T == struct) || is(T == union)) { 65 import std.traits : hasUDA; 66 import std.meta : anySatisfy; 67 /* TODO: remove and adapt according to answers here: 68 * https://forum.dlang.org/thread/dkohvpbmakbdbhnmnmbg@forum.dlang.org */ 69 // static if (__traits(hasMember, T, "__postblit")) { 70 // static if (__traits(isDisabled, T.__postblit)) 71 // enum mustAddGCRangeOfStructOrUnion = anySatisfy!(mustAddGCRangeOfMember, T.tupleof[0 .. $ - 1]); 72 // else 73 // enum mustAddGCRangeOfStructOrUnion = anySatisfy!(mustAddGCRangeOfMember, T.tupleof); 74 // } else { 75 // enum mustAddGCRangeOfStructOrUnion = anySatisfy!(mustAddGCRangeOfMember, T.tupleof); 76 // } 77 enum mustAddGCRangeOfStructOrUnion = anySatisfy!(mustAddGCRangeOfMember, T.tupleof); 78 } 79 80 private template mustAddGCRangeOfMember(alias member) { 81 import std.traits : hasUDA; 82 enum mustAddGCRangeOfMember = !hasUDA!(member, NoGc) && mustAddGCRange!(typeof(member)); 83 } 84 85 /// no-GC-managed struct with a disabled postblit 86 pure nothrow @safe @nogc unittest { 87 static struct S { 88 this(this) @disable; 89 @NoGc int* _ptr; 90 } 91 static if (__traits(hasMember, S, "__postblit")) { 92 static assert(__traits(isDisabled, S.__postblit)); 93 } 94 // See https://forum.dlang.org/post/dkohvpbmakbdbhnmnmbg@forum.dlang.org 95 static assert(!mustAddGCRangeOfStructOrUnion!S); 96 } 97 98 /// GC-managed struct 99 pure nothrow @safe @nogc unittest { 100 static struct S { 101 int* _ptr; 102 } 103 // See https://forum.dlang.org/post/dkohvpbmakbdbhnmnmbg@forum.dlang.org 104 static assert(mustAddGCRangeOfStructOrUnion!S); 105 } 106 107 /// 108 pure nothrow @safe @nogc unittest { 109 struct SmallBin { 110 string[1] s; 111 } 112 static assert(mustAddGCRange!SmallBin); 113 114 union HybridBin { 115 SmallBin small; 116 } 117 static assert(mustAddGCRange!HybridBin); 118 } 119 120 /// 121 pure nothrow @safe @nogc unittest { 122 struct S { 123 @NoGc int[] a; 124 } 125 static assert(!mustAddGCRange!S); 126 } 127 128 /// 129 pure nothrow @safe @nogc unittest { 130 class Foo { 131 @NoGc int[] a; 132 @NoGc void* b; 133 } 134 version (none) static assert(!mustAddGCRange!Foo); /+ TODO: activate +/ 135 136 class Bar { 137 int[] a; 138 @NoGc void* b; 139 } 140 static assert(mustAddGCRange!Bar); 141 142 class Baz : Bar { 143 @NoGc void* c; 144 } 145 static assert(mustAddGCRange!Baz); 146 147 struct S { 148 int x; 149 } 150 static assert(!mustAddGCRange!S); 151 152 struct T { 153 int* x; 154 } 155 static assert(mustAddGCRange!T); 156 static assert(mustAddGCRange!(T[1])); 157 158 struct U { 159 @NoGc int* x; 160 } 161 static assert(!mustAddGCRange!U); 162 static assert(!mustAddGCRange!(U[1])); 163 164 union N { 165 S s; 166 U u; 167 } 168 static assert(!mustAddGCRange!N); 169 static assert(!mustAddGCRange!(N[1])); 170 171 union M { 172 S s; 173 T t; 174 } 175 static assert(mustAddGCRange!M); 176 static assert(mustAddGCRange!(M[1])); 177 }