-
Notifications
You must be signed in to change notification settings - Fork 50.2k
[compiler] Implement exhaustive dependency checking for manual memoization #34394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
a1cd347 to
f1c7583
Compare
…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
…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)
…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)
…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)
…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)
f1c7583 to
40ba057
Compare
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts
Outdated
Show resolved
Hide resolved
40ba057 to
bef52de
Compare
| > 24 | return []; | ||
| | ^^^^^^^^^^^^^^ | ||
| > 25 | }, [x, y.z, z?.y?.a]); | ||
| | ^^^^ Unnecessary dependencies `x`, `y.z`, `z?.y?.a` |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
xandx.yas 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].
bef52de to
96014db
Compare
| loc: value.loc, | ||
| }); | ||
| startMemo = value; | ||
| dependencies.clear(); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
| * 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. |
There was a problem hiding this comment.
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
96014db to
611f607
Compare
93696d8 to
5809d69
Compare
5809d69 to
8409003
Compare
…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).
8409003 to
ca0a3c4
Compare
…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)
…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)
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-depssuppressions 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.