@@ -23,10 +23,12 @@ import {
2323 isUseRefType ,
2424 GeneratedSource ,
2525 SourceLocation ,
26+ IdentifierName ,
2627} from '../HIR' ;
2728import { eachInstructionLValue , eachInstructionOperand } from '../HIR/visitors' ;
2829import { isMutable } from '../ReactiveScopes/InferReactiveScopeVariables' ;
2930import { assertExhaustive } from '../Utils/utils' ;
31+ import { printInstruction } from '../HIR/PrintHIR' ;
3032
3133type TypeOfValue = 'ignored' | 'fromProps' | 'fromState' | 'fromPropsAndState' ;
3234
@@ -41,8 +43,9 @@ type ValidationContext = {
4143 readonly errors : CompilerError ;
4244 readonly derivationCache : DerivationCache ;
4345 readonly effects : Set < HIRFunction > ;
44- readonly setStateCache : Map < string | undefined | null , Array < Place > > ;
45- readonly effectSetStateCache : Map < string | undefined | null , Array < Place > > ;
46+ // These should replace the setStateCache and effectSetStateCache once we have
47+ readonly setStateLoads : Map < IdentifierId , IdentifierId | null > ;
48+ readonly setStateUsages : Map < IdentifierId , Set < SourceLocation > > ;
4649} ;
4750
4851class DerivationCache {
@@ -188,13 +191,16 @@ export function validateNoDerivedComputationsInEffects_exp(
188191 Array < Place >
189192 > = new Map ( ) ;
190193
194+ const setStateLoads : Map < IdentifierId , IdentifierId > = new Map ( ) ;
195+ const setStateUsages : Map < IdentifierId , Set < SourceLocation > > = new Map ( ) ;
196+
191197 const context : ValidationContext = {
192198 functions,
193199 errors,
194200 derivationCache,
195201 effects,
196- setStateCache ,
197- effectSetStateCache ,
202+ setStateLoads ,
203+ setStateUsages ,
198204 } ;
199205
200206 if ( fn . fnType === 'Hook' ) {
@@ -241,10 +247,7 @@ export function validateNoDerivedComputationsInEffects_exp(
241247 validateEffect ( effect , context ) ;
242248 }
243249
244- console . log ( derivationCache . cache ) ;
245- if ( errors . hasAnyErrors ( ) ) {
246- throw errors ;
247- }
250+ return errors . asResult ( ) ;
248251}
249252
250253function recordPhiDerivations (
@@ -287,11 +290,56 @@ function joinValue(
287290 return 'fromPropsAndState' ;
288291}
289292
293+ function maybeRecordSetState (
294+ instr : Instruction ,
295+ loads : Map < IdentifierId , IdentifierId | null > ,
296+ usages : Map < IdentifierId , Set < SourceLocation > > ,
297+ ) {
298+ for ( const operand of eachInstructionLValue ( instr ) ) {
299+ if ( isSetStateType ( operand . identifier ) ) {
300+ if ( instr . value . kind === 'LoadLocal' ) {
301+ loads . set ( operand . identifier . id , instr . value . place . identifier . id ) ;
302+ } else {
303+ // this is a root setState
304+ loads . set ( operand . identifier . id , null ) ;
305+ usages . set ( operand . identifier . id , new Set ( [ operand . loc ] ) ) ;
306+ }
307+ }
308+ }
309+ }
310+
311+ function getRootSetState (
312+ key : IdentifierId ,
313+ loads : Map < IdentifierId , IdentifierId | null > ,
314+ visited : Set < IdentifierId > = new Set ( ) ,
315+ ) : IdentifierId | null {
316+ if ( visited . has ( key ) ) {
317+ return null ;
318+ }
319+ visited . add ( key ) ;
320+
321+ const parentId = loads . get ( key ) ;
322+
323+ if ( parentId === undefined ) {
324+ return null ;
325+ }
326+
327+ if ( parentId === null ) {
328+ return key ;
329+ }
330+
331+ return getRootSetState ( parentId , loads , visited ) ;
332+ }
333+
290334function recordInstructionDerivations (
291335 instr : Instruction ,
292336 context : ValidationContext ,
293337 isFirstPass : boolean ,
294338) : void {
339+ if ( isFirstPass ) {
340+ maybeRecordSetState ( instr , context . setStateLoads , context . setStateUsages ) ;
341+ }
342+
295343 let typeOfValue : TypeOfValue = 'ignored' ;
296344 const sources : Set < IdentifierId > = new Set ( ) ;
297345 const { lvalue, value} = instr ;
@@ -326,15 +374,14 @@ function recordInstructionDerivations(
326374 }
327375
328376 for ( const operand of eachInstructionOperand ( instr ) ) {
329- if (
330- isSetStateType ( operand . identifier ) &&
331- operand . loc !== GeneratedSource &&
332- isFirstPass
333- ) {
334- if ( context . setStateCache . has ( operand . loc . identifierName ) ) {
335- context . setStateCache . get ( operand . loc . identifierName ) ! . push ( operand ) ;
336- } else {
337- context . setStateCache . set ( operand . loc . identifierName , [ operand ] ) ;
377+ if ( isSetStateType ( operand . identifier ) && isFirstPass ) {
378+ // this is a root setState
379+ const rootSetStateId = getRootSetState (
380+ operand . identifier . id ,
381+ context . setStateLoads ,
382+ ) ;
383+ if ( rootSetStateId !== null ) {
384+ context . setStateUsages . get ( rootSetStateId ) ?. add ( operand . loc ) ;
338385 }
339386 }
340387
@@ -472,11 +519,17 @@ function validateEffect(
472519
473520 const effectDerivedSetStateCalls : Array < {
474521 value : CallExpression ;
475- loc : SourceLocation ;
522+ id : IdentifierId ;
476523 sourceIds : Set < IdentifierId > ;
477524 typeOfValue : TypeOfValue ;
478525 } > = [ ] ;
479526
527+ const effectSetStateLoads : Map < IdentifierId , IdentifierId > = new Map ( ) ;
528+ const effectSetStateUsages : Map <
529+ IdentifierId ,
530+ Set < SourceLocation >
531+ > = new Map ( ) ;
532+
480533 const globals : Set < IdentifierId > = new Set ( ) ;
481534 for ( const block of effectFunction . body . blocks . values ( ) ) {
482535 for ( const pred of block . preds ) {
@@ -492,19 +545,16 @@ function validateEffect(
492545 return ;
493546 }
494547
548+ maybeRecordSetState ( instr , effectSetStateLoads , effectSetStateUsages ) ;
549+
495550 for ( const operand of eachInstructionOperand ( instr ) ) {
496- if (
497- isSetStateType ( operand . identifier ) &&
498- operand . loc !== GeneratedSource
499- ) {
500- if ( context . effectSetStateCache . has ( operand . loc . identifierName ) ) {
501- context . effectSetStateCache
502- . get ( operand . loc . identifierName ) !
503- . push ( operand ) ;
504- } else {
505- context . effectSetStateCache . set ( operand . loc . identifierName , [
506- operand ,
507- ] ) ;
551+ if ( isSetStateType ( operand . identifier ) ) {
552+ const rootSetStateId = getRootSetState (
553+ operand . identifier . id ,
554+ effectSetStateLoads ,
555+ ) ;
556+ if ( rootSetStateId !== null ) {
557+ effectSetStateUsages . get ( rootSetStateId ) ?. add ( operand . loc ) ;
508558 }
509559 }
510560 }
@@ -522,7 +572,7 @@ function validateEffect(
522572 if ( argMetadata !== undefined ) {
523573 effectDerivedSetStateCalls . push ( {
524574 value : instr . value ,
525- loc : instr . value . callee . loc ,
575+ id : instr . value . callee . identifier . id ,
526576 sourceIds : argMetadata . sourcesIds ,
527577 typeOfValue : argMetadata . typeOfValue ,
528578 } ) ;
@@ -557,14 +607,10 @@ function validateEffect(
557607
558608 for ( const derivedSetStateCall of effectDerivedSetStateCalls ) {
559609 if (
560- derivedSetStateCall . loc !== GeneratedSource &&
561- context . effectSetStateCache . has ( derivedSetStateCall . loc . identifierName ) &&
562- context . setStateCache . has ( derivedSetStateCall . loc . identifierName ) &&
563- context . effectSetStateCache . get ( derivedSetStateCall . loc . identifierName ) !
564- . length ===
565- context . setStateCache . get ( derivedSetStateCall . loc . identifierName ) !
566- . length -
567- 1
610+ effectSetStateUsages . has ( derivedSetStateCall . id ) &&
611+ context . setStateUsages . has ( derivedSetStateCall . id ) &&
612+ effectSetStateUsages . get ( derivedSetStateCall . id ) ! . size ===
613+ context . setStateUsages . get ( derivedSetStateCall . id ) ! . size - 1
568614 ) {
569615 const allSourceIds = Array . from ( derivedSetStateCall . sourceIds ) ;
570616 const trees = allSourceIds
0 commit comments