[λ] 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