Skip to content

[pull] main from react:main#571

Merged
pull[bot] merged 2 commits into
code:mainfrom
react:main
Jun 18, 2026
Merged

[pull] main from react:main#571
pull[bot] merged 2 commits into
code:mainfrom
react:main

Conversation

@pull

@pull pull Bot commented Jun 18, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

poteto and others added 2 commits June 17, 2026 15:54
…g the variable (#36732)

A counter initialized before a memo scope and incremented inside it
(`a++` or `a = a + 1` in a `for` body) was emitted as a scope
*dependency*, compared at its pre-loop value (constant every render)
while the cache stored its post-loop value. The memo could never hit, so
the scope recomputed on every render.

Root cause: the phi-union rule in
`InferReactiveScopeVariables.findDisjointMutableValues` only unioned a
phi into the scope when the phi value was mutated after creation, which
range-extension only does for object mutation. Primitive reassignments
around a loop back-edge never extend ranges, so the counter's SSA
versions stayed outside the scope and downstream dependency propagation
classified the pre-loop read as a dep.

The fix unions a phi with its operands and declaration when any operand
is defined at or after the phi's block, i.e. the value is reassigned
around a loop back-edge. This matches the shape the compiler already
produced for non-primitive loop reassignment (`x = [...x, i]`).
Implemented identically in the TypeScript compiler and the Rust port.

Both `a++` and `a = a + 1` variants are pinned by fixtures; the first
commit documents the previously-wrong codegen, the second fixes it
(counter becomes a scope output, dep on `count` only).

Corpus delta beyond the new fixtures is 4 fixtures, all strict
improvements with byte-identical eval output: `for-in-statement-break`,
`for-in-statement-continue`, `for-in-statement-type-inference` (loops
previously re-ran every render, now memoized), and `sequence-expression`
(two memo blocks collapse into one).

Known limitation, unchanged from before: conditional reassignment in a
loop (`for (...) { if (c) a++; }`) routes through a join phi that this
rule cannot see; that shape behaves as it did before this change.

Verification: TS snap 1806/1806, Rust snap 1806/1806, cargo workspace
green, scoped TS-vs-Rust HIR parity harness green.

Closes #34971
## Summary

Dependency maintenance for the Rust compiler crates.

### Fast Refresh hash: sha2 + hmac → hmac-sha256
The Fast Refresh source hash (`enableResetCacheOnSourceFileChanges`)
only needs an HMAC-SHA256 of the source text, matching the TS compiler's
`createHmac('sha256', code).digest('hex')`. RustCrypto's `sha2` + `hmac`
pulled in **11 crates** (`sha2`, `hmac`, `digest`, `block-buffer`,
`typenum`, `crypto-common`, `hybrid-array`, `const-oid`, `cpufeatures`,
`cmov`, `ctutils`) — generic-hashing / constant-time machinery that is
irrelevant for a non-security content fingerprint. This replaces them
with the single **zero-dependency** `hmac-sha256` crate: a net reduction
of **10 crates**.

HMAC-SHA256 is a standardized deterministic algorithm, so the emitted
hash is unchanged. A new unit test
`source_file_hash_matches_node_create_hmac` pins the result against
Node's `createHmac('sha256', code).digest('hex')` for several inputs.

### Other bumps
- `napi` / `napi-derive` 2 → 3
- `similar` 2 → 3 (dev-dependency)
- Drop the unused `react_compiler_lowering` dependency from
`react_compiler_ssa`

`cargo check --workspace --all-targets` passes, including the napi 3
native crate.
@pull pull Bot locked and limited conversation to collaborators Jun 18, 2026
@pull pull Bot added the ⤵️ pull label Jun 18, 2026
@pull pull Bot merged commit 560db51 into code:main Jun 18, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants