Skip to content

Commit 7212340

Browse files
committed
[compiler] Add DependencyPath optional property
Adds an `optional: boolean` property to each token in a DependencyPath, currently always set to false. Also updates the equality and printing logic for paths to account for this field. Subsequent PRs will update our logic to determine which manual dependencies were optional, then we can start inferring optional deps as well. ghstack-source-id: 882ee87 Pull Request resolved: facebook/react#30813
1 parent e9356cc commit 7212340

File tree

8 files changed

+55
-9
lines changed

8 files changed

+55
-9
lines changed

compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,10 +1500,13 @@ export type ReactiveScopeDependency = {
15001500
export function areEqualPaths(a: DependencyPath, b: DependencyPath): boolean {
15011501
return (
15021502
a.length === b.length &&
1503-
a.every((item, ix) => item.property === b[ix].property)
1503+
a.every(
1504+
(item, ix) =>
1505+
item.property === b[ix].property && item.optional === b[ix].optional,
1506+
)
15041507
);
15051508
}
1506-
export type DependencyPath = Array<{property: string}>;
1509+
export type DependencyPath = Array<{property: string; optional: boolean}>;
15071510

15081511
/*
15091512
* Simulated opaque type for BlockIds to prevent using normal numbers as block ids

compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -869,7 +869,7 @@ export function printManualMemoDependency(
869869
? val.root.value.identifier.name.value
870870
: printIdentifier(val.root.value.identifier);
871871
}
872-
return `${rootStr}${val.path.length > 0 ? '.' : ''}${val.path.join('.')}`;
872+
return `${rootStr}${val.path.map(v => `${v.optional ? '?.' : '.'}${v.property}`).join('')}`;
873873
}
874874
export function printType(type: Type): string {
875875
if (type.kind === 'Type') return '';

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ export function collectMaybeMemoDependencies(
6868
if (object != null) {
6969
return {
7070
root: object.root,
71-
path: [...object.path, {property: value.property}],
71+
// TODO: determine if the access is optional
72+
path: [...object.path, {property: value.property, optional: false}],
7273
};
7374
}
7475
break;

compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/DeriveMinimalDependencies.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77

88
import {CompilerError} from '../CompilerError';
9-
import {Identifier, ReactiveScopeDependency} from '../HIR';
9+
import {DependencyPath, Identifier, ReactiveScopeDependency} from '../HIR';
1010
import {printIdentifier} from '../HIR/PrintHIR';
1111
import {assertExhaustive} from '../Utils/utils';
1212

@@ -252,7 +252,7 @@ type DependencyNode = {
252252
};
253253

254254
type ReduceResultNode = {
255-
relativePath: Array<{property: string}>;
255+
relativePath: DependencyPath;
256256
accessType: PropertyAccessType;
257257
};
258258

@@ -283,7 +283,10 @@ function deriveMinimalDependenciesInSubtree(
283283
const childResult = deriveMinimalDependenciesInSubtree(childNode).map(
284284
({relativePath, accessType}) => {
285285
return {
286-
relativePath: [{property: childName}, ...relativePath],
286+
relativePath: [
287+
{property: childName, optional: false},
288+
...relativePath,
289+
],
287290
accessType,
288291
};
289292
},

compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateScopeDependencies.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ class Context {
485485
};
486486
}
487487

488-
objectDependency.path.push({property});
488+
objectDependency.path.push({property, optional: false});
489489

490490
return objectDependency;
491491
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ function prettyPrintScopeDependency(val: ReactiveScopeDependency): string {
116116
} else {
117117
rootStr = '[unnamed]';
118118
}
119-
return `${rootStr}${val.path.length > 0 ? '.' : ''}${val.path.join('.')}`;
119+
return `${rootStr}${val.path.map(v => `${v.optional ? '?.' : '.'}${v.property}`).join('')}`;
120120
}
121121

122122
enum CompareDependencyResult {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @validatePreserveExistingMemoizationGuarantees
6+
function Component(props) {
7+
const data = useMemo(() => {
8+
return props.items?.edges?.nodes ?? [];
9+
}, [props.items?.edges?.nodes]);
10+
return <Foo data={data} />;
11+
}
12+
13+
```
14+
15+
16+
## Error
17+
18+
```
19+
1 | // @validatePreserveExistingMemoizationGuarantees
20+
2 | function Component(props) {
21+
> 3 | const data = useMemo(() => {
22+
| ^^^^^^^
23+
> 4 | return props.items?.edges?.nodes ?? [];
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
25+
> 5 | }, [props.items?.edges?.nodes]);
26+
| ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (3:5)
27+
6 | return <Foo data={data} />;
28+
7 | }
29+
8 |
30+
```
31+
32+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @validatePreserveExistingMemoizationGuarantees
2+
function Component(props) {
3+
const data = useMemo(() => {
4+
return props.items?.edges?.nodes ?? [];
5+
}, [props.items?.edges?.nodes]);
6+
return <Foo data={data} />;
7+
}

0 commit comments

Comments
 (0)