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