From c3a9a029ca46e5df6869cb5d3a478785e84d1f4e Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Sat, 15 Aug 2026 09:13:18 +0200 Subject: [PATCH 1/2] Fix exponentiation right operand precedence Signed-off-by: Christoph Knittel --- compiler/core/js_op_util.ml | 2 +- tests/tests/src/exponentiation_test.mjs | 48 ++++++++++++++++++------- tests/tests/src/exponentiation_test.res | 11 ++++++ 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/compiler/core/js_op_util.ml b/compiler/core/js_op_util.ml index 8d2d8636396..1ac99090215 100644 --- a/compiler/core/js_op_util.ml +++ b/compiler/core/js_op_util.ml @@ -41,7 +41,7 @@ let op_prec (op : Js_op.binop) = | Lsl | Lsr | Asr -> (10, 10, 11) | Bnot | Plus | Minus -> (11, 11, 12) | Mul | Div | Mod -> (12, 12, 13) - | Pow -> (13, 14, 12) + | Pow -> (13, 14, 13) let op_int_prec (op : Js_op.int_op) = match op with diff --git a/tests/tests/src/exponentiation_test.mjs b/tests/tests/src/exponentiation_test.mjs index c39ea9465db..debb6adbc00 100644 --- a/tests/tests/src/exponentiation_test.mjs +++ b/tests/tests/src/exponentiation_test.mjs @@ -7,25 +7,49 @@ let intPow = ((a, b) => Math.pow(a, b) | 0); let four = 4; +function floatPowDiv(base, numerator, denominator) { + return base ** (numerator / denominator); +} + +function floatPowMul(base, left, right) { + return base ** (left * right); +} + +function floatPowMod(base, value, modulus) { + return base ** (value % modulus); +} + +function bigintPowMul(base, left, right) { + return base ** (left * right); +} + Mocha.describe("Exponentiation_test", () => { Mocha.test("exponentiation operations", () => { - Test_utils.eq("File \"exponentiation_test.res\", line 11, characters 7-14", 2 ** 3 ** 2, Math.pow(2, Math.pow(3, 2))); - Test_utils.eq("File \"exponentiation_test.res\", line 12, characters 7-14", 2 ** (-3) ** 2, Math.pow(2, Math.pow(-3, 2))); - Test_utils.eq("File \"exponentiation_test.res\", line 13, characters 7-14", (2 ** 3) ** 2, Math.pow(Math.pow(2, 3), 2)); - Test_utils.eq("File \"exponentiation_test.res\", line 14, characters 7-14", (-2) ** 2, Math.pow(-2, 2)); - Test_utils.eq("File \"exponentiation_test.res\", line 16, characters 7-14", 512, intPow(2, intPow(3, 2))); - Test_utils.eq("File \"exponentiation_test.res\", line 17, characters 7-14", 512, intPow(2, intPow(-3, 2))); - Test_utils.eq("File \"exponentiation_test.res\", line 18, characters 7-14", 64, intPow(intPow(2, 3), 2)); - Test_utils.eq("File \"exponentiation_test.res\", line 19, characters 7-14", -2147483648, intPow(-2, 31)); - Test_utils.eq("File \"exponentiation_test.res\", line 20, characters 7-14", 0, intPow(2, 32)); - Test_utils.eq("File \"exponentiation_test.res\", line 21, characters 7-14", 0, intPow(2147483647, 2)); - Test_utils.eq("File \"exponentiation_test.res\", line 22, characters 7-14", 0, intPow(-2147483648, 2)); - Test_utils.eq("File \"exponentiation_test.res\", line 24, characters 7-14", 256, four ** four | 0); + Test_utils.eq("File \"exponentiation_test.res\", line 17, characters 7-14", 2 ** 3 ** 2, Math.pow(2, Math.pow(3, 2))); + Test_utils.eq("File \"exponentiation_test.res\", line 18, characters 7-14", 2 ** (-3) ** 2, Math.pow(2, Math.pow(-3, 2))); + Test_utils.eq("File \"exponentiation_test.res\", line 19, characters 7-14", (2 ** 3) ** 2, Math.pow(Math.pow(2, 3), 2)); + Test_utils.eq("File \"exponentiation_test.res\", line 20, characters 7-14", (-2) ** 2, Math.pow(-2, 2)); + Test_utils.eq("File \"exponentiation_test.res\", line 22, characters 7-14", 512, intPow(2, intPow(3, 2))); + Test_utils.eq("File \"exponentiation_test.res\", line 23, characters 7-14", 512, intPow(2, intPow(-3, 2))); + Test_utils.eq("File \"exponentiation_test.res\", line 24, characters 7-14", 64, intPow(intPow(2, 3), 2)); + Test_utils.eq("File \"exponentiation_test.res\", line 25, characters 7-14", -2147483648, intPow(-2, 31)); + Test_utils.eq("File \"exponentiation_test.res\", line 26, characters 7-14", 0, intPow(2, 32)); + Test_utils.eq("File \"exponentiation_test.res\", line 27, characters 7-14", 0, intPow(2147483647, 2)); + Test_utils.eq("File \"exponentiation_test.res\", line 28, characters 7-14", 0, intPow(-2147483648, 2)); + Test_utils.eq("File \"exponentiation_test.res\", line 30, characters 7-14", 256, four ** four | 0); + Test_utils.eq("File \"exponentiation_test.res\", line 32, characters 7-14", 2 ** (0 / 10000), 1); + Test_utils.eq("File \"exponentiation_test.res\", line 33, characters 7-14", 2 ** (3 * 4), 4096); + Test_utils.eq("File \"exponentiation_test.res\", line 34, characters 7-14", 2 ** (5 % 3), 4); + Test_utils.eq("File \"exponentiation_test.res\", line 35, characters 7-14", 2n ** (3n * 2n), 64n); }); }); export { intPow, four, + floatPowDiv, + floatPowMul, + floatPowMod, + bigintPowMul, } /* Not a pure module */ diff --git a/tests/tests/src/exponentiation_test.res b/tests/tests/src/exponentiation_test.res index 19fb9cb7b44..216de749954 100644 --- a/tests/tests/src/exponentiation_test.res +++ b/tests/tests/src/exponentiation_test.res @@ -6,6 +6,12 @@ external jsPow: (float, float) => float = "Math.pow" let intPow: (int, int) => int = %raw(`(a, b) => Math.pow(a, b) | 0`) let four: int = %raw(`4`) +let floatPowDiv = (base: float, numerator: float, denominator: float) => + base ** (numerator /. denominator) +let floatPowMul = (base: float, left: float, right: float) => base ** (left *. right) +let floatPowMod = (base: float, value: float, modulus: float) => base ** (value % modulus) +let bigintPowMul = (base: bigint, left: bigint, right: bigint) => base ** (left * right) + describe(__MODULE__, () => { test("exponentiation operations", () => { eq(__LOC__, 2. ** 3. ** 2., jsPow(2., jsPow(3., 2.))) @@ -22,5 +28,10 @@ describe(__MODULE__, () => { eq(__LOC__, -2147483648 ** 2, intPow(-2147483648, 2)) eq(__LOC__, 4 ** 4, four ** four) + + eq(__LOC__, floatPowDiv(2., 0., 10000.), 1.) + eq(__LOC__, floatPowMul(2., 3., 4.), 4096.) + eq(__LOC__, floatPowMod(2., 5., 3.), 4.) + eq(__LOC__, bigintPowMul(2n, 3n, 2n), 64n) }) }) From 254cb45efd5e6bb454107af616afa91e44d9193c Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Sat, 15 Aug 2026 09:14:14 +0200 Subject: [PATCH 2/2] Update changelog for exponentiation precedence fix Signed-off-by: Christoph Knittel --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8f640f28a6..2fb9c18002e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ #### :bug: Bug fix +- Preserve parentheses around multiplication, division, and modulo expressions used as exponents. https://github.com/rescript-lang/rescript/pull/8550 - Preserve multibyte characters when wrapping long source lines in compiler code frames. https://github.com/rescript-lang/rescript/pull/8520 - Fix reanalyze optional-argument diagnostics for functions passed or returned as first-class values. https://github.com/rescript-lang/rescript/pull/8321