-
Notifications
You must be signed in to change notification settings - Fork 47k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
compiler: treat pruned scope outputs as reactive
Mostly addresses the issue with non-reactive pruned scopes. Before, values from pruned scopes would not be memoized, but could still be depended upon by downstream scopes. However, those downstream scopes would assume the value could never change. This could allow the developer to observe two different versions of a value - the freshly created one (if observed outside a scope) or a cached one (if observed inside, or through) a scope which used the value but didn't depend on it. The fix here is to consider the outputs of pruned reactive scopes as reactive. Note that this is a partial fix because of things like control variables — the full solution would be to mark these values as reactive, and then re-run InferReactivePlaces. We can do this once we've fully converted our pipeline to use HIR everywhere. For now, this should fix most issues in practice because PruneNonReactiveDependencies already does basic alias tracking (see new fixture). ghstack-source-id: 364430bbeca4cfca2fbf9df4d92b2e61b3352311 Pull Request resolved: #29790
- Loading branch information
1 parent
d193455
commit 7d445ac
Showing
14 changed files
with
366 additions
and
224 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
119 changes: 0 additions & 119 deletions
119
.../src/__tests__/fixtures/compiler/bug-invalid-pruned-scope-leaks-value.expect.md
This file was deleted.
Oops, something went wrong.
43 changes: 0 additions & 43 deletions
43
...in-react-compiler/src/__tests__/fixtures/compiler/bug-invalid-pruned-scope-leaks-value.ts
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
112 changes: 112 additions & 0 deletions
112
...__/fixtures/compiler/repro-invalid-pruned-scope-leaks-value-via-alias.expect.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
|
||
## Input | ||
|
||
```javascript | ||
import invariant from "invariant"; | ||
import { | ||
makeObject_Primitives, | ||
mutate, | ||
sum, | ||
useIdentity, | ||
} from "shared-runtime"; | ||
|
||
/** | ||
* Here, `z`'s original memo block is removed due to the inner hook call. | ||
* However, we also infer that `z` is non-reactive, so by default we would create | ||
* the memo block for `thing = [y, z]` as only depending on `y`. | ||
* | ||
* This could then mean that `thing[1]` and `z` may not refer to the same value, | ||
* since z recreates every time but `thing` doesn't correspondingly invalidate. | ||
* | ||
* The fix is to consider pruned memo block outputs as reactive, since they will | ||
* recreate on every render. This means `thing` depends on both y and z. | ||
*/ | ||
function MyApp({ count }) { | ||
const z = makeObject_Primitives(); | ||
const x = useIdentity(2); | ||
const y = sum(x, count); | ||
mutate(z); | ||
const z2 = z; | ||
const thing = [y, z2]; | ||
if (thing[1] !== z) { | ||
invariant(false, "oh no!"); | ||
} | ||
return thing; | ||
} | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: MyApp, | ||
params: [{ count: 2 }], | ||
sequentialRenders: [{ count: 2 }, { count: 2 }, { count: 3 }], | ||
}; | ||
|
||
``` | ||
|
||
## Code | ||
|
||
```javascript | ||
import { c as _c } from "react/compiler-runtime"; | ||
import invariant from "invariant"; | ||
import { | ||
makeObject_Primitives, | ||
mutate, | ||
sum, | ||
useIdentity, | ||
} from "shared-runtime"; | ||
|
||
/** | ||
* Here, `z`'s original memo block is removed due to the inner hook call. | ||
* However, we also infer that `z` is non-reactive, so by default we would create | ||
* the memo block for `thing = [y, z]` as only depending on `y`. | ||
* | ||
* This could then mean that `thing[1]` and `z` may not refer to the same value, | ||
* since z recreates every time but `thing` doesn't correspondingly invalidate. | ||
* | ||
* The fix is to consider pruned memo block outputs as reactive, since they will | ||
* recreate on every render. This means `thing` depends on both y and z. | ||
*/ | ||
function MyApp(t0) { | ||
const $ = _c(6); | ||
const { count } = t0; | ||
const z = makeObject_Primitives(); | ||
const x = useIdentity(2); | ||
let t1; | ||
if ($[0] !== x || $[1] !== count) { | ||
t1 = sum(x, count); | ||
$[0] = x; | ||
$[1] = count; | ||
$[2] = t1; | ||
} else { | ||
t1 = $[2]; | ||
} | ||
const y = t1; | ||
mutate(z); | ||
const z2 = z; | ||
let t2; | ||
if ($[3] !== y || $[4] !== z2) { | ||
t2 = [y, z2]; | ||
$[3] = y; | ||
$[4] = z2; | ||
$[5] = t2; | ||
} else { | ||
t2 = $[5]; | ||
} | ||
const thing = t2; | ||
if (thing[1] !== z) { | ||
invariant(false, "oh no!"); | ||
} | ||
return thing; | ||
} | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: MyApp, | ||
params: [{ count: 2 }], | ||
sequentialRenders: [{ count: 2 }, { count: 2 }, { count: 3 }], | ||
}; | ||
|
||
``` | ||
### Eval output | ||
(kind: ok) [4,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
[4,{"a":0,"b":"value1","c":true,"wat0":"joe"}] | ||
[5,{"a":0,"b":"value1","c":true,"wat0":"joe"}] |
Oops, something went wrong.