Skip to content

Conversation

@josephsavona
Copy link
Member

@josephsavona josephsavona commented Sep 4, 2025

The compiler currently drops manual memoization and rewrites it using its own inference. If the existing manual memo dependencies has missing or extra dependencies, compilation can change behavior by running the computation more often (if deps were missing) or less often (if there were extra deps). We currently address this by relying on the developer to use the ESLint plugin and have eslint-disable-next-line react-hooks/exhaustive-deps suppressions in their code. If a suppression exists, we skip compilation.

But not everyone is using the linter! Relying on the linter is also imprecise since it forces us to bail out on exhaustive-deps checks that only effect (ahem) effects — and while it isn't good to have incorrect deps on effects, it isn't a problem for compilation.

So this PR is a rough sketch of validating manual memoization dependencies in the compiler. Long-term we could use this to also check effect deps and replace the ExhaustiveDeps lint rule, but for now I'm focused specifically on manual memoization use-cases. If this works, we can stop bailing out on ESLint suppressions, since the compiler will implement all the appropriate checks (we already check rules of hooks).


Stack created with Sapling. Best reviewed with ReviewStack.

@meta-cla meta-cla bot added the CLA Signed label Sep 4, 2025
@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Sep 4, 2025
@josephsavona josephsavona force-pushed the pr34394 branch 8 times, most recently from a1cd347 to f1c7583 Compare September 6, 2025 02:38
josephsavona added a commit that referenced this pull request Sep 6, 2025
…34406)

Adds missing locations to all the statement kinds that we produce in
codegenInstruction(), and adds generic handling of source locations for
the nodes produced by codegenInstructionValue(). There are definitely
some places where we are still missing a location, but this should
address some of the known issues we've seen such as missing location on
`throw`.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34406).
* #34394
* __->__ #34406
* #34346
github-actions bot pushed a commit that referenced this pull request Sep 6, 2025
…34406)

Adds missing locations to all the statement kinds that we produce in
codegenInstruction(), and adds generic handling of source locations for
the nodes produced by codegenInstructionValue(). There are definitely
some places where we are still missing a location, but this should
address some of the known issues we've seen such as missing location on
`throw`.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34406).
* #34394
* __->__ #34406
* #34346

DiffTrain build for [f5e96b9](f5e96b9)
github-actions bot pushed a commit that referenced this pull request Sep 6, 2025
…34406)

Adds missing locations to all the statement kinds that we produce in
codegenInstruction(), and adds generic handling of source locations for
the nodes produced by codegenInstructionValue(). There are definitely
some places where we are still missing a location, but this should
address some of the known issues we've seen such as missing location on
`throw`.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34406).
* #34394
* __->__ #34406
* #34346

DiffTrain build for [f5e96b9](f5e96b9)
github-actions bot pushed a commit to code/lib-react that referenced this pull request Sep 7, 2025
…acebook#34406)

Adds missing locations to all the statement kinds that we produce in
codegenInstruction(), and adds generic handling of source locations for
the nodes produced by codegenInstructionValue(). There are definitely
some places where we are still missing a location, but this should
address some of the known issues we've seen such as missing location on
`throw`.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34406).
* facebook#34394
* __->__ facebook#34406
* facebook#34346

DiffTrain build for [f5e96b9](facebook@f5e96b9)
github-actions bot pushed a commit to code/lib-react that referenced this pull request Sep 7, 2025
…acebook#34406)

Adds missing locations to all the statement kinds that we produce in
codegenInstruction(), and adds generic handling of source locations for
the nodes produced by codegenInstructionValue(). There are definitely
some places where we are still missing a location, but this should
address some of the known issues we've seen such as missing location on
`throw`.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34406).
* facebook#34394
* __->__ facebook#34406
* facebook#34346

DiffTrain build for [f5e96b9](facebook@f5e96b9)
@josephsavona josephsavona marked this pull request as ready for review September 10, 2025 15:11
> 24 | return [];
| ^^^^^^^^^^^^^^
> 25 | }, [x, y.z, z?.y?.a]);
| ^^^^ Unnecessary dependencies `x`, `y.z`, `z?.y?.a`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In follow-ups i want to align the way we calculate the manual deps in DropManualMemo and the inferred deps in this pass, including adding locations to all the deps. Then we could point to the specific locations here.

Further, we should track a location for the manual deps array literal, so that we can emit a code suggestion to replace w the correct deps.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another todo list item: add a separate feature flag to control modes:

  • the compiler just needs to know that the deps are "correct". Having eg x and x.y as deps should be fine.
  • for linting, we probably want to have the most precise deps and no unnecessary extras. We'd want to error on deps [x, x.y] and suggest replacing with [x].

loc: value.loc,
});
startMemo = value;
dependencies.clear();
Copy link
Contributor

@mofeiZ mofeiZ Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: should this be an assertion instead since onFinishMemoize already clears dependencies and locals?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, deps are always aggregated and have to be cleared before entering a scope. We don't technically need to clear them when exiting a scope in onFinishMemoize but it doesn't hurt either.

}

/**
* This function determines the dependencies of the given function relative to
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mofeiZ more about the approach here

Comment on lines +322 to +324
* That said, LoadLocal/LoadContext does not immediately take a dependency,
* we store the dependency in a temporary and set it as used when that temporary
* is referenced as an operand.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'll add a note but this is necessary because of property chains and optionals

…ation

The compiler currently drops manual memoization and rewrites it using its own inference. If the existing manual memo dependencies has missing or extra dependencies, compilation can change behavior by running the computation more often (if deps were missing) or less often (if there were extra deps). We currently address this by relying on the developer to use the ESLint plugin and have `eslint-disable-next-line react-hooks/exhaustive-deps` suppressions in their code. If a suppression exists, we skip compilation.

But not everyone is using the linter! Relying on the linter is also imprecise since it forces us to bail out on exhaustive-deps checks that only effect (ahem) effects — and while it isn't good to have incorrect deps on effects, it isn't a problem for compilation.

So this PR is a rough sketch of validating manual memoization dependencies in the compiler. Long-term we could use this to also check effect deps and replace the ExhaustiveDeps lint rule, but for now I'm focused specifically on manual memoization use-cases. If this works, we can stop bailing out on ESLint suppressions, since the compiler will implement all the appropriate checks (we already check rules of hooks).
@josephsavona josephsavona merged commit bcc3fd8 into main Nov 21, 2025
18 checks passed
github-actions bot pushed a commit that referenced this pull request Nov 21, 2025
…ation (#34394)

The compiler currently drops manual memoization and rewrites it using
its own inference. If the existing manual memo dependencies has missing
or extra dependencies, compilation can change behavior by running the
computation more often (if deps were missing) or less often (if there
were extra deps). We currently address this by relying on the developer
to use the ESLint plugin and have `eslint-disable-next-line
react-hooks/exhaustive-deps` suppressions in their code. If a
suppression exists, we skip compilation.

But not everyone is using the linter! Relying on the linter is also
imprecise since it forces us to bail out on exhaustive-deps checks that
only effect (ahem) effects — and while it isn't good to have incorrect
deps on effects, it isn't a problem for compilation.

So this PR is a rough sketch of validating manual memoization
dependencies in the compiler. Long-term we could use this to also check
effect deps and replace the ExhaustiveDeps lint rule, but for now I'm
focused specifically on manual memoization use-cases. If this works, we
can stop bailing out on ESLint suppressions, since the compiler will
implement all the appropriate checks (we already check rules of hooks).

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34394).
* #34472
* #34471
* __->__ #34394

DiffTrain build for [bcc3fd8](bcc3fd8)
github-actions bot pushed a commit that referenced this pull request Nov 21, 2025
…ation (#34394)

The compiler currently drops manual memoization and rewrites it using
its own inference. If the existing manual memo dependencies has missing
or extra dependencies, compilation can change behavior by running the
computation more often (if deps were missing) or less often (if there
were extra deps). We currently address this by relying on the developer
to use the ESLint plugin and have `eslint-disable-next-line
react-hooks/exhaustive-deps` suppressions in their code. If a
suppression exists, we skip compilation.

But not everyone is using the linter! Relying on the linter is also
imprecise since it forces us to bail out on exhaustive-deps checks that
only effect (ahem) effects — and while it isn't good to have incorrect
deps on effects, it isn't a problem for compilation.

So this PR is a rough sketch of validating manual memoization
dependencies in the compiler. Long-term we could use this to also check
effect deps and replace the ExhaustiveDeps lint rule, but for now I'm
focused specifically on manual memoization use-cases. If this works, we
can stop bailing out on ESLint suppressions, since the compiler will
implement all the appropriate checks (we already check rules of hooks).

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34394).
* #34472
* #34471
* __->__ #34394

DiffTrain build for [bcc3fd8](bcc3fd8)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed React Core Team Opened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants