Commit Graph

1984 Commits

Author SHA1 Message Date
Niklas Mollenhauer f5af92d2c4 feat(compiler): Support MetaProperty (#29752)
## Summary
See #29737

## How did you test this change?
As the feature requires module support and the test runner does
currently not support running tests as modules, I could only test it via
playground.
2024-06-08 16:18:35 -07:00
Lauren Tan 20841f9a62 [compiler] Always emit variable decl in gating mode
This PR makes it so we always emit a const VariableDeclaration for
compiled functions in gating mode. If the original declaration's parent
was an ExportDefaultDeclaration we'll also append a new
ExportDefaultDeclaration pointing to the new identifier. This allows
code that adds optional properties to the function declaration to still
work in gating mode

ghstack-source-id: 5705479135
Pull Request resolved: https://github.com/facebook/react/pull/29806
2024-06-07 15:50:31 -04:00
Lauren Tan a65de775e2 [compiler] Add repro for func properties bug with gating
When gating is enabled, any function declaration properties that were
previously set (typically `Function.displayName`) would cause a crash
after compilation as the original identifier is no longer present.

ghstack-source-id: beb7e25856
Pull Request resolved: https://github.com/facebook/react/pull/29802
2024-06-07 15:50:30 -04:00
Lauren Tan 0a5e0b0c2e [compiler] Fix outdated comment in scripts/release
ghstack-source-id: a37c6faa8b
Pull Request resolved: https://github.com/facebook/react/pull/29800
2024-06-07 12:29:50 -04:00
Joe Savona 827cbea417 compiler: Add support for ref effects
Fixes false positives where we currently disallow mutations of refs from callbacks passed to JSX, if the ref is also passed to jsx. We consider these to be mutations of "frozen" values, but refs are explicitly allowed to have interior mutability. The fix is to always allow (at leat within InferReferenceEffects) for refs to be mutated. This means we completely rely on ValidateNoRefAccessInRender to validate ref access and stop reporting false positives.

ghstack-source-id: 1a30609f5f
Pull Request resolved: https://github.com/facebook/react/pull/29733
2024-06-06 17:19:42 -07:00
Lauren Tan 29b1278790 [compiler] Check for __DEV__ for FastRefresh
We don't always have the NODE_ENV set, so additionally check for the
__DEV__ global if it has one set.

ghstack-source-id: 3719a4710a
Pull Request resolved: https://github.com/facebook/react/pull/29785
2024-06-06 13:54:55 -04:00
XiaoPi 70194be403 fix<compiler>: reread the testfilter file if filter enabled during the watch process (#29775)
Resolve #29720
In the above PR, I overlooked that we can change the filter mode during the watch process. Now it's fixed.
2024-06-06 10:48:24 -07:00
XiaoPi 704aeed022 feat<Compiler>: consider that the dispatch function from useReducer is non-reactive (#29705)
Summary
The dispatch function from useReducer is stable, so it is also non-reactive.

the related PR: #29665
the related comment: #29674 (comment)

I am not sure if the location of the new test file is appropriate😅.

How did you test this change?
Added the specific test compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useReducer-returned-dispatcher-is-non-reactive.expect.md.
2024-06-05 16:51:09 -07:00
XiaoPi 408258268e fix<compiler>: only call readTestFilter if the filter option is enabled (#29720)
Following the instructions in the compiler/docs/DEVELOPMENT_GUIDE.md, we are stuck on the command `yarn snap --watch` because it calls readTestFilter even though the filter option is not enabled.
2024-06-03 16:09:58 -07:00
Joe Savona 8b677b1e6e compiler: Allow opting out of installed library check
ghstack-source-id: eedd024d36
Pull Request resolved: https://github.com/facebook/react/pull/29742
2024-06-03 15:30:55 -07:00
Lauren Tan d77dd31a32 Bump version to 0.0.0-experimental-7054a14-20240601 2024-06-01 08:15:27 +09:00
Lauren Tan c6b651bee0 Bump version to 0.0.0-experimental-51a85ea-20240601 2024-06-01 08:15:27 +09:00
Lauren Tan b17016c869 Bump version to 0.0.0-experimental-938cd9a-20240601 2024-06-01 08:15:27 +09:00
Lauren Tan 113c8e7f72 [compiler:eslint] Don't crash if hermes parser fails to parse
Eslint rules should never throw, so if we fail to parse with Babel or
Hermes, we should just ignore the error. This should fix issues such as
trying to run the eslint rule on non tsx|ts|jsx|js files, Hermes parser
not supporting certain JS syntax, etc.

I didn't add a test for this as our eslint-rule-tester config uses
hermes-eslint parser, so it wasn't possible to add a top level await as
it would crash hermes-eslint before our rule was triggered. Similarly I
couldn't add a test for non-JS files as it would not be parseable by
hermes-eslint.

Fixes #29107

ghstack-source-id: 60afcdb89a
Pull Request resolved: https://github.com/facebook/react/pull/29631
2024-06-01 08:04:41 +09:00
Mike Vitousek ec6fe57a50 [compiler] rfc: Include location information in identifiers and reactive scopes for debugging
Summary: Using the change detection code to debug codebases that violate the rules of react is a lot easier when we have a source location corresponding to the value that has changed inappropriately. I didn't see an easy way to track that information in the existing data structures at the point of codegen, so this PR adds locations to identifiers and reactive scopes (the location of a reactive scope is the range of the locations of its included identifiers).

I'm interested if there's a better way to do this that I missed!

ghstack-source-id: aed5f7edda
Pull Request resolved: https://github.com/facebook/react/pull/29658
2024-05-31 14:06:04 -07:00
Mike Vitousek 522d22f299 [compiler] Recompute values every time
Summary: This PR expands the analysis from the previous in the stack in order to also capture when a value can incorrectly change within a single render, rather than just changing between two renders. In the case where dependencies have changed and so a new value is being computed, we now compute the value twice and compare the results. This would, for example, catch when we call Math.random() in render.

The generated code is a little convoluted, because we don't want to have to traverse the generated code and substitute variable names with new ones. Instead, we save the initial value to the cache as normal, then run the computation block again and compare the resulting values to the cached ones. Then, to make sure that the cached values are identical to the computed ones, we reassign the cached values into the output variables.

ghstack-source-id: d0f11a4cb2
Pull Request resolved: https://github.com/facebook/react/pull/29657
2024-05-31 14:06:02 -07:00
Mike Vitousek c69211a9df [compiler] Prune dependencies that are only used by useRef or useState
Summary: jmbrown215 recently had an observation that the arguments to useState/useRef are only used when a component renders for the first time, and never afterwards. We can skip more computation that we previously could, with reactive blocks that previously recomputed values when inputs changed now only ever computing them on the first render.

ghstack-source-id: 5d044ef787
Pull Request resolved: https://github.com/facebook/react/pull/29653
2024-05-31 14:06:00 -07:00
Mike Vitousek 5c420e3824 [compiler] Debug tool to emit change detection code rather than memoization
Summary: The essential assumption of the compiler is that if the inputs to a computation have not changed, then the output should not change either--computation that the compiler optimizes is idempotent.

This is, of course, known to be false in practice, because this property rests on requirements (the Rules of React) that are loosely enforced at best. When rolling out the compiler to a codebase that might have rules of react violations, how should developers debug any issues that arise?

This diff attempts one approach to that: when the option is set, rather than simply skipping computation when dependencies haven't changed, we will *still perform the computation*, but will then use a runtime function to compare the original value and the resultant value. The runtime function can be customized, but the idea is that it will perform a structural equality check on the values, and if the values aren't structurally equal, we can report an error, including information about what file and what variable was to blame.

This assists in debugging by narrowing down what specific computation is responsible for a difference in behavior between the uncompiled code and the program after compilation.

ghstack-source-id: 50dad3dacf
Pull Request resolved: https://github.com/facebook/react/pull/29656
2024-05-31 14:05:58 -07:00
Mike Vitousek 8b01a2e0bf [compiler] Option to always take the non-memo branch
Summary: This adds a debugging mode to the compiler that simply adds a `|| true` to the guard on all memoization blocks, which results in the generated code never using memoized values and always recomputing them. This is designed as a validation tool for the compiler's correctness--every program *should* behave exactly the same with this option enabled as it would with it disabled, and so any difference in behavior should be investigated as either a compiler bug or a pipeline issue.

(We add `|| true` rather than dropping the conditional block entirely because we still want to exercise the guard tests, in case the guards themselves are the source of an error, like reading a property from undefined in a guard.)

ghstack-source-id: 955a47ec16
Pull Request resolved: https://github.com/facebook/react/pull/29655
2024-05-31 14:04:54 -07:00
Mike Vitousek 28fe581bac [compiler] Option for preserving calls to useMemo/useCallback
Summary: This adds a compiler option to not drop existing manual memoization and leaving useMemo/useCallback in the generated source. Why do we need this, given that we also have options to validate or ensure that existing memoization is preserved? It's because later diffs on this stack are designed to alter the behavior of the memoization that the compiler emits, in order to detect rules of react violations and debug issues. We don't want to change the behavior of user-level memoization, however, since doing so would be altering the semantics of the user's program in an unacceptable way.

ghstack-source-id: 89dccdec9c
Pull Request resolved: https://github.com/facebook/react/pull/29654
2024-05-31 14:02:13 -07:00
Josh Wilson aa3d6c0840 Add react-easy-state to list of known incompatible libraries. (#29661)
Like mobx, this library depends on mutating a Proxied store and breaks reference equality checks.
2024-05-30 16:37:09 -07:00
Niklas Mollenhauer c2b45ef0dd feat(compiler): Implement constant folding for more binary expressions (#29650)
## Summary

There are already most arithmetic operators in constant propagation:
`+`, `-`, `*`, `/`.
We could add more, namely: `|`, `&`, `^`, `<<`, `>>`, `>>>` and `%`:

Input:
```js
function f() {
  return [
    123.45 | 0,
    123.45 & 0,
    123.45 ^ 0,
    123 << 0,
    123 >> 0,
    123 >>> 0,
    123.45 | 1,
    123.45 & 1,
    123.45 ^ 1,
    123 << 1,
    123 >> 1,
    123 >>> 1,
    3 ** 2,
    3 ** 2.5,
    3.5 ** 2,
    2 ** 3 ** 0.5,
    4 % 2,
    4 % 2.5,
    4 % 3,
    4.5 % 2,
  ];
}
```
Output:
```js
function f() {
  return [
    123, 0, 123, 123, 123, 123, 123, 1, 122, 246, 61, 61, 9,
    15.588457268119896, 12.25, 3.3219970854839125, 0, 1.5, 1, 0.5,
  ];
}
```

Resolves #29649

## How did you test this change?
See tests.

Note:
This PR was done without waiting for approval in #29649, so feel free to
just close it without any comment.
2024-05-29 10:35:19 -07:00
Joe Savona 867edc6576 compiler: ValidateNoRefInRender detects writes of refs
Improves ValidateNoRefAccessInRender, detecting modifications of refs during render.

Fixes #29161

ghstack-source-id: 99078b3cea
Pull Request resolved: https://github.com/facebook/react/pull/29170
2024-05-29 09:27:44 -07:00
Niklas Mollenhauer 320da67570 feat(compiler): Compiler Logical Negation Constant Propagation (#29623)
## Summary

Resolves #29622

## How did you test this change?
I verified the implementation using the test.

Note:
This PR was done without waiting for approval in #29622, so feel free to
just close it.
2024-05-29 09:17:43 -07:00
Joe Savona afb2c39ec3 compiler: fixtures for fast-refresh mode (w todos)
ghstack-source-id: 65dd14fe9b
Pull Request resolved: https://github.com/facebook/react/pull/29175
2024-05-29 07:47:05 -07:00
Joe Savona c272789ce5 compiler: Add todo for getter/setter syntax
We were missing a check that ObjectMethods are not getters or setters. In our experience this is pretty rare within React components and hooks themselves, so let's start with a todo.

Closes #29586

ghstack-source-id: 03c6cce9a9
Pull Request resolved: https://github.com/facebook/react/pull/29592
2024-05-29 07:45:53 -07:00
Joe Savona 49ed6f0740 compiler: Allow global mutation in jsx props
Fixes https://x.com/raibima/status/1794395807216738792

The issue is that if you pass a global-modifying function as prop to JSX, we currently report that it's invalid to modify a global during rendering. The problem is that we don't really know when/if the child component will actually call that function prop. It would be against the rules to call the function during render, but it's totally fine to call it during an event handler or from a useEffect.

Since we don't know at the call-site how the child will use the function, we should allow such calls. In the future we could improve this in a few ways:
* For all functions that modify globals, codegen an assertion or warning into the function that fires if it's called "during render". We'd have to precisely define what "during render" is, but this would at least help developers catch this dynamically.
* Use the type system to distinguish "event/effect" and "render" functions to help developers avoid accidentally mutating globals during render.

ghstack-source-id: 4aba4e6d21
Pull Request resolved: https://github.com/facebook/react/pull/29591
2024-05-29 07:45:53 -07:00
Lauren Tan e2e12f3351 Update .git-blame-ignore-revs
- Moves the file as it needs to be in root git directory
- Removes now unreachable commits due to repo merge
- Add run prettier commit c998bb1ed4 to ignored revs

ghstack-source-id: d9dfa7099f
Pull Request resolved: https://github.com/facebook/react/pull/29630
2024-05-29 18:12:16 +09:00
Lauren Tan b44263addb Bump version to 0.0.0-experimental-31393f7-20240529 2024-05-29 12:01:36 +09:00
Lauren Tan bd30dc3ae2 Bump version to 0.0.0-experimental-a97cca1-20240529 2024-05-29 12:01:35 +09:00
Lauren Tan 84c47b3d52 Bump version to 0.0.0-experimental-487cb0e-20240529 2024-05-29 12:01:35 +09:00
Lauren Tan 81c3775816 [compiler] Ignore run prettier commit in git blame 2024-05-29 11:47:42 +09:00
Lauren Tan 9d530e94c4 [compiler:babel] Don't read config files when not running as part of
user's pipeline

When the user app has a babel.config file that is missing the compiler,
strange things happen as babel does some strange merging of options from
the user's config and in various callsites like in our eslint rule and
healthcheck script. To minimize odd behavior, we default to not reading
the user's babel.config

Fixes #29135

ghstack-source-id: d6fdc43c5c
Pull Request resolved: https://github.com/facebook/react/pull/29211
2024-05-29 11:46:27 +09:00
Lauren Tan c998bb1ed4 [compiler] Run prettier, fix snap
After this is merged, I'll add it to .git-blame-ignore-revs. I can't do
it now as the hash will change after ghstack lands this stack.

ghstack-source-id: 054ca869b7
Pull Request resolved: https://github.com/facebook/react/pull/29214
2024-05-29 11:46:27 +09:00
Lauren Tan 61aa159086 [compiler] Fix up prettier
Our prettier setup is all messed up after the merge, so this PR should
fix things

ghstack-source-id: f825460ea6
Pull Request resolved: https://github.com/facebook/react/pull/29213
2024-05-29 11:46:27 +09:00
Niklas Mollenhauer a9a0106808 feat(compiler): Implement constant string concat propagation (#29621)
## Summary

Resolves #29617

## How did you test this change?
I verified the implementation using the test.
2024-05-28 16:15:46 -07:00
Joe Savona bd4bb32fe7 compiler: fix for calls on builtin jsx/function types
When I added new builtin types for jsx and functions, i forget to add a shape definition. This meant that attempting to accesss a property or method on these types would cause an internal error with an unresolved shape. That wasn't obvious because we rarely call methods on these types.

I confirmed that the new fixtures here fail without the fix.

ghstack-source-id: aa8f8d75a3
Pull Request resolved: https://github.com/facebook/react/pull/29624
2024-05-28 15:17:49 -07:00
Joe Savona 46339720d7 compiler: error on reassigning to const
We currently don't report an error if the code attempts to reassign a const. Our thinking has been that we're not trying to catch all possible mistakes you could make in JavaScript — that's what ESLint, TypeScript, and Flow are for — and that we want to focus on React errors. However, accidentally reassigning a const is easy to catch and doesn't get in the way of other analysis so let's implement it.

Note that React Compiler's ESLint plugin won't report these errors by default, but they will show up in playground.

Fixes #29598

ghstack-source-id: a0af8b9a48
Pull Request resolved: https://github.com/facebook/react/pull/29619
2024-05-28 13:09:17 -07:00
Joseph Savona 4ec6a6f714 Repro function expr hoisting (#29615)
Modified version of @mofeiZ's #29232 with CI passing (had to run
prettier)

---------

Co-authored-by: Mofei Zhang <feifei0@meta.com>
2024-05-28 10:06:05 -07:00
Joe Savona 935180c7e0 compiler: only resolve globals and react imports
Updates Environment#getGlobalDeclaration() to only resolve "globals" if they are a true global or an import from react/react-dom. We still keep the logic to resolve hook-like names as custom hooks. Notably, this means that a local `Array` reference won't get confused with our Array global declaration, a local `useState` (or import from something other than React) won't get confused as `React.useState()`, etc.

I tried to write a proper fixture test to test that we react to changes to a custom setState setter function, but I think there may be an issue with snap and how it handles re-renders from effects. I think the tests are good here but open to feedback if we want to go down the rabbit hole of figuring out a proper snap test for this.

ghstack-source-id: 5e9a8f6e0d
Pull Request resolved: https://github.com/facebook/react/pull/29190
2024-05-24 10:20:26 +01:00
Joe Savona 788ed90b18 compiler: getGlobalDeclaration() takes a NonLocalBinding
No-op refactor to make Environment#getGlobalDeclaration() take a NonLocalBinding instead of just a name. The idea is that in subsequent PRs we can use information about the binding to resolve a type more accurately. For example, we can resolve `Array` differently if its an import or local and not the global Array. Similar for resolving local `useState` differently than the one from React.

ghstack-source-id: c8063e6fb8
Pull Request resolved: https://github.com/facebook/react/pull/29189
2024-05-24 10:20:26 +01:00
Joe Savona 5061f31f4b compiler: distinguish globals/imports/module-locals
We currently use `LoadGlobal` and `StoreGlobal` to represent any read (or write) of a variable defined outside the component or hook that is being compiled. This is mostly fine, but for a lot of things we want to do going forward (resolving types across modules, for example) it helps to understand the actual source of a variable.

This PR is an incremental step in that direction. We continue to use LoadGlobal/StoreGlobal, but LoadGlobal now has a `binding:NonLocalBinding` instead of just the name of the global. The NonLocalBinding type tells us whether it was an import (and which kind, the source module name etc), a module-local binding, or a true global. By keeping the LoadGlobal/StoreGlobal instructions, most code that deals with "anything not declared locally" doesn't have to care about the difference. However, code that _does_ want to know the source of the value can figure it out.

ghstack-source-id: e701d4ebc0
Pull Request resolved: https://github.com/facebook/react/pull/29188
2024-05-24 10:20:26 +01:00
Joe Savona 5fe8c0b4ec compiler: repro for unmerged scopes due to intermediates
Repro of a case where we should ideally merge consecutive scopes, but where intermediate temporaries prevent the scopes from merging.

We'd need to reorder instructions in order to merge these.

ghstack-source-id: 4f05672604
Pull Request resolved: https://github.com/facebook/react/pull/29197
2024-05-23 17:44:56 +01:00
Joe Savona b687fd27b5 compiler: Use types to decide which scopes are eligible for merging
In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

ghstack-source-id: 0e3e05f24e
Pull Request resolved: https://github.com/facebook/react/pull/29157
2024-05-23 17:44:56 +01:00
Joe Savona 890896b2db compiler: Improve merging of memo scopes that invalidate together
Improves merging of consecutive scopes so that we now merge two scopes if the dependencies of the second scope are a subset of the previous scope's output *and* that dependency has a type that will always produce a new value (array, object, jsx, function) if it is re-evaluated.

To make this easier, we extend the set of builtin types to include ones for function expressions and JSX and to infer these types in InferTypes. This allows using the already inferred types in MergeReactiveScopesThatInvalidateTogether.

ghstack-source-id: e9119fc4e0
Pull Request resolved: https://github.com/facebook/react/pull/29156
2024-05-23 17:44:55 +01:00
Joe Savona 82a0a5f88a compiler: fixture for suboptimal jsx sibling memo block merging
React Compiler attempts to merge consecutive reactive scopes in order to reduce overhead. The basic idea is that if two consecutive scopes would always invalidate together then we should merge them. It gets more complicated, though, because values produced by the earlier scope may not always invalidate when their inputs do. For example, a scope that produces `fn(x)` may not invalidate on all changes to `x` if the function is `Math.max(x, 10)` (changing x from 8 to 9 won't change the output).

Previously we were conservative and only merged if either:
* the two scopes had the same dependencies
* the second scope's deps exactly matched the previous scope's outputs.

You can see this in the new fixture, where the second `<button>` gets its own scope, which happens because the preceding scope has an extra output that isn't a dep of the `<button>`'s scope.

ghstack-source-id: d869c8d4df
Pull Request resolved: https://github.com/facebook/react/pull/29155
2024-05-23 17:44:55 +01:00
Henry Q. Dineen 4c2e457c7c compiler: Handle TSNonNullAssertion expressions (#29218)
## Summary

We ran React compiler against part of our codebase and collected
compiler errors. One of the more common non-actionable errors is caused
by usage of the `!` TypeScript non-null assertion operation:

```
(BuildHIR::lowerExpression) Handle TSNonNullExpression expressions
```

It seems like React Compiler _should_ be able to support this by just
ignoring the syntax and using the underlying expression. I'm sure a lot
of our non-null assertion usage should not exist and I understand if
React Compiler does not want to support this syntax. It wasn't obvious
to me if this omission was intentional or if there are future plans to
use `TSNonNullExpression` as part of the compiler's analysis. If there
are no future plans it seems like just ignoring it should be fine.

## How did you test this change?

```sh
❯ yarn snap --filter
yarn run v1.17.3
$ yarn workspace babel-plugin-react-compiler run snap --filter
$ node ../snap/dist/main.js --filter
 PASS  non-null-assertion
1 Tests, 1 Passed, 0 Failed
```
2024-05-22 23:42:06 +01:00
Lauren Tan 0f58454803 [compiler:playground] Update babel.config.js
Use new defaults

ghstack-source-id: d2da1df69d
Pull Request resolved: https://github.com/facebook/react/pull/29205
2024-05-21 18:12:30 -04:00
Lauren Tan b759b71ead [compiler:playground] JS tab is expanded by default
When using the playground you typically want to see what it outputs, so
let's make the JS tab expanded by default.

ghstack-source-id: 721bc4c381
Pull Request resolved: https://github.com/facebook/react/pull/29203
2024-05-21 18:12:30 -04:00
Lauren Tan 0a0a5c02f1 [compiler:playground] Wait for build before running playground
Explicitly waits for the build to finish since the playground requires
them to run

ghstack-source-id: 0bd7d5272d
Pull Request resolved: https://github.com/facebook/react/pull/29180
2024-05-21 15:44:04 -04:00