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