From 8d19a518ca63d131a0d11a6a4590e59b5db23ce1 Mon Sep 17 00:00:00 2001 From: JinShil Date: Thu, 6 Jun 2019 21:24:01 +0900 Subject: [PATCH] Refactor object.d: Move __cmp and __equals to rt --- mak/COPY | 3 + mak/SRCS | 3 + mak/WINDOWS | 7 + src/object.d | 419 +---------------------------------- src/rt/array/comparison.d | 248 +++++++++++++++++++++ src/rt/array/equality.d | 192 ++++++++++++++++ test/profile/mytrace.def.exp | 2 +- 7 files changed, 456 insertions(+), 418 deletions(-) create mode 100644 src/rt/array/comparison.d create mode 100644 src/rt/array/equality.d diff --git a/mak/COPY b/mak/COPY index c3d65a07d36..2de8a3c228d 100644 --- a/mak/COPY +++ b/mak/COPY @@ -403,4 +403,7 @@ COPY=\ $(IMPDIR)\core\sys\windows\wtsapi32.d \ $(IMPDIR)\core\sys\windows\wtypes.d \ \ + $(IMPDIR)\rt\array\comparison.d \ + $(IMPDIR)\rt\array\equality.d \ + \ $(IMPDIR)\etc\linux\memoryerror.d diff --git a/mak/SRCS b/mak/SRCS index 782dbf33ba1..e068b323c1f 100644 --- a/mak/SRCS +++ b/mak/SRCS @@ -451,6 +451,9 @@ SRCS=\ src\rt\tracegc.d \ src\rt\unwind.d \ \ + src\rt\array\comparison.d \ + src\rt\array\equality.d \ + \ src\rt\backtrace\dwarf.d \ src\rt\backtrace\elf.d \ src\rt\backtrace\macho.d \ diff --git a/mak/WINDOWS b/mak/WINDOWS index aa81456d161..276aa9e424a 100644 --- a/mak/WINDOWS +++ b/mak/WINDOWS @@ -55,6 +55,7 @@ copydir: $(IMPDIR) mkdir $(IMPDIR)\core\sys\solaris\sys mkdir $(IMPDIR)\core\sys\windows mkdir $(IMPDIR)\etc\linux + mkdir $(IMPDIR)\rt\array copy: $(COPY) @@ -1186,3 +1187,9 @@ $(IMPDIR)\core\sys\windows\wtypes.d : src\core\sys\windows\wtypes.d $(IMPDIR)\etc\linux\memoryerror.d : src\etc\linux\memoryerror.d copy $** $@ + +$(IMPDIR)\rt\array\comparison.d : src\rt\array\comparison.d + copy $** $@ + +$(IMPDIR)\rt\array\equality.d : src\rt\array\equality.d + copy $** $@ diff --git a/src/object.d b/src/object.d index 43bdf6c285d..b3f2822f39c 100644 --- a/src/object.d +++ b/src/object.d @@ -38,78 +38,8 @@ alias dstring = immutable(dchar)[]; version (D_ObjectiveC) public import core.attribute : selector; -int __cmp(T)(scope const T[] lhs, scope const T[] rhs) @trusted - if (__traits(isScalar, T)) -{ - // Compute U as the implementation type for T - static if (is(T == ubyte) || is(T == void) || is(T == bool)) - alias U = char; - else static if (is(T == wchar)) - alias U = ushort; - else static if (is(T == dchar)) - alias U = uint; - else static if (is(T == ifloat)) - alias U = float; - else static if (is(T == idouble)) - alias U = double; - else static if (is(T == ireal)) - alias U = real; - else - alias U = T; - - static if (is(U == char)) - { - import core.internal.string : dstrcmp; - return dstrcmp(cast(char[]) lhs, cast(char[]) rhs); - } - else static if (!is(U == T)) - { - // Reuse another implementation - return __cmp(cast(U[]) lhs, cast(U[]) rhs); - } - else - { - version (BigEndian) - static if (__traits(isUnsigned, T) ? !is(T == __vector) : is(T : P*, P)) - { - if (!__ctfe) - { - import core.stdc.string : memcmp; - int c = memcmp(lhs.ptr, rhs.ptr, (lhs.length <= rhs.length ? lhs.length : rhs.length) * T.sizeof); - if (c) - return c; - static if (size_t.sizeof <= uint.sizeof && T.sizeof >= 2) - return cast(int) lhs.length - cast(int) rhs.length; - else - return int(lhs.length > rhs.length) - int(lhs.length < rhs.length); - } - } - - immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length; - foreach (const u; 0 .. len) - { - static if (__traits(isFloating, T)) - { - immutable a = lhs.ptr[u], b = rhs.ptr[u]; - static if (is(T == cfloat) || is(T == cdouble) - || is(T == creal)) - { - // Use rt.cmath2._Ccmp instead ? - auto r = (a.re > b.re) - (a.re < b.re); - if (!r) r = (a.im > b.im) - (a.im < b.im); - } - else - { - const r = (a > b) - (a < b); - } - if (r) return r; - } - else if (lhs.ptr[u] != rhs.ptr[u]) - return lhs.ptr[u] < rhs.ptr[u] ? -1 : 1; - } - return lhs.length < rhs.length ? -1 : (lhs.length > rhs.length); - } -} +public import rt.array.comparison : __cmp; +public import rt.array.equality : __equals; // Compare class and interface objects for ordering. private int __cmp(Obj)(Obj lhs, Obj rhs) @@ -125,170 +55,6 @@ if (is(Obj : Object)) return lhs.opCmp(rhs); } -// This function is called by the compiler when dealing with array -// comparisons in the semantic analysis phase of CmpExp. The ordering -// comparison is lowered to a call to this template. -int __cmp(T1, T2)(T1[] s1, T2[] s2) -if (!__traits(isScalar, T1) && !__traits(isScalar, T2)) -{ - import core.internal.traits : Unqual; - alias U1 = Unqual!T1; - alias U2 = Unqual!T2; - - static if (is(U1 == void) && is(U2 == void)) - static @trusted ref inout(ubyte) at(inout(void)[] r, size_t i) { return (cast(inout(ubyte)*) r.ptr)[i]; } - else - static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; } - - // All unsigned byte-wide types = > dstrcmp - immutable len = s1.length <= s2.length ? s1.length : s2.length; - - foreach (const u; 0 .. len) - { - static if (__traits(compiles, __cmp(at(s1, u), at(s2, u)))) - { - auto c = __cmp(at(s1, u), at(s2, u)); - if (c != 0) - return c; - } - else static if (__traits(compiles, at(s1, u).opCmp(at(s2, u)))) - { - auto c = at(s1, u).opCmp(at(s2, u)); - if (c != 0) - return c; - } - else static if (__traits(compiles, at(s1, u) < at(s2, u))) - { - if (at(s1, u) != at(s2, u)) - return at(s1, u) < at(s2, u) ? -1 : 1; - } - else - { - // TODO: fix this legacy bad behavior, see - // https://issues.dlang.org/show_bug.cgi?id=17244 - static assert(is(U1 == U2), "Internal error."); - import core.stdc.string : memcmp; - auto c = (() @trusted => memcmp(&at(s1, u), &at(s2, u), U1.sizeof))(); - if (c != 0) - return c; - } - } - return s1.length < s2.length ? -1 : (s1.length > s2.length); -} - -// integral types -@safe unittest -{ - void compareMinMax(T)() - { - T[2] a = [T.max, T.max]; - T[2] b = [T.min, T.min]; - - assert(__cmp(a, b) > 0); - assert(__cmp(b, a) < 0); - } - - compareMinMax!int; - compareMinMax!uint; - compareMinMax!long; - compareMinMax!ulong; - compareMinMax!short; - compareMinMax!ushort; - compareMinMax!byte; - compareMinMax!dchar; - compareMinMax!wchar; -} - -// char types (dstrcmp) -@safe unittest -{ - void compareMinMax(T)() - { - T[2] a = [T.max, T.max]; - T[2] b = [T.min, T.min]; - - assert(__cmp(a, b) > 0); - assert(__cmp(b, a) < 0); - } - - compareMinMax!ubyte; - compareMinMax!bool; - compareMinMax!char; - compareMinMax!(const char); - - string s1 = "aaaa"; - string s2 = "bbbb"; - assert(__cmp(s2, s1) > 0); - assert(__cmp(s1, s2) < 0); -} - -// fp types -@safe unittest -{ - void compareMinMax(T)() - { - T[2] a = [T.max, T.max]; - T[2] b = [T.min_normal, T.min_normal]; - T[2] c = [T.max, T.min_normal]; - T[1] d = [T.max]; - - assert(__cmp(a, b) > 0); - assert(__cmp(b, a) < 0); - assert(__cmp(a, c) > 0); - assert(__cmp(a, d) > 0); - assert(__cmp(d, c) < 0); - assert(__cmp(c, c) == 0); - } - - compareMinMax!real; - compareMinMax!float; - compareMinMax!double; - compareMinMax!ireal; - compareMinMax!ifloat; - compareMinMax!idouble; - compareMinMax!creal; - //compareMinMax!cfloat; - compareMinMax!cdouble; - - // qualifiers - compareMinMax!(const real); - compareMinMax!(immutable real); -} - -// void[] -@safe unittest -{ - void[] a; - const(void)[] b; - - (() @trusted - { - a = cast(void[]) "bb"; - b = cast(const(void)[]) "aa"; - })(); - - assert(__cmp(a, b) > 0); - assert(__cmp(b, a) < 0); -} - -// arrays of arrays with mixed modifiers -@safe unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=17876 - bool less1(immutable size_t[][] a, size_t[][] b) { return a < b; } - bool less2(const void[][] a, void[][] b) { return a < b; } - bool less3(inout size_t[][] a, size_t[][] b) { return a < b; } - - immutable size_t[][] a = [[1, 2], [3, 4]]; - size_t[][] b = [[1, 2], [3, 5]]; - assert(less1(a, b)); - assert(less3(a, b)); - - auto va = [cast(immutable void[])a[0], a[1]]; - auto vb = [cast(void[])b[0], b[1]]; - assert(less2(va, vb)); -} - // objects @safe unittest { @@ -348,187 +114,6 @@ if (!__traits(isScalar, T1) && !__traits(isScalar, T2)) assert(a < "я"); } -// `lhs == rhs` lowers to `__equals(lhs, rhs)` for dynamic arrays -bool __equals(T1, T2)(T1[] lhs, T2[] rhs) -{ - import core.internal.traits : Unqual; - alias U1 = Unqual!T1; - alias U2 = Unqual!T2; - - static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; } - static @trusted R trustedCast(R, S)(S[] r) { return cast(R) r; } - - if (lhs.length != rhs.length) - return false; - - if (lhs.length == 0 && rhs.length == 0) - return true; - - static if (is(U1 == void) && is(U2 == void)) - { - return __equals(trustedCast!(ubyte[])(lhs), trustedCast!(ubyte[])(rhs)); - } - else static if (is(U1 == void)) - { - return __equals(trustedCast!(ubyte[])(lhs), rhs); - } - else static if (is(U2 == void)) - { - return __equals(lhs, trustedCast!(ubyte[])(rhs)); - } - else static if (!is(U1 == U2)) - { - // This should replace src/object.d _ArrayEq which - // compares arrays of different types such as long & int, - // char & wchar. - // Compiler lowers to __ArrayEq in dmd/src/opover.d - foreach (const u; 0 .. lhs.length) - { - if (at(lhs, u) != at(rhs, u)) - return false; - } - return true; - } - else static if (__traits(isIntegral, U1)) - { - - if (!__ctfe) - { - import core.stdc.string : memcmp; - return () @trusted { return memcmp(cast(void*)lhs.ptr, cast(void*)rhs.ptr, lhs.length * U1.sizeof) == 0; }(); - } - else - { - foreach (const u; 0 .. lhs.length) - { - if (at(lhs, u) != at(rhs, u)) - return false; - } - return true; - } - } - else - { - foreach (const u; 0 .. lhs.length) - { - static if (__traits(compiles, __equals(at(lhs, u), at(rhs, u)))) - { - if (!__equals(at(lhs, u), at(rhs, u))) - return false; - } - else static if (__traits(isFloating, U1)) - { - if (at(lhs, u) != at(rhs, u)) - return false; - } - else static if (is(U1 : Object) && is(U2 : Object)) - { - if (!(cast(Object)at(lhs, u) is cast(Object)at(rhs, u) - || at(lhs, u) && (cast(Object)at(lhs, u)).opEquals(cast(Object)at(rhs, u)))) - return false; - } - else static if (__traits(hasMember, U1, "opEquals")) - { - if (!at(lhs, u).opEquals(at(rhs, u))) - return false; - } - else static if (is(U1 == delegate)) - { - if (at(lhs, u) != at(rhs, u)) - return false; - } - else static if (is(U1 == U11*, U11)) - { - if (at(lhs, u) != at(rhs, u)) - return false; - } - else static if (__traits(isAssociativeArray, U1)) - { - if (at(lhs, u) != at(rhs, u)) - return false; - } - else - { - if (at(lhs, u).tupleof != at(rhs, u).tupleof) - return false; - } - } - - return true; - } -} - -@safe unittest -{ - assert(__equals([], [])); - assert(!__equals([1, 2], [1, 2, 3])); -} - -@safe unittest -{ - auto a = "hello"c; - - assert(a != "hel"); - assert(a != "helloo"); - assert(a != "betty"); - assert(a == "hello"); - assert(a != "hxxxx"); - - float[] fa = [float.nan]; - assert(fa != fa); -} - -@safe unittest -{ - struct A - { - int a; - } - - auto arr1 = [A(0), A(2)]; - auto arr2 = [A(0), A(1)]; - auto arr3 = [A(0), A(1)]; - - assert(arr1 != arr2); - assert(arr2 == arr3); -} - -@safe unittest -{ - struct A - { - int a; - int b; - - bool opEquals(const A other) - { - return this.a == other.b && this.b == other.a; - } - } - - auto arr1 = [A(1, 0), A(0, 1)]; - auto arr2 = [A(1, 0), A(0, 1)]; - auto arr3 = [A(0, 1), A(1, 0)]; - - assert(arr1 != arr2); - assert(arr2 == arr3); -} - -// https://issues.dlang.org/show_bug.cgi?id=18252 -@safe unittest -{ - string[int][] a1, a2; - assert(__equals(a1, a2)); - assert(a1 == a2); - a1 ~= [0: "zero"]; - a2 ~= [0: "zero"]; - assert(__equals(a1, a2)); - assert(a1 == a2); - a2[0][1] = "one"; - assert(!__equals(a1, a2)); - assert(a1 != a2); -} - /** Destroys the given object and optionally resets to initial state. It's used to _destroy an object, calling its destructor or finalizer so it no longer diff --git a/src/rt/array/comparison.d b/src/rt/array/comparison.d new file mode 100644 index 00000000000..01005122960 --- /dev/null +++ b/src/rt/array/comparison.d @@ -0,0 +1,248 @@ +/** + * This module contains compiler support for comparing dynamic arrays + * + * Copyright: Copyright Digital Mars 2000 - 2019. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) + * Source: $(DRUNTIMESRC rt/_array.d) + */ + +module rt.array.comparison; + +int __cmp(T)(scope const T[] lhs, scope const T[] rhs) @trusted + if (__traits(isScalar, T)) +{ + // Compute U as the implementation type for T + static if (is(T == ubyte) || is(T == void) || is(T == bool)) + alias U = char; + else static if (is(T == wchar)) + alias U = ushort; + else static if (is(T == dchar)) + alias U = uint; + else static if (is(T == ifloat)) + alias U = float; + else static if (is(T == idouble)) + alias U = double; + else static if (is(T == ireal)) + alias U = real; + else + alias U = T; + + static if (is(U == char)) + { + import core.internal.string : dstrcmp; + return dstrcmp(cast(char[]) lhs, cast(char[]) rhs); + } + else static if (!is(U == T)) + { + // Reuse another implementation + return __cmp(cast(U[]) lhs, cast(U[]) rhs); + } + else + { + version (BigEndian) + static if (__traits(isUnsigned, T) ? !is(T == __vector) : is(T : P*, P)) + { + if (!__ctfe) + { + import core.stdc.string : memcmp; + int c = memcmp(lhs.ptr, rhs.ptr, (lhs.length <= rhs.length ? lhs.length : rhs.length) * T.sizeof); + if (c) + return c; + static if (size_t.sizeof <= uint.sizeof && T.sizeof >= 2) + return cast(int) lhs.length - cast(int) rhs.length; + else + return int(lhs.length > rhs.length) - int(lhs.length < rhs.length); + } + } + + immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length; + foreach (const u; 0 .. len) + { + static if (__traits(isFloating, T)) + { + immutable a = lhs.ptr[u], b = rhs.ptr[u]; + static if (is(T == cfloat) || is(T == cdouble) + || is(T == creal)) + { + // Use rt.cmath2._Ccmp instead ? + auto r = (a.re > b.re) - (a.re < b.re); + if (!r) r = (a.im > b.im) - (a.im < b.im); + } + else + { + const r = (a > b) - (a < b); + } + if (r) return r; + } + else if (lhs.ptr[u] != rhs.ptr[u]) + return lhs.ptr[u] < rhs.ptr[u] ? -1 : 1; + } + return lhs.length < rhs.length ? -1 : (lhs.length > rhs.length); + } +} + +// This function is called by the compiler when dealing with array +// comparisons in the semantic analysis phase of CmpExp. The ordering +// comparison is lowered to a call to this template. +int __cmp(T1, T2)(T1[] s1, T2[] s2) +if (!__traits(isScalar, T1) && !__traits(isScalar, T2)) +{ + import core.internal.traits : Unqual; + alias U1 = Unqual!T1; + alias U2 = Unqual!T2; + + static if (is(U1 == void) && is(U2 == void)) + static @trusted ref inout(ubyte) at(inout(void)[] r, size_t i) { return (cast(inout(ubyte)*) r.ptr)[i]; } + else + static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; } + + // All unsigned byte-wide types = > dstrcmp + immutable len = s1.length <= s2.length ? s1.length : s2.length; + + foreach (const u; 0 .. len) + { + static if (__traits(compiles, __cmp(at(s1, u), at(s2, u)))) + { + auto c = __cmp(at(s1, u), at(s2, u)); + if (c != 0) + return c; + } + else static if (__traits(compiles, at(s1, u).opCmp(at(s2, u)))) + { + auto c = at(s1, u).opCmp(at(s2, u)); + if (c != 0) + return c; + } + else static if (__traits(compiles, at(s1, u) < at(s2, u))) + { + if (at(s1, u) != at(s2, u)) + return at(s1, u) < at(s2, u) ? -1 : 1; + } + else + { + // TODO: fix this legacy bad behavior, see + // https://issues.dlang.org/show_bug.cgi?id=17244 + static assert(is(U1 == U2), "Internal error."); + import core.stdc.string : memcmp; + auto c = (() @trusted => memcmp(&at(s1, u), &at(s2, u), U1.sizeof))(); + if (c != 0) + return c; + } + } + return s1.length < s2.length ? -1 : (s1.length > s2.length); +} + +// integral types +@safe unittest +{ + void compareMinMax(T)() + { + T[2] a = [T.max, T.max]; + T[2] b = [T.min, T.min]; + + assert(__cmp(a, b) > 0); + assert(__cmp(b, a) < 0); + } + + compareMinMax!int; + compareMinMax!uint; + compareMinMax!long; + compareMinMax!ulong; + compareMinMax!short; + compareMinMax!ushort; + compareMinMax!byte; + compareMinMax!dchar; + compareMinMax!wchar; +} + +// char types (dstrcmp) +@safe unittest +{ + void compareMinMax(T)() + { + T[2] a = [T.max, T.max]; + T[2] b = [T.min, T.min]; + + assert(__cmp(a, b) > 0); + assert(__cmp(b, a) < 0); + } + + compareMinMax!ubyte; + compareMinMax!bool; + compareMinMax!char; + compareMinMax!(const char); + + string s1 = "aaaa"; + string s2 = "bbbb"; + assert(__cmp(s2, s1) > 0); + assert(__cmp(s1, s2) < 0); +} + +// fp types +@safe unittest +{ + void compareMinMax(T)() + { + T[2] a = [T.max, T.max]; + T[2] b = [T.min_normal, T.min_normal]; + T[2] c = [T.max, T.min_normal]; + T[1] d = [T.max]; + + assert(__cmp(a, b) > 0); + assert(__cmp(b, a) < 0); + assert(__cmp(a, c) > 0); + assert(__cmp(a, d) > 0); + assert(__cmp(d, c) < 0); + assert(__cmp(c, c) == 0); + } + + compareMinMax!real; + compareMinMax!float; + compareMinMax!double; + compareMinMax!ireal; + compareMinMax!ifloat; + compareMinMax!idouble; + compareMinMax!creal; + //compareMinMax!cfloat; + compareMinMax!cdouble; + + // qualifiers + compareMinMax!(const real); + compareMinMax!(immutable real); +} + +// void[] +@safe unittest +{ + void[] a; + const(void)[] b; + + (() @trusted + { + a = cast(void[]) "bb"; + b = cast(const(void)[]) "aa"; + })(); + + assert(__cmp(a, b) > 0); + assert(__cmp(b, a) < 0); +} + +// arrays of arrays with mixed modifiers +@safe unittest +{ + // https://issues.dlang.org/show_bug.cgi?id=17876 + bool less1(immutable size_t[][] a, size_t[][] b) { return a < b; } + bool less2(const void[][] a, void[][] b) { return a < b; } + bool less3(inout size_t[][] a, size_t[][] b) { return a < b; } + + immutable size_t[][] a = [[1, 2], [3, 4]]; + size_t[][] b = [[1, 2], [3, 5]]; + assert(less1(a, b)); + assert(less3(a, b)); + + auto va = [cast(immutable void[])a[0], a[1]]; + auto vb = [cast(void[])b[0], b[1]]; + assert(less2(va, vb)); +} diff --git a/src/rt/array/equality.d b/src/rt/array/equality.d new file mode 100644 index 00000000000..cbb00c989ca --- /dev/null +++ b/src/rt/array/equality.d @@ -0,0 +1,192 @@ +/** + * This module contains compiler support determining equality of dynamic arrays. + * + * Copyright: Copyright Digital Mars 2000 - 2019. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) + * Source: $(DRUNTIMESRC rt/_array.d) + */ + + module rt.array.equality; + + // `lhs == rhs` lowers to `__equals(lhs, rhs)` for dynamic arrays +bool __equals(T1, T2)(T1[] lhs, T2[] rhs) +{ + import core.internal.traits : Unqual; + alias U1 = Unqual!T1; + alias U2 = Unqual!T2; + + static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; } + static @trusted R trustedCast(R, S)(S[] r) { return cast(R) r; } + + if (lhs.length != rhs.length) + return false; + + if (lhs.length == 0 && rhs.length == 0) + return true; + + static if (is(U1 == void) && is(U2 == void)) + { + return __equals(trustedCast!(ubyte[])(lhs), trustedCast!(ubyte[])(rhs)); + } + else static if (is(U1 == void)) + { + return __equals(trustedCast!(ubyte[])(lhs), rhs); + } + else static if (is(U2 == void)) + { + return __equals(lhs, trustedCast!(ubyte[])(rhs)); + } + else static if (!is(U1 == U2)) + { + // This should replace src/object.d _ArrayEq which + // compares arrays of different types such as long & int, + // char & wchar. + // Compiler lowers to __ArrayEq in dmd/src/opover.d + foreach (const u; 0 .. lhs.length) + { + if (at(lhs, u) != at(rhs, u)) + return false; + } + return true; + } + else static if (__traits(isIntegral, U1)) + { + + if (!__ctfe) + { + import core.stdc.string : memcmp; + return () @trusted { return memcmp(cast(void*)lhs.ptr, cast(void*)rhs.ptr, lhs.length * U1.sizeof) == 0; }(); + } + else + { + foreach (const u; 0 .. lhs.length) + { + if (at(lhs, u) != at(rhs, u)) + return false; + } + return true; + } + } + else + { + foreach (const u; 0 .. lhs.length) + { + static if (__traits(compiles, __equals(at(lhs, u), at(rhs, u)))) + { + if (!__equals(at(lhs, u), at(rhs, u))) + return false; + } + else static if (__traits(isFloating, U1)) + { + if (at(lhs, u) != at(rhs, u)) + return false; + } + else static if (is(U1 : Object) && is(U2 : Object)) + { + if (!(cast(Object)at(lhs, u) is cast(Object)at(rhs, u) + || at(lhs, u) && (cast(Object)at(lhs, u)).opEquals(cast(Object)at(rhs, u)))) + return false; + } + else static if (__traits(hasMember, U1, "opEquals")) + { + if (!at(lhs, u).opEquals(at(rhs, u))) + return false; + } + else static if (is(U1 == delegate)) + { + if (at(lhs, u) != at(rhs, u)) + return false; + } + else static if (is(U1 == U11*, U11)) + { + if (at(lhs, u) != at(rhs, u)) + return false; + } + else static if (__traits(isAssociativeArray, U1)) + { + if (at(lhs, u) != at(rhs, u)) + return false; + } + else + { + if (at(lhs, u).tupleof != at(rhs, u).tupleof) + return false; + } + } + + return true; + } +} + +@safe unittest +{ + assert(__equals([], [])); + assert(!__equals([1, 2], [1, 2, 3])); +} + +@safe unittest +{ + auto a = "hello"c; + + assert(a != "hel"); + assert(a != "helloo"); + assert(a != "betty"); + assert(a == "hello"); + assert(a != "hxxxx"); + + float[] fa = [float.nan]; + assert(fa != fa); +} + +@safe unittest +{ + struct A + { + int a; + } + + auto arr1 = [A(0), A(2)]; + auto arr2 = [A(0), A(1)]; + auto arr3 = [A(0), A(1)]; + + assert(arr1 != arr2); + assert(arr2 == arr3); +} + +@safe unittest +{ + struct A + { + int a; + int b; + + bool opEquals(const A other) + { + return this.a == other.b && this.b == other.a; + } + } + + auto arr1 = [A(1, 0), A(0, 1)]; + auto arr2 = [A(1, 0), A(0, 1)]; + auto arr3 = [A(0, 1), A(1, 0)]; + + assert(arr1 != arr2); + assert(arr2 == arr3); +} + +// https://issues.dlang.org/show_bug.cgi?id=18252 +@safe unittest +{ + string[int][] a1, a2; + assert(__equals(a1, a2)); + assert(a1 == a2); + a1 ~= [0: "zero"]; + a2 ~= [0: "zero"]; + assert(__equals(a1, a2)); + assert(a1 == a2); + a2[0][1] = "one"; + assert(!__equals(a1, a2)); + assert(a1 != a2); +} diff --git a/test/profile/mytrace.def.exp b/test/profile/mytrace.def.exp index f94dd4da36c..64427f879ac 100644 --- a/test/profile/mytrace.def.exp +++ b/test/profile/mytrace.def.exp @@ -1,6 +1,6 @@ FUNCTIONS - _D6object__T5__cmpTaZQjFNaNbNiNeMxAaMxQeZi + _D2rt5array10comparison__T5__cmpTaZQjFNaNbNiNeMxAaMxQeZi _Dmain _D4core8internal6string__T7dstrcmpZQjFNaNbNiNeMxAaMxQeZi _D7profile3fooFkZk