Skip to content

Commit 9228bdb

Browse files
committed
[Compiler] Don't count a setState in the dependency array of the effect it is called on as a usage
1 parent 93fc574 commit 9228bdb

File tree

3 files changed

+104
-7
lines changed

3 files changed

+104
-7
lines changed

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoDerivedComputationsInEffects_exp.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
BasicBlock,
2323
isUseRefType,
2424
SourceLocation,
25+
ArrayExpression,
2526
} from '../HIR';
2627
import {eachInstructionLValue, eachInstructionOperand} from '../HIR/visitors';
2728
import {isMutable} from '../ReactiveScopes/InferReactiveScopeVariables';
@@ -36,11 +37,17 @@ type DerivationMetadata = {
3637
isStateSource: boolean;
3738
};
3839

40+
type EffectMetadata = {
41+
effect: HIRFunction;
42+
dependencies: ArrayExpression;
43+
};
44+
3945
type ValidationContext = {
4046
readonly functions: Map<IdentifierId, FunctionExpression>;
47+
readonly candidateDependencies: Map<IdentifierId, ArrayExpression>;
4148
readonly errors: CompilerError;
4249
readonly derivationCache: DerivationCache;
43-
readonly effects: Set<HIRFunction>;
50+
readonly effectsCache: Map<IdentifierId, EffectMetadata>;
4451
readonly setStateLoads: Map<IdentifierId, IdentifierId | null>;
4552
readonly setStateUsages: Map<IdentifierId, Set<SourceLocation>>;
4653
};
@@ -175,18 +182,20 @@ export function validateNoDerivedComputationsInEffects_exp(
175182
fn: HIRFunction,
176183
): Result<void, CompilerError> {
177184
const functions: Map<IdentifierId, FunctionExpression> = new Map();
185+
const candidateDependencies: Map<IdentifierId, ArrayExpression> = new Map();
178186
const derivationCache = new DerivationCache();
179187
const errors = new CompilerError();
180-
const effects: Set<HIRFunction> = new Set();
188+
const effectsCache: Map<IdentifierId, EffectMetadata> = new Map();
181189

182190
const setStateLoads: Map<IdentifierId, IdentifierId> = new Map();
183191
const setStateUsages: Map<IdentifierId, Set<SourceLocation>> = new Map();
184192

185193
const context: ValidationContext = {
186194
functions,
195+
candidateDependencies,
187196
errors,
188197
derivationCache,
189-
effects,
198+
effectsCache,
190199
setStateLoads,
191200
setStateUsages,
192201
};
@@ -229,8 +238,8 @@ export function validateNoDerivedComputationsInEffects_exp(
229238
isFirstPass = false;
230239
} while (context.derivationCache.snapshot());
231240

232-
for (const effect of effects) {
233-
validateEffect(effect, context);
241+
for (const [, effect] of effectsCache) {
242+
validateEffect(effect.effect, effect.dependencies, context);
234243
}
235244

236245
return errors.asResult();
@@ -354,8 +363,14 @@ function recordInstructionDerivations(
354363
value.args[1].kind === 'Identifier'
355364
) {
356365
const effectFunction = context.functions.get(value.args[0].identifier.id);
357-
if (effectFunction != null) {
358-
context.effects.add(effectFunction.loweredFunc.func);
366+
const deps = context.candidateDependencies.get(
367+
value.args[1].identifier.id,
368+
);
369+
if (effectFunction != null && deps != null) {
370+
context.effectsCache.set(value.args[0].identifier.id, {
371+
effect: effectFunction.loweredFunc.func,
372+
dependencies: deps,
373+
});
359374
}
360375
} else if (isUseStateType(lvalue.identifier) && value.args.length > 0) {
361376
typeOfValue = 'fromState';
@@ -367,6 +382,8 @@ function recordInstructionDerivations(
367382
);
368383
return;
369384
}
385+
} else if (value.kind === 'ArrayExpression') {
386+
context.candidateDependencies.set(lvalue.identifier.id, value);
370387
}
371388

372389
for (const operand of eachInstructionOperand(instr)) {
@@ -596,6 +613,7 @@ function getFnLocalDeps(
596613

597614
function validateEffect(
598615
effectFunction: HIRFunction,
616+
dependencies: ArrayExpression,
599617
context: ValidationContext,
600618
): void {
601619
const seenBlocks: Set<BlockId> = new Set();
@@ -612,6 +630,16 @@ function validateEffect(
612630
Set<SourceLocation>
613631
> = new Map();
614632

633+
// Consider setStates in the effect's dependency array as being part of effectSetStateUsages
634+
for (const dep of dependencies.elements) {
635+
if (dep.kind === 'Identifier') {
636+
const usage = context.setStateUsages.get(dep.identifier.id);
637+
if (usage !== undefined) {
638+
effectSetStateUsages.set(dep.identifier.id, usage);
639+
}
640+
}
641+
}
642+
615643
let cleanUpFunctionDeps: Set<IdentifierId> | undefined;
616644

617645
const globals: Set<IdentifierId> = new Set();
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @validateNoDerivedComputationsInEffects_exp
6+
7+
function Component({ prop }) {
8+
9+
const [s, setS] = useState(0)
10+
useEffect(() => {
11+
setS(prop)
12+
}, [prop, setS]);
13+
14+
return (<div>{prop}</div>)
15+
}
16+
17+
```
18+
19+
## Code
20+
21+
```javascript
22+
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp
23+
24+
function Component(t0) {
25+
const $ = _c(5);
26+
const { prop } = t0;
27+
28+
const [, setS] = useState(0);
29+
let t1;
30+
let t2;
31+
if ($[0] !== prop) {
32+
t1 = () => {
33+
setS(prop);
34+
};
35+
t2 = [prop, setS];
36+
$[0] = prop;
37+
$[1] = t1;
38+
$[2] = t2;
39+
} else {
40+
t1 = $[1];
41+
t2 = $[2];
42+
}
43+
useEffect(t1, t2);
44+
let t3;
45+
if ($[3] !== prop) {
46+
t3 = <div>{prop}</div>;
47+
$[3] = prop;
48+
$[4] = t3;
49+
} else {
50+
t3 = $[4];
51+
}
52+
return t3;
53+
}
54+
55+
```
56+
57+
### Eval output
58+
(kind: exception) Fixture not implemented
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// @validateNoDerivedComputationsInEffects_exp
2+
3+
function Component({ prop }) {
4+
5+
const [s, setS] = useState(0)
6+
useEffect(() => {
7+
setS(prop)
8+
}, [prop, setS]);
9+
10+
return (<div>{prop}</div>)
11+
}

0 commit comments

Comments
 (0)