Files
Rusty Raven 2b19aecd0e [rust-compiler] Fix exponentiation to match js semantics (#37587)
## Summary

The Rust port's constant propagation folds `**` with `f64::powf`, which
follows IEEE 754. IEEE `pow` returns `1` for `pow(1, NaN)` and `pow(±1,
±∞)`, but ECMAScript's
[`Number::exponentiate`](https://tc39.es/ecma262/#sec-numeric-types-number-exponentiate)
returns `NaN` in those cases. The TS compiler folds with native `lhs **
rhs`, so the two backends disagree, and the Rust-compiled output changes
runtime behavior:

```js
const x = 1 ** (1 / 0); // JS: NaN, Rust-compiled: 1
```

This adds `js_exponentiate`, which returns `NaN` when `|base| == 1` and
the exponent is not finite, and otherwise defers to `powf`. `**=` lowers
to the same `BinaryOperator::Exponent`, so it is covered too.

To check the guard is neither too narrow nor too broad, I compared
`f64::powf` against JS `**` over all 225 pairs drawn from {NaN,
±Infinity, ±0, ±1, ±2, ±0.5, ±3, ±1e308}. The only mismatches were `1 **
NaN`, `1 ** ±Infinity` and `(-1) ** ±Infinity`. The condition also
matches `(-1) ** NaN`, where `powf` already returns `NaN`.

While in this file I noticed two places where `js_to_number` diverges
from `ToNumber`. I left them out to keep this PR focused, and can send a
follow-up if that's useful:

- `trimmed.parse::<f64>()` accepts `"inf"`, `"infinity"` and
`"INFINITY"`, so `"inf" == 1 / 0` folds to `true` (JS: `false`).
- The `0x`/`0o`/`0b` branches use `u64::from_str_radix`, which fails
past 64 bits (`"0xFFFFFFFFFFFFFFFFF" == 295147905179352830000` folds to
`false`, JS: `true`) and accepts a sign after the prefix (`"0x+10" ==
16` folds to `true`, JS: `false`).

## How did you test this change?

Added `constant-propagation-exponent-non-finite.js` with the five
mismatching cases plus five controls: `(-1) ** NaN`, `NaN ** 0`, `2 **
Infinity`, `0.5 ** Infinity` and `2 ** 10`. The snapshot was generated
with the TS compiler.

- Before the fix, `yarn snap --rust -p
constant-propagation-exponent-non-finite` fails on eval output: expected
`[null,null,null,null,null,null,1,null,0,1024]`, got
`[1,1,1,1,1,null,1,null,0,1024]`.
- With a narrower guard (`exponent.is_infinite()` instead of
`!exponent.is_finite()`) it still fails, only on `1 ** NaN`.
- With the fix, it passes on both backends.

I also ran the steps from `compiler_rust.yml` locally:

- `cargo check`, `cargo build`, `cargo test --workspace` (49 passed)
- `bash scripts/test-babel-ast.sh`
- `bash scripts/test-rust-port.sh`: 1816 passed, 0 failed (1815 on
`main`, before the new fixture)
- `yarn snap --rust` and `yarn snap`: 1817 passed, 0 failed
2026-09-16 12:35:58 +02:00
..