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 }