| /** |
| * A few predefined implementations for primitive types and arrays thereof. Also a couple of helpers. |
| * |
| * Copyright: Copyright Kenji Hara 2014-. |
| * License: <a href="https://boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. |
| * Authors: Kenji Hara |
| * Source: $(DRUNTIMESRC rt/util/_typeinfo.d) |
| */ |
| module rt.util.typeinfo; |
| import rt.util.utility : d_cfloat, d_cdouble, d_creal, isComplex; |
| static import core.internal.hash; |
| |
| // Three-way compare for integrals: negative if `lhs < rhs`, positive if `lhs > rhs`, 0 otherwise. |
| pragma(inline, true) |
| private int cmp3(T)(const T lhs, const T rhs) |
| if (__traits(isIntegral, T)) |
| { |
| static if (T.sizeof < int.sizeof) |
| // Taking the difference will always fit in an int. |
| return int(lhs) - int(rhs); |
| else |
| return (lhs > rhs) - (lhs < rhs); |
| } |
| |
| // Three-way compare for real fp types. NaN is smaller than all valid numbers. |
| // Code is small and fast, see https://godbolt.org/z/fzb877 |
| pragma(inline, true) |
| private int cmp3(T)(const T d1, const T d2) |
| if (is(T == float) || is(T == double) || is(T == real)) |
| { |
| if (d2 != d2) |
| return d1 == d1; // 0 if both ar NaN, 1 if d1 is valid and d2 is NaN. |
| // If d1 is NaN, both comparisons are false so we get -1, as needed. |
| return (d1 > d2) - !(d1 >= d2); |
| } |
| |
| // Three-way compare for complex types. |
| pragma(inline, true) |
| private int cmp3(T)(const T f1, const T f2) |
| if (isComplex!T) |
| { |
| if (int result = cmp3(f1.re, f2.re)) |
| return result; |
| return cmp3(f1.im, f2.im); |
| } |
| |
| unittest |
| { |
| assert(cmp3(short.max, short.min) > 0); |
| assert(cmp3(42, 42) == 0); |
| assert(cmp3(int.max, int.min) > 0); |
| |
| double x, y; |
| assert(cmp3(x, y) == 0); |
| assert(cmp3(y, x) == 0); |
| x = 42; |
| assert(cmp3(x, y) > 0); |
| assert(cmp3(y, x) < 0); |
| y = 43; |
| assert(cmp3(x, y) < 0); |
| assert(cmp3(y, x) > 0); |
| y = 42; |
| assert(cmp3(x, y) == 0); |
| assert(cmp3(y, x) == 0); |
| |
| d_cdouble u, v; |
| assert(cmp3(u, v) == 0); |
| assert(cmp3(v, u) == 0); |
| u = d_cdouble(42, 42); |
| assert(cmp3(u, v) > 0); |
| assert(cmp3(v, u) < 0); |
| v = d_cdouble(43, 42); |
| assert(cmp3(u, v) < 0); |
| assert(cmp3(v, u) > 0); |
| v = d_cdouble(42, 43); |
| assert(cmp3(u, v) < 0); |
| assert(cmp3(v, u) > 0); |
| v = d_cdouble(42, 42); |
| assert(cmp3(u, v) == 0); |
| assert(cmp3(v, u) == 0); |
| } |
| |
| // @@@DEPRECATED_2.105@@@ |
| template Array(T) |
| if (isComplex!T) |
| { |
| pure nothrow @safe: |
| |
| bool equals(T[] s1, T[] s2) |
| { |
| size_t len = s1.length; |
| if (len != s2.length) |
| return false; |
| for (size_t u = 0; u < len; u++) |
| { |
| if (!Floating!T.equals(s1[u], s2[u])) |
| return false; |
| } |
| return true; |
| } |
| |
| int compare(T[] s1, T[] s2) |
| { |
| size_t len = s1.length; |
| if (s2.length < len) |
| len = s2.length; |
| for (size_t u = 0; u < len; u++) |
| { |
| if (int c = Floating!T.compare(s1[u], s2[u])) |
| return c; |
| } |
| return (s1.length > s2.length) - (s1.length < s2.length); |
| } |
| |
| size_t hashOf(scope const T[] val) |
| { |
| size_t hash = 0; |
| foreach (ref o; val) |
| { |
| hash = core.internal.hash.hashOf(Floating!T.hashOf(o), hash); |
| } |
| return hash; |
| } |
| } |
| |
| version (CoreUnittest) |
| { |
| alias TypeTuple(T...) = T; |
| } |
| unittest |
| { |
| // Bugzilla 13052 |
| |
| static struct SX(F) { F f; } |
| TypeInfo ti; |
| |
| // real types |
| foreach (F; TypeTuple!(float, double, real)) |
| (){ // workaround #2396 |
| alias S = SX!F; |
| F f1 = +0.0, |
| f2 = -0.0; |
| |
| assert(f1 == f2); |
| assert(f1 !is f2); |
| ti = typeid(F); |
| assert(ti.getHash(&f1) == ti.getHash(&f2)); |
| |
| F[] a1 = [f1, f1, f1]; |
| F[] a2 = [f2, f2, f2]; |
| assert(a1 == a2); |
| assert(a1 !is a2); |
| ti = typeid(F[]); |
| assert(ti.getHash(&a1) == ti.getHash(&a2)); |
| |
| F[][] aa1 = [a1, a1, a1]; |
| F[][] aa2 = [a2, a2, a2]; |
| assert(aa1 == aa2); |
| assert(aa1 !is aa2); |
| ti = typeid(F[][]); |
| assert(ti.getHash(&aa1) == ti.getHash(&aa2)); |
| |
| S s1 = {f1}, |
| s2 = {f2}; |
| assert(s1 == s2); |
| assert(s1 !is s2); |
| ti = typeid(S); |
| assert(ti.getHash(&s1) == ti.getHash(&s2)); |
| |
| S[] da1 = [S(f1), S(f1), S(f1)], |
| da2 = [S(f2), S(f2), S(f2)]; |
| assert(da1 == da2); |
| assert(da1 !is da2); |
| ti = typeid(S[]); |
| assert(ti.getHash(&da1) == ti.getHash(&da2)); |
| |
| S[3] sa1 = {f1}, |
| sa2 = {f2}; |
| assert(sa1 == sa2); |
| assert(sa1[] !is sa2[]); |
| ti = typeid(S[3]); |
| assert(ti.getHash(&sa1) == ti.getHash(&sa2)); |
| }(); |
| } |
| |
| // Reduces to `T` if `cond` is `true` or `U` otherwise. Consider moving elsewhere if useful. |
| private template Select(bool cond, T, U) |
| { |
| static if (cond) alias Select = T; |
| else alias Select = U; |
| } |
| |
| /* |
| TypeInfo information for built-in types. |
| |
| A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and |
| equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example: |
| `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap |
| the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect |
| during compilation whether they have different signedness and override appropriately. For initializer, we |
| detect if we need to override. The overriding initializer should be nonzero. |
| */ |
| private class TypeInfoGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo, TypeInfoGeneric!Base) |
| if (T.sizeof == Base.sizeof && T.alignof == Base.alignof) |
| { |
| const: nothrow: pure: @trusted: |
| |
| // Returns the type name. |
| override string toString() const pure nothrow @safe { return T.stringof; } |
| |
| // `getHash` is the same for `Base` and `T`, introduce it just once. |
| static if (is(T == Base)) |
| override size_t getHash(scope const void* p) |
| { |
| return hashOf(*cast(const T *)p); |
| } |
| |
| // `equals` is the same for `Base` and `T`, introduce it just once. |
| static if (is(T == Base)) |
| override bool equals(in void* p1, in void* p2) |
| { |
| return *cast(const T *)p1 == *cast(const T *)p2; |
| } |
| |
| // `T` and `Base` may have different signedness, so this function is introduced conditionally. |
| static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) |
| override int compare(in void* p1, in void* p2) |
| { |
| return cmp3(*cast(const T*) p1, *cast(const T*) p2); |
| } |
| |
| static if (is(T == Base)) |
| override @property size_t tsize() |
| { |
| return T.sizeof; |
| } |
| |
| static if (is(T == Base)) |
| override @property size_t talign() |
| { |
| return T.alignof; |
| } |
| |
| // Override initializer only if necessary. |
| static if (is(T == Base) || T.init != Base.init) |
| override const(void)[] initializer() |
| { |
| static if (__traits(isZeroInit, T)) |
| { |
| return (cast(void *)null)[0 .. T.sizeof]; |
| } |
| else |
| { |
| static immutable T[1] c; |
| return c; |
| } |
| } |
| |
| // `swap` is the same for `Base` and `T`, so introduce only once. |
| static if (is(T == Base)) |
| override void swap(void *p1, void *p2) |
| { |
| auto t = *cast(T *) p1; |
| *cast(T *)p1 = *cast(T *)p2; |
| *cast(T *)p2 = t; |
| } |
| |
| static if (is(T == Base) || RTInfo!T != RTInfo!Base) |
| override @property immutable(void)* rtInfo() |
| { |
| return RTInfo!T; |
| } |
| |
| static if (is(T == Base)) |
| { |
| static if ((__traits(isFloating, T) && T.mant_dig != 64) || |
| (isComplex!T && T.re.mant_dig != 64)) |
| // FP types except 80-bit X87 are passed in SIMD register. |
| override @property uint flags() const { return 2; } |
| } |
| } |
| |
| unittest |
| { |
| assert(typeid(int).toString == "int"); |
| |
| with (typeid(double)) |
| { |
| double a = 42, b = 43; |
| assert(equals(&a, &a)); |
| assert(!equals(&a, &b)); |
| assert(compare(&a, &a) == 0); |
| assert(compare(&a, &b) == -1); |
| assert(compare(&b, &a) == 1); |
| } |
| |
| with (typeid(short)) |
| { |
| short c = 42, d = 43; |
| assert(equals(&c, &c)); |
| assert(!equals(&c, &d)); |
| assert(compare(&c, &c) == 0); |
| assert(compare(&c, &d) == -1); |
| assert(compare(&d, &c) == 1); |
| assert(initializer.ptr is null); |
| assert(initializer.length == short.sizeof); |
| swap(&d, &c); |
| assert(c == 43 && d == 42); |
| } |
| } |
| |
| /* |
| TypeInfo information for arrays of built-in types. |
| |
| A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and |
| equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example: |
| `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap |
| the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect |
| during compilation whether they have different signedness and override appropriately. For initializer, we |
| detect if we need to override. The overriding initializer should be nonzero. |
| */ |
| private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo_Array, TypeInfoArrayGeneric!Base) |
| { |
| static if (is(T == Base)) |
| override bool opEquals(const Object o) const @safe nothrow { return TypeInfo.opEquals(cast(const TypeInfo) o); } |
| |
| alias opEquals = typeof(super).opEquals; |
| alias opEquals = TypeInfo.opEquals; |
| |
| override string toString() const { return (T[]).stringof; } |
| |
| static if (is(T == Base)) |
| override size_t getHash(scope const void* p) @trusted const |
| { |
| return hashOf(*cast(const T[]*) p); |
| } |
| |
| static if (is(T == Base)) |
| override bool equals(in void* p1, in void* p2) const |
| { |
| // Just reuse the builtin. |
| return *cast(const(T)[]*) p1 == *cast(const(T)[]*) p2; |
| } |
| |
| static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) |
| override int compare(in void* p1, in void* p2) const |
| { |
| // Can't reuse __cmp in object.d because that handles NaN differently. |
| // (Q: would it make sense to unify behaviors?) |
| // return __cmp(*cast(const T[]*) p1, *cast(const T[]*) p2); |
| auto lhs = *cast(const T[]*) p1; |
| auto rhs = *cast(const T[]*) p2; |
| size_t len = lhs.length; |
| if (rhs.length < len) |
| len = rhs.length; |
| for (size_t u = 0; u < len; u++) |
| { |
| if (int result = cmp3(lhs.ptr[u], rhs.ptr[u])) |
| return result; |
| } |
| return cmp3(lhs.length, rhs.length); } |
| |
| override @property inout(TypeInfo) next() inout |
| { |
| return cast(inout) typeid(T); |
| } |
| } |
| |
| unittest |
| { |
| assert(typeid(int[]) == typeid(int[])); |
| assert(typeid(int[]) != typeid(uint[])); |
| assert(typeid(int[]).toString == "int[]"); |
| |
| with (typeid(double[])) |
| { |
| double[] a = [ 1, 2, 3 ], b = [ 2, 3 ]; |
| assert(equals(&a, &a)); |
| assert(!equals(&a, &b)); |
| assert(compare(&a, &a) == 0); |
| assert(compare(&a, &b) == -1); |
| assert(compare(&b, &a) == 1); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Predefined TypeInfos |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| // void |
| class TypeInfo_v : TypeInfoGeneric!ubyte |
| { |
| const: nothrow: pure: @trusted: |
| |
| override string toString() const pure nothrow @safe { return "void"; } |
| |
| override size_t getHash(scope const void* p) |
| { |
| assert(0); |
| } |
| |
| override @property uint flags() nothrow pure |
| { |
| return 1; |
| } |
| } |
| |
| unittest |
| { |
| assert(typeid(void).toString == "void"); |
| assert(typeid(void).flags == 1); |
| } |
| |
| // All integrals. |
| class TypeInfo_h : TypeInfoGeneric!ubyte {} |
| class TypeInfo_b : TypeInfoGeneric!(bool, ubyte) {} |
| class TypeInfo_g : TypeInfoGeneric!(byte, ubyte) {} |
| class TypeInfo_a : TypeInfoGeneric!(char, ubyte) {} |
| class TypeInfo_t : TypeInfoGeneric!ushort {} |
| class TypeInfo_s : TypeInfoGeneric!(short, ushort) {} |
| class TypeInfo_u : TypeInfoGeneric!(wchar, ushort) {} |
| class TypeInfo_w : TypeInfoGeneric!(dchar, uint) {} |
| class TypeInfo_k : TypeInfoGeneric!uint {} |
| class TypeInfo_i : TypeInfoGeneric!(int, uint) {} |
| class TypeInfo_m : TypeInfoGeneric!ulong {} |
| class TypeInfo_l : TypeInfoGeneric!(long, ulong) {} |
| static if (is(cent)) class TypeInfo_zi : TypeInfoGeneric!cent {} |
| static if (is(ucent)) class TypeInfo_zk : TypeInfoGeneric!ucent {} |
| |
| // All simple floating-point types. |
| class TypeInfo_f : TypeInfoGeneric!float {} |
| class TypeInfo_d : TypeInfoGeneric!double {} |
| class TypeInfo_e : TypeInfoGeneric!real {} |
| |
| // All imaginary floating-point types. |
| |
| // ifloat @@@DEPRECATED_2.105@@@ |
| deprecated class TypeInfo_o : TypeInfoGeneric!float |
| { |
| override string toString() const pure nothrow @safe { return "ifloat"; } |
| } |
| |
| // idouble @@@DEPRECATED_2.105@@@ |
| deprecated class TypeInfo_p : TypeInfoGeneric!double |
| { |
| override string toString() const pure nothrow @safe { return "idouble"; } |
| } |
| |
| // ireal @@@DEPRECATED_2.105@@@ |
| deprecated class TypeInfo_j : TypeInfoGeneric!real |
| { |
| override string toString() const pure nothrow @safe { return "ireal"; } |
| } |
| |
| // All complex floating-point types. |
| |
| // cfloat @@@DEPRECATED_2.105@@@ |
| deprecated class TypeInfo_q : TypeInfoGeneric!d_cfloat |
| { |
| override string toString() const pure nothrow @safe { return "cfloat"; } |
| |
| const: nothrow: pure: @trusted: |
| static if (__traits(hasMember, TypeInfo, "argTypes")) |
| override int argTypes(out TypeInfo arg1, out TypeInfo arg2) |
| { |
| arg1 = typeid(double); |
| return 0; |
| } |
| } |
| |
| // cdouble @@@DEPRECATED_2.105@@@ |
| deprecated class TypeInfo_r : TypeInfoGeneric!d_cdouble |
| { |
| override string toString() const pure nothrow @safe { return "cdouble"; } |
| |
| const: nothrow: pure: @trusted: |
| static if (__traits(hasMember, TypeInfo, "argTypes")) |
| override int argTypes(out TypeInfo arg1, out TypeInfo arg2) |
| { |
| arg1 = typeid(double); |
| arg2 = typeid(double); |
| return 0; |
| } |
| } |
| |
| // creal @@@DEPRECATED_2.105@@@ |
| deprecated class TypeInfo_c : TypeInfoGeneric!d_creal |
| { |
| override string toString() const pure nothrow @safe { return "creal"; } |
| |
| const: nothrow: pure: @trusted: |
| static if (__traits(hasMember, TypeInfo, "argTypes")) |
| override int argTypes(out TypeInfo arg1, out TypeInfo arg2) |
| { |
| arg1 = typeid(real); |
| arg2 = typeid(real); |
| return 0; |
| } |
| } |
| |
| // Arrays of all integrals. |
| class TypeInfo_Ah : TypeInfoArrayGeneric!ubyte {} |
| class TypeInfo_Ab : TypeInfoArrayGeneric!(bool, ubyte) {} |
| class TypeInfo_Ag : TypeInfoArrayGeneric!(byte, ubyte) {} |
| class TypeInfo_Aa : TypeInfoArrayGeneric!(char, ubyte) {} |
| class TypeInfo_Axa : TypeInfoArrayGeneric!(const char) {} |
| class TypeInfo_Aya : TypeInfoArrayGeneric!(immutable char) |
| { |
| // Must override this, otherwise "string" is returned. |
| override string toString() const { return "immutable(char)[]"; } |
| } |
| class TypeInfo_At : TypeInfoArrayGeneric!ushort {} |
| class TypeInfo_As : TypeInfoArrayGeneric!(short, ushort) {} |
| class TypeInfo_Au : TypeInfoArrayGeneric!(wchar, ushort) {} |
| class TypeInfo_Ak : TypeInfoArrayGeneric!uint {} |
| class TypeInfo_Ai : TypeInfoArrayGeneric!(int, uint) {} |
| class TypeInfo_Aw : TypeInfoArrayGeneric!(dchar, uint) {} |
| class TypeInfo_Am : TypeInfoArrayGeneric!ulong {} |
| class TypeInfo_Al : TypeInfoArrayGeneric!(long, ulong) {} |
| |
| version (CoreUnittest) |
| private extern (C) void[] _adSort(void[] a, TypeInfo ti); |
| |
| unittest |
| { |
| assert(typeid(string).toString() == "immutable(char)[]"); |
| int[][] a = [[5,3,8,7], [2,5,3,8,7]]; |
| _adSort(*cast(void[]*)&a, typeid(a[0])); |
| assert(a == [[2,5,3,8,7], [5,3,8,7]]); |
| |
| a = [[5,3,8,7], [5,3,8]]; |
| _adSort(*cast(void[]*)&a, typeid(a[0])); |
| assert(a == [[5,3,8], [5,3,8,7]]); |
| } |
| |
| unittest |
| { |
| // https://issues.dlang.org/show_bug.cgi?id=13073: original code uses int subtraction which is susceptible to |
| // integer overflow, causing the following case to fail. |
| int[] a = [int.max, int.max]; |
| int[] b = [int.min, int.min]; |
| assert(a > b); |
| assert(b < a); |
| } |
| |
| unittest |
| { |
| // Original test case from issue 13073 |
| uint x = 0x22_DF_FF_FF; |
| uint y = 0xA2_DF_FF_FF; |
| assert(!(x < y && y < x)); |
| uint[] a = [x]; |
| uint[] b = [y]; |
| assert(!(a < b && b < a)); // Original failing case |
| uint[1] a1 = [x]; |
| uint[1] b1 = [y]; |
| assert(!(a1 < b1 && b1 < a1)); // Original failing case |
| } |
| |
| // Arrays of all simple floating-point types. |
| class TypeInfo_Af : TypeInfoArrayGeneric!float {} |
| class TypeInfo_Ad : TypeInfoArrayGeneric!double {} |
| class TypeInfo_Ae : TypeInfoArrayGeneric!real {} |
| |
| // Arrays of all imaginary floating-point types. |
| |
| // ifloat @@@DEPRECATED_2.105@@@ |
| deprecated class TypeInfo_Ao : TypeInfoArrayGeneric!float |
| { |
| override string toString() const pure nothrow @safe { return "ifloat[]"; } |
| } |
| |
| // idouble @@@DEPRECATED_2.105@@@ |
| deprecated class TypeInfo_Ap : TypeInfoArrayGeneric!double |
| { |
| override string toString() const pure nothrow @safe { return "idouble[]"; } |
| } |
| |
| // ireal @@@DEPRECATED_2.105@@@ |
| deprecated class TypeInfo_Aj : TypeInfoArrayGeneric!real |
| { |
| override string toString() const pure nothrow @safe { return "ireal[]"; } |
| } |
| |
| // Arrays of all complex floating-point types. |
| |
| // cfloat @@@DEPRECATED_2.105@@@ |
| deprecated class TypeInfo_Aq : TypeInfoArrayGeneric!d_cfloat |
| { |
| override string toString() const pure nothrow @safe { return "cfloat[]"; } |
| } |
| |
| // cdouble @@@DEPRECATED_2.105@@@ |
| deprecated class TypeInfo_Ar : TypeInfoArrayGeneric!d_cdouble |
| { |
| override string toString() const pure nothrow @safe { return "cdouble[]"; } |
| } |
| |
| // creal @@@DEPRECATED_2.105@@@ |
| deprecated class TypeInfo_Ac : TypeInfoArrayGeneric!d_creal |
| { |
| override string toString() const pure nothrow @safe { return "creal[]"; } |
| } |
| |
| // void[] is a bit different, behaves like ubyte[] for comparison purposes. |
| class TypeInfo_Av : TypeInfo_Ah |
| { |
| override string toString() const { return "void[]"; } |
| |
| override @property inout(TypeInfo) next() inout |
| { |
| return cast(inout) typeid(void); |
| } |
| |
| unittest |
| { |
| assert(typeid(void[]).toString == "void[]"); |
| assert(typeid(void[]).next == typeid(void)); |
| } |
| } |
| |
| // all delegates |
| unittest |
| { |
| assert(typeid(void delegate(int)).flags == 1); |
| } |
| |
| // typeof(null) |
| class TypeInfo_n : TypeInfo |
| { |
| const: pure: @nogc: nothrow: @safe: |
| |
| override string toString() { return "typeof(null)"; } |
| |
| override size_t getHash(scope const void*) { return 0; } |
| |
| override bool equals(in void*, in void*) { return true; } |
| |
| override int compare(in void*, in void*) { return 0; } |
| |
| override @property size_t tsize() { return typeof(null).sizeof; } |
| |
| override const(void)[] initializer() @trusted { return (cast(void *)null)[0 .. size_t.sizeof]; } |
| |
| override void swap(void*, void*) {} |
| |
| override @property immutable(void)* rtInfo() { return rtinfoNoPointers; } |
| } |
| |
| unittest |
| { |
| with (typeid(typeof(null))) |
| { |
| assert(toString == "typeof(null)"); |
| assert(getHash(null) == 0); |
| assert(equals(null, null)); |
| assert(compare(null, null) == 0); |
| assert(tsize == typeof(null).sizeof); |
| assert(initializer.ptr is null); |
| assert(initializer.length == typeof(null).sizeof); |
| assert(rtInfo == rtinfoNoPointers); |
| } |
| } |
| |
| // Test typeinfo for classes. |
| unittest |
| { |
| static class Bacon |
| { |
| int sizzle = 1; |
| override int opCmp(Object rhs) const |
| { |
| if (auto rhsb = cast(Bacon) rhs) |
| return (sizzle > rhsb.sizzle) - (sizzle < rhsb.sizzle); |
| return 0; |
| } |
| } |
| Object obj = new Bacon; |
| Bacon obj2 = new Bacon; |
| obj2.sizzle = 2; |
| auto dummy = new Object; |
| with (typeid(obj)) |
| { |
| assert(toString[$ - 6 .. $] == ".Bacon"); |
| assert(getHash(&obj) != 0); |
| assert(equals(&obj, &obj)); |
| assert(!equals(&obj, &obj2)); |
| assert(compare(&obj, &dummy) == 0); |
| assert(compare(&obj, &obj) == 0); |
| assert(compare(&obj, &obj2) == -1); |
| assert(compare(&obj2, &obj) == 1); |
| assert(tsize == Object.sizeof); |
| assert(rtInfo == RTInfo!Bacon); |
| assert(tsize == Object.sizeof); |
| assert(initializer.ptr !is null); |
| assert(initializer.length == __traits(classInstanceSize, Bacon)); |
| assert(flags == 1); |
| } |
| } |