Repository navigation
[compiler] Don't report errors for functions reached by forward mutation edges - #37779
Aryan007-dev wants to merge 1 commit into
Conversation
…ion edges InferMutationAliasingRanges treats mutating a function value as calling it and reports the function's errors (eg impure calls). A function reached through a forward edge only had one of its captured values mutated, eg when a hoisted context variable it references is initialized, so this reported false purity errors for event handlers that reference a later-declared or self-referencing function. Only report on backward traversal, and track reported functions separately so a later real call through the context variable is still caught.
|
Hi @Aryan007-dev! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at [email protected]. Thanks! |
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
|
Nice writeup, the forward-edge explanation matches what I found when I was poking at this. I took a look at repro 1 (the I put that up separately in #37799 so it doesn't hold this one up. It touches the same condition in |
Summary
Partially addresses #37761. This fixes repros 2, 3 and 4 there (forward and self-referencing closures). Repro 1 (
.map()) has a different cause, described below.InferMutationAliasingRangestreats mutating a function value as calling it. Whenmutate()reaches aFunctionnode it records the function's errors (Impure,MutateFrozen,MutateGlobal). However, the traversal also reaches function nodes by forward edges (Capture x -> fn). That only means a value the function captured was mutated; the function itself was not called.This happens with hoisted context variables.
StoreContext later = ...emitsMutate later, and the mutation flows forward into every closure that capturedlater. Those closures are then reported even if they only ever run from an event handler or timer:Self-referencing closures (
const tick = () => { Date.now(); setTimeout(tick) }) hit the same path.Changes
mutate()only appends function errors when the node is reached by backward traversal: the call target itself, or something it aliases or captures.node.transitive == null && node.local == nulldoubled as the "report once" guard. A forward visit now marks the node as mutated without reporting it. Without a fix, that would suppress the report on a later real call, e.g.const fact = k => ... fact(k - 1) ...; fact(n)during render, which goesLoadContext→CreateFrom→ captured function. So reported functions are now tracked in their own set.render()uses the same set.react_compiler_inference/src/infer_mutation_aliasing_ranges.rs.Not addressed: repro 1 from #37761
With
items.map(item => <button onClick={() => open(item)} />), the receiver type is unknown, so the call is modelled asMutateTransitiveConditionallyon the callback. The transitive walk then follows the callback's capture ofopenbackwards. That is the same path that correctly reportsitems.map(item => open(item)). Telling the two apart needs the callback's own aliasing signature, which is a bigger design question, so I left it out of this PR.How did you test this change?
New fixtures:
impure-call-in-handler-calling-later-declared-function: issue repro 2. Previously errored, now compiles.impure-call-in-later-declared-function-called-from-handler: issue repro 3. Previously errored, now compiles.impure-call-in-self-referencing-function: issue repro 4. Previously errored, now compiles.error.invalid-impure-call-via-later-declared-function-in-render: calling through a later-declared function during render is still reported.error.invalid-impure-call-in-self-referencing-function-in-render: a self-referencing function called during render is still reported. An earlier version of this change, without the separate reported set, failed this fixture.Checks run in
compiler/:yarn snap: 1832/1832 passed (no existing fixture output changed).yarn snap --rust: 1832/1832 passed.bash scripts/test-rust-port.sh: 1831/1831 passed.bash scripts/test-babel-ast.sh: ok.yarn workspace babel-plugin-react-compiler lint: passes.cargo fmt --check: clean. Prettier (repo options): clean.