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