Skip to content

Commit b9917a9

Browse files
committed
[compiler] Fix mutable ranges for StoreContext
ghstack-source-id: 1d78b11 Pull Request resolved: #33386
1 parent 62a6b3d commit b9917a9

File tree

5 files changed

+112
-13
lines changed

5 files changed

+112
-13
lines changed

compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
Hole,
1616
IdentifierId,
1717
Instruction,
18+
InstructionKind,
1819
InstructionValue,
1920
isArrayType,
2021
isMapType,
@@ -768,7 +769,7 @@ function applyEffect(
768769
}
769770
}
770771

771-
const DEBUG = true;
772+
const DEBUG = false;
772773

773774
class InferenceState {
774775
env: Environment;
@@ -1452,7 +1453,21 @@ function computeSignatureForInstruction(
14521453
}
14531454
break;
14541455
}
1455-
case 'DeclareContext':
1456+
case 'DeclareContext': {
1457+
// Context variables are conceptually like mutable boxes
1458+
effects.push({
1459+
kind: 'Create',
1460+
into: value.lvalue.place,
1461+
value: ValueKind.Mutable,
1462+
});
1463+
effects.push({
1464+
kind: 'Create',
1465+
into: lvalue,
1466+
// The result can't be referenced so this value doesn't matter
1467+
value: ValueKind.Primitive,
1468+
});
1469+
break;
1470+
}
14561471
case 'DeclareLocal': {
14571472
// TODO check this
14581473
effects.push({
@@ -1481,13 +1496,25 @@ function computeSignatureForInstruction(
14811496
break;
14821497
}
14831498
case 'LoadContext': {
1499+
// We load from the context
14841500
effects.push({kind: 'Assign', from: value.place, into: lvalue});
14851501
break;
14861502
}
14871503
case 'StoreContext': {
1488-
effects.push({kind: 'Mutate', value: value.lvalue.place});
1504+
// We're storing into the conceptual box
1505+
if (value.lvalue.kind === InstructionKind.Reassign) {
1506+
effects.push({kind: 'Mutate', value: value.lvalue.place});
1507+
} else {
1508+
// Context variables are conceptually like mutable boxes
1509+
effects.push({
1510+
kind: 'Create',
1511+
into: value.lvalue.place,
1512+
value: ValueKind.Mutable,
1513+
});
1514+
}
1515+
// Which aliases the value
14891516
effects.push({
1490-
kind: 'Assign',
1517+
kind: 'Alias',
14911518
from: value.value,
14921519
into: value.lvalue.place,
14931520
});

compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingRanges.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
} from '../HIR/visitors';
2121
import DisjointSet from '../Utils/DisjointSet';
2222
import {assertExhaustive} from '../Utils/utils';
23+
import {debugAliases} from './InferMutableRanges';
2324
import {inferMutableRangesForAlias} from './InferMutableRangesForAlias';
2425

2526
/**
@@ -44,7 +45,9 @@ export function inferMutationAliasingRanges(fn: HIRFunction): void {
4445

4546
for (const instr of block.instructions) {
4647
for (const lvalue of eachInstructionLValue(instr)) {
47-
lvalue.identifier.mutableRange.start = instr.id;
48+
if (lvalue.identifier.mutableRange.start === 0) {
49+
lvalue.identifier.mutableRange.start = instr.id;
50+
}
4851
lvalue.identifier.mutableRange.end = makeInstructionId(
4952
Math.max(instr.id + 1, lvalue.identifier.mutableRange.end),
5053
);
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @validatePreserveExistingMemoizationGuarantees @enableNewMutationAliasingModel
6+
import {useCallback} from 'react';
7+
import {makeArray} from 'shared-runtime';
8+
9+
// This case is already unsound in source, so we can safely bailout
10+
function Foo(props) {
11+
let x = [];
12+
x.push(props);
13+
14+
// makeArray() is captured, but depsList contains [props]
15+
const cb = useCallback(() => [x], [x]);
16+
17+
x = makeArray();
18+
19+
return cb;
20+
}
21+
export const FIXTURE_ENTRYPOINT = {
22+
fn: Foo,
23+
params: [{}],
24+
};
25+
26+
```
27+
28+
29+
## Error
30+
31+
```
32+
9 |
33+
10 | // makeArray() is captured, but depsList contains [props]
34+
> 11 | const cb = useCallback(() => [x], [x]);
35+
| ^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This dependency may be mutated later, which could cause the value to change unexpectedly (11:11)
36+
37+
CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output. (11:11)
38+
12 |
39+
13 | x = makeArray();
40+
14 |
41+
```
42+
43+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// @validatePreserveExistingMemoizationGuarantees @enableNewMutationAliasingModel
2+
import {useCallback} from 'react';
3+
import {makeArray} from 'shared-runtime';
4+
5+
// This case is already unsound in source, so we can safely bailout
6+
function Foo(props) {
7+
let x = [];
8+
x.push(props);
9+
10+
// makeArray() is captured, but depsList contains [props]
11+
const cb = useCallback(() => [x], [x]);
12+
13+
x = makeArray();
14+
15+
return cb;
16+
}
17+
export const FIXTURE_ENTRYPOINT = {
18+
fn: Foo,
19+
params: [{}],
20+
};

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/reactive-ref.expect.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,28 @@ function ReactiveRefInEffect(props) {
2222
```javascript
2323
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
2424
function ReactiveRefInEffect(props) {
25-
const $ = _c(2);
25+
const $ = _c(4);
2626
const ref1 = useRef("initial value");
2727
const ref2 = useRef("initial value");
2828
let ref;
29-
if (props.foo) {
30-
ref = ref1;
29+
if ($[0] !== props.foo) {
30+
if (props.foo) {
31+
ref = ref1;
32+
} else {
33+
ref = ref2;
34+
}
35+
$[0] = props.foo;
36+
$[1] = ref;
3137
} else {
32-
ref = ref2;
38+
ref = $[1];
3339
}
3440
let t0;
35-
if ($[0] !== ref) {
41+
if ($[2] !== ref) {
3642
t0 = () => print(ref);
37-
$[0] = ref;
38-
$[1] = t0;
43+
$[2] = ref;
44+
$[3] = t0;
3945
} else {
40-
t0 = $[1];
46+
t0 = $[3];
4147
}
4248
useEffect(t0);
4349
}

0 commit comments

Comments
 (0)