Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[compiler] Repro for object spread and Array.from with mutable iterators #32520

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@

## Input

```javascript
/**
* TODO: object spreads should have conditionally mutate semantics
* Found differences in evaluator results
* Non-forget (expected):
* (kind: ok) [3,1,5,4]
* [3,1,5,4]
* [4,1,5,4]
* Forget:
* (kind: ok) [3,1,5,4]
* [3,1,5,4]
* [4]
*/

function useBar({arg}) {
'use memo';

/**
* Note that mutableIterator is mutated by the later object spread. Therefore,
* `s.values()` should be memoized within the same block as the object spread.
* In terms of compiler internals, they should have the same reactive scope.
*/
const s = new Set([1, 5, 4]);
const mutableIterator = s.values();

return [arg, ...mutableIterator];
}

export const FIXTURE_ENTRYPOINT = {
fn: useBar,
params: [{arg: 3}],
sequentialRenders: [{arg: 3}, {arg: 3}, {arg: 4}],
};

```

## Code

```javascript
import { c as _c } from "react/compiler-runtime"; /**
* TODO: object spreads should have conditionally mutate semantics
* Found differences in evaluator results
* Non-forget (expected):
* (kind: ok) [3,1,5,4]
* [3,1,5,4]
* [4,1,5,4]
* Forget:
* (kind: ok) [3,1,5,4]
* [3,1,5,4]
* [4]
*/

function useBar(t0) {
"use memo";
const $ = _c(3);
const { arg } = t0;
let t1;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const s = new Set([1, 5, 4]);
t1 = s.values();
$[0] = t1;
} else {
t1 = $[0];
}
const mutableIterator = t1;
let t2;
if ($[1] !== arg) {
t2 = [arg, ...mutableIterator];
$[1] = arg;
$[2] = t2;
} else {
t2 = $[2];
}
return t2;
}

export const FIXTURE_ENTRYPOINT = {
fn: useBar,
params: [{ arg: 3 }],
sequentialRenders: [{ arg: 3 }, { arg: 3 }, { arg: 4 }],
};

```

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* TODO: object spreads should have conditionally mutate semantics
* Found differences in evaluator results
* Non-forget (expected):
* (kind: ok) [3,1,5,4]
* [3,1,5,4]
* [4,1,5,4]
* Forget:
* (kind: ok) [3,1,5,4]
* [3,1,5,4]
* [4]
*/

function useBar({arg}) {
'use memo';

/**
* Note that mutableIterator is mutated by the later object spread. Therefore,
* `s.values()` should be memoized within the same block as the object spread.
* In terms of compiler internals, they should have the same reactive scope.
*/
const s = new Set([1, 5, 4]);
const mutableIterator = s.values();

return [arg, ...mutableIterator];
Comment on lines +17 to +25
Copy link
Contributor

Choose a reason for hiding this comment

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

ohhh, nice find

}

export const FIXTURE_ENTRYPOINT = {
fn: useBar,
params: [{arg: 3}],
sequentialRenders: [{arg: 3}, {arg: 3}, {arg: 4}],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@

## Input

```javascript
import {useIdentity, ValidateMemoization} from 'shared-runtime';

/**
* TODO fixture for granular iterator semantics:
* 1. ConditionallyMutate the iterator itself, depending on whether the iterator
* is a mutable iterator.
* 2. Capture effect on elements within the iterator.
*/
function Validate({x, input}) {
'use no memo';
return (
<>
<ValidateMemoization inputs={[]} output={x[0]} onlyCheckCompiled={true} />
<ValidateMemoization
inputs={[input]}
output={x[1]}
onlyCheckCompiled={true}
/>
</>
);
}
function useFoo(input) {
'use memo';
/**
* TODO: We should be able to memoize {} separately from `x`.
*/
const x = Array.from([{}]);
useIdentity();
x.push([input]);
return <Validate x={x} input={input} />;
}

export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [1],
};

```

## Code

```javascript
import { c as _c } from "react/compiler-runtime";
import { useIdentity, ValidateMemoization } from "shared-runtime";

/**
* TODO fixture for granular iterator semantics:
* 1. ConditionallyMutate the iterator itself, depending on whether the iterator
* is a mutable iterator.
* 2. Capture effect on elements within the iterator.
*/
function Validate({ x, input }) {
"use no memo";
return (
<>
<ValidateMemoization inputs={[]} output={x[0]} onlyCheckCompiled={true} />
<ValidateMemoization
inputs={[input]}
output={x[1]}
onlyCheckCompiled={true}
/>
</>
);
}
function useFoo(input) {
"use memo";
const $ = _c(3);

const x = Array.from([{}]);
useIdentity();
x.push([input]);
let t0;
if ($[0] !== input || $[1] !== x) {
t0 = <Validate x={x} input={input} />;
$[0] = input;
$[1] = x;
$[2] = t0;
} else {
t0 = $[2];
}
return t0;
}

export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [1],
};

```

### Eval output
(kind: ok) <div>{"inputs":[],"output":{}}</div><div>{"inputs":[1],"output":[1]}</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {useIdentity, ValidateMemoization} from 'shared-runtime';

/**
* TODO fixture for granular iterator semantics:
* 1. ConditionallyMutate the iterator itself, depending on whether the iterator
Copy link
Contributor

Choose a reason for hiding this comment

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

there's no iterator in this example?

* is a mutable iterator.
* 2. Capture effect on elements within the iterator.
*/
function Validate({x, input}) {
'use no memo';
return (
<>
<ValidateMemoization inputs={[]} output={x[0]} onlyCheckCompiled={true} />
<ValidateMemoization
inputs={[input]}
output={x[1]}
onlyCheckCompiled={true}
/>
</>
);
}
function useFoo(input) {
'use memo';
/**
* TODO: We should be able to memoize {} separately from `x`.
*/
const x = Array.from([{}]);
useIdentity();
x.push([input]);
return <Validate x={x} input={input} />;
}

export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [1],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@

## Input

```javascript
import {useIdentity, ValidateMemoization} from 'shared-runtime';

/**
* Fixture to assert that we can infer the type and effects of an array created
* with `Array.from`.
*/
function Validate({x, val1, val2}) {
'use no memo';
return (
<>
<ValidateMemoization
inputs={[val1]}
output={x[0]}
onlyCheckCompiled={true}
/>
<ValidateMemoization
inputs={[val2]}
output={x[1]}
onlyCheckCompiled={true}
/>
</>
);
}
function useFoo({val1, val2}) {
'use memo';
const x = Array.from([]);
useIdentity();
x.push([val1]);
x.push([val2]);
return <Validate x={x} val1={val1} val2={val2} />;
}

export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [{val1: 1, val2: 2}],
};

```

## Code

```javascript
import { c as _c } from "react/compiler-runtime";
import { useIdentity, ValidateMemoization } from "shared-runtime";

/**
* Fixture to assert that we can infer the type and effects of an array created
* with `Array.from`.
*/
function Validate({ x, val1, val2 }) {
"use no memo";
return (
<>
<ValidateMemoization
inputs={[val1]}
output={x[0]}
onlyCheckCompiled={true}
/>

<ValidateMemoization
inputs={[val2]}
output={x[1]}
onlyCheckCompiled={true}
/>
</>
);
}
function useFoo(t0) {
"use memo";
const $ = _c(4);
const { val1, val2 } = t0;

const x = Array.from([]);
useIdentity();
x.push([val1]);
x.push([val2]);
let t1;
if ($[0] !== val1 || $[1] !== val2 || $[2] !== x) {
t1 = <Validate x={x} val1={val1} val2={val2} />;
$[0] = val1;
$[1] = val2;
$[2] = x;
$[3] = t1;
} else {
t1 = $[3];
}
return t1;
}

export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [{ val1: 1, val2: 2 }],
};

```

### Eval output
(kind: ok) <div>{"inputs":[1],"output":[1]}</div><div>{"inputs":[2],"output":[2]}</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {useIdentity, ValidateMemoization} from 'shared-runtime';

/**
* Fixture to assert that we can infer the type and effects of an array created
* with `Array.from`.
*/
function Validate({x, val1, val2}) {
'use no memo';
return (
<>
<ValidateMemoization
inputs={[val1]}
output={x[0]}
onlyCheckCompiled={true}
/>
<ValidateMemoization
inputs={[val2]}
output={x[1]}
onlyCheckCompiled={true}
/>
</>
);
}
function useFoo({val1, val2}) {
'use memo';
const x = Array.from([]);
useIdentity();
x.push([val1]);
x.push([val2]);
return <Validate x={x} val1={val1} val2={val2} />;
}

export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [{val1: 1, val2: 2}],
};
Loading
Loading