From f718f295df3f6ee5d7fd6fc19e39ac663821b00a Mon Sep 17 00:00:00 2001 From: metagn Date: Sun, 25 Jun 2023 01:01:08 +0300 Subject: fix VM uint conversion size bug, stricter int gen on JS (#22150) * fix VM uint conversion bug, stricter int gen on JS fixes #19929 * fix float -> uint64 conversion too * no need to mask to source type * simpler diff with explanation, add test for described issue --- compiler/jsgen.nim | 22 ++++++++++++++++------ compiler/vm.nim | 10 +++++++--- tests/js/tneginthash.nim | 21 +++++++++++++++++++++ tests/stdlib/thashes.nim | 3 +++ tests/vm/ttouintconv.nim | 7 +++++++ 5 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 tests/js/tneginthash.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index ecfc22108..1fbf6c74c 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2513,10 +2513,12 @@ proc genConv(p: PProc, n: PNode, r: var TCompRes) = elif src.kind == tyUInt64: r.res = "BigInt.asIntN(64, $1)" % [r.res] elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: - if fromInt or fromUint: + if fromUint or src.kind in {tyBool, tyChar, tyEnum}: r.res = "BigInt($1)" % [r.res] + elif fromInt: # could be negative + r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res] elif src.kind in {tyFloat..tyFloat64}: - r.res = "BigInt(Math.trunc($1))" % [r.res] + r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res] elif src.kind == tyInt64: r.res = "BigInt.asUintN(64, $1)" % [r.res] elif toUint or dest.kind in tyFloat..tyFloat64: @@ -2755,10 +2757,12 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = elif src.kind == tyUInt64: r.res = "BigInt.asIntN(64, $1)" % [r.res] elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: - if fromInt or fromUint: + if fromUint or src.kind in {tyBool, tyChar, tyEnum}: r.res = "BigInt($1)" % [r.res] + elif fromInt: # could be negative + r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res] elif src.kind in {tyFloat..tyFloat64}: - r.res = "BigInt(Math.trunc($1))" % [r.res] + r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res] elif src.kind == tyInt64: r.res = "BigInt.asUintN(64, $1)" % [r.res] elif dest.kind in tyFloat..tyFloat64: @@ -2790,11 +2794,17 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = if optJsBigInt64 in p.config.globalOptions: r.res.add('n') of tyInt64: - r.res = rope(n.intVal) + let wrap = n.intVal < 0 # wrap negative integers with parens + if wrap: r.res.add '(' + r.res.addInt n.intVal if optJsBigInt64 in p.config.globalOptions: r.res.add('n') + if wrap: r.res.add ')' else: - r.res = rope(n.intVal) + let wrap = n.intVal < 0 # wrap negative integers with parens + if wrap: r.res.add '(' + r.res.addInt n.intVal + if wrap: r.res.add ')' r.kind = resExpr of nkNilLit: if isEmptyType(n.typ): diff --git a/compiler/vm.nim b/compiler/vm.nim index 8b5faabfe..7376ff165 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -443,12 +443,16 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): of tyFloat..tyFloat64: dest.intVal = int(src.floatVal) else: - let srcSize = getSize(c.config, styp) let destSize = getSize(c.config, desttyp) - let srcDist = (sizeof(src.intVal) - srcSize) * 8 let destDist = (sizeof(dest.intVal) - destSize) * 8 var value = cast[BiggestUInt](src.intVal) - value = (value shl srcDist) shr srcDist + when false: + # this would make uint64(-5'i8) evaluate to 251 + # but at runtime, uint64(-5'i8) is 18446744073709551611 + # so don't do it + let srcSize = getSize(c.config, styp) + let srcDist = (sizeof(src.intVal) - srcSize) * 8 + value = (value shl srcDist) shr srcDist value = (value shl destDist) shr destDist dest.intVal = cast[BiggestInt](value) of tyBool: diff --git a/tests/js/tneginthash.nim b/tests/js/tneginthash.nim new file mode 100644 index 000000000..c082405c9 --- /dev/null +++ b/tests/js/tneginthash.nim @@ -0,0 +1,21 @@ +# issue #19929 + +import std/[tables, hashes] + +type Foo = object + a: int + +proc hash(f: Foo): Hash = + var h: Hash = 0 + h = h !& hash(f.a) + result = !$h + +proc transpose[T, S](data: array[T, S]): Table[S, T] = + for i, x in data: + result[x] = i + +const xs = [Foo(a: 5), Foo(a: -5)] +const x = transpose(xs) + +doAssert x[Foo(a: -5)] == 1 +doAssert x[Foo(a: 5)] == 0 diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 6b5e055b4..b6fbbbdb7 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -29,6 +29,9 @@ block hashes: const wy123 = hashWangYi1(123) doAssert wy123 != 0 doAssert hashWangYi1(123) == wy123 + const wyNeg123 = hashWangYi1(-123) + doAssert wyNeg123 != 0 + doAssert hashWangYi1(-123) == wyNeg123 # "hashIdentity value incorrect at 456" diff --git a/tests/vm/ttouintconv.nim b/tests/vm/ttouintconv.nim index ff2187a36..8c43a3adb 100644 --- a/tests/vm/ttouintconv.nim +++ b/tests/vm/ttouintconv.nim @@ -75,3 +75,10 @@ macro foo2() = foo() foo2() + +block: + const neg5VM = block: + let x = -5'i8 + uint64(x) + let y = -5'i8 + doAssert uint64(y) == neg5VM -- cgit 1.4.1-2-gfad0