Skip to content

Conversation

@jorge-cab
Copy link
Contributor

@jorge-cab jorge-cab commented Oct 23, 2025

Summary:
With this we are now comparing a snapshot of the derivationCache with the new changes every time we are done recording the derivations happening in the HIR.

We have to do this after recording everything since we still do some mutations on the cache when recording mutations.

Test Plan:
Test the following in playground:

// @validateNoDerivedComputationsInEffects_exp

function Component({ value }) {
  const [checked, setChecked] = useState('');

  useEffect(() => {
    setChecked(value === '' ? [] : value.split(','));
  }, [value]);

  return (
    <div>{checked}</div>
  )
}

This no longer causes an infinite loop.

Added a test case in the next PR in the stack


Stack created with Sapling. Best reviewed with ReviewStack.

@meta-cla meta-cla bot added the CLA Signed label Oct 23, 2025
@jorge-cab jorge-cab changed the title [compiler] Prevent overriding a derivationEntry and instead aggregate them [compiler] Prevent overriding a derivationEntry on effect mutation and instead update typeOfValue Oct 23, 2025
@jorge-cab jorge-cab changed the title [compiler] Prevent overriding a derivationEntry on effect mutation and instead update typeOfValue [compiler] Prevent overriding a derivationEntry on effect mutation and instead update typeOfValue and fix infinite loops Oct 27, 2025
@jorge-cab jorge-cab marked this pull request as ready for review October 28, 2025 01:25
typeOfValue,
if (
isMutable(instr, operand) &&
context.derivationCache.cache.has(operand.identifier.id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the derivation cache does not already have this entry, we still may want to record a typeOfValue. Or is this already handled by your fixpoint iteration logic?

function useHook() {
  const [s, setS] = useState();
  const cb = () => append(arr, s);  // `arr` is mutable here and should get marked as `fromState` 
  const arr = [];
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see, I didn't add it because I couldn't think of an example when it would record a derivation for the first time on mutation. I'll add it

context.derivationCache.takeSnapshot();

for (const block of fn.body.blocks.values()) {
recordPhiDerivations(block, context);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm what's the reasoning for moving when we record phis? A block's phis often defines values used by the block so we may find that the derivation cache is missing values when processing instructions with this change.

In the below example, we want to call recordPhiDerivations to insert x@2 into the context. Then, when we walk over the instructions of the fallthrough block (LoadLocal x@2, Call doSomething), we can read the proper typeOfValue from context

let x = thing1; // x@0
if (cond) {
  x = {}; // x@1
}
// x@2 = phi(x@0, x@1)
doSomething(x) // <- references x@2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, my bad, I moved it when debugging the infinite loop. I'll move it back

…d instead update typeOfValue and fix infinite loops

Summary:
With this we are now comparing a snapshot of the derivationCache with the new changes every time we are done recording the derivations happening in the HIR.

We have to do this after recording everything since we still do some mutations on the cache when recording mutations.



Test Plan:
Test the following in playground:
```
// @validateNoDerivedComputationsInEffects_exp

function Component({ value }) {
  const [checked, setChecked] = useState('');

  useEffect(() => {
    setChecked(value === '' ? [] : value.split(','));
  }, [value]);

  return (
    <div>{checked}</div>
  )
}
```

This no longer causes an infinite loop.

Added a test case in the next PR in the stack
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants