1 module rt.util.typeinfo; 2 3 import rt.util.utility; 4 5 // Reduces to `T` if `cond` is `true` or `U` otherwise. 6 private template Select(bool cond, T, U) 7 { 8 static if (cond) alias Select = T; 9 else alias Select = U; 10 } 11 12 template Floating(T) 13 if (is(T == float) || is(T == double) || is(T == real)) 14 { 15 pure nothrow @safe: 16 17 bool equals(T f1, T f2) 18 { 19 return f1 == f2; 20 } 21 22 int compare(T d1, T d2) 23 { 24 if (d1 != d1 || d2 != d2) // if either are NaN 25 { 26 if (d1 != d1) 27 { 28 if (d2 != d2) 29 return 0; 30 return -1; 31 } 32 return 1; 33 } 34 return (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1); 35 } 36 } 37 38 // @@@DEPRECATED_2.105@@@ 39 template Floating(T) 40 if (isComplex!T) 41 { 42 pure nothrow @safe: 43 44 bool equals(T f1, T f2) 45 { 46 return f1.re == f2.re && f1.im == f2.im; 47 } 48 49 int compare(T f1, T f2) 50 { 51 int result; 52 53 if (f1.re < f2.re) 54 result = -1; 55 else if (f1.re > f2.re) 56 result = 1; 57 else if (f1.im < f2.im) 58 result = -1; 59 else if (f1.im > f2.im) 60 result = 1; 61 else 62 result = 0; 63 return result; 64 } 65 66 size_t hashOf(scope const T val) 67 { 68 return 0; 69 //return core.internal.hash.hashOf(val.re, core.internal.hash.hashOf(val.im)); 70 } 71 } 72 73 template Array(T) 74 if (is(T == float) || is(T == double) || is(T == real)) 75 { 76 pure nothrow @safe: 77 78 bool equals(T[] s1, T[] s2) 79 { 80 size_t len = s1.length; 81 if (len != s2.length) 82 return false; 83 for (size_t u = 0; u < len; u++) 84 { 85 if (!Floating!T.equals(s1[u], s2[u])) 86 return false; 87 } 88 return true; 89 } 90 91 int compare(T[] s1, T[] s2) 92 { 93 size_t len = s1.length; 94 if (s2.length < len) 95 len = s2.length; 96 for (size_t u = 0; u < len; u++) 97 { 98 if (int c = Floating!T.compare(s1[u], s2[u])) 99 return c; 100 } 101 return (s1.length > s2.length) - (s1.length < s2.length); 102 } 103 104 //public alias hashOf = core.internal.hash.hashOf; 105 } 106 107 // @@@DEPRECATED_2.105@@@ 108 template Array(T) 109 if (isComplex!T) 110 { 111 pure nothrow @safe: 112 113 bool equals(T[] s1, T[] s2) 114 { 115 size_t len = s1.length; 116 if (len != s2.length) 117 return false; 118 for (size_t u = 0; u < len; u++) 119 { 120 if (!Floating!T.equals(s1[u], s2[u])) 121 return false; 122 } 123 return true; 124 } 125 126 int compare(T[] s1, T[] s2) 127 { 128 size_t len = s1.length; 129 if (s2.length < len) 130 len = s2.length; 131 for (size_t u = 0; u < len; u++) 132 { 133 if (int c = Floating!T.compare(s1[u], s2[u])) 134 return c; 135 } 136 return (s1.length > s2.length) - (s1.length < s2.length); 137 } 138 139 size_t hashOf(scope const T[] val) 140 { 141 return 0; 142 /+size_t hash = 0; 143 foreach (ref o; val) 144 { 145 hash = core.internal.hash.hashOf(Floating!T.hashOf(o), hash); 146 } 147 return hash;+/ 148 } 149 } 150 151 152 153 /* 154 TypeInfo information for built-in types. 155 A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and 156 equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example: 157 `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap 158 the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect 159 during compilation whether they have different signedness and override appropriately. For initializer, we 160 detect if we need to override. The overriding initializer should be nonzero. 161 */ 162 private class TypeInfoGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo, TypeInfoGeneric!Base) 163 if (T.sizeof == Base.sizeof && T.alignof == Base.alignof) 164 { 165 const: nothrow: pure: @trusted: 166 167 // Returns the type name. 168 override string toString() const pure nothrow @safe { return T.stringof; } 169 170 // `getHash` is the same for `Base` and `T`, introduce it just once. 171 static if (is(T == Base)) 172 /+override size_t getHash(scope const void* p) 173 { 174 static if (__traits(isFloating, T) || isComplex!T) 175 return Floating!T.hashOf(*cast(T*)p); 176 else 177 return hashOf(*cast(const T *)p); 178 }+/ 179 180 // `equals` is the same for `Base` and `T`, introduce it just once. 181 static if (is(T == Base)) 182 override bool equals(in void* p1, in void* p2) 183 { 184 static if (__traits(isFloating, T) || isComplex!T) 185 return Floating!T.equals(*cast(T*)p1, *cast(T*)p2); 186 else 187 return *cast(T *)p1 == *cast(T *)p2; 188 } 189 190 // `T` and `Base` may have different signedness, so this function is introduced conditionally. 191 static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) 192 /+override int compare(in void* p1, in void* p2) 193 { 194 static if (__traits(isFloating, T) || isComplex!T) 195 { 196 return Floating!T.compare(*cast(T*)p1, *cast(T*)p2); 197 } 198 else static if (T.sizeof < int.sizeof) 199 { 200 // Taking the difference will always fit in an int. 201 return int(*cast(T *) p1) - int(*cast(T *) p2); 202 } 203 else 204 { 205 auto lhs = *cast(T *) p1, rhs = *cast(T *) p2; 206 return (lhs > rhs) - (lhs < rhs); 207 } 208 +/ 209 210 static if (is(T == Base)) 211 override @property size_t tsize() nothrow pure 212 { 213 return T.sizeof; 214 } 215 216 static if (is(T == Base)) 217 /+override @property size_t talign() nothrow pure 218 { 219 return T.alignof; 220 }+/ 221 222 // Override initializer only if necessary. 223 static if (is(T == Base) || T.init != Base.init) 224 override const(void)[] initializer() @trusted 225 { 226 static if (__traits(isZeroInit, T)) 227 { 228 return (cast(void *)null)[0 .. T.sizeof]; 229 } 230 else 231 { 232 static immutable T[1] c; 233 return c; 234 } 235 } 236 237 // `swap` is the same for `Base` and `T`, so introduce only once. 238 static if (is(T == Base)) 239 /+override void swap(void *p1, void *p2) 240 { 241 auto t = *cast(T *) p1; 242 *cast(T *)p1 = *cast(T *)p2; 243 *cast(T *)p2 = t; 244 }+/ 245 246 static if (is(T == Base) || RTInfo!T != RTInfo!Base) 247 /+override @property immutable(void)* rtInfo() nothrow pure const @safe 248 { 249 return RTInfo!T; 250 }+/ 251 252 static if (is(T == Base)) 253 { 254 /+static if ((__traits(isFloating, T) && T.mant_dig != 64) || 255 (isComplex!T && T.re.mant_dig != 64)) 256 // FP types except 80-bit X87 are passed in SIMD register. 257 override @property uint flags() const { return 2; }+/ 258 } 259 } 260 261 262 /* 263 TypeInfo information for arrays of built-in types. 264 A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and 265 equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example: 266 `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap 267 the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect 268 during compilation whether they have different signedness and override appropriately. For initializer, we 269 detect if we need to override. The overriding initializer should be nonzero. 270 */ 271 version(LWDR_INTERNAL_ti_next) 272 private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo_Array, TypeInfoArrayGeneric!Base) 273 { 274 static if (is(T == Base)) 275 override bool opEquals(Object o) { return TypeInfo.opEquals(o); } 276 277 override string toString() const { return (T[]).stringof; } 278 279 /+static if (is(T == Base)) { 280 override size_t getHash(scope const void* p) @trusted const 281 { 282 return 0; 283 } 284 }+/ 285 286 static if (is(T == Base)) { 287 override bool equals(in void* p1, in void* p2) const 288 { 289 static if (__traits(isFloating, T) || isComplex!T) 290 { 291 return Array!T.equals(*cast(T[]*)p1, *cast(T[]*)p2); 292 } 293 else 294 { 295 import core.stdc.string; 296 auto s1 = *cast(T[]*)p1; 297 auto s2 = *cast(T[]*)p2; 298 return s1.length == s2.length && 299 memcmp(s1.ptr, s2.ptr, s1.length) == 0; 300 } 301 } 302 } 303 304 static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) { 305 /+override int compare(in void* p1, in void* p2) const 306 { 307 static if (__traits(isFloating, T) || isComplex!T) 308 { 309 return Array!T.compare(*cast(T[]*)p1, *cast(T[]*)p2); 310 } 311 else 312 { 313 auto s1 = *cast(T[]*)p1; 314 auto s2 = *cast(T[]*)p2; 315 auto len = s1.length; 316 317 if (s2.length < len) 318 len = s2.length; 319 for (size_t u = 0; u < len; u++) 320 { 321 if (int result = (s1[u] > s2[u]) - (s1[u] < s2[u])) 322 return result; 323 } 324 return (s1.length > s2.length) - (s1.length < s2.length); 325 } 326 }+/ 327 } 328 329 version(LWDR_INTERNAL_ti_next) 330 override @property inout(TypeInfo) next() inout 331 { 332 return cast(inout) typeid(T); 333 } 334 } 335 336 // void 337 class TypeInfo_v : TypeInfoGeneric!ubyte 338 { 339 const: nothrow: pure: @trusted: 340 341 override string toString() const pure nothrow @safe { return "void"; } 342 343 /+override size_t getHash(scope const void* p) 344 { 345 assert(0); 346 } 347 348 override @property uint flags() nothrow pure 349 { 350 return 1; 351 } 352 353 unittest 354 { 355 assert(typeid(void).toString == "void"); 356 assert(typeid(void).flags == 1); 357 }+/ 358 } 359 360 class TypeInfo_h : TypeInfoGeneric!ubyte {} 361 class TypeInfo_b : TypeInfoGeneric!(bool, ubyte) {} 362 class TypeInfo_g : TypeInfoGeneric!(byte, ubyte) {} 363 class TypeInfo_a : TypeInfoGeneric!(char, ubyte) {} 364 class TypeInfo_t : TypeInfoGeneric!ushort {} 365 class TypeInfo_s : TypeInfoGeneric!(short, ushort) {} 366 class TypeInfo_u : TypeInfoGeneric!(wchar, ushort) {} 367 class TypeInfo_w : TypeInfoGeneric!(dchar, uint) {} 368 class TypeInfo_k : TypeInfoGeneric!uint {} 369 class TypeInfo_i : TypeInfoGeneric!(int, uint) {} 370 class TypeInfo_m : TypeInfoGeneric!ulong {} 371 class TypeInfo_l : TypeInfoGeneric!(long, ulong) {} 372 static if (is(cent)) class TypeInfo_zi : TypeInfoGeneric!cent {} 373 static if (is(ucent)) class TypeInfo_zk : TypeInfoGeneric!ucent {} 374 375 // All simple floating-point types. 376 class TypeInfo_f : TypeInfoGeneric!float {} 377 class TypeInfo_d : TypeInfoGeneric!double {} 378 class TypeInfo_e : TypeInfoGeneric!real {} 379 380 version(LWDR_DynamicArray) { 381 382 class TypeInfo_Ah : TypeInfoArrayGeneric!ubyte {} 383 class TypeInfo_Ab : TypeInfoArrayGeneric!(bool, ubyte) {} 384 class TypeInfo_Ag : TypeInfoArrayGeneric!(byte, ubyte) {} 385 class TypeInfo_Aa : TypeInfoArrayGeneric!(char, ubyte) {} 386 class TypeInfo_Axa : TypeInfoArrayGeneric!(const char) {} 387 class TypeInfo_Aya : TypeInfoArrayGeneric!(immutable char) 388 { 389 // Must override this, otherwise "string" is returned. 390 override string toString() const { return "immutable(char)[]"; } 391 } 392 class TypeInfo_At : TypeInfoArrayGeneric!ushort {} 393 class TypeInfo_As : TypeInfoArrayGeneric!(short, ushort) {} 394 class TypeInfo_Au : TypeInfoArrayGeneric!(wchar, ushort) {} 395 class TypeInfo_Ak : TypeInfoArrayGeneric!uint {} 396 class TypeInfo_Ai : TypeInfoArrayGeneric!(int, uint) {} 397 class TypeInfo_Aw : TypeInfoArrayGeneric!(dchar, uint) {} 398 class TypeInfo_Am : TypeInfoArrayGeneric!ulong {} 399 class TypeInfo_Al : TypeInfoArrayGeneric!(long, ulong) {} 400 401 // Arrays of all simple floating-point types. 402 class TypeInfo_Af : TypeInfoArrayGeneric!float {} 403 class TypeInfo_Ad : TypeInfoArrayGeneric!double {} 404 class TypeInfo_Ae : TypeInfoArrayGeneric!real {} 405 } 406 407 // typeof(null) 408 class TypeInfo_n : TypeInfo 409 { 410 override string toString() const @safe { return "typeof(null)"; } 411 412 /+override size_t getHash(scope const void* p) const 413 { 414 return 0; 415 }+/ 416 417 override bool equals(in void* p1, in void* p2) const @trusted 418 { 419 return true; 420 } 421 422 /+override int compare(in void* p1, in void* p2) const @trusted 423 { 424 return 0; 425 }+/ 426 427 override @property size_t tsize() const 428 { 429 return typeof(null).sizeof; 430 } 431 432 override const(void)[] initializer() const @trusted 433 { 434 __gshared immutable void[typeof(null).sizeof] init; 435 return init; 436 } 437 438 /+override void swap(void *p1, void *p2) const @trusted 439 { 440 }+/ 441 442 //override @property immutable(void)* rtInfo() nothrow pure const @safe { return rtinfoNoPointers; } 443 444 unittest 445 { 446 with (typeid(typeof(null))) 447 { 448 assert(toString == "typeof(null)"); 449 assert(getHash(null) == 0); 450 assert(equals(null, null)); 451 assert(compare(null, null) == 0); 452 assert(tsize == typeof(null).sizeof); 453 assert(initializer == new ubyte[(void*).sizeof]); 454 assert(rtInfo == rtinfoNoPointers); 455 } 456 } 457 } 458 459 version(LWDR_DynamicArray) { 460 // void[] is a bit different, behaves like ubyte[] for comparison purposes. 461 class TypeInfo_Av : TypeInfo_Ah 462 { 463 override string toString() const { return "void[]"; } 464 465 version(LWDR_INTERNAL_ti_next) 466 override @property inout(TypeInfo) next() inout 467 { 468 return cast(inout) typeid(void); 469 } 470 471 unittest 472 { 473 assert(typeid(void[]).toString == "void[]"); 474 assert(typeid(void[]).next == typeid(void)); 475 } 476 } 477 }