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 { 22 import std.traits : isPointer, isArray, isStaticArray, isScalarType; 23 static if (is(T == class) || // a class is memory-wise 24 isPointer!T) // just a pointer, consistent with opCmp 25 { 26 enum mustAddGCRange = true; 27 } 28 else static if (isScalarType!T) 29 { 30 enum mustAddGCRange = false; 31 } 32 else static if (is(T == struct) || 33 is(T == union)) 34 { 35 enum mustAddGCRange = mustAddGCRangeOfStructOrUnion!T; 36 } 37 else static if (isArray!T) 38 { 39 static if (isStaticArray!T) 40 { 41 static if (T.length == 0) 42 { 43 enum mustAddGCRange = false; 44 } 45 else 46 { 47 enum mustAddGCRange = mustAddGCRange!(typeof(T.init[0])); 48 } 49 } 50 else 51 { 52 enum mustAddGCRange = true; 53 } 54 } 55 else 56 { 57 static assert(0, "Handle type " ~ T.stringof); 58 } 59 } 60 61 /// 62 @safe pure nothrow @nogc unittest 63 { 64 static assert(!mustAddGCRange!int); 65 static assert(mustAddGCRange!(int*)); 66 static assert(mustAddGCRange!(int*[1])); 67 static assert(!mustAddGCRange!(int*[0])); 68 static assert(mustAddGCRange!(int[])); 69 } 70 71 /// Helper for `mustAddGCRange`. 72 private template mustAddGCRangeOfStructOrUnion(T) 73 if (is(T == struct) || 74 is(T == union)) 75 { 76 import std.traits : hasUDA; 77 import std.meta : anySatisfy; 78 /* TODO: remove and adapt according to answers here: 79 * https://forum.dlang.org/thread/dkohvpbmakbdbhnmnmbg@forum.dlang.org */ 80 // static if (__traits(hasMember, T, "__postblit")) 81 // { 82 // static if (__traits(isDisabled, T.__postblit)) 83 // { 84 // enum mustAddGCRangeOfStructOrUnion = anySatisfy!(mustAddGCRangeOfMember, T.tupleof[0 .. $ - 1]); 85 // } 86 // else 87 // { 88 // enum mustAddGCRangeOfStructOrUnion = anySatisfy!(mustAddGCRangeOfMember, T.tupleof); 89 // } 90 // } 91 // else 92 // { 93 // enum mustAddGCRangeOfStructOrUnion = anySatisfy!(mustAddGCRangeOfMember, T.tupleof); 94 // } 95 enum mustAddGCRangeOfStructOrUnion = anySatisfy!(mustAddGCRangeOfMember, T.tupleof); 96 } 97 98 private template mustAddGCRangeOfMember(alias member) 99 { 100 import std.traits : hasUDA; 101 enum mustAddGCRangeOfMember = !hasUDA!(member, NoGc) && mustAddGCRange!(typeof(member)); 102 } 103 104 /// no-GC-managed struct with a disabled postblit 105 @safe pure nothrow @nogc unittest 106 { 107 static struct S 108 { 109 @disable this(this); 110 @NoGc int* _ptr; 111 } 112 static if (__traits(hasMember, S, "__postblit")) 113 { 114 static assert(__traits(isDisabled, S.__postblit)); 115 } 116 // See https://forum.dlang.org/post/dkohvpbmakbdbhnmnmbg@forum.dlang.org 117 static assert(!mustAddGCRangeOfStructOrUnion!S); 118 } 119 120 /// GC-managed struct 121 @safe pure nothrow @nogc unittest 122 { 123 static struct S 124 { 125 int* _ptr; 126 } 127 // See https://forum.dlang.org/post/dkohvpbmakbdbhnmnmbg@forum.dlang.org 128 static assert(mustAddGCRangeOfStructOrUnion!S); 129 } 130 131 /// 132 @safe pure nothrow @nogc unittest 133 { 134 struct SmallBin 135 { 136 string[1] s; 137 } 138 static assert(mustAddGCRange!SmallBin); 139 140 union HybridBin 141 { 142 SmallBin small; 143 } 144 static assert(mustAddGCRange!HybridBin); 145 } 146 147 /// 148 @safe pure nothrow @nogc unittest 149 { 150 struct S 151 { 152 @NoGc int[] a; 153 } 154 static assert(!mustAddGCRange!S); 155 } 156 157 /// 158 version(none) 159 @safe pure nothrow @nogc unittest 160 { 161 class Foo 162 { 163 @NoGc int[] a; 164 @NoGc void* b; 165 } 166 static assert(!mustAddGCRange!Foo); 167 168 class Bar 169 { 170 int[] a; 171 @NoGc void* b; 172 } 173 static assert(mustAddGCRange!Bar); 174 175 class Baz : Bar 176 { 177 @NoGc void* c; 178 } 179 static assert(mustAddGCRange!Baz); 180 181 struct S 182 { 183 int x; 184 } 185 static assert(!mustAddGCRange!S); 186 187 struct T 188 { 189 int* x; 190 } 191 static assert(mustAddGCRange!T); 192 static assert(mustAddGCRange!(T[1])); 193 194 struct U 195 { 196 @NoGc int* x; 197 } 198 static assert(!mustAddGCRange!U); 199 static assert(!mustAddGCRange!(U[1])); 200 201 union N 202 { 203 S s; 204 U u; 205 } 206 static assert(!mustAddGCRange!N); 207 static assert(!mustAddGCRange!(N[1])); 208 209 union M 210 { 211 S s; 212 T t; 213 } 214 static assert(mustAddGCRange!M); 215 static assert(mustAddGCRange!(M[1])); 216 }