Skip to content

Commit d41877b

Browse files
authored
Merge pull request #8798 from n8sh/issue-24083
Fix Issue 24083 - Int128.opCmp's behavior with negative numbers is inconsistent with Int128.opEquals
2 parents 75372ef + dcdb02c commit d41877b

File tree

1 file changed

+50
-10
lines changed

1 file changed

+50
-10
lines changed

std/int128.d

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ private import core.int128;
1818

1919
public struct Int128
2020
{
21-
@safe pure nothrow @nogc:
22-
21+
@safe pure nothrow @nogc
22+
{
2323
Cent data; /// core.int128.Cent
2424

2525
/****************
@@ -253,23 +253,32 @@ public struct Int128
253253
mixin("this = this " ~ op ~ " op2;");
254254
return this;
255255
}
256+
} // @safe pure nothrow @nogc
256257

257-
/** support signed arithmentic comparison operators < <= > >=
258+
/** support arithmentic comparison operators < <= > >=
258259
* Params: op2 = right hand operand
259260
* Returns: -1 for less than, 0 for equals, 1 for greater than
260261
*/
261-
int opCmp(Int128 op2) const
262+
int opCmp(Int128 op2) const @nogc nothrow pure @safe
262263
{
263264
return this == op2 ? 0 : gt(this.data, op2.data) * 2 - 1;
264265
}
265266

266-
/** support signed arithmentic comparison operators < <= > >=
267-
* Params: op2 = right hand operand
268-
* Returns: -1 for less than, 0 for equals, 1 for greater than
269-
*/
270-
int opCmp(long op2) const
267+
/// ditto
268+
int opCmp(Int)(const Int op2) const @nogc nothrow pure @safe
269+
if (is(Int : long) && __traits(isIntegral, Int))
271270
{
272-
return opCmp(Int128(0, op2));
271+
static if (__traits(isUnsigned, Int))
272+
return opCmp(Int128(0, op2));
273+
else
274+
return opCmp(Int128((cast(long) op2) >> 63, op2));
275+
}
276+
277+
/// ditto
278+
int opCmp(IntLike)(auto ref IntLike op2) const
279+
if (is(IntLike : long) && !__traits(isIntegral, IntLike))
280+
{
281+
return opCmp(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0]));
273282
}
274283

275284
enum min = Int128(long.min, 0); /// minimum value
@@ -372,3 +381,34 @@ unittest
372381
c = Int128(-1L);
373382
assert(c == -1L);
374383
}
384+
385+
@system unittest
386+
{
387+
alias Seq(T...) = T;
388+
Int128 c = Int128(-1L);
389+
assert(c.opCmp(-1L) == 0);
390+
// To avoid regression calling opCmp with any integral type needs to
391+
// work without the compiler complaining "opCmp called with argument
392+
// X matches both <...>".
393+
static foreach (Int; Seq!(long, int, short, byte, ulong, uint, ushort, ubyte, dchar, wchar, char))
394+
assert(c < Int.max);
395+
static foreach (Int; Seq!(int, short, byte))
396+
assert(c.opCmp(Int(-1)) == 0);
397+
assert(c < true);
398+
// To avoid regression calling opCmp with any type that converts to an
399+
// integral type through alias this needs to work regardless of whether
400+
// the alias is safe/pure/nothrow/nogc and regardless of whether the
401+
// type has a disabled postblit.
402+
static struct Wrapped(T)
403+
{
404+
T value;
405+
uint count;
406+
T get() @system { ++count; return value; } // not const
407+
alias get this;
408+
@disable this(this); // no implicit copies
409+
}
410+
assert(c.opCmp(Wrapped!long(-1)) == 0);
411+
auto w = Wrapped!ulong(ulong.max);
412+
w.count++; // avoid invalid D-Scanner message that w could have been declared const
413+
assert(c < w);
414+
}

0 commit comments

Comments
 (0)