From 6b67f597a6bc286fdf716c6840ece0c56997aba1 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Tue, 14 Feb 2023 09:33:23 -0800 Subject: [PATCH] Capturing a frozen value is a Read If a value is known to be frozen (or potentially frozen), then it doesn't need to be considered 'captured' since no mutation can occur via aliasing. --- .../src/Inference/InferReferenceEffects.ts | 9 +- ...s-dep-and-redeclare-maybe-frozen.expect.md | 127 ++++++++++++++++++ ...iable-as-dep-and-redeclare-maybe-frozen.js | 35 +++++ ...me-variable-as-dep-and-redeclare.expect.md | 124 +++++++++++++++++ .../hir/same-variable-as-dep-and-redeclare.js | 35 +++++ 5 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.js diff --git a/compiler/forget/src/Inference/InferReferenceEffects.ts b/compiler/forget/src/Inference/InferReferenceEffects.ts index bf64963d09..29c5a00099 100644 --- a/compiler/forget/src/Inference/InferReferenceEffects.ts +++ b/compiler/forget/src/Inference/InferReferenceEffects.ts @@ -308,7 +308,14 @@ class Environment { break; } case Effect.Capture: { - effect = Effect.Capture; + if ( + valueKind === ValueKind.Frozen || + valueKind === ValueKind.MaybeFrozen + ) { + effect = Effect.Read; + } else { + effect = Effect.Capture; + } break; } case Effect.Read: { diff --git a/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.expect.md b/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.expect.md new file mode 100644 index 0000000000..50fae20d8d --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.expect.md @@ -0,0 +1,127 @@ + +## Input + +```javascript +// note: comments are for the ideal scopes, not what is currently +// emitted +function foo(props) { + // scope 0: deps=[props.a] decl=[x] reassign=none + let x = []; + x.push(props.a); + + // scope 1: deps=[x] decl=[header] reassign=none + const header = props.showHeader ?
{x}
: null; + + // scope 2: + // deps=[x, props.b, props.c] + // decl=none + // reassign=[x] + const y = [x]; // y depends on the earlier x + x = []; // x reassigned + y.push(props.b); // interleaved mutation of x/y + x.push(props.c); // interleaved mutation + + // scope 3 ... + const content = ( +
+ {x} + {y} +
+ ); + + // scope 4 ... + return ( + <> + {header} + {content} + + ); +} + +``` + +## Code + +```javascript +// note: comments are for the ideal scopes, not what is currently +// emitted +function foo(props) { + const $ = React.unstable_useMemoCache(); + const c_0 = $[0] !== props.a; + let x; + if (c_0) { + x = []; + x.push(props.a); + $[0] = props.a; + $[1] = x; + } else { + x = $[1]; + } + const c_2 = $[2] !== props; + const c_3 = $[3] !== x; + let t0; + if (c_2 || c_3) { + t0 = props.showHeader ?
{x}
: null; + $[2] = props; + $[3] = x; + $[4] = t0; + } else { + t0 = $[4]; + } + const header = t0; + const c_5 = $[5] !== x; + const c_6 = $[6] !== props.b; + const c_7 = $[7] !== props.c; + let x$0; + let y; + if (c_5 || c_6 || c_7) { + y = [x]; + x$0 = []; + y.push(props.b); + x$0.push(props.c); + $[5] = x; + $[6] = props.b; + $[7] = props.c; + $[8] = x$0; + $[9] = y; + } else { + x$0 = $[8]; + y = $[9]; + } + const c_10 = $[10] !== x$0; + const c_11 = $[11] !== y; + let content; + if (c_10 || c_11) { + content = ( +
+ {x$0} + {y} +
+ ); + $[10] = x$0; + $[11] = y; + $[12] = content; + } else { + content = $[12]; + } + const c_13 = $[13] !== header; + const c_14 = $[14] !== content; + let t1; + if (c_13 || c_14) { + t1 = ( + <> + {header} + {content} + + ); + $[13] = header; + $[14] = content; + $[15] = t1; + } else { + t1 = $[15]; + } + return t1; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.js b/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.js new file mode 100644 index 0000000000..67149fb3f3 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.js @@ -0,0 +1,35 @@ +// note: comments are for the ideal scopes, not what is currently +// emitted +function foo(props) { + // scope 0: deps=[props.a] decl=[x] reassign=none + let x = []; + x.push(props.a); + + // scope 1: deps=[x] decl=[header] reassign=none + const header = props.showHeader ?
{x}
: null; + + // scope 2: + // deps=[x, props.b, props.c] + // decl=none + // reassign=[x] + const y = [x]; // y depends on the earlier x + x = []; // x reassigned + y.push(props.b); // interleaved mutation of x/y + x.push(props.c); // interleaved mutation + + // scope 3 ... + const content = ( +
+ {x} + {y} +
+ ); + + // scope 4 ... + return ( + <> + {header} + {content} + + ); +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.expect.md b/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.expect.md new file mode 100644 index 0000000000..f975bb3535 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.expect.md @@ -0,0 +1,124 @@ + +## Input + +```javascript +// note: comments are for the ideal scopes, not what is currently +// emitted +function foo(props) { + // scope 0: deps=[props.a] decl=[x] reassign=none + let x = []; + x.push(props.a); + + // scope 1: deps=[x] decl=[header] reassign=none + const header =
{x}
; + + // scope 2: + // deps=[x, props.b, props.c] + // decl=none + // reassign=[x] + const y = [x]; // y depends on the earlier x + x = []; // x reassigned + y.push(props.b); // interleaved mutation of x/y + x.push(props.c); // interleaved mutation + + // scope 3 ... + const content = ( +
+ {x} + {y} +
+ ); + + // scope 4 ... + return ( + <> + {header} + {content} + + ); +} + +``` + +## Code + +```javascript +// note: comments are for the ideal scopes, not what is currently +// emitted +function foo(props) { + const $ = React.unstable_useMemoCache(); + const c_0 = $[0] !== props.a; + let x; + if (c_0) { + x = []; + x.push(props.a); + $[0] = props.a; + $[1] = x; + } else { + x = $[1]; + } + const c_2 = $[2] !== x; + let header; + if (c_2) { + header =
{x}
; + $[2] = x; + $[3] = header; + } else { + header = $[3]; + } + const c_4 = $[4] !== x; + const c_5 = $[5] !== props.b; + const c_6 = $[6] !== props.c; + let x$0; + let y; + if (c_4 || c_5 || c_6) { + y = [x]; + x$0 = []; + y.push(props.b); + x$0.push(props.c); + $[4] = x; + $[5] = props.b; + $[6] = props.c; + $[7] = x$0; + $[8] = y; + } else { + x$0 = $[7]; + y = $[8]; + } + const c_9 = $[9] !== x$0; + const c_10 = $[10] !== y; + let content; + if (c_9 || c_10) { + content = ( +
+ {x$0} + {y} +
+ ); + $[9] = x$0; + $[10] = y; + $[11] = content; + } else { + content = $[11]; + } + const c_12 = $[12] !== header; + const c_13 = $[13] !== content; + let t0; + if (c_12 || c_13) { + t0 = ( + <> + {header} + {content} + + ); + $[12] = header; + $[13] = content; + $[14] = t0; + } else { + t0 = $[14]; + } + return t0; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.js b/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.js new file mode 100644 index 0000000000..6d8c5cb3f9 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.js @@ -0,0 +1,35 @@ +// note: comments are for the ideal scopes, not what is currently +// emitted +function foo(props) { + // scope 0: deps=[props.a] decl=[x] reassign=none + let x = []; + x.push(props.a); + + // scope 1: deps=[x] decl=[header] reassign=none + const header =
{x}
; + + // scope 2: + // deps=[x, props.b, props.c] + // decl=none + // reassign=[x] + const y = [x]; // y depends on the earlier x + x = []; // x reassigned + y.push(props.b); // interleaved mutation of x/y + x.push(props.c); // interleaved mutation + + // scope 3 ... + const content = ( +
+ {x} + {y} +
+ ); + + // scope 4 ... + return ( + <> + {header} + {content} + + ); +}