@@ -42,6 +42,7 @@ type IdentifierSidemap = {
42
42
react : Set < IdentifierId > ;
43
43
maybeDepsLists : Map < IdentifierId , Array < Place > > ;
44
44
maybeDeps : Map < IdentifierId , ManualMemoDependency > ;
45
+ optionals : Set < IdentifierId > ;
45
46
} ;
46
47
47
48
/**
@@ -52,6 +53,7 @@ type IdentifierSidemap = {
52
53
export function collectMaybeMemoDependencies (
53
54
value : InstructionValue ,
54
55
maybeDeps : Map < IdentifierId , ManualMemoDependency > ,
56
+ optional : boolean ,
55
57
) : ManualMemoDependency | null {
56
58
switch ( value . kind ) {
57
59
case 'LoadGlobal' : {
@@ -69,7 +71,7 @@ export function collectMaybeMemoDependencies(
69
71
return {
70
72
root : object . root ,
71
73
// TODO: determine if the access is optional
72
- path : [ ...object . path , { property : value . property , optional : false } ] ,
74
+ path : [ ...object . path , { property : value . property , optional} ] ,
73
75
} ;
74
76
}
75
77
break ;
@@ -162,7 +164,11 @@ function collectTemporaries(
162
164
break ;
163
165
}
164
166
}
165
- const maybeDep = collectMaybeMemoDependencies ( value , sidemap . maybeDeps ) ;
167
+ const maybeDep = collectMaybeMemoDependencies (
168
+ value ,
169
+ sidemap . maybeDeps ,
170
+ sidemap . optionals . has ( lvalue . identifier . id ) ,
171
+ ) ;
166
172
// We don't expect named lvalues during this pass (unlike ValidatePreservingManualMemo)
167
173
if ( maybeDep != null ) {
168
174
sidemap . maybeDeps . set ( lvalue . identifier . id , maybeDep ) ;
@@ -338,12 +344,14 @@ export function dropManualMemoization(func: HIRFunction): void {
338
344
func . env . config . validatePreserveExistingMemoizationGuarantees ||
339
345
func . env . config . validateNoSetStateInRender ||
340
346
func . env . config . enablePreserveExistingMemoizationGuarantees ;
347
+ const optionals = findOptionalPlaces ( func ) ;
341
348
const sidemap : IdentifierSidemap = {
342
349
functions : new Map ( ) ,
343
350
manualMemos : new Map ( ) ,
344
351
react : new Set ( ) ,
345
352
maybeDeps : new Map ( ) ,
346
353
maybeDepsLists : new Map ( ) ,
354
+ optionals,
347
355
} ;
348
356
let nextManualMemoId = 0 ;
349
357
@@ -481,6 +489,40 @@ function findOptionalPlaces(fn: HIRFunction): Set<IdentifierId> {
481
489
const optionals = new Set < IdentifierId > ( ) ;
482
490
for ( const [ , block ] of fn . body . blocks ) {
483
491
if ( block . terminal . kind === 'optional' ) {
492
+ const optionalTerminal = block . terminal ;
493
+ let testBlock = fn . body . blocks . get ( block . terminal . test ) ! ;
494
+ loop: while ( true ) {
495
+ const terminal = testBlock . terminal ;
496
+ switch ( terminal . kind ) {
497
+ case 'branch' : {
498
+ if ( terminal . fallthrough === optionalTerminal . fallthrough ) {
499
+ // found it
500
+ const consequent = fn . body . blocks . get ( terminal . consequent ) ! ;
501
+ const last = consequent . instructions . at ( - 1 ) ;
502
+ if ( last !== undefined && last . value . kind === 'StoreLocal' ) {
503
+ optionals . add ( last . value . value . identifier . id ) ;
504
+ }
505
+ break loop;
506
+ } else {
507
+ testBlock = fn . body . blocks . get ( terminal . fallthrough ) ! ;
508
+ }
509
+ break ;
510
+ }
511
+ case 'optional' :
512
+ case 'logical' :
513
+ case 'sequence' :
514
+ case 'ternary' : {
515
+ testBlock = fn . body . blocks . get ( terminal . fallthrough ) ! ;
516
+ break ;
517
+ }
518
+ default : {
519
+ CompilerError . invariant ( false , {
520
+ reason : `Unexpected terminal in optional` ,
521
+ loc : terminal . loc ,
522
+ } ) ;
523
+ }
524
+ }
525
+ }
484
526
}
485
527
}
486
528
return optionals ;
0 commit comments