@samitouri / QOS-React-2 / commits / 4b55399fb8

[λ] Use context identifiers in inner functions

This threads through context identifiers to inner functions and also uses them as dependencies if they are mutated in the inner function.

Sathya Gunasekaran committed Feb 3, 2023 at 17:42 UTC 4b55399fb8d9aecb8ed0b961c3bb719e0440afd2
5 files changed +46 -11
compiler/forget/src/HIR/BuildHIR.ts
+13 -3
@@ -50,10 +50,10 @@ import HIRBuilder, { Environment } from "./HIRBuilder";
50 */
51 export function lower(
52 func: NodePath<t.Function>,
53 - capturedRefs?: t.Identifier[]
53 + capturedRefs: t.Identifier[] = []
54 ): Result<HIRFunction, CompilerError> {
55 const env = new Environment();
56 - const builder = new HIRBuilder(env);
56 + const builder = new HIRBuilder(env, capturedRefs);
57 const context: Place[] = [];
58
59 for (const ref of capturedRefs ?? []) {
@@ -1336,7 +1336,17 @@ function lowerExpression(
1336 }
1337 const componentScope: Scope = expr.scope.parent.getFunctionParent()!;
1338 const captured = gatherCapturedDeps(builder, expr, componentScope);
1339 - const lowering = lower(expr, captured.identifiers);
1339 +
1340 + // TODO(gsn): In the future, we could only pass in the context identifiers
1341 + // that are actually used by this function and it's nested functions, rather
1342 + // than all context identifiers.
1343 + //
1344 + // This isn't a problem in practice because use Babel's scope analysis to
1345 + // identify the correct references.
1346 + const lowering = lower(expr, [
1347 + ...builder.context,
1348 + ...captured.identifiers,
1349 + ]);
1350 let loweredFunc: HIRFunction;
1351 if (lowering.isErr()) {
1352 lowering
compiler/forget/src/HIR/HIRBuilder.ts
+7 -1
@@ -88,6 +88,7 @@ export default class HIRBuilder {
88 #current: WipBlock = newBlock(makeBlockId(0), "block");
89 #entry: BlockId = makeBlockId(0);
90 #scopes: Array<Scope> = [];
91 + #context: t.Identifier[];
92 #bindings: Map<string, { node: t.Identifier; identifier: Identifier }> =
93 new Map();
94 #env: Environment;
@@ -97,8 +98,13 @@ export default class HIRBuilder {
98 return this.#env.nextIdentifierId;
99 }
100
100 - constructor(env: Environment) {
101 + get context(): t.Identifier[] {
102 + return this.#context;
103 + }
104 +
105 + constructor(env: Environment, context: t.Identifier[]) {
106 this.#env = env;
107 + this.#context = context;
108 }
109
110 debug(): string {
compiler/forget/src/Inference/AnalyseFunctions.ts
+21 -2
@@ -1,3 +1,4 @@
1 +import invariant from "invariant";
2 import {
3 HIRFunction,
4 FunctionExpression,
@@ -44,7 +45,7 @@ export default function analyseFunctions(func: HIRFunction) {
45 switch (instr.value.kind) {
46 case "FunctionExpression": {
47 lower(instr.value.loweredFunc);
47 - infer(instr.value, properties);
48 + infer(instr.value, properties, func.context);
49 break;
50 }
51 case "PropertyLoad": {
@@ -74,7 +75,8 @@ function lower(func: HIRFunction) {
75
76 function infer(
77 value: FunctionExpression,
77 - properties: Map<Identifier, Dependency>
78 + properties: Map<Identifier, Dependency>,
79 + context: Place[]
80 ) {
81 const mutations = new Set(
82 value.loweredFunc.context
@@ -99,6 +101,23 @@ function infer(
101 }
102 }
103
104 + // This could potentially add duplicate deps to mutatedDeps in the case of
105 + // mutating a context ref in the child function and in this parent function.
106 + // It might be useful to dedupe this.
107 + //
108 + // In practice this never really matters because the Component function has no
109 + // context refs, so it will never have duplicate deps.
110 + for (const place of context) {
111 + invariant(
112 + place.identifier.name !== null,
113 + "context refs should always have a name"
114 + );
115 +
116 + if (mutations.has(place.identifier.name)) {
117 + mutatedDeps.push(place);
118 + }
119 + }
120 +
121 value.mutatedDeps = mutatedDeps;
122 }
123
compiler/forget/src/__tests__/fixtures/hir/capture_mutate-across-fns.expect.md renamed
+5 -5
@@ -23,16 +23,16 @@ function component(a) {
23 let z;
24 if (c_0) {
25 z = { a: a };
26 + (function () {
27 + (function () {
28 + z.b = 1;
29 + })();
30 + })();
31 $[0] = a;
32 $[1] = z;
33 } else {
34 z = $[1];
35 }
31 - (function () {
32 - (function () {
33 - z.b = 1;
34 - })();
35 - })();
36 return z;
37 }
38
compiler/forget/src/__tests__/fixtures/hir/capture_mutate-across-fns.js renamed