1 module rt.util.typeinfo; 2 3 static import core.internal.hash; 4 5 private struct _Complex(T) { T re; T im; } 6 7 enum __c_complex_float : _Complex!float; 8 enum __c_complex_double : _Complex!double; 9 enum __c_complex_real : _Complex!real; // This is why we don't use stdc.config 10 11 alias d_cfloat = __c_complex_float; 12 alias d_cdouble = __c_complex_double; 13 alias d_creal = __c_complex_real; 14 15 enum isComplex(T) = is(T == d_cfloat) || is(T == d_cdouble) || is(T == d_creal); 16 // Three-way compare for integrals: negative if `lhs < rhs`, positive if `lhs > rhs`, 0 otherwise. 17 pragma(inline, true) 18 private int cmp3(T)(const T lhs, const T rhs) 19 if (__traits(isIntegral, T)) 20 { 21 static if (T.sizeof < int.sizeof) 22 // Taking the difference will always fit in an int. 23 return int(lhs) - int(rhs); 24 else 25 return (lhs > rhs) - (lhs < rhs); 26 } 27 28 // Three-way compare for real fp types. NaN is smaller than all valid numbers. 29 // Code is small and fast, see https://godbolt.org/z/fzb877 30 pragma(inline, true) 31 private int cmp3(T)(const T d1, const T d2) 32 if (is(T == float) || is(T == double) || is(T == real)) 33 { 34 if (d2 != d2) 35 return d1 == d1; // 0 if both ar NaN, 1 if d1 is valid and d2 is NaN. 36 // If d1 is NaN, both comparisons are false so we get -1, as needed. 37 return (d1 > d2) - !(d1 >= d2); 38 } 39 40 // Three-way compare for complex types. 41 pragma(inline, true) 42 private int cmp3(T)(const T f1, const T f2) 43 if (isComplex!T) 44 { 45 if (int result = cmp3(f1.re, f2.re)) 46 return result; 47 return cmp3(f1.im, f2.im); 48 } 49 50 // Reduces to `T` if `cond` is `true` or `U` otherwise. Consider moving elsewhere if useful. 51 private template Select(bool cond, T, U) 52 { 53 static if (cond) alias Select = T; 54 else alias Select = U; 55 } 56 57 /* 58 TypeInfo information for built-in types. 59 60 A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and 61 equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example: 62 `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap 63 the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect 64 during compilation whether they have different signedness and override appropriately. For initializer, we 65 detect if we need to override. The overriding initializer should be nonzero. 66 */ 67 private class TypeInfoGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo, TypeInfoGeneric!Base) 68 if (T.sizeof == Base.sizeof && T.alignof == Base.alignof) 69 { 70 const: nothrow: pure: @trusted: 71 72 // Returns the type name. 73 override string toString() const pure nothrow @safe { return T.stringof; } 74 75 // `getHash` is the same for `Base` and `T`, introduce it just once. 76 static if (is(T == Base)) 77 override size_t getHash(scope const void* p) 78 { 79 return hashOf(*cast(const T *)p); 80 } 81 82 // `equals` is the same for `Base` and `T`, introduce it just once. 83 static if (is(T == Base)) 84 override bool equals(in void* p1, in void* p2) 85 { 86 return *cast(const T *)p1 == *cast(const T *)p2; 87 } 88 89 // `T` and `Base` may have different signedness, so this function is introduced conditionally. 90 static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) 91 override int compare(in void* p1, in void* p2) 92 { 93 return cmp3(*cast(const T*) p1, *cast(const T*) p2); 94 } 95 96 static if (is(T == Base)) 97 override @property size_t tsize() 98 { 99 return T.sizeof; 100 } 101 102 static if (is(T == Base)) 103 override @property size_t talign() 104 { 105 return T.alignof; 106 } 107 108 // Override initializer only if necessary. 109 static if (is(T == Base) || T.init != Base.init) 110 override const(void)[] initializer() 111 { 112 static if (__traits(isZeroInit, T)) 113 { 114 return (cast(void *)null)[0 .. T.sizeof]; 115 } 116 else 117 { 118 static immutable T[1] c; 119 return c; 120 } 121 } 122 123 // `swap` is the same for `Base` and `T`, so introduce only once. 124 static if (is(T == Base)) 125 override void swap(void *p1, void *p2) 126 { 127 auto t = *cast(T *) p1; 128 *cast(T *)p1 = *cast(T *)p2; 129 *cast(T *)p2 = t; 130 } 131 132 static if (is(T == Base) || RTInfo!T != RTInfo!Base) 133 override @property immutable(void)* rtInfo() 134 { 135 return RTInfo!T; 136 } 137 138 static if (is(T == Base)) 139 { 140 static if ((__traits(isFloating, T) && T.mant_dig != 64) || 141 (isComplex!T && T.re.mant_dig != 64)) 142 // FP types except 80-bit X87 are passed in SIMD register. 143 override @property uint flags() const { return 2; } 144 } 145 } 146 147 /* 148 TypeInfo information for arrays of built-in types. 149 150 A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and 151 equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example: 152 `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap 153 the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect 154 during compilation whether they have different signedness and override appropriately. For initializer, we 155 detect if we need to override. The overriding initializer should be nonzero. 156 */ 157 private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo_Array, TypeInfoArrayGeneric!Base) 158 { 159 static if (is(T == Base)) 160 override bool opEquals(const Object o) const @safe nothrow { return TypeInfo.opEquals(cast(const TypeInfo) o); } 161 162 alias opEquals = typeof(super).opEquals; 163 alias opEquals = TypeInfo.opEquals; 164 165 override string toString() const { return (T[]).stringof; } 166 167 static if (is(T == Base)) 168 override size_t getHash(scope const void* p) @trusted const 169 { 170 return hashOf(*cast(const T[]*) p); 171 } 172 173 static if (is(T == Base)) 174 override bool equals(in void* p1, in void* p2) const 175 { 176 // Just reuse the builtin. 177 return *cast(const(T)[]*) p1 == *cast(const(T)[]*) p2; 178 } 179 180 static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) 181 override int compare(in void* p1, in void* p2) const 182 { 183 // Can't reuse __cmp in object.d because that handles NaN differently. 184 // (Q: would it make sense to unify behaviors?) 185 // return __cmp(*cast(const T[]*) p1, *cast(const T[]*) p2); 186 auto lhs = *cast(const T[]*) p1; 187 auto rhs = *cast(const T[]*) p2; 188 size_t len = lhs.length; 189 if (rhs.length < len) 190 len = rhs.length; 191 for (size_t u = 0; u < len; u++) 192 { 193 if (int result = cmp3(lhs.ptr[u], rhs.ptr[u])) 194 return result; 195 } 196 return cmp3(lhs.length, rhs.length); } 197 198 override @property inout(TypeInfo) next() inout 199 { 200 return cast(inout) typeid(T); 201 } 202 } 203 204 //////////////////////////////////////////////////////////////////////////////// 205 // Predefined TypeInfos 206 //////////////////////////////////////////////////////////////////////////////// 207 208 // void 209 class TypeInfo_v : TypeInfoGeneric!ubyte 210 { 211 const: nothrow: pure: @trusted: 212 213 override string toString() const pure nothrow @safe { return "void"; } 214 215 override size_t getHash(scope const void* p) 216 { 217 assert(0); 218 } 219 220 override @property uint flags() nothrow pure 221 { 222 return 1; 223 } 224 } 225 226 // All integrals. 227 class TypeInfo_h : TypeInfoGeneric!ubyte {} 228 class TypeInfo_b : TypeInfoGeneric!(bool, ubyte) {} 229 class TypeInfo_g : TypeInfoGeneric!(byte, ubyte) {} 230 class TypeInfo_a : TypeInfoGeneric!(char, ubyte) {} 231 class TypeInfo_t : TypeInfoGeneric!ushort {} 232 class TypeInfo_s : TypeInfoGeneric!(short, ushort) {} 233 class TypeInfo_u : TypeInfoGeneric!(wchar, ushort) {} 234 class TypeInfo_w : TypeInfoGeneric!(dchar, uint) {} 235 class TypeInfo_k : TypeInfoGeneric!uint {} 236 class TypeInfo_i : TypeInfoGeneric!(int, uint) {} 237 class TypeInfo_m : TypeInfoGeneric!ulong {} 238 class TypeInfo_l : TypeInfoGeneric!(long, ulong) {} 239 static if (is(cent)) class TypeInfo_zi : TypeInfoGeneric!cent {} 240 static if (is(ucent)) class TypeInfo_zk : TypeInfoGeneric!ucent {} 241 242 // All simple floating-point types. 243 class TypeInfo_f : TypeInfoGeneric!float {} 244 class TypeInfo_d : TypeInfoGeneric!double {} 245 class TypeInfo_e : TypeInfoGeneric!real {} 246 247 // Arrays of all integrals. 248 class TypeInfo_Ah : TypeInfoArrayGeneric!ubyte {} 249 class TypeInfo_Ab : TypeInfoArrayGeneric!(bool, ubyte) {} 250 class TypeInfo_Ag : TypeInfoArrayGeneric!(byte, ubyte) {} 251 class TypeInfo_Aa : TypeInfoArrayGeneric!(char, ubyte) {} 252 class TypeInfo_Axa : TypeInfoArrayGeneric!(const char) {} 253 class TypeInfo_Aya : TypeInfoArrayGeneric!(immutable char) 254 { 255 // Must override this, otherwise "string" is returned. 256 override string toString() const { return "immutable(char)[]"; } 257 } 258 class TypeInfo_At : TypeInfoArrayGeneric!ushort {} 259 class TypeInfo_As : TypeInfoArrayGeneric!(short, ushort) {} 260 class TypeInfo_Au : TypeInfoArrayGeneric!(wchar, ushort) {} 261 class TypeInfo_Ak : TypeInfoArrayGeneric!uint {} 262 class TypeInfo_Ai : TypeInfoArrayGeneric!(int, uint) {} 263 class TypeInfo_Aw : TypeInfoArrayGeneric!(dchar, uint) {} 264 class TypeInfo_Am : TypeInfoArrayGeneric!ulong {} 265 class TypeInfo_Al : TypeInfoArrayGeneric!(long, ulong) {} 266 267 // Arrays of all simple floating-point types. 268 class TypeInfo_Af : TypeInfoArrayGeneric!float {} 269 class TypeInfo_Ad : TypeInfoArrayGeneric!double {} 270 class TypeInfo_Ae : TypeInfoArrayGeneric!real {} 271 272 // void[] is a bit different, behaves like ubyte[] for comparison purposes. 273 class TypeInfo_Av : TypeInfo_Ah 274 { 275 override string toString() const { return "void[]"; } 276 277 override @property inout(TypeInfo) next() inout 278 { 279 return cast(inout) typeid(void); 280 } 281 282 unittest 283 { 284 assert(typeid(void[]).toString == "void[]"); 285 assert(typeid(void[]).next == typeid(void)); 286 } 287 } 288 // typeof(null) 289 class TypeInfo_n : TypeInfo 290 { 291 const: pure: @nogc: nothrow: @safe: 292 293 override string toString() { return "typeof(null)"; } 294 295 override size_t getHash(scope const void*) { return 0; } 296 297 override bool equals(in void*, in void*) { return true; } 298 299 override int compare(in void*, in void*) { return 0; } 300 301 override @property size_t tsize() { return typeof(null).sizeof; } 302 303 override const(void)[] initializer() @trusted { return (cast(void *)null)[0 .. size_t.sizeof]; } 304 305 override void swap(void*, void*) {} 306 307 override @property immutable(void)* rtInfo() { return rtinfoNoPointers; } 308 }