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
equals(T[]s1,T[]s2)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
compare(T[]s1,T[]s2)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
hashOf(scope const T[]val)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
version(CoreUnittest)126 version (CoreUnittest)
127 {
128 alias TypeTuple(T...) = T;
129 }
130 unittest
131 {
132 // Bugzilla 13052
133
SX(F)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.
Select(bool cond,T,U)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.
toString()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))
getHash(scope const void * p)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))
equals(in void * p1,in void * p2)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))
compare(in void * p1,in void * p2)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))
tsize()233 override @property size_t tsize()
234 {
235 return T.sizeof;
236 }
237
238 static if (is(T == Base))
talign()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)
initializer()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))
swap(void * p1,void * p2)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)
immutable(void)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.
flags()279 override @property uint flags() const { return 2; }
280 }
281 }
282
283 unittest
284 {
285 assert(typeid(int).toString == "int");
286
with(typeid (double))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
with(typeid (short))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))
opEquals(const Object o)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
toString()330 override string toString() const { return (T[]).stringof; }
331
332 static if (is(T == Base))
getHash(scope const void * p)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))
equals(in void * p1,in void * p2)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))
compare(in void * p1,in void * p2)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
inout(TypeInfo)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
with(typeid (double[]))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
toString()395 override string toString() const pure nothrow @safe { return "void"; }
396
getHash(scope const void * p)397 override size_t getHash(scope const void* p)
398 {
399 assert(0);
400 }
401
flags()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 {
toString()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 {
toString()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 {
toString()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 {
toString()460 override string toString() const pure nothrow @safe { return "cfloat"; }
461
462 const: nothrow: pure: @trusted:
463 static if (__traits(hasMember, TypeInfo, "argTypes"))
argTypes(out TypeInfo arg1,out TypeInfo arg2)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 {
toString()474 override string toString() const pure nothrow @safe { return "cdouble"; }
475
476 const: nothrow: pure: @trusted:
477 static if (__traits(hasMember, TypeInfo, "argTypes"))
argTypes(out TypeInfo arg1,out TypeInfo arg2)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 {
toString()489 override string toString() const pure nothrow @safe { return "creal"; }
490
491 const: nothrow: pure: @trusted:
492 static if (__traits(hasMember, TypeInfo, "argTypes"))
argTypes(out TypeInfo arg1,out TypeInfo arg2)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.
toString()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 {
toString()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 {
toString()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 {
toString()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 {
toString()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 {
toString()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 {
toString()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 {
toString()608 override string toString() const { return "void[]"; }
609
inout(TypeInfo)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
toString()633 override string toString() { return "typeof(null)"; }
634
getHash(scope const void *)635 override size_t getHash(scope const void*) { return 0; }
636
equals(in void *,in void *)637 override bool equals(in void*, in void*) { return true; }
638
compare(in void *,in void *)639 override int compare(in void*, in void*) { return 0; }
640
tsize()641 override @property size_t tsize() { return typeof(null).sizeof; }
642
initializer()643 override const(void)[] initializer() @trusted { return (cast(void *)null)[0 .. size_t.sizeof]; }
644
swap(void *,void *)645 override void swap(void*, void*) {}
646
immutable(void)647 override @property immutable(void)* rtInfo() { return rtinfoNoPointers; }
648 }
649
650 unittest
651 {
with(typeid (typeof (null)))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;
opCmp(Object rhs)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;
with(typeid (obj))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 }
700