Commit Graph
2168 Commits
Author SHA1 Message Date
Joe Savona 1b7478246d [compiler] Special-case phi inference for mixed readonly type
This allows us to handle common operations such as `useFragment(...).edges.nodes ?? []` where we have a `Phi(MixedReadonly, Array)`. The underlying pattern remains general-purpose and not Relay-specific, and any API that returns transitively "mixed" data (primitives, arrays, plain objects) can benefit from the same type refinement.

ghstack-source-id: 5128310894
Pull Request resolved: https://github.com/facebook/react/pull/30797
2024-08-23 15:24:44 -07:00
Joe Savona 4f54674078 [compiler] Infer phi types, extend mutable ranges to account for Store effects
Redo of an earlier (pre-OSS) PR to infer types of phi nodes. There are a few pieces to this:

1. Update InferTypes to infer the type of `phi.id.type`, not the unused `phi.type`.
2. Update the algorithm to verify that all the phi types are actually equal, not just have the same kind.
3. Handle circular types by removing the cycle.

However, that reveals another issue: InferMutableRanges currently infers the results of `Store` effects _after_ its fixpoint loop. That was fine when a Store could never occur on a phi (since they wouldn't have a type to get a function signature from). Now though, we can have Store effects occur on phis, and we need to ensure that this correctly updates the mutable range of the phi operands - recursively. See new test that fails without the fixpoint loop.

ghstack-source-id: 2e1b02844d
Pull Request resolved: https://github.com/facebook/react/pull/30796
2024-08-23 15:24:44 -07:00
Joe Savona c9c170b63b [compiler] Remove phi type, infer phi.id.type
ghstack-source-id: 0c26bb224c
Pull Request resolved: https://github.com/facebook/react/pull/30795
2024-08-23 15:24:44 -07:00
Joe Savona 37c6ea849c [compiler] Typedefs for Array.prototype.flatMap
ghstack-source-id: af4c7ac2fd
Pull Request resolved: https://github.com/facebook/react/pull/30794
2024-08-23 15:24:44 -07:00
Joe Savona 039c5c08fa [compiler] Repros for missing memoization due to lack of phi type inference
This is a complex case: we not only need phi type inference but also need to be able infer the union of `MixedReadonly | Array`.

ghstack-source-id: 935088910d
Pull Request resolved: https://github.com/facebook/react/pull/30793
2024-08-23 15:24:44 -07:00
Lauren Tan b57d282369 Revert "[compiler][eslint] remove compilationMode override; report bailouts on first line"
This reverts commit b34b750729.

This hack doesn't play well internally so I'm reverting this for now
(but keeping the compilationMode override). I'll audit the locations we
report later and try to make them more accurate so we won't need this
workaround.

ghstack-source-id: b6be29c11d
Pull Request resolved: https://github.com/facebook/react/pull/30792
2024-08-22 15:04:39 -04:00
Joe Savona 7a3fcc9898 [compiler] Flatten returnIdentifier to just returnType
We don't a full Identifier object for the return type, we can just store the type.

ghstack-source-id: 4594d64ce3
Pull Request resolved: https://github.com/facebook/react/pull/30790
2024-08-22 09:33:09 -07:00
Joe Savona 98b5740821 [compiler] Rename HIRFunction.returnType
Rename this field so we can use it for the actual return type.

ghstack-source-id: 118d7dcfbb
Pull Request resolved: https://github.com/facebook/react/pull/30789
2024-08-22 09:33:09 -07:00
Joe Savona 8410c8b959 [compiler] Infer return types of function expressions
Uses the returnIdentifier added in the previous PR to provide a stable identifier for which we can infer a return type for functions, then wires up the equations in InferTypes to infer the type.

ghstack-source-id: 22c0a9ea09
Pull Request resolved: https://github.com/facebook/react/pull/30785
2024-08-22 09:33:09 -07:00
Joe Savona 217a0efcd9 [compiler] Add returnIdentifier to function expressions
This gives us a place to store type information, used in follow-up PRs.

ghstack-source-id: ee0bfa253f
Pull Request resolved: https://github.com/facebook/react/pull/30784
2024-08-22 09:33:09 -07:00
Joe Savona 8a20fc3b19 [compiler] Repro of missing memoization due to capturing w/o mutation
If you have a function expression which _captures_ a mutable value (but does not mutate it), and that function is invoked during render, we infer the invocation as a mutation of the captured value. But in some circumstances we can prove that the captured value cannot have been mutated, and could in theory avoid inferring a mutation.

ghstack-source-id: 47664e48ce
Pull Request resolved: https://github.com/facebook/react/pull/30783
2024-08-22 09:33:09 -07:00
Joe Savona 689c6bd3fd [compiler][wip] Environment option for resolving imported module types
Adds a new Environment config option which allows specifying a function that is called to resolve types of imported modules. The function is passed the name of the imported module (the RHS of the import stmt) and can return a TypeConfig, which is a recursive type of the following form:

* Object of valid identifier keys (or "*" for wildcard) and values that are TypeConfigs
* Function with various properties, whose return type is a TypeConfig
* or a reference to a builtin type using one of a small list (currently Ref, Array, MixedReadonly, Primitive)

Rather than have to eagerly supply all known types (most of which may not be used) when creating the config, this function can do so lazily. During InferTypes we call `getGlobalDeclaration()` to resolve global types. Originally this was just for known react modules, but if the new config option is passed we also call it to see if it can resolve a type. For `import {name} from 'module'` syntax, we first resolve the module type and then call `getPropertyType(moduleType, 'name')` to attempt to retrieve the property of the module (the module would obviously have to be typed as an object type for this to have a chance of yielding a result). If the module type is returned as null, or the property doesn't exist, we fall through to the original checking of whether the name was hook-like.

TODO:
* testing
* cache the results of modules so we don't have to re-parse/install their types on each LoadGlobal of the same module
* decide what to do if the module types are invalid. probably better to fatal rather than bail out, since this would indicate an invalid configuration.

ghstack-source-id: bfdbf67e3d
Pull Request resolved: https://github.com/facebook/react/pull/30771
2024-08-22 09:33:09 -07:00
Joe Savona 0ef00b3e17 [compiler] Transitively freezing functions marks values as frozen, not effects
The fixture from the previous PR was getting inconsistent behavior because of the following:
1. Create an object in a useMemo
2. Create a callback in a useCallback, where the callback captures the object from (1) into a local object, then passes that local object into a logging method. We have to assume the logging method could modify the local object, and transitively, the object from (1).
3. Call the callback during render.
4. Pass the callback to JSX.

We correctly infer that the object from (1) is captured and modified in (2). However, in (4) we transitively freeze the callback. When transitively freezing functions we were previously doing two things: updating our internal abstract model of the program values to reflect the values as being frozen *and* also updating function operands to change their effects to freeze.

As the case above demonstrates, that can clobber over information about real potential mutability. The potential fix here is to only walk our abstract value model to mark values as frozen, but _not_ override operand effects. Conceptually, this is a forward data flow propagation — but walking backward to update effects is pushing information backwards in the algorithm. An alternative would be to mark that data was propagated backwards, and trigger another loop over the CFG to propagate information forward again given the updated effects. But the fix in this PR is more correct.

ghstack-source-id: c05e716f37
Pull Request resolved: https://github.com/facebook/react/pull/30766
2024-08-22 09:33:08 -07:00
Joe Savona f7bb717e9e [compiler] Repro for missing memoization due to inferred mutation
This fixture bails out on ValidatePreserveExistingMemo but would ideally memoize since the original memoization is safe. It's trivial to make it pass by commenting out the commented line (`LogEvent.log(() => object)`). I would expect the compiler to infer this as possible mutation of `logData`, since `object` captures a reference to `logData`. But somehow `logData` is getting memoized successfully, but we still infer the callback, `setCurrentIndex`, as having a mutable range that extends to the `setCurrentIndex()` call after the useCallback.

ghstack-source-id: 4f82e34510
Pull Request resolved: https://github.com/facebook/react/pull/30764
2024-08-22 09:33:08 -07:00
Joe Savona d2413bf377 [compiler] Validate against JSX in try statements
Per comments on the new validation pass, this disallows creating JSX (expression/fragment) within a try statement. Developers sometimes use this pattern thinking that they can catch errors during the rendering of the element, without realizing that rendering is lazy. The validation allows us to teach developers about the error boundary pattern.

ghstack-source-id: 0bc722aeae
Pull Request resolved: https://github.com/facebook/react/pull/30725
2024-08-19 11:24:23 -07:00
Joe Savona 177b2419b2 [compiler] Validate environment config while parsing plugin opts
Addresses a todo from a while back. We now validate environment options when parsing the plugin options, which means we can stop re-parsing/validating in later phases.

ghstack-source-id: b19806e843
Pull Request resolved: https://github.com/facebook/react/pull/30726
2024-08-16 17:05:03 -07:00
Lauren Tan a58276cbc3 [playground] Allow (Arrow)FunctionExpressions
This was a pet peeve where our playground could only compile top level
FunctionDeclarations. Just synthesize a fake identifier if it doesn't
have one.

ghstack-source-id: 882483c79c
Pull Request resolved: https://github.com/facebook/react/pull/30729
2024-08-16 18:12:05 -04:00
Lauren Tan 34edf3b684 [compiler] Surface unused opt out directives in eslint
This PR updates the eslint plugin to report unused opt out directives.
One of the downsides of the opt out directive is that it opts the
component/hook out of compilation forever, even if the underlying issue
was fixed in product code or fixed in the compiler.

ghstack-source-id: 81deb5c11b
Pull Request resolved: https://github.com/facebook/react/pull/30721
2024-08-16 18:12:05 -04:00
Lauren Tan e9a869fbb5 [compiler] Run compiler pipeline on 'use no forget'
This PR updates the babel plugin to continue the compilation pipeline as
normal on components/hooks that have been opted out using a directive.
Instead, we no longer emit the compiled function when the directive is
present.

Previously, we would skip over the entire pipeline. By continuing to
enter the pipeline, we'll be able to detect if there are unused
directives.

The end result is:

- (no change) 'use forget' will always opt into compilation
- (new) 'use no forget' will opt out of compilation but continue to log
  errors without throwing them. This means that a Program containing
multiple functions (some of which are opted out) will continue to
compile correctly

ghstack-source-id: 5bd85df2f8
Pull Request resolved: https://github.com/facebook/react/pull/30720
2024-08-16 18:12:04 -04:00
Mike Vitousek 5edbe29dbe [compiler] Make ref enforcement on by default
Summary:
The change earlier in this stack makes it less safe to have ref enforcement disabled. This diff enables it by default.

ghstack-source-id: d3ab5f1b28
Pull Request resolved: https://github.com/facebook/react/pull/30716
2024-08-16 13:27:14 -04:00
Mike Vitousek 21a95239e1 [compiler] Allow functions containing refs to be returned
Summary:
We previously were excessively strict about preventing functions that access refs from being returned--doing so is potentially valid for hooks, because the return value may only be used in an event or effect.

ghstack-source-id: cfa8bb1b54
Pull Request resolved: https://github.com/facebook/react/pull/30724
2024-08-16 13:27:14 -04:00
Mike Vitousek 1016174af5 [compiler] Reposition ref-in-render errors to the read location of .current
Summary:
Since we want to make ref-in-render errors enabled by default, we should position those errors at the location of the read. Not only will this be a better experience, but it also aligns the behavior of Forget and Flow.

This PR also cleans up the resulting error messages to not emit implementation details about place values.

ghstack-source-id: 1d11317068
Pull Request resolved: https://github.com/facebook/react/pull/30723
2024-08-16 13:27:13 -04:00
Mike Vitousek 8a53160111 [compiler] Don't error on ref-in-render on StartMemoize
Test Plan:
Fixes the previous issue: ref enforcement ignores memoization marker instructions

ghstack-source-id: f35d6a611c
Pull Request resolved: https://github.com/facebook/react/pull/30715
2024-08-16 13:27:13 -04:00
Mike Vitousek 7468ac530e [compiler] Fixture to show ref-in-render enforcement issue with useCallback
Test Plan:
Documents that useCallback calls interfere with it being ok for refs to escape as part of functions into jsx

ghstack-source-id: a5df427981
Pull Request resolved: https://github.com/facebook/react/pull/30714
2024-08-16 13:27:13 -04:00
Mike Vitousek 5030e08575 [compiler] Exclude refs and ref values from having mutable ranges
Summary:
Refs, as stable values that the rules of react around mutability do not apply to, currently are treated as having mutable ranges, and through aliasing, this can extend the mutable range for other values and disrupt good memoization for those values. This PR excludes refs and their .current values from having mutable ranges.

Note that this is unsafe if ref access is allowed in render: if a mutable value is assigned to ref.current and then ref.current is mutated later, we won't realize that the original mutable value's range extends.

ghstack-source-id: e8f36ac25e
Pull Request resolved: https://github.com/facebook/react/pull/30713
2024-08-16 13:27:13 -04:00
Mike Vitousek 50d2197dd5 [compiler] Support for member expression inc/decrement
Test Plan:
Builds support for a.x++ and friends. Similar to a.x += y, emits it as an assignment expression.

ghstack-source-id: 8f3979913a
Pull Request resolved: https://github.com/facebook/react/pull/30697
2024-08-15 15:00:36 -04:00
Mike Vitousek bb6acc20af [compiler] Allow hoisting of destructured variable declarations
Summary:
It doesn't seem as though this invariant was necessary

ghstack-source-id: b27e765259
Pull Request resolved: https://github.com/facebook/react/pull/30699
2024-08-15 14:27:38 -04:00
Joe Savona c39da3e4de [compiler] Off-by-default validation against setState directly in passive effect
Per discussion today, adds validation against calling setState "during" passive effects. Basically, it's fine to _schedule_ setState to be called (via a timeout, listener, etc) but generally not recommended to call setState during the effect since that will trigger a cascading render.

This validation is off by default, i'm putting this up for discussion and to experiment with it internally.

ghstack-source-id: 5f385ddab5
Pull Request resolved: https://github.com/facebook/react/pull/30685
2024-08-15 10:38:23 -04:00
Mike Vitousek dd8e0ba857 [compiler] Support for useTransition
Summary:
UseTransition is a builtin hook that returns a stable value, like useState. This PR represents that in Forget, and marks the startTransition function as stable.

ghstack-source-id: 0e76a64f2d
Pull Request resolved: https://github.com/facebook/react/pull/30681
2024-08-14 13:54:39 -04:00
Mike Vitousek 179197a22a [compiler] Allow different dependencies from explicit memoization when dependency is a ref
Summary:
In theory, as I understand it, the result of a useRef will never change between renders, because we'll always provide the same ref value consistently. That means that memoization that depends on a ref value will never re-compute, so I think we could not infer it as a dependency in Forget. This diff, however, doesn't do that: it instead allows the validatePreserveExistingMemoizationGuarantees analysis to admit mismatches between explicit dependencies and implicit ones when the implicit dependency is a ref that doesn't exist in source.

ghstack-source-id: 685d859d1e
Pull Request resolved: https://github.com/facebook/react/pull/30679
2024-08-14 13:54:39 -04:00
Mike Vitousek 8e60bacd08 [compiler] Reordering of logical expressions
ghstack-source-id: ad484f9745
Pull Request resolved: https://github.com/facebook/react/pull/30678
2024-08-13 16:57:45 -04:00
Mike Vitousek a601d1da36 [compiler] Allow lets to be hoisted
ghstack-source-id: 02f4698bd9
Pull Request resolved: https://github.com/facebook/react/pull/30674
2024-08-13 16:35:56 -04:00
Mike Vitousek 0d7c12c779 [compiler][ez] Enable some sprout tests that no longer need to be disabled
Summary:
As title. Better support for flow typing, bugfixes, etc fixes these

ghstack-source-id: 6326653ce4
Pull Request resolved: https://github.com/facebook/react/pull/30591
2024-08-12 12:55:55 -07:00
Mike Vitousek 6532c41256 [compiler] Allow macro methods
Summary:
Builds support for macros that are invoked as methods rather than just function calls or jsx.

We now record macros as a schema that represents arbitrary member expressions including wildcards (so we can support, e.g., myMacro.*.foo.bar). When examining PropertyLoads in the macro memoization stage, we build up a map of partially-satisfied macro patterns until we determine that the pattern has been fully satisfied, at which point we treat the result of the PropertyLoad as a macro value.

ghstack-source-id: d78d9ba704
Pull Request resolved: https://github.com/facebook/react/pull/30589
2024-08-12 12:55:55 -07:00
Mike Vitousek d50e024fd4 [compiler] Promote temporaries when necessary to prevent codegen reordering over side-effectful operations
ghstack-source-id: 639191e63a
Pull Request resolved: https://github.com/facebook/react/pull/30554
2024-08-12 12:36:49 -07:00
Lauren Tan fbe81b214a [compiler] Publish to latest tag
> [!NOTE]
> The `latest` tag is published by default if no tag is specified, which
> is what we had done since the first release of the compiler

In my last PR to auto publish compiler releases I had added the
experimental tag to be used in publishing. However because we had
already previously published to the latest tag (which is non-removable)
this means that the `latest` tag is pinned to an old version. That makes
untagged installs of the compiler default to that old version instead of
whatever is the latest.

This changes the behavior back to what it was before. Since we are still
in the experimental release of the compiler anyway it seems fine to use
the latest tag. When we reach stable, we can update this to only push to
latest for stable releases.

ghstack-source-id: 1809481b45
Pull Request resolved: https://github.com/facebook/react/pull/30666
2024-08-12 14:41:11 -04:00
Mofei Zhang 8d74e8c73a [compiler] Patch error reporting for blocklisted imports
ghstack-source-id: 614c1e9c04
Pull Request resolved: https://github.com/facebook/react/pull/30652
2024-08-09 15:19:07 -07:00
hylinzandGitHub 2504dbd669 Punctuation & correcting spelling mistakes (#30592) 2024-08-09 14:53:53 -07:00
Joe Savona 54a150dc6e [compiler][be] Remove completed todo comment
We made block types explicit a long time ago, this comment is super stale

ghstack-source-id: 810a34bb4c
Pull Request resolved: https://github.com/facebook/react/pull/30633
2024-08-09 14:11:01 -07:00
Joe Savona 229038eab8 [compiler][be] Cleanup class naming in PromoteUsedTemporaries
I forgot to clean this up before landing #30573.

ghstack-source-id: 2141471912
Pull Request resolved: https://github.com/facebook/react/pull/30632
2024-08-09 14:11:01 -07:00
Mofei Zhang 2d2cc042d7 [compiler][ez] Option to bail out on blocklisted imports
ghstack-source-id: 540d154b25
Pull Request resolved: https://github.com/facebook/react/pull/30643
2024-08-09 09:10:01 -07:00
Mofei Zhang 3871fdadaa [compiler][be] Clean up compilation skipping logic in Program
ghstack-source-id: fe2c81de9d
Pull Request resolved: https://github.com/facebook/react/pull/30642
2024-08-09 09:10:01 -07:00
Mofei Zhang 0fb03c4952 [compiler][ez] Add types for Math.min, recursive global
ghstack-source-id: 9940b8f96e
Pull Request resolved: https://github.com/facebook/react/pull/30641
2024-08-09 09:10:01 -07:00
Jan KassensandGitHub fb1c63fb76 [compiler] remove duplicate root files (#30639)
These files are duplicates from the root from a time when React Compiler
was in a different repo.
2024-08-08 12:42:45 -04:00
Sathya Gunsasekaran 9d2da5913a [compiler] Add context callee import if required
Previously the compiler would add an import for the specified context
callee even if the context access was not lowered, leading to unused
imports.

This PR tracks if lowering has happened and adds the import only when
necessary.

ghstack-source-id: 6ad794da41
Pull Request resolved: https://github.com/facebook/react/pull/30628
2024-08-08 15:53:13 +01:00
Sathya Gunsasekaran 83cc13f746 [compiler] Rewrite useContext callee
If a value is specified for the LowerContextAccess environment config,
we rewrite the callee from 'useContext' to the specificed value.

This will allow us run an experiment internally.

ghstack-source-id: 00e161b988
Pull Request resolved: https://github.com/facebook/react/pull/30612
2024-08-08 15:53:13 +01:00
Sol LeeandGitHub 8b31835fc0 [compiler] Replace for...in with for...of for array of strings (#30631)
closes https://github.com/facebook/react/issues/30627

Thanks!
2024-08-07 22:07:04 -07:00
Mofei Zhang a037dabd42 [compiler] Patch ValidatePreserveMemo to bailout correctly for refs
ghstack-source-id: b9c13bf5f8
Pull Request resolved: https://github.com/facebook/react/pull/30603
2024-08-07 18:57:57 -07:00
Mofei Zhang 53194c8792 [compiler] Remove transitive memo check in validatePreserveMemo
ghstack-source-id: 45521370e4
Pull Request resolved: https://github.com/facebook/react/pull/30630
2024-08-07 18:57:56 -07:00
Mofei Zhang 2f8ff3deb2 [compiler][repro] False positives for ValidatePreserveMemo
ghstack-source-id: 7fa94fea86
Pull Request resolved: https://github.com/facebook/react/pull/30629
2024-08-07 18:57:56 -07:00