Separate mode to validate preserving manual memoization
Adds a new mode which validates that existing manual memoization is preserved _without_ using information from the manual memoization to affect compilation. This gives us a way to try out the more aggressive version of Forget — ignoring manual memoization — first and see how much code bails out and what patterns cause this. We can then proceed to enable the mode to actually _preserve_ existing memo guarantees only where necessary.
Joe Savona committed
Dec 15, 2023 at 17:12 UTC
c77acb3ac664a85d303f898220eb35dc8d2c0bca
4 files changed
+30
-6
compiler/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts
+4
-1
@@ -349,7 +349,10 @@ function* runWithEnvironment(
349
validateMemoizedEffectDependencies(reactiveFunction);
350
}
351
352
- if (env.config.enablePreserveExistingMemoizationGuarantees) {
352
+ if (
353
+ env.config.enablePreserveExistingMemoizationGuarantees ||
354
+ env.config.validatePreserveExistingMemoizationGuarantees
355
+ ) {
356
validatePreservedManualMemoization(reactiveFunction);
357
}
358
compiler/packages/babel-plugin-react-forget/src/HIR/Environment.ts
+15
@@ -126,6 +126,21 @@ const EnvironmentConfigSchema = z.object({
126
*/
127
enablePreserveExistingMemoizationGuarantees: z.boolean().default(false),
128
129
+ /**
130
+ * Validates that all useMemo/useCallback values are also memoized by Forget. This mode can be
131
+ * used with or without @enablePreserveExistingMemoizationGuarantees.
132
+ *
133
+ * With enablePreserveExistingMemoizationGuarantees, this validation enables automatically and
134
+ * verifies that Forget was able to preserve manual memoization semantics under that mode's
135
+ * additional assumptions about the input.
136
+ *
137
+ * With enablePreserveExistingMemoizationGuarantees off, this validation ignores manual memoization
138
+ * when determining program behavior, and only uses information from useMemo/useCallback to check
139
+ * that the memoization was preserved. This can be useful for determining where referential equalities
140
+ * may change under Forget.
141
+ */
142
+ validatePreserveExistingMemoizationGuarantees: z.boolean().default(false),
143
+
144
// 🌲
145
enableForest: z.boolean().default(false),
146
// <🌲>
compiler/packages/babel-plugin-react-forget/src/Inference/DropManualMemoization.ts
+6
-4
@@ -16,7 +16,7 @@ import {
16
SpreadPattern,
17
makeInstructionId,
18
} from "../HIR";
19
-import { createTemporaryPlace } from "../HIR/HIRBuilder";
19
+import { createTemporaryPlace, markInstructionIds } from "../HIR/HIRBuilder";
20
import { HookKind } from "../HIR/ObjectShape";
21
import { eachInstructionValueOperand } from "../HIR/visitors";
22
@@ -114,7 +114,8 @@ export function dropManualMemoization(func: HIRFunction): void {
114
};
115
116
if (
117
- func.env.config.enablePreserveExistingMemoizationGuarantees
117
+ func.env.config.enablePreserveExistingMemoizationGuarantees ||
118
+ func.env.config.validatePreserveExistingMemoizationGuarantees
119
) {
120
/**
121
* When this flag is enabled we also compile in a 'Memoize' instruction
@@ -215,7 +216,8 @@ export function dropManualMemoization(func: HIRFunction): void {
216
loc: instr.value.loc,
217
};
218
if (
218
- func.env.config.enablePreserveExistingMemoizationGuarantees
219
+ func.env.config.enablePreserveExistingMemoizationGuarantees ||
220
+ func.env.config.validatePreserveExistingMemoizationGuarantees
221
) {
222
nextInstructions =
223
nextInstructions ?? block.instructions.slice(0, i);
@@ -295,6 +297,6 @@ export function dropManualMemoization(func: HIRFunction): void {
297
}
298
}
299
if (hasChanges) {
298
- // markInstructionIds(func.body);
300
+ markInstructionIds(func.body);
301
}
302
}
compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts
+5
-1
@@ -1163,7 +1163,11 @@ function inferBlock(
1163
continue;
1164
}
1165
case "Memoize": {
1166
- state.reference(instrValue.value, Effect.Freeze, ValueReason.Other);
1166
+ if (env.config.enablePreserveExistingMemoizationGuarantees) {
1167
+ state.reference(instrValue.value, Effect.Freeze, ValueReason.Other);
1168
+ } else {
1169
+ state.reference(instrValue.value, Effect.Read, ValueReason.Other);
1170
+ }
1171
const lvalue = instr.lvalue;
1172
lvalue.effect = Effect.ConditionallyMutate;
1173
state.initialize(instrValue, {