feat(compiler): implement constant folding for unary minus (#33140)
## Summary `-constant` is represented as a `UnaryExpression` node that is currently not part of constant folding. If the operand is a constant number, the node is folded to `constant * -1`. This also coerces `-0` to `0`, resulting in `0 === -0` being folded to `true`. ## How did you test this change? See attached tests
Niklas Mollenhauer committed
May 7, 2025 at 19:15 UTC
946da518eb2d64d808f9204a72e05892d3005f3f
4 files changed
+117
-1
compiler/packages/babel-plugin-react-compiler/src/Optimization/ConstantPropagation.ts
+17
@@ -327,6 +327,23 @@ function evaluateInstruction(
327
}
328
return null;
329
}
330
+ case '-': {
331
+ const operand = read(constants, value.value);
332
+ if (
333
+ operand !== null &&
334
+ operand.kind === 'Primitive' &&
335
+ typeof operand.value === 'number'
336
+ ) {
337
+ const result: Primitive = {
338
+ kind: 'Primitive',
339
+ value: operand.value * -1,
340
+ loc: value.loc,
341
+ };
342
+ instr.value = result;
343
+ return result;
344
+ }
345
+ return null;
346
+ }
347
default:
348
return null;
349
}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/constant-propagation-unary-number.expect.md
new
+74
@@ -0,0 +1,74 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import {Stringify} from 'shared-runtime';
6
+
7
+function foo() {
8
+ const a = -1;
9
+ return (
10
+ <Stringify
11
+ value={[
12
+ 2 * a,
13
+ -0,
14
+ 0 === -0,
15
+ -Infinity,
16
+ -NaN,
17
+ a * NaN,
18
+ a * Infinity,
19
+ a * -Infinity,
20
+ ]}
21
+ />
22
+ );
23
+}
24
+
25
+export const FIXTURE_ENTRYPOINT = {
26
+ fn: foo,
27
+ params: [],
28
+ isComponent: false,
29
+};
30
+
31
+```
32
+
33
+## Code
34
+
35
+```javascript
36
+import { c as _c } from "react/compiler-runtime";
37
+import { Stringify } from "shared-runtime";
38
+
39
+function foo() {
40
+ const $ = _c(1);
41
+ let t0;
42
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
43
+ t0 = (
44
+ <Stringify
45
+ value={[
46
+ -2,
47
+ 0,
48
+ true,
49
+ -Infinity,
50
+ -NaN,
51
+
52
+ -1 * NaN,
53
+ -1 * Infinity,
54
+ -1 * -Infinity,
55
+ ]}
56
+ />
57
+ );
58
+ $[0] = t0;
59
+ } else {
60
+ t0 = $[0];
61
+ }
62
+ return t0;
63
+}
64
+
65
+export const FIXTURE_ENTRYPOINT = {
66
+ fn: foo,
67
+ params: [],
68
+ isComponent: false,
69
+};
70
+
71
+```
72
+
73
+### Eval output
74
+(kind: ok) <div>{"value":[-2,0,true,null,null,null,null,null]}</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/constant-propagation-unary-number.js
new
+25
@@ -0,0 +1,25 @@
1
+import {Stringify} from 'shared-runtime';
2
+
3
+function foo() {
4
+ const a = -1;
5
+ return (
6
+ <Stringify
7
+ value={[
8
+ 2 * a,
9
+ -0,
10
+ 0 === -0,
11
+ -Infinity,
12
+ -NaN,
13
+ a * NaN,
14
+ a * Infinity,
15
+ a * -Infinity,
16
+ ]}
17
+ />
18
+ );
19
+}
20
+
21
+export const FIXTURE_ENTRYPOINT = {
22
+ fn: foo,
23
+ params: [],
24
+ isComponent: false,
25
+};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/constant-propagation-unary.expect.md
+1
-1
@@ -58,7 +58,7 @@ function foo() {
58
n0: true,
59
n1: false,
60
n2: false,
61
- n3: !-1,
61
+ n3: false,
62
s0: true,
63
s1: false,
64
s2: false,