diff --git a/compiler/forget/src/HIR/HIR.ts b/compiler/forget/src/HIR/HIR.ts index e6e7b8d589..3570e62324 100644 --- a/compiler/forget/src/HIR/HIR.ts +++ b/compiler/forget/src/HIR/HIR.ts @@ -531,6 +531,7 @@ export enum ValueKind { Frozen = "frozen", Immutable = "immutable", Mutable = "mutable", + Context = "context", } /** diff --git a/compiler/forget/src/Inference/InferReferenceEffects.ts b/compiler/forget/src/Inference/InferReferenceEffects.ts index 246b23617a..0a35a12c91 100644 --- a/compiler/forget/src/Inference/InferReferenceEffects.ts +++ b/compiler/forget/src/Inference/InferReferenceEffects.ts @@ -26,6 +26,7 @@ import { } from "../HIR/PrintHIR"; import { eachInstructionOperand, + eachInstructionValueOperand, eachTerminalOperand, eachTerminalSuccessor, } from "../HIR/visitors"; @@ -100,7 +101,7 @@ export default function inferReferenceEffects(fn: HIRFunction) { properties: null, loc: ref.loc, }; - initialEnvironment.initialize(value, ValueKind.Mutable); + initialEnvironment.initialize(value, ValueKind.Context); initialEnvironment.define(ref, value); } @@ -274,6 +275,7 @@ class Environment { case Effect.Freeze: { if ( valueKind === ValueKind.Mutable || + valueKind === ValueKind.Context || valueKind === ValueKind.MaybeFrozen ) { effect = Effect.Freeze; @@ -285,7 +287,10 @@ class Environment { break; } case Effect.Mutate: { - if (valueKind === ValueKind.Mutable) { + if ( + valueKind === ValueKind.Mutable || + valueKind === ValueKind.Context + ) { effect = Effect.Mutate; } else { effect = Effect.Read; @@ -488,6 +493,9 @@ class Environment { * types. To ensure that any sequence of joins btw those three states yields the * correct maybe-frozen, these two have to produce a frozen value. * - | maybe-frozen => maybe-frozen + * - immutable | context => context + * - mutable | context => context + * - frozen | context => maybe-frozen * * ┌──────────────────────────┐ * │ Immutable │───┐ @@ -513,10 +521,21 @@ function mergeValues(a: ValueKind, b: ValueKind): ValueKind { if (a === ValueKind.Frozen || b === ValueKind.Frozen) { // frozen | mutable return ValueKind.MaybeFrozen; + } else if (a === ValueKind.Context || b === ValueKind.Context) { + // context | mutable + return ValueKind.Context; } else { // mutable | immutable return ValueKind.Mutable; } + } else if (a === ValueKind.Context || b === ValueKind.Context) { + if (a === ValueKind.Frozen || b === ValueKind.Frozen) { + // frozen | context + return ValueKind.MaybeFrozen; + } else { + // context | immutable + return ValueKind.Context; + } } else { // frozen | immutable return ValueKind.Frozen; @@ -664,7 +683,11 @@ function inferBlock(env: Environment, block: BasicBlock) { continue; } case "PropertyStore": { - env.reference(instrValue.value, Effect.Capture); + const effect = + env.kind(instrValue.object) === ValueKind.Context + ? Effect.Mutate + : Effect.Capture; + env.reference(instrValue.value, effect); env.reference(instrValue.object, Effect.Store); const lvalue = instr.lvalue; @@ -692,7 +715,11 @@ function inferBlock(env: Environment, block: BasicBlock) { continue; } case "ComputedStore": { - env.reference(instrValue.value, Effect.Capture); + const effect = + env.kind(instrValue.object) === ValueKind.Context + ? Effect.Mutate + : Effect.Capture; + env.reference(instrValue.value, effect); env.reference(instrValue.property, Effect.Capture); env.reference(instrValue.object, Effect.Store); diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.expect.md b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.expect.md new file mode 100644 index 0000000000..d936f8018a --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.expect.md @@ -0,0 +1,40 @@ + +## Input + +```javascript +function component(a) { + let x = { a }; + let y; + (function () { + y["x"] = x; + })(); + mutate(y); + return y; +} + +``` + +## Code + +```javascript +function component(a) { + const $ = React.unstable_useMemoCache(); + const c_0 = $[0] !== a; + let y; + if (c_0) { + const x = { a: a }; + y = undefined; + (function () { + y["x"] = x; + })(); + $[0] = a; + $[1] = y; + } else { + y = $[1]; + } + mutate(y); + return y; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.js b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.js new file mode 100644 index 0000000000..d3a669613b --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.js @@ -0,0 +1,9 @@ +function component(a) { + let x = { a }; + let y; + (function () { + y["x"] = x; + })(); + mutate(y); + return y; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.expect.md b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.expect.md new file mode 100644 index 0000000000..2402cdfb39 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.expect.md @@ -0,0 +1,40 @@ + +## Input + +```javascript +function component(a) { + let x = { a }; + let y; + (function () { + y.x = x; + })(); + mutate(y); + return y; +} + +``` + +## Code + +```javascript +function component(a) { + const $ = React.unstable_useMemoCache(); + const c_0 = $[0] !== a; + let y; + if (c_0) { + const x = { a: a }; + y = undefined; + (function () { + y.x = x; + })(); + $[0] = a; + $[1] = y; + } else { + y = $[1]; + } + mutate(y); + return y; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.js b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.js new file mode 100644 index 0000000000..6da8bf9d1a --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.js @@ -0,0 +1,9 @@ +function component(a) { + let x = { a }; + let y; + (function () { + y.x = x; + })(); + mutate(y); + return y; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.expect.md b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.expect.md new file mode 100644 index 0000000000..53055ae54b --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.expect.md @@ -0,0 +1,42 @@ + +## Input + +```javascript +function component(a) { + let x = { a }; + let y = {}; + (function () { + let a = y; + a["x"] = x; + })(); + mutate(y); + return y; +} + +``` + +## Code + +```javascript +function component(a) { + const $ = React.unstable_useMemoCache(); + const c_0 = $[0] !== a; + let y; + if (c_0) { + const x = { a: a }; + y = {}; + (function () { + let a = y; + a["x"] = x; + })(); + mutate(y); + $[0] = a; + $[1] = y; + } else { + y = $[1]; + } + return y; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.js b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.js new file mode 100644 index 0000000000..dba7a25cf1 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.js @@ -0,0 +1,10 @@ +function component(a) { + let x = { a }; + let y = {}; + (function () { + let a = y; + a["x"] = x; + })(); + mutate(y); + return y; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.expect.md b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.expect.md new file mode 100644 index 0000000000..4116b7bb76 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.expect.md @@ -0,0 +1,42 @@ + +## Input + +```javascript +function component(a) { + let x = { a }; + let y = {}; + (function () { + let a = y; + a.x = x; + })(); + mutate(y); + return y; +} + +``` + +## Code + +```javascript +function component(a) { + const $ = React.unstable_useMemoCache(); + const c_0 = $[0] !== a; + let y; + if (c_0) { + const x = { a: a }; + y = {}; + (function () { + let a = y; + a.x = x; + })(); + mutate(y); + $[0] = a; + $[1] = y; + } else { + y = $[1]; + } + return y; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.js b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.js new file mode 100644 index 0000000000..0599917f1b --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.js @@ -0,0 +1,10 @@ +function component(a) { + let x = { a }; + let y = {}; + (function () { + let a = y; + a.x = x; + })(); + mutate(y); + return y; +}