1 /** 2 * A few predefined implementations for primitive types and arrays thereof. Also a couple of helpers. 3 * 4 * Copyright: Copyright Kenji Hara 2014-. 5 * License: <a href="https://boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. 6 * Authors: Kenji Hara 7 * Source: $(DRUNTIMESRC rt/util/_typeinfo.d) 8 */ 9 module rt.util.typeinfo; 10 import rt.util.utility : d_cfloat, d_cdouble, d_creal, isComplex; 11 static import core.internal.hash; 12 13 // Three-way compare for integrals: negative if `lhs < rhs`, positive if `lhs > rhs`, 0 otherwise. 14 pragma(inline, true) 15 private int cmp3(T)(const T lhs, const T rhs) 16 if (__traits(isIntegral, T)) 17 { 18 static if (T.sizeof < int.sizeof) 19 // Taking the difference will always fit in an int. 20 return int(lhs) - int(rhs); 21 else 22 return (lhs > rhs) - (lhs < rhs); 23 } 24 25 // Three-way compare for real fp types. NaN is smaller than all valid numbers. 26 // Code is small and fast, see https://godbolt.org/z/fzb877 27 pragma(inline, true) 28 private int cmp3(T)(const T d1, const T d2) 29 if (is(T == float) || is(T == double) || is(T == real)) 30 { 31 if (d2 != d2) 32 return d1 == d1; // 0 if both ar NaN, 1 if d1 is valid and d2 is NaN. 33 // If d1 is NaN, both comparisons are false so we get -1, as needed. 34 return (d1 > d2) - !(d1 >= d2); 35 } 36 37 // Three-way compare for complex types. 38 pragma(inline, true) 39 private int cmp3(T)(const T f1, const T f2) 40 if (isComplex!T) 41 { 42 if (int result = cmp3(f1.re, f2.re)) 43 return result; 44 return cmp3(f1.im, f2.im); 45 } 46 47 unittest 48 { 49 assert(cmp3(short.max, short.min) > 0); 50 assert(cmp3(42, 42) == 0); 51 assert(cmp3(int.max, int.min) > 0); 52 53 double x, y; 54 assert(cmp3(x, y) == 0); 55 assert(cmp3(y, x) == 0); 56 x = 42; 57 assert(cmp3(x, y) > 0); 58 assert(cmp3(y, x) < 0); 59 y = 43; 60 assert(cmp3(x, y) < 0); 61 assert(cmp3(y, x) > 0); 62 y = 42; 63 assert(cmp3(x, y) == 0); 64 assert(cmp3(y, x) == 0); 65 66 d_cdouble u, v; 67 assert(cmp3(u, v) == 0); 68 assert(cmp3(v, u) == 0); 69 u = d_cdouble(42, 42); 70 assert(cmp3(u, v) > 0); 71 assert(cmp3(v, u) < 0); 72 v = d_cdouble(43, 42); 73 assert(cmp3(u, v) < 0); 74 assert(cmp3(v, u) > 0); 75 v = d_cdouble(42, 43); 76 assert(cmp3(u, v) < 0); 77 assert(cmp3(v, u) > 0); 78 v = d_cdouble(42, 42); 79 assert(cmp3(u, v) == 0); 80 assert(cmp3(v, u) == 0); 81 } 82 83 // @@@DEPRECATED_2.105@@@ 84 template Array(T) 85 if (isComplex!T) 86 { 87 pure nothrow @safe: 88 89 bool equals(T[] s1, T[] s2) 90 { 91 size_t len = s1.length; 92 if (len != s2.length) 93 return false; 94 for (size_t u = 0; u < len; u++) 95 { 96 if (!Floating!T.equals(s1[u], s2[u])) 97 return false; 98 } 99 return true; 100 } 101 102 int compare(T[] s1, T[] s2) 103 { 104 size_t len = s1.length; 105 if (s2.length < len) 106 len = s2.length; 107 for (size_t u = 0; u < len; u++) 108 { 109 if (int c = Floating!T.compare(s1[u], s2[u])) 110 return c; 111 } 112 return (s1.length > s2.length) - (s1.length < s2.length); 113 } 114 115 size_t hashOf(scope const T[] val) 116 { 117 size_t hash = 0; 118 foreach (ref o; val) 119 { 120 hash = core.internal.hash.hashOf(Floating!T.hashOf(o), hash); 121 } 122 return hash; 123 } 124 } 125 126 version (CoreUnittest) 127 { 128 alias TypeTuple(T...) = T; 129 } 130 unittest 131 { 132 // Bugzilla 13052 133 134 static struct SX(F) { F f; } 135 TypeInfo ti; 136 137 // real types 138 foreach (F; TypeTuple!(float, double, real)) 139 (){ // workaround #2396 140 alias S = SX!F; 141 F f1 = +0.0, 142 f2 = -0.0; 143 144 assert(f1 == f2); 145 assert(f1 !is f2); 146 ti = typeid(F); 147 assert(ti.getHash(&f1) == ti.getHash(&f2)); 148 149 F[] a1 = [f1, f1, f1]; 150 F[] a2 = [f2, f2, f2]; 151 assert(a1 == a2); 152 assert(a1 !is a2); 153 ti = typeid(F[]); 154 assert(ti.getHash(&a1) == ti.getHash(&a2)); 155 156 F[][] aa1 = [a1, a1, a1]; 157 F[][] aa2 = [a2, a2, a2]; 158 assert(aa1 == aa2); 159 assert(aa1 !is aa2); 160 ti = typeid(F[][]); 161 assert(ti.getHash(&aa1) == ti.getHash(&aa2)); 162 163 S s1 = {f1}, 164 s2 = {f2}; 165 assert(s1 == s2); 166 assert(s1 !is s2); 167 ti = typeid(S); 168 assert(ti.getHash(&s1) == ti.getHash(&s2)); 169 170 S[] da1 = [S(f1), S(f1), S(f1)], 171 da2 = [S(f2), S(f2), S(f2)]; 172 assert(da1 == da2); 173 assert(da1 !is da2); 174 ti = typeid(S[]); 175 assert(ti.getHash(&da1) == ti.getHash(&da2)); 176 177 S[3] sa1 = {f1}, 178 sa2 = {f2}; 179 assert(sa1 == sa2); 180 assert(sa1[] !is sa2[]); 181 ti = typeid(S[3]); 182 assert(ti.getHash(&sa1) == ti.getHash(&sa2)); 183 }(); 184 } 185 186 // Reduces to `T` if `cond` is `true` or `U` otherwise. Consider moving elsewhere if useful. 187 private template Select(bool cond, T, U) 188 { 189 static if (cond) alias Select = T; 190 else alias Select = U; 191 } 192 193 /* 194 TypeInfo information for built-in types. 195 196 A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and 197 equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example: 198 `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap 199 the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect 200 during compilation whether they have different signedness and override appropriately. For initializer, we 201 detect if we need to override. The overriding initializer should be nonzero. 202 */ 203 private class TypeInfoGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo, TypeInfoGeneric!Base) 204 if (T.sizeof == Base.sizeof && T.alignof == Base.alignof) 205 { 206 const: nothrow: pure: @trusted: 207 208 // Returns the type name. 209 override string toString() const pure nothrow @safe { return T.stringof; } 210 211 // `getHash` is the same for `Base` and `T`, introduce it just once. 212 static if (is(T == Base)) 213 override size_t getHash(scope const void* p) 214 { 215 return hashOf(*cast(const T *)p); 216 } 217 218 // `equals` is the same for `Base` and `T`, introduce it just once. 219 static if (is(T == Base)) 220 override bool equals(in void* p1, in void* p2) 221 { 222 return *cast(const T *)p1 == *cast(const T *)p2; 223 } 224 225 // `T` and `Base` may have different signedness, so this function is introduced conditionally. 226 static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) 227 override int compare(in void* p1, in void* p2) 228 { 229 return cmp3(*cast(const T*) p1, *cast(const T*) p2); 230 } 231 232 static if (is(T == Base)) 233 override @property size_t tsize() 234 { 235 return T.sizeof; 236 } 237 238 static if (is(T == Base)) 239 override @property size_t talign() 240 { 241 return T.alignof; 242 } 243 244 // Override initializer only if necessary. 245 static if (is(T == Base) || T.init != Base.init) 246 override const(void)[] initializer() 247 { 248 static if (__traits(isZeroInit, T)) 249 { 250 return (cast(void *)null)[0 .. T.sizeof]; 251 } 252 else 253 { 254 static immutable T[1] c; 255 return c; 256 } 257 } 258 259 // `swap` is the same for `Base` and `T`, so introduce only once. 260 static if (is(T == Base)) 261 override void swap(void *p1, void *p2) 262 { 263 auto t = *cast(T *) p1; 264 *cast(T *)p1 = *cast(T *)p2; 265 *cast(T *)p2 = t; 266 } 267 268 static if (is(T == Base) || RTInfo!T != RTInfo!Base) 269 override @property immutable(void)* rtInfo() 270 { 271 return RTInfo!T; 272 } 273 274 static if (is(T == Base)) 275 { 276 static if ((__traits(isFloating, T) && T.mant_dig != 64) || 277 (isComplex!T && T.re.mant_dig != 64)) 278 // FP types except 80-bit X87 are passed in SIMD register. 279 override @property uint flags() const { return 2; } 280 } 281 } 282 283 unittest 284 { 285 assert(typeid(int).toString == "int"); 286 287 with (typeid(double)) 288 { 289 double a = 42, b = 43; 290 assert(equals(&a, &a)); 291 assert(!equals(&a, &b)); 292 assert(compare(&a, &a) == 0); 293 assert(compare(&a, &b) == -1); 294 assert(compare(&b, &a) == 1); 295 } 296 297 with (typeid(short)) 298 { 299 short c = 42, d = 43; 300 assert(equals(&c, &c)); 301 assert(!equals(&c, &d)); 302 assert(compare(&c, &c) == 0); 303 assert(compare(&c, &d) == -1); 304 assert(compare(&d, &c) == 1); 305 assert(initializer.ptr is null); 306 assert(initializer.length == short.sizeof); 307 swap(&d, &c); 308 assert(c == 43 && d == 42); 309 } 310 } 311 312 /* 313 TypeInfo information for arrays of built-in types. 314 315 A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and 316 equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example: 317 `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap 318 the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect 319 during compilation whether they have different signedness and override appropriately. For initializer, we 320 detect if we need to override. The overriding initializer should be nonzero. 321 */ 322 private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo_Array, TypeInfoArrayGeneric!Base) 323 { 324 static if (is(T == Base)) 325 override bool opEquals(const Object o) const @safe nothrow { return TypeInfo.opEquals(cast(const TypeInfo) o); } 326 327 alias opEquals = typeof(super).opEquals; 328 alias opEquals = TypeInfo.opEquals; 329 330 override string toString() const { return (T[]).stringof; } 331 332 static if (is(T == Base)) 333 override size_t getHash(scope const void* p) @trusted const 334 { 335 return hashOf(*cast(const T[]*) p); 336 } 337 338 static if (is(T == Base)) 339 override bool equals(in void* p1, in void* p2) const 340 { 341 // Just reuse the builtin. 342 return *cast(const(T)[]*) p1 == *cast(const(T)[]*) p2; 343 } 344 345 static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) 346 override int compare(in void* p1, in void* p2) const 347 { 348 // Can't reuse __cmp in object.d because that handles NaN differently. 349 // (Q: would it make sense to unify behaviors?) 350 // return __cmp(*cast(const T[]*) p1, *cast(const T[]*) p2); 351 auto lhs = *cast(const T[]*) p1; 352 auto rhs = *cast(const T[]*) p2; 353 size_t len = lhs.length; 354 if (rhs.length < len) 355 len = rhs.length; 356 for (size_t u = 0; u < len; u++) 357 { 358 if (int result = cmp3(lhs.ptr[u], rhs.ptr[u])) 359 return result; 360 } 361 return cmp3(lhs.length, rhs.length); } 362 363 override @property inout(TypeInfo) next() inout 364 { 365 return cast(inout) typeid(T); 366 } 367 } 368 369 unittest 370 { 371 assert(typeid(int[]) == typeid(int[])); 372 assert(typeid(int[]) != typeid(uint[])); 373 assert(typeid(int[]).toString == "int[]"); 374 375 with (typeid(double[])) 376 { 377 double[] a = [ 1, 2, 3 ], b = [ 2, 3 ]; 378 assert(equals(&a, &a)); 379 assert(!equals(&a, &b)); 380 assert(compare(&a, &a) == 0); 381 assert(compare(&a, &b) == -1); 382 assert(compare(&b, &a) == 1); 383 } 384 } 385 386 //////////////////////////////////////////////////////////////////////////////// 387 // Predefined TypeInfos 388 //////////////////////////////////////////////////////////////////////////////// 389 390 // void 391 class TypeInfo_v : TypeInfoGeneric!ubyte 392 { 393 const: nothrow: pure: @trusted: 394 395 override string toString() const pure nothrow @safe { return "void"; } 396 397 override size_t getHash(scope const void* p) 398 { 399 assert(0); 400 } 401 402 override @property uint flags() nothrow pure 403 { 404 return 1; 405 } 406 } 407 408 unittest 409 { 410 assert(typeid(void).toString == "void"); 411 assert(typeid(void).flags == 1); 412 } 413 414 // All integrals. 415 class TypeInfo_h : TypeInfoGeneric!ubyte {} 416 class TypeInfo_b : TypeInfoGeneric!(bool, ubyte) {} 417 class TypeInfo_g : TypeInfoGeneric!(byte, ubyte) {} 418 class TypeInfo_a : TypeInfoGeneric!(char, ubyte) {} 419 class TypeInfo_t : TypeInfoGeneric!ushort {} 420 class TypeInfo_s : TypeInfoGeneric!(short, ushort) {} 421 class TypeInfo_u : TypeInfoGeneric!(wchar, ushort) {} 422 class TypeInfo_w : TypeInfoGeneric!(dchar, uint) {} 423 class TypeInfo_k : TypeInfoGeneric!uint {} 424 class TypeInfo_i : TypeInfoGeneric!(int, uint) {} 425 class TypeInfo_m : TypeInfoGeneric!ulong {} 426 class TypeInfo_l : TypeInfoGeneric!(long, ulong) {} 427 static if (is(cent)) class TypeInfo_zi : TypeInfoGeneric!cent {} 428 static if (is(ucent)) class TypeInfo_zk : TypeInfoGeneric!ucent {} 429 430 // All simple floating-point types. 431 class TypeInfo_f : TypeInfoGeneric!float {} 432 class TypeInfo_d : TypeInfoGeneric!double {} 433 class TypeInfo_e : TypeInfoGeneric!real {} 434 435 // All imaginary floating-point types. 436 437 // ifloat @@@DEPRECATED_2.105@@@ 438 deprecated class TypeInfo_o : TypeInfoGeneric!float 439 { 440 override string toString() const pure nothrow @safe { return "ifloat"; } 441 } 442 443 // idouble @@@DEPRECATED_2.105@@@ 444 deprecated class TypeInfo_p : TypeInfoGeneric!double 445 { 446 override string toString() const pure nothrow @safe { return "idouble"; } 447 } 448 449 // ireal @@@DEPRECATED_2.105@@@ 450 deprecated class TypeInfo_j : TypeInfoGeneric!real 451 { 452 override string toString() const pure nothrow @safe { return "ireal"; } 453 } 454 455 // All complex floating-point types. 456 457 // cfloat @@@DEPRECATED_2.105@@@ 458 deprecated class TypeInfo_q : TypeInfoGeneric!d_cfloat 459 { 460 override string toString() const pure nothrow @safe { return "cfloat"; } 461 462 const: nothrow: pure: @trusted: 463 static if (__traits(hasMember, TypeInfo, "argTypes")) 464 override int argTypes(out TypeInfo arg1, out TypeInfo arg2) 465 { 466 arg1 = typeid(double); 467 return 0; 468 } 469 } 470 471 // cdouble @@@DEPRECATED_2.105@@@ 472 deprecated class TypeInfo_r : TypeInfoGeneric!d_cdouble 473 { 474 override string toString() const pure nothrow @safe { return "cdouble"; } 475 476 const: nothrow: pure: @trusted: 477 static if (__traits(hasMember, TypeInfo, "argTypes")) 478 override int argTypes(out TypeInfo arg1, out TypeInfo arg2) 479 { 480 arg1 = typeid(double); 481 arg2 = typeid(double); 482 return 0; 483 } 484 } 485 486 // creal @@@DEPRECATED_2.105@@@ 487 deprecated class TypeInfo_c : TypeInfoGeneric!d_creal 488 { 489 override string toString() const pure nothrow @safe { return "creal"; } 490 491 const: nothrow: pure: @trusted: 492 static if (__traits(hasMember, TypeInfo, "argTypes")) 493 override int argTypes(out TypeInfo arg1, out TypeInfo arg2) 494 { 495 arg1 = typeid(real); 496 arg2 = typeid(real); 497 return 0; 498 } 499 } 500 501 // Arrays of all integrals. 502 class TypeInfo_Ah : TypeInfoArrayGeneric!ubyte {} 503 class TypeInfo_Ab : TypeInfoArrayGeneric!(bool, ubyte) {} 504 class TypeInfo_Ag : TypeInfoArrayGeneric!(byte, ubyte) {} 505 class TypeInfo_Aa : TypeInfoArrayGeneric!(char, ubyte) {} 506 class TypeInfo_Axa : TypeInfoArrayGeneric!(const char) {} 507 class TypeInfo_Aya : TypeInfoArrayGeneric!(immutable char) 508 { 509 // Must override this, otherwise "string" is returned. 510 override string toString() const { return "immutable(char)[]"; } 511 } 512 class TypeInfo_At : TypeInfoArrayGeneric!ushort {} 513 class TypeInfo_As : TypeInfoArrayGeneric!(short, ushort) {} 514 class TypeInfo_Au : TypeInfoArrayGeneric!(wchar, ushort) {} 515 class TypeInfo_Ak : TypeInfoArrayGeneric!uint {} 516 class TypeInfo_Ai : TypeInfoArrayGeneric!(int, uint) {} 517 class TypeInfo_Aw : TypeInfoArrayGeneric!(dchar, uint) {} 518 class TypeInfo_Am : TypeInfoArrayGeneric!ulong {} 519 class TypeInfo_Al : TypeInfoArrayGeneric!(long, ulong) {} 520 521 version (CoreUnittest) 522 private extern (C) void[] _adSort(void[] a, TypeInfo ti); 523 524 unittest 525 { 526 assert(typeid(string).toString() == "immutable(char)[]"); 527 int[][] a = [[5,3,8,7], [2,5,3,8,7]]; 528 _adSort(*cast(void[]*)&a, typeid(a[0])); 529 assert(a == [[2,5,3,8,7], [5,3,8,7]]); 530 531 a = [[5,3,8,7], [5,3,8]]; 532 _adSort(*cast(void[]*)&a, typeid(a[0])); 533 assert(a == [[5,3,8], [5,3,8,7]]); 534 } 535 536 unittest 537 { 538 // https://issues.dlang.org/show_bug.cgi?id=13073: original code uses int subtraction which is susceptible to 539 // integer overflow, causing the following case to fail. 540 int[] a = [int.max, int.max]; 541 int[] b = [int.min, int.min]; 542 assert(a > b); 543 assert(b < a); 544 } 545 546 unittest 547 { 548 // Original test case from issue 13073 549 uint x = 0x22_DF_FF_FF; 550 uint y = 0xA2_DF_FF_FF; 551 assert(!(x < y && y < x)); 552 uint[] a = [x]; 553 uint[] b = [y]; 554 assert(!(a < b && b < a)); // Original failing case 555 uint[1] a1 = [x]; 556 uint[1] b1 = [y]; 557 assert(!(a1 < b1 && b1 < a1)); // Original failing case 558 } 559 560 // Arrays of all simple floating-point types. 561 class TypeInfo_Af : TypeInfoArrayGeneric!float {} 562 class TypeInfo_Ad : TypeInfoArrayGeneric!double {} 563 class TypeInfo_Ae : TypeInfoArrayGeneric!real {} 564 565 // Arrays of all imaginary floating-point types. 566 567 // ifloat @@@DEPRECATED_2.105@@@ 568 deprecated class TypeInfo_Ao : TypeInfoArrayGeneric!float 569 { 570 override string toString() const pure nothrow @safe { return "ifloat[]"; } 571 } 572 573 // idouble @@@DEPRECATED_2.105@@@ 574 deprecated class TypeInfo_Ap : TypeInfoArrayGeneric!double 575 { 576 override string toString() const pure nothrow @safe { return "idouble[]"; } 577 } 578 579 // ireal @@@DEPRECATED_2.105@@@ 580 deprecated class TypeInfo_Aj : TypeInfoArrayGeneric!real 581 { 582 override string toString() const pure nothrow @safe { return "ireal[]"; } 583 } 584 585 // Arrays of all complex floating-point types. 586 587 // cfloat @@@DEPRECATED_2.105@@@ 588 deprecated class TypeInfo_Aq : TypeInfoArrayGeneric!d_cfloat 589 { 590 override string toString() const pure nothrow @safe { return "cfloat[]"; } 591 } 592 593 // cdouble @@@DEPRECATED_2.105@@@ 594 deprecated class TypeInfo_Ar : TypeInfoArrayGeneric!d_cdouble 595 { 596 override string toString() const pure nothrow @safe { return "cdouble[]"; } 597 } 598 599 // creal @@@DEPRECATED_2.105@@@ 600 deprecated class TypeInfo_Ac : TypeInfoArrayGeneric!d_creal 601 { 602 override string toString() const pure nothrow @safe { return "creal[]"; } 603 } 604 605 // void[] is a bit different, behaves like ubyte[] for comparison purposes. 606 class TypeInfo_Av : TypeInfo_Ah 607 { 608 override string toString() const { return "void[]"; } 609 610 override @property inout(TypeInfo) next() inout 611 { 612 return cast(inout) typeid(void); 613 } 614 615 unittest 616 { 617 assert(typeid(void[]).toString == "void[]"); 618 assert(typeid(void[]).next == typeid(void)); 619 } 620 } 621 622 // all delegates 623 unittest 624 { 625 assert(typeid(void delegate(int)).flags == 1); 626 } 627 628 // typeof(null) 629 class TypeInfo_n : TypeInfo 630 { 631 const: pure: @nogc: nothrow: @safe: 632 633 override string toString() { return "typeof(null)"; } 634 635 override size_t getHash(scope const void*) { return 0; } 636 637 override bool equals(in void*, in void*) { return true; } 638 639 override int compare(in void*, in void*) { return 0; } 640 641 override @property size_t tsize() { return typeof(null).sizeof; } 642 643 override const(void)[] initializer() @trusted { return (cast(void *)null)[0 .. size_t.sizeof]; } 644 645 override void swap(void*, void*) {} 646 647 override @property immutable(void)* rtInfo() { return rtinfoNoPointers; } 648 } 649 650 unittest 651 { 652 with (typeid(typeof(null))) 653 { 654 assert(toString == "typeof(null)"); 655 assert(getHash(null) == 0); 656 assert(equals(null, null)); 657 assert(compare(null, null) == 0); 658 assert(tsize == typeof(null).sizeof); 659 assert(initializer.ptr is null); 660 assert(initializer.length == typeof(null).sizeof); 661 assert(rtInfo == rtinfoNoPointers); 662 } 663 } 664 665 // Test typeinfo for classes. 666 unittest 667 { 668 static class Bacon 669 { 670 int sizzle = 1; 671 override int opCmp(Object rhs) const 672 { 673 if (auto rhsb = cast(Bacon) rhs) 674 return (sizzle > rhsb.sizzle) - (sizzle < rhsb.sizzle); 675 return 0; 676 } 677 } 678 Object obj = new Bacon; 679 Bacon obj2 = new Bacon; 680 obj2.sizzle = 2; 681 auto dummy = new Object; 682 with (typeid(obj)) 683 { 684 assert(toString[$ - 6 .. $] == ".Bacon"); 685 assert(getHash(&obj) != 0); 686 assert(equals(&obj, &obj)); 687 assert(!equals(&obj, &obj2)); 688 assert(compare(&obj, &dummy) == 0); 689 assert(compare(&obj, &obj) == 0); 690 assert(compare(&obj, &obj2) == -1); 691 assert(compare(&obj2, &obj) == 1); 692 assert(tsize == Object.sizeof); 693 assert(rtInfo == RTInfo!Bacon); 694 assert(tsize == Object.sizeof); 695 assert(initializer.ptr !is null); 696 assert(initializer.length == __traits(classInstanceSize, Bacon)); 697 assert(flags == 1); 698 } 699 }