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.
This commit is contained in:
Joe Savona
2023-02-14 09:33:23 -08:00
parent 759a7e027b
commit 6b67f597a6
5 changed files with 329 additions and 1 deletions
@@ -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: {
@@ -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 ? <div>{x}</div> : 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 = (
<div>
{x}
{y}
</div>
);
// 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 ? <div>{x}</div> : 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 = (
<div>
{x$0}
{y}
</div>
);
$[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;
}
```
@@ -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 ? <div>{x}</div> : 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 = (
<div>
{x}
{y}
</div>
);
// scope 4 ...
return (
<>
{header}
{content}
</>
);
}
@@ -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 = <div>{x}</div>;
// 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 = (
<div>
{x}
{y}
</div>
);
// 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 = <div>{x}</div>;
$[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 = (
<div>
{x$0}
{y}
</div>
);
$[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;
}
```
@@ -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 = <div>{x}</div>;
// 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 = (
<div>
{x}
{y}
</div>
);
// scope 4 ...
return (
<>
{header}
{content}
</>
);
}