[compiler] Propagate CreateFunction effects for functions that return functions#33642
Draft
josephsavona wants to merge 2 commits intomainfrom
Draft
[compiler] Propagate CreateFunction effects for functions that return functions#33642josephsavona wants to merge 2 commits intomainfrom
josephsavona wants to merge 2 commits intomainfrom
Conversation
This was referenced Jun 25, 2025
josephsavona
commented
Jun 25, 2025
| t1 = $[1]; | ||
| } | ||
| const arr = t1; | ||
| fn(arr); |
Member
Author
There was a problem hiding this comment.
Before, all we knew is that fn (created by fnFactory) is a mutable value, and therefore could be mutating arr. Now we know that fn is a function, with a specific signature, and that it doesn't mutate it's argument. That means we can memoize arr independently.
josephsavona
commented
Jun 25, 2025
Comment on lines
+474
to
+519
| const returned: Set<Node> = new Set(); | ||
| const queue: Array<Node> = [state.nodes.get(fn.returns.identifier)!]; | ||
| const seen: Set<Node> = new Set(); | ||
| while (queue.length !== 0) { | ||
| const node = queue.pop()!; | ||
| if (seen.has(node)) { | ||
| continue; | ||
| } | ||
| seen.add(node); | ||
| for (const id of node.aliases.keys()) { | ||
| queue.push(state.nodes.get(id)!); | ||
| } | ||
| for (const id of node.createdFrom.keys()) { | ||
| queue.push(state.nodes.get(id)!); | ||
| } | ||
| if (node.id.id === fn.returns.identifier.id) { | ||
| continue; | ||
| } | ||
| switch (node.value.kind) { | ||
| case 'Assign': | ||
| case 'CreateFrom': { | ||
| break; | ||
| } | ||
| case 'Phi': | ||
| case 'Object': | ||
| case 'Function': { | ||
| returned.add(node); | ||
| break; | ||
| } | ||
| default: { | ||
| assertExhaustive( | ||
| node.value, | ||
| `Unexpected node value kind '${(node.value as any).kind}'`, | ||
| ); | ||
| } | ||
| } | ||
| } |
Member
Author
There was a problem hiding this comment.
i want to clean this logic up a bit before landing
josephsavona
added a commit
that referenced
this pull request
Jun 25, 2025
…33624) Closes #33577, a bug with ExtractScopeDeclarationsFromDestructuring and codegen when a function param is reassigned. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33624). * #33643 * #33642 * #33640 * #33625 * __->__ #33624
josephsavona
added a commit
that referenced
this pull request
Jun 25, 2025
Small cosmetic win, found this when i was looking at some code internally with lots of cases that all share the same logic. Previously, all the but last one would have an empty block. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33625). * #33643 * #33642 * #33640 * __->__ #33625 * #33624
josephsavona
added a commit
that referenced
this pull request
Jun 25, 2025
We now have `HIRFunction.returns: Place` as well as `returnType: Type`.
I want to add additional return information, so as a first step i'm
consolidating everything under an object at `HIRFunction.returns:
{place: Place}`. We use the type of this place as the return type. Next
step is to add more properties to this object to represent things like
the return kind.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33640).
* #33643
* #33642
* __->__ #33640
* #33625
* #33624
github-actions bot
pushed a commit
that referenced
this pull request
Jun 25, 2025
Small cosmetic win, found this when i was looking at some code internally with lots of cases that all share the same logic. Previously, all the but last one would have an empty block. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33625). * #33643 * #33642 * #33640 * __->__ #33625 * #33624 DiffTrain build for [e130c08](e130c08)
github-actions bot
pushed a commit
that referenced
this pull request
Jun 25, 2025
Small cosmetic win, found this when i was looking at some code internally with lots of cases that all share the same logic. Previously, all the but last one would have an empty block. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33625). * #33643 * #33642 * #33640 * __->__ #33625 * #33624 DiffTrain build for [e130c08](e130c08)
github-actions bot
pushed a commit
that referenced
this pull request
Jun 25, 2025
We now have `HIRFunction.returns: Place` as well as `returnType: Type`.
I want to add additional return information, so as a first step i'm
consolidating everything under an object at `HIRFunction.returns:
{place: Place}`. We use the type of this place as the return type. Next
step is to add more properties to this object to represent things like
the return kind.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33640).
* #33643
* #33642
* __->__ #33640
* #33625
* #33624
DiffTrain build for [123ff13](123ff13)
github-actions bot
pushed a commit
that referenced
this pull request
Jun 25, 2025
We now have `HIRFunction.returns: Place` as well as `returnType: Type`.
I want to add additional return information, so as a first step i'm
consolidating everything under an object at `HIRFunction.returns:
{place: Place}`. We use the type of this place as the return type. Next
step is to add more properties to this object to represent things like
the return kind.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33640).
* #33643
* #33642
* __->__ #33640
* #33625
* #33624
DiffTrain build for [123ff13](123ff13)
github-actions bot
pushed a commit
that referenced
this pull request
Jun 25, 2025
…33624) Closes #33577, a bug with ExtractScopeDeclarationsFromDestructuring and codegen when a function param is reassigned. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33624). * #33643 * #33642 * #33640 * #33625 * __->__ #33624 DiffTrain build for [9894c48](9894c48)
github-actions bot
pushed a commit
that referenced
this pull request
Jun 25, 2025
…33624) Closes #33577, a bug with ExtractScopeDeclarationsFromDestructuring and codegen when a function param is reassigned. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33624). * #33643 * #33642 * #33640 * #33625 * __->__ #33624 DiffTrain build for [9894c48](9894c48)
This was referenced Jun 25, 2025
d7a7cfa to
7bf2cc9
Compare
josephsavona
added a commit
that referenced
this pull request
Aug 27, 2025
We currently assume that any functions passes as props may be event handlers or effect functions, and thus don't check for side effects such as mutating globals. However, if a prop is a function that returns JSX that is a sure sign that it's actually a render helper and not an event handler or effect function. So we now emit a `Render` effect for any prop that is a JSX-returning function, triggering all of our render validation. This required a small fix to InferTypes: we weren't correctly populating the `return` type of function types during unification. I also improved the printing of types so we can see the inferred return types. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33647). * #33643 * #33650 * #33642 * __->__ #33647
github-actions bot
pushed a commit
that referenced
this pull request
Aug 27, 2025
We currently assume that any functions passes as props may be event handlers or effect functions, and thus don't check for side effects such as mutating globals. However, if a prop is a function that returns JSX that is a sure sign that it's actually a render helper and not an event handler or effect function. So we now emit a `Render` effect for any prop that is a JSX-returning function, triggering all of our render validation. This required a small fix to InferTypes: we weren't correctly populating the `return` type of function types during unification. I also improved the printing of types so we can see the inferred return types. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33647). * #33643 * #33650 * #33642 * __->__ #33647 DiffTrain build for [33a1095](33a1095)
github-actions bot
pushed a commit
that referenced
this pull request
Aug 27, 2025
We currently assume that any functions passes as props may be event handlers or effect functions, and thus don't check for side effects such as mutating globals. However, if a prop is a function that returns JSX that is a sure sign that it's actually a render helper and not an event handler or effect function. So we now emit a `Render` effect for any prop that is a JSX-returning function, triggering all of our render validation. This required a small fix to InferTypes: we weren't correctly populating the `return` type of function types during unification. I also improved the printing of types so we can see the inferred return types. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33647). * #33643 * #33650 * #33642 * __->__ #33647 DiffTrain build for [33a1095](33a1095)
In InferReferenceEffects we used `InstructionValue` as the key to represent values, since each time we process an instruction this object will be the same. However this was always a bit of a hack, and in the new model and InferMutationAliasingEffects we can instead use the (creation) effect as the stable value. This avoids an extra layer of memoization since the effects are already interned anyway.
… functions
If you have a local helper function that itself returns a function (`() => () => { ... }`), we currently infer the return effect of the outer function as `Create mutable`. We correctly track the aliasing, but we lose some precision because we don't understand that a function specifically is being returned.
Here, we do some extra analysis of which values are returned in InferMutationAliasingRanges, and if the sole return value is a function we infer a `CreateFunction` effect. We also infer an `Assign` (instead of a Create) if the sole return value was one of the context variables or parameters.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
If you have a local helper function that itself returns a function (
() => () => { ... }), we currently infer the return effect of the outer function asCreate mutable. We correctly track the aliasing, but we lose some precision because we don't understand that a function specifically is being returned.Here, we do some extra analysis of which values are returned in InferMutationAliasingRanges, and if the sole return value is a function we infer a
CreateFunctioneffect. We also infer anAssign(instead of a Create) if the sole return value was one of the context variables or parameters.Stack created with Sapling. Best reviewed with ReviewStack.