diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.expect.md new file mode 100644 index 0000000000000..70f41f40477ae --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.expect.md @@ -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 }], +}; + +``` + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.js new file mode 100644 index 0000000000000..c83a9e53e6acc --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-array-spread-mutable-iterator.js @@ -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]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useBar, + params: [{arg: 3}], + sequentialRenders: [{arg: 3}, {arg: 3}, {arg: 4}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md new file mode 100644 index 0000000000000..1ba01dc5bf4df --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.expect.md @@ -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 ( + <> + + + + ); +} +function useFoo(input) { + 'use memo'; + /** + * TODO: We should be able to memoize {} separately from `x`. + */ + const x = Array.from([{}]); + useIdentity(); + x.push([input]); + return ; +} + +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 ( + <> + + + + ); +} +function useFoo(input) { + "use memo"; + const $ = _c(3); + + const x = Array.from([{}]); + useIdentity(); + x.push([input]); + let t0; + if ($[0] !== input || $[1] !== x) { + t0 = ; + $[0] = input; + $[1] = x; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [1], +}; + +``` + +### Eval output +(kind: ok)
{"inputs":[],"output":{}}
{"inputs":[1],"output":[1]}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.js new file mode 100644 index 0000000000000..27d861692cfa4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-granular-iterator-semantics.js @@ -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 + * is a mutable iterator. + * 2. Capture effect on elements within the iterator. + */ +function Validate({x, input}) { + 'use no memo'; + return ( + <> + + + + ); +} +function useFoo(input) { + 'use memo'; + /** + * TODO: We should be able to memoize {} separately from `x`. + */ + const x = Array.from([{}]); + useIdentity(); + x.push([input]); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [1], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.expect.md new file mode 100644 index 0000000000000..6061464afce78 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.expect.md @@ -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 ( + <> + + + + ); +} +function useFoo({val1, val2}) { + 'use memo'; + const x = Array.from([]); + useIdentity(); + x.push([val1]); + x.push([val2]); + return ; +} + +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 ( + <> + + + + + ); +} +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 = ; + $[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)
{"inputs":[1],"output":[1]}
{"inputs":[2],"output":[2]}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.js new file mode 100644 index 0000000000000..d1a80a4ea7090 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-type-inference-array-from.js @@ -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 ( + <> + + + + ); +} +function useFoo({val1, val2}) { + 'use memo'; + const x = Array.from([]); + useIdentity(); + x.push([val1]); + x.push([val2]); + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [{val1: 1, val2: 2}], +}; diff --git a/compiler/packages/snap/src/SproutTodoFilter.ts b/compiler/packages/snap/src/SproutTodoFilter.ts index 0817bbf89c582..bbed77c35a90d 100644 --- a/compiler/packages/snap/src/SproutTodoFilter.ts +++ b/compiler/packages/snap/src/SproutTodoFilter.ts @@ -462,6 +462,7 @@ const skipFilter = new Set([ // bugs 'bug-object-expression-computed-key-modified-during-after-construction-hoisted-sequence-expr', + 'bug-array-spread-mutable-iterator', `bug-capturing-func-maybealias-captured-mutate`, 'bug-aliased-capture-aliased-mutate', 'bug-aliased-capture-mutate',