From 6c1c6cf7c144734f97faeb3a18b9193d6299e279 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Mon, 6 Mar 2017 15:08:47 -0500 Subject: [PATCH 1/8] One more pass through __cmp --- src/core/internal/string.d | 30 +++++--- src/object.d | 151 +++++++++++-------------------------- 2 files changed, 62 insertions(+), 119 deletions(-) diff --git a/src/core/internal/string.d b/src/core/internal/string.d index 28b1e36246..47741772a3 100644 --- a/src/core/internal/string.d +++ b/src/core/internal/string.d @@ -192,16 +192,22 @@ unittest int dstrcmp( scope const char[] s1, scope const char[] s2 ) @trusted { - import core.stdc.string : memcmp; - - int ret = 0; - auto len = s1.length; - if( s2.length < len ) - len = s2.length; - if( 0 != (ret = memcmp( s1.ptr, s2.ptr, len )) ) - return ret; - return s1.length > s2.length ? 1 : - s1.length == s2.length ? 0 : -1; -} - + immutable len = s1.length <= s2.length ? s1.length : s2.length; + if (__ctfe) + { + foreach (const u; 0 .. len) + { + if (s1[u] != s2[u]) + return s1[u] > s2[u] ? 1 : -1; + } + } + else + { + import core.stdc.string : memcmp; + const ret = memcmp( s1.ptr, s2.ptr, len ); + if( ret ) + return ret; + } + return s1.length < s2.length ? -1 : (s1.length > s2.length); +} diff --git a/src/object.d b/src/object.d index c361c790ab..5661ea0051 100644 --- a/src/object.d +++ b/src/object.d @@ -3241,6 +3241,23 @@ template RTInfo(T) enum RTInfo = null; } +private int __cmp(F)(const F f1, const F f2) +if (__traits(isFloating, F)) +{ + static if (is(F == cfloat) || is(F == cdouble) || is(F == creal)) + { + // Use rt.cmath2._Ccmp instead ? + auto r = __cmp(f1.re, f2.re); + if (!r) r = __cmp(f1.im, f2.im); + return r; + } + else static if (is(F == float) || is(F == double) || is(F == real)) + { + return (f1 > f2) - (f2 > f1); + } + else static assert(false, "Internal error"); +} + // 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. @@ -3249,6 +3266,7 @@ int __cmp(T1, T2)(T1[] s1, T2[] s2) import core.internal.traits : Unqual; alias U1 = Unqual!T1; alias U2 = Unqual!T2; + static assert(is(U1 == U2), "Internal error."); static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; } @@ -3258,10 +3276,7 @@ int __cmp(T1, T2)(T1[] s1, T2[] s2) // which combined result in the same code as below. static if (is(U1 : Object) && is(U2 : Object)) { - auto len = s1.length; - - if (s2.length < len) - len = s2.length; + immutable len = s1.length <= s2.length ? s1.length : s2.length; foreach (const u; 0 .. len) { @@ -3278,7 +3293,7 @@ int __cmp(T1, T2)(T1[] s1, T2[] s2) return 1; auto c = o1.opCmp(o2); if (c != 0) - return c < 0 ? -1 : 1; + return c; } else { @@ -3287,127 +3302,49 @@ int __cmp(T1, T2)(T1[] s1, T2[] s2) } return s1.length < s2.length ? -1 : (s1.length > s2.length); } - // floating point types - else static if (__traits(isFloating, U1)) + // Imaginary types + else static if (is(U1 == ifloat) || is(U1 == idouble) || is(U1 == ireal)) { + // Special-case imaginary types to use comparison for "concrete" types static if (is(U1 == ifloat)) alias F = float; else static if (is(U1 == idouble)) alias F = double; else static if (is(U1 == ireal)) alias F = real; - else alias F = U1; - - static int compare(F f1, F f2) - { - static if (is(F == cfloat) || is(F == cdouble) || is(F == creal)) - { - // Use rt.cmath2._Ccmp instead ? - int result; - - if (f1.re < f2.re) - result = -1; - else if (f1.re > f2.re) - result = 1; - else if (f1.im < f2.im) - result = -1; - else if (f1.im > f2.im) - result = 1; - else - result = 0; - return result; - } - else static if (is(F == float) || is(F == double) || is(F == real)) - { - // d1 is NaN 2^0 + d2 is NaN 2^1 - auto NaNs = (f1 != f1) | ((f2 != f2) << 1); - - return (NaNs == 3) ? 0 : - (NaNs == 2) ? 1 : - (NaNs == 1) ? -1 : - (f1 < f2) ? -1 : (f1 > f2); - } - else static assert(false, "Internal error"); - } - auto len = s1.length; - if (s2.length < len) - len = s2.length; - - auto fpArray1 = () { return cast(F[])s1; }(); - auto fpArray2 = () { return cast(F[])s2; }(); - - foreach (const u; 0 .. len) - { - if (int c = compare(fpArray1[u], fpArray2[u])) - return c; - } - return s1.length < s2.length ? -1 : (s1.length > s2.length); + auto fpArray1 = () @trusted { return cast(F[])s1; }(); + auto fpArray2 = () @trusted { return cast(F[])s2; }(); + return __cmp(fpArray1, fpArray2); } - // char types = > dstrcmp - else static if ((is(U1 == ubyte) && is(U2 == ubyte)) - || (is(U1 == void) && is(U2 == void)) - || (is(U1 == bool) && is(U2 == bool)) - || (is(U1 == char) && is(U2 == char))) + // All unsigned byte-wide types = > dstrcmp + else static if (is(U1 == ubyte) || is(U1 == void) || is(U1 == bool) + || is(U1 == char)) { - if (!__ctfe) - { - import core.internal.string : dstrcmp; - return () @trusted { return dstrcmp(cast(char[])s1, cast(char[])s2); }(); - } - else - { - // pretty ugly... - auto len = s1.length; - - if (s2.length < len) - len = s2.length; - - foreach (const u; 0 .. len) - { - auto e1 = at(s1, u); - auto e2 = at(s2, u); - - if (e1 < e2) - return -1; - - if (e1 > e2) - return 1; - } - - return s1.length < s2.length ? -1 : (s1.length > s2.length); - } + import core.internal.string : dstrcmp; + return (() @trusted => dstrcmp(cast(char[])s1, cast(char[])s2))(); } - // integral, struct, nested arrays + // Everything else else { - auto len = s1.length; - - if (s2.length < len) - len = s2.length; + immutable len = s1.length <= s2.length ? s1.length : s2.length; foreach (const u; 0 .. len) { - auto e1 = at(s1, u); - auto e2 = at(s2, u); - // structs - static if (__traits(compiles, e1.opCmp(e2))) + static if (__traits(compiles, at(s1, u).opCmp(at(s2, u)))) { - auto c = e1.opCmp(e2); + auto c = at(s1, u).opCmp(at(s2, u)); if (c != 0) - return c < 0 ? -1 : 1; + return c; } - else static if (__traits(compiles, __cmp(e1, e2))) + else static if (__traits(compiles, __cmp(at(s1, u), at(s2, u)))) { - auto c = __cmp(e1, e2); + auto c = __cmp(at(s1, u), at(s2, u)); if (c != 0) - return c < 0 ? -1 : 1; + return c; } else { - if (e1 < e2) - return -1; - - if (e1 > e2) - return 1; + if (at(s1, u) != at(s2, u)) + return at(s1, u) < at(s2, u) ? -1 : 1; } } return s1.length < s2.length ? -1 : (s1.length > s2.length); @@ -3415,7 +3352,7 @@ int __cmp(T1, T2)(T1[] s1, T2[] s2) } // integral types -unittest +@safe unittest { void compareMinMax(T)() { @@ -3438,7 +3375,7 @@ unittest } // char types (dstrcmp) -unittest +@safe unittest { void compareMinMax(T)() { @@ -3461,7 +3398,7 @@ unittest } // fp types -unittest +@safe unittest { void compareMinMax(T)() { From 1fa3903cc7e7fd6325bfb386302e24864decae98 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Mon, 6 Mar 2017 18:02:34 -0500 Subject: [PATCH 2/8] Factor in implementation for class and interface types --- src/object.d | 72 ++++++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/src/object.d b/src/object.d index 5661ea0051..84833b39d2 100644 --- a/src/object.d +++ b/src/object.d @@ -3241,6 +3241,7 @@ template RTInfo(T) enum RTInfo = null; } +// Compare floating point numbers for ordering (one instantiation per width). private int __cmp(F)(const F f1, const F f2) if (__traits(isFloating, F)) { @@ -3251,11 +3252,31 @@ if (__traits(isFloating, F)) if (!r) r = __cmp(f1.im, f2.im); return r; } - else static if (is(F == float) || is(F == double) || is(F == real)) + else { return (f1 > f2) - (f2 > f1); } - else static assert(false, "Internal error"); +} + +// Compare class and interface objects for ordering. +private int __cmp(Obj)(Obj lhs, Obj rhs) +if (is(Obj : Object)) +{ + if (o1 is o2) + return 0; + // Regard null references as always being "less than" + if (o1) + { + if (!o2) + return 1; + auto c = o1.opCmp(o2); + if (c != 0) + return c; + } + else + { + return -1; + } } // This function is called by the compiler when dealing with array @@ -3270,40 +3291,8 @@ int __cmp(T1, T2)(T1[] s1, T2[] s2) static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; } - // objects - // Old code went to object.d/TypeInfo_array.compare and then to - // object.d/TypeInfo_Class.compare - // which combined result in the same code as below. - static if (is(U1 : Object) && is(U2 : Object)) - { - immutable len = s1.length <= s2.length ? s1.length : s2.length; - - foreach (const u; 0 .. len) - { - auto o1 = at(s1, u); - auto o2 = at(s2, u); - - if (o1 is o2) - continue; - - // Regard null references as always being "less than" - if (o1) - { - if (!o2) - return 1; - auto c = o1.opCmp(o2); - if (c != 0) - return c; - } - else - { - return -1; - } - } - return s1.length < s2.length ? -1 : (s1.length > s2.length); - } - // Imaginary types - else static if (is(U1 == ifloat) || is(U1 == idouble) || is(U1 == ireal)) + // Use the same implementation for real and imaginary floats + static if (is(U1 == ifloat) || is(U1 == idouble) || is(U1 == ireal)) { // Special-case imaginary types to use comparison for "concrete" types static if (is(U1 == ifloat)) alias F = float; @@ -3321,23 +3310,22 @@ int __cmp(T1, T2)(T1[] s1, T2[] s2) import core.internal.string : dstrcmp; return (() @trusted => dstrcmp(cast(char[])s1, cast(char[])s2))(); } - // Everything else + // Everything else: class, interface, struct, other built-ins else { immutable len = s1.length <= s2.length ? s1.length : s2.length; foreach (const u; 0 .. len) { - // structs - static if (__traits(compiles, at(s1, u).opCmp(at(s2, u)))) + static if (__traits(compiles, __cmp(at(s1, u), at(s2, u)))) { - auto c = at(s1, u).opCmp(at(s2, u)); + auto c = __cmp(at(s1, u), at(s2, u)); if (c != 0) return c; } - else static if (__traits(compiles, __cmp(at(s1, u), at(s2, u)))) + else static if (__traits(compiles, at(s1, u).opCmp(at(s2, u)))) { - auto c = __cmp(at(s1, u), at(s2, u)); + auto c = at(s1, u).opCmp(at(s2, u)); if (c != 0) return c; } From 3d9ce7f80400cb8f77d5e4ca915c968ddb1c91d2 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Mon, 6 Mar 2017 19:43:27 -0500 Subject: [PATCH 3/8] Fix __cmp for object types, was not even called --- src/object.d | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/object.d b/src/object.d index 84833b39d2..4f28490512 100644 --- a/src/object.d +++ b/src/object.d @@ -3242,19 +3242,19 @@ template RTInfo(T) } // Compare floating point numbers for ordering (one instantiation per width). -private int __cmp(F)(const F f1, const F f2) +private int __cmp(F)(const F lhs, const F rhs) if (__traits(isFloating, F)) { static if (is(F == cfloat) || is(F == cdouble) || is(F == creal)) { // Use rt.cmath2._Ccmp instead ? - auto r = __cmp(f1.re, f2.re); - if (!r) r = __cmp(f1.im, f2.im); + auto r = __cmp(lhs.re, lhs.re); + if (!r) r = __cmp(lhs.im, lhs.im); return r; } else { - return (f1 > f2) - (f2 > f1); + return (lhs > rhs) - (lhs > rhs); } } @@ -3262,21 +3262,14 @@ if (__traits(isFloating, F)) private int __cmp(Obj)(Obj lhs, Obj rhs) if (is(Obj : Object)) { - if (o1 is o2) + if (lhs is rhs) return 0; // Regard null references as always being "less than" - if (o1) - { - if (!o2) - return 1; - auto c = o1.opCmp(o2); - if (c != 0) - return c; - } - else - { + if (!lhs) return -1; - } + if (!rhs) + return 1; + return lhs.opCmp(rhs); } // This function is called by the compiler when dealing with array @@ -3413,14 +3406,14 @@ int __cmp(T1, T2)(T1[] s1, T2[] s2) } //objects -unittest +@safe unittest { class C { int i; this(int i) { this.i = i; } - override int opCmp(Object c) const + override int opCmp(Object c) const @safe { return i - (cast(C)c).i; } @@ -3428,12 +3421,16 @@ unittest auto c1 = new C(1); auto c2 = new C(2); + assert(__cmp(c1, null) > 0); + assert(__cmp(null, c1) < 0); + assert(__cmp(c1, c1) == 0); + assert(__cmp(c1, c2) < 0); + assert(__cmp(c2, c1) > 0); - assert(__cmp([c1, c1], [c2, c2]) < 0); + assert(__cmp([c1, c1][], [c2, c2][]) < 0); assert(__cmp([c2, c2], [c1, c1]) > 0); } - // Helper functions private inout(TypeInfo) getElement(inout TypeInfo value) @trusted pure nothrow From f3a0e19ab4a4cd377e847df7525ed45c7f23efa6 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Mon, 6 Mar 2017 20:32:24 -0500 Subject: [PATCH 4/8] Fix copypasta mistake --- src/object.d | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/object.d b/src/object.d index 4f28490512..d02e149b23 100644 --- a/src/object.d +++ b/src/object.d @@ -3248,13 +3248,13 @@ if (__traits(isFloating, F)) static if (is(F == cfloat) || is(F == cdouble) || is(F == creal)) { // Use rt.cmath2._Ccmp instead ? - auto r = __cmp(lhs.re, lhs.re); - if (!r) r = __cmp(lhs.im, lhs.im); + auto r = __cmp(lhs.re, rhs.re); + if (!r) r = __cmp(lhs.im, rhs.im); return r; } else { - return (lhs > rhs) - (lhs > rhs); + return (lhs > rhs) - (lhs < rhs); } } From 24e775ad12171eaf4fd16b3feb578dc69354f5b4 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 7 Mar 2017 10:56:09 -0500 Subject: [PATCH 5/8] Preserve legacy weird behavior by which structs and unions in arrays were compared with memcmp --- src/object.d | 109 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 42 deletions(-) diff --git a/src/object.d b/src/object.d index d02e149b23..82224c2759 100644 --- a/src/object.d +++ b/src/object.d @@ -3272,10 +3272,34 @@ if (is(Obj : Object)) return lhs.opCmp(rhs); } +int __cmp(T)(const T[] lhs, const T[] rhs) @trusted +if (__traits(isScalar, T)) +{ + immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length; + foreach (const u; 0 .. len) + { + static if (__traits(isFloating, T)) + { + auto r = __cmp(lhs.ptr[u], rhs.ptr[u]); + if (r) return r; + } + else static if (is(T == ubyte) || is(T == void) || is(T == bool) + || is(T == char)) + { + import core.internal.string : dstrcmp; + return dstrcmp(cast(char[]) lhs, cast(char[]) rhs); + } + 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)) { import core.internal.traits : Unqual; alias U1 = Unqual!T1; @@ -3284,52 +3308,37 @@ int __cmp(T1, T2)(T1[] s1, T2[] s2) static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; } - // Use the same implementation for real and imaginary floats - static if (is(U1 == ifloat) || is(U1 == idouble) || is(U1 == ireal)) - { - // Special-case imaginary types to use comparison for "concrete" types - static if (is(U1 == ifloat)) alias F = float; - else static if (is(U1 == idouble)) alias F = double; - else static if (is(U1 == ireal)) alias F = real; - - auto fpArray1 = () @trusted { return cast(F[])s1; }(); - auto fpArray2 = () @trusted { return cast(F[])s2; }(); - return __cmp(fpArray1, fpArray2); - } // All unsigned byte-wide types = > dstrcmp - else static if (is(U1 == ubyte) || is(U1 == void) || is(U1 == bool) - || is(U1 == char)) - { - import core.internal.string : dstrcmp; - return (() @trusted => dstrcmp(cast(char[])s1, cast(char[])s2))(); - } - // Everything else: class, interface, struct, other built-ins - else - { - immutable len = s1.length <= s2.length ? s1.length : s2.length; + immutable len = s1.length <= s2.length ? s1.length : s2.length; - foreach (const u; 0 .. len) + foreach (const u; 0 .. len) + { + static if (__traits(compiles, __cmp(at(s1, u), at(s2, u)))) { - 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 - { - if (at(s1, u) != at(s2, u)) - return at(s1, u) < at(s2, u) ? -1 : 1; - } + 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 + import core.stdc.string : memcmp; + return (() @trusted => memcmp(&at(s1, u), &at(s2, u), U1.sizeof))(); } - return s1.length < s2.length ? -1 : (s1.length > s2.length); } + return s1.length < s2.length ? -1 : (s1.length > s2.length); } // integral types @@ -3405,7 +3414,7 @@ int __cmp(T1, T2)(T1[] s1, T2[] s2) compareMinMax!(immutable real); } -//objects +// objects @safe unittest { class C @@ -3431,6 +3440,22 @@ int __cmp(T1, T2)(T1[] s1, T2[] s2) assert(__cmp([c2, c2], [c1, c1]) > 0); } +// structs +@safe unittest +{ + struct C + { + ubyte i; + this(ubyte i) { this.i = i; } + } + + auto c1 = C(1); + auto c2 = C(2); + + assert(__cmp([c1, c1][], [c2, c2][]) < 0); + assert(__cmp([c2, c2], [c1, c1]) > 0); +} + // Helper functions private inout(TypeInfo) getElement(inout TypeInfo value) @trusted pure nothrow From 7d07225bda809f69106819da04a10ff8cd082c28 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 7 Mar 2017 13:39:40 -0500 Subject: [PATCH 6/8] Further reduction of code --- src/object.d | 59 +++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/object.d b/src/object.d index 82224c2759..a14f2cadaa 100644 --- a/src/object.d +++ b/src/object.d @@ -3241,23 +3241,6 @@ template RTInfo(T) enum RTInfo = null; } -// Compare floating point numbers for ordering (one instantiation per width). -private int __cmp(F)(const F lhs, const F rhs) -if (__traits(isFloating, F)) -{ - static if (is(F == cfloat) || is(F == cdouble) || is(F == creal)) - { - // Use rt.cmath2._Ccmp instead ? - auto r = __cmp(lhs.re, rhs.re); - if (!r) r = __cmp(lhs.im, rhs.im); - return r; - } - else - { - return (lhs > rhs) - (lhs < rhs); - } -} - // Compare class and interface objects for ordering. private int __cmp(Obj)(Obj lhs, Obj rhs) if (is(Obj : Object)) @@ -3275,24 +3258,38 @@ if (is(Obj : Object)) int __cmp(T)(const T[] lhs, const T[] rhs) @trusted if (__traits(isScalar, T)) { - immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length; - foreach (const u; 0 .. len) + static if (is(T == ubyte) || is(T == void) || is(T == bool) + || is(T == char)) { - static if (__traits(isFloating, T)) - { - auto r = __cmp(lhs.ptr[u], rhs.ptr[u]); - if (r) return r; - } - else static if (is(T == ubyte) || is(T == void) || is(T == bool) - || is(T == char)) + import core.internal.string : dstrcmp; + return dstrcmp(cast(char[]) lhs, cast(char[]) rhs); + } + else + { + immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length; + foreach (const u; 0 .. len) { - import core.internal.string : dstrcmp; - return dstrcmp(cast(char[]) lhs, cast(char[]) rhs); + 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 < a.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; } - 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); } - return lhs.length < rhs.length ? -1 : (lhs.length > rhs.length); } // This function is called by the compiler when dealing with array From 8107d51eba00c2f68f6d1f9332d179d8413cf66f Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 7 Mar 2017 13:50:19 -0500 Subject: [PATCH 7/8] Aw what the heck implemented reuse of implementation for all builtin types --- src/object.d | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/object.d b/src/object.d index a14f2cadaa..c74deecf00 100644 --- a/src/object.d +++ b/src/object.d @@ -3258,12 +3258,32 @@ if (is(Obj : Object)) int __cmp(T)(const T[] lhs, const T[] rhs) @trusted if (__traits(isScalar, T)) { - static if (is(T == ubyte) || is(T == void) || is(T == bool) - || is(T == char)) + // 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 { immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length; From fc83c27b4acf6acd88e4ace21ae47d91603440c5 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 7 Mar 2017 14:00:37 -0500 Subject: [PATCH 8/8] Oooops... --- src/object.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/object.d b/src/object.d index c74deecf00..fb1709450b 100644 --- a/src/object.d +++ b/src/object.d @@ -3297,7 +3297,7 @@ if (__traits(isScalar, T)) { // Use rt.cmath2._Ccmp instead ? auto r = (a.re > b.re) - (a.re < b.re); - if (!r) r = (a.im > b.im) - (a.im < a.im); + if (!r) r = (a.im > b.im) - (a.im < b.im); } else {