Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,32 @@ function collectNonNullsInBlocks(
assumedNonNullObjects.add(entry);
}
}
} else if (
fn.env.config.enablePreserveExistingMemoizationGuarantees &&
instr.value.kind === 'StartMemoize' &&
instr.value.deps != null
) {
for (const dep of instr.value.deps) {
if (dep.root.kind === 'NamedLocal') {
if (
!isImmutableAtInstr(dep.root.value.identifier, instr.id, context)
) {
continue;
}
for (let i = 0; i < dep.path.length; i++) {
const pathEntry = dep.path[i]!;
if (pathEntry.optional) {
break;
}
const depNode = context.registry.getOrCreateProperty({
identifier: dep.root.value.identifier,
path: dep.path.slice(0, i),
reactive: dep.root.value.reactive,
});
assumedNonNullObjects.add(depNode);
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export const EnvironmentConfigSchema = z.object({
* that if a useEffect or useCallback references a function value, that function value will be
* considered frozen, and in turn all of its referenced variables will be considered frozen as well.
*/
enablePreserveExistingMemoizationGuarantees: z.boolean().default(false),
enablePreserveExistingMemoizationGuarantees: z.boolean().default(true),

/**
* Validates that all useMemo/useCallback values are also memoized by Forget. This mode can be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
## Input

```javascript
// @enablePreserveExistingMemoizationGuarantees:false
// bar(props.b) is an allocating expression that produces a primitive, which means
// that Forget should memoize it.
// Correctness:
Expand All @@ -16,7 +17,8 @@ function AllocatingPrimitiveAsDep(props) {
## Code

```javascript
import { c as _c } from "react/compiler-runtime"; // bar(props.b) is an allocating expression that produces a primitive, which means
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
// bar(props.b) is an allocating expression that produces a primitive, which means
// that Forget should memoize it.
// Correctness:
// - y depends on either bar(props.b) or bar(props.b) + 1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false
// bar(props.b) is an allocating expression that produces a primitive, which means
// that Forget should memoize it.
// Correctness:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
## Input

```javascript
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';

const someGlobal = {value: 0};
Expand Down Expand Up @@ -32,7 +33,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code

```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
import { useMemo } from "react";

const someGlobal = { value: 0 };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';

const someGlobal = {value: 0};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
## Input

```javascript
// @enablePreserveExistingMemoizationGuarantees:false
function Component(props) {
let a = foo();
// freeze `a` so we know the next line cannot mutate it
Expand All @@ -17,7 +18,7 @@ function Component(props) {
## Code

```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
function Component(props) {
const $ = _c(2);
const a = foo();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false
function Component(props) {
let a = foo();
// freeze `a` so we know the next line cannot mutate it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
## Input

```javascript
// @enablePreserveExistingMemoizationGuarantees:false
import {Stringify, identity} from 'shared-runtime';

function foo() {
Expand Down Expand Up @@ -64,7 +65,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code

```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
import { Stringify, identity } from "shared-runtime";

function foo() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false
import {Stringify, identity} from 'shared-runtime';

function foo() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
## Input

```javascript
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {Stringify} from 'shared-runtime';

Expand All @@ -25,7 +26,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code

```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
import { useMemo } from "react";
import { Stringify } from "shared-runtime";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {Stringify} from 'shared-runtime';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
## Input

```javascript
// @enablePreserveExistingMemoizationGuarantees:false
function foo(props) {
let x, y;
({x, y} = {x: props.a, y: props.b});
Expand All @@ -21,6 +22,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code

```javascript
// @enablePreserveExistingMemoizationGuarantees:false
function foo(props) {
let x;
let y;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @enablePreserveExistingMemoizationGuarantees:false
function foo(props) {
let x, y;
({x, y} = {x: props.a, y: props.b});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
## Input

```javascript
// @flow @enableNewMutationAliasingModel
// @flow @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false
/**
* This hook returns a function that when called with an input object,
* will return the result of mapping that input with the supplied map
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @flow @enableNewMutationAliasingModel
// @flow @enableNewMutationAliasingModel @enablePreserveExistingMemoizationGuarantees:false
/**
* This hook returns a function that when called with an input object,
* will return the result of mapping that input with the supplied map
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
## Input

```javascript
// @validatePreserveExistingMemoizationGuarantees
// @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false

/**
* Repro from https://github.com/facebook/react/issues/34262
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @validatePreserveExistingMemoizationGuarantees
// @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false

/**
* Repro from https://github.com/facebook/react/issues/34262
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
## Input

```javascript
// @validatePreserveExistingMemoizationGuarantees
// @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false

import {identity, Stringify, useHook} from 'shared-runtime';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @validatePreserveExistingMemoizationGuarantees
// @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false

import {identity, Stringify, useHook} from 'shared-runtime';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@

## Input

```javascript
// @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies @enableTreatFunctionDepsAsConditional:false

import {useMemo} from 'react';
import {identity, ValidateMemoization} from 'shared-runtime';

function Component({x}) {
const object = useMemo(() => {
return identity({
callback: () => {
// This is a bug in our dependency inference: we stop capturing dependencies
// after x.a.b?.c. But what this dependency is telling us is that if `x.a.b`
// was non-nullish, then we can access `.c.d?.e`. Thus we should take the
// full property chain, exactly as-is with optionals/non-optionals, as a
// dependency
return identity(x.a.b?.c.d?.e);
},
});
}, [x.a.b?.c.d?.e]);
const result = useMemo(() => {
return [object.callback()];
}, [object]);
return <Inner x={x} result={result} />;
}

function Inner({x, result}) {
'use no memo';
return <ValidateMemoization inputs={[x.y.z]} output={result} />;
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{x: {y: {z: 42}}}],
sequentialRenders: [
{x: {y: {z: 42}}},
{x: {y: {z: 42}}},
{x: {y: {z: 3.14}}},
{x: {y: {z: 42}}},
{x: {y: {z: 3.14}}},
{x: {y: {z: 42}}},
],
};

```


## Error

```
Found 1 error:

Compilation Skipped: Existing memoization could not be preserved

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. The inferred dependency was `x.a.b?.c`, but the source dependencies were [x.a.b?.c.d?.e]. Inferred less specific property than source.

error.todo-preserve-memo-deps-mixed-optional-nonoptional-property-chain.ts:7:25
5 |
6 | function Component({x}) {
> 7 | const object = useMemo(() => {
| ^^^^^^^
> 8 | return identity({
| ^^^^^^^^^^^^^^^^^^^^^
> 9 | callback: () => {
| ^^^^^^^^^^^^^^^^^^^^^
> 10 | // This is a bug in our dependency inference: we stop capturing dependencies
| ^^^^^^^^^^^^^^^^^^^^^
> 11 | // after x.a.b?.c. But what this dependency is telling us is that if `x.a.b`
| ^^^^^^^^^^^^^^^^^^^^^
> 12 | // was non-nullish, then we can access `.c.d?.e`. Thus we should take the
| ^^^^^^^^^^^^^^^^^^^^^
> 13 | // full property chain, exactly as-is with optionals/non-optionals, as a
| ^^^^^^^^^^^^^^^^^^^^^
> 14 | // dependency
| ^^^^^^^^^^^^^^^^^^^^^
> 15 | return identity(x.a.b?.c.d?.e);
| ^^^^^^^^^^^^^^^^^^^^^
> 16 | },
| ^^^^^^^^^^^^^^^^^^^^^
> 17 | });
| ^^^^^^^^^^^^^^^^^^^^^
> 18 | }, [x.a.b?.c.d?.e]);
| ^^^^ Could not preserve existing manual memoization
19 | const result = useMemo(() => {
20 | return [object.callback()];
21 | }, [object]);
```


Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// @enablePreserveExistingMemoizationGuarantees @validatePreserveExistingMemoizationGuarantees @enableOptionalDependencies @enableTreatFunctionDepsAsConditional:false

import {useMemo} from 'react';
import {identity, ValidateMemoization} from 'shared-runtime';

function Component({x}) {
const object = useMemo(() => {
return identity({
callback: () => {
// This is a bug in our dependency inference: we stop capturing dependencies
// after x.a.b?.c. But what this dependency is telling us is that if `x.a.b`
// was non-nullish, then we can access `.c.d?.e`. Thus we should take the
// full property chain, exactly as-is with optionals/non-optionals, as a
// dependency
return identity(x.a.b?.c.d?.e);
},
});
}, [x.a.b?.c.d?.e]);
const result = useMemo(() => {
return [object.callback()];
}, [object]);
return <Inner x={x} result={result} />;
}

function Inner({x, result}) {
'use no memo';
return <ValidateMemoization inputs={[x.y.z]} output={result} />;
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{x: {y: {z: 42}}}],
sequentialRenders: [
{x: {y: {z: 42}}},
{x: {y: {z: 42}}},
{x: {y: {z: 3.14}}},
{x: {y: {z: 42}}},
{x: {y: {z: 3.14}}},
{x: {y: {z: 42}}},
],
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
## Input

```javascript
// @flow @validatePreserveExistingMemoizationGuarantees
// @flow @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {logValue, useFragment, useHook, typedLog} from 'shared-runtime';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @flow @validatePreserveExistingMemoizationGuarantees
// @flow @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {logValue, useFragment, useHook, typedLog} from 'shared-runtime';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
## Input

```javascript
// @flow @validatePreserveExistingMemoizationGuarantees
// @flow @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false
import {useFragment} from 'react-relay';
import LogEvent from 'LogEvent';
import {useCallback, useMemo} from 'react';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @flow @validatePreserveExistingMemoizationGuarantees
// @flow @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false
import {useFragment} from 'react-relay';
import LogEvent from 'LogEvent';
import {useCallback, useMemo} from 'react';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
## Input

```javascript
// @validatePreserveExistingMemoizationGuarantees
// @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {ValidateMemoization, useHook} from 'shared-runtime';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @validatePreserveExistingMemoizationGuarantees
// @validatePreserveExistingMemoizationGuarantees @enablePreserveExistingMemoizationGuarantees:false
import {useMemo} from 'react';
import {ValidateMemoization, useHook} from 'shared-runtime';

Expand Down
Loading
Loading