@@ -155,9 +155,7 @@ export function inferMutationAliasingEffects(
155155 }
156156 queue ( fn . body . entry , initialState ) ;
157157
158- const signatureCache : Map < Instruction , InstructionSignature > = new Map ( ) ;
159- const effectInstructionValueCache : Map < AliasingEffect , InstructionValue > =
160- new Map ( ) ;
158+ const context = new Context ( ) ;
161159
162160 let count = 0 ;
163161 while ( queuedStates . size !== 0 ) {
@@ -179,7 +177,7 @@ export function inferMutationAliasingEffects(
179177
180178 statesByBlock . set ( blockId , incomingState ) ;
181179 const state = incomingState . clone ( ) ;
182- inferBlock ( state , block , signatureCache , effectInstructionValueCache ) ;
180+ inferBlock ( context , state , block ) ;
183181
184182 for ( const nextBlockId of eachTerminalSuccessor ( block . terminal ) ) {
185183 queue ( nextBlockId , state ) ;
@@ -189,6 +187,13 @@ export function inferMutationAliasingEffects(
189187 return Ok ( undefined ) ;
190188}
191189
190+ class Context {
191+ instructionSignatureCache : Map < Instruction , InstructionSignature > = new Map ( ) ;
192+ effectInstructionValueCache : Map < AliasingEffect , InstructionValue > =
193+ new Map ( ) ;
194+ catchHandlers : Map < BlockId , Place > = new Map ( ) ;
195+ }
196+
192197function inferParam (
193198 param : Place | SpreadPattern ,
194199 initialState : InferenceState ,
@@ -205,29 +210,45 @@ function inferParam(
205210}
206211
207212function inferBlock (
213+ context : Context ,
208214 state : InferenceState ,
209215 block : BasicBlock ,
210- instructionSignatureCache : Map < Instruction , InstructionSignature > ,
211- effectInstructionValueCache : Map < AliasingEffect , InstructionValue > ,
212216) : void {
213217 for ( const phi of block . phis ) {
214218 state . inferPhi ( phi ) ;
215219 }
216220
217221 for ( const instr of block . instructions ) {
218- let instructionSignature = instructionSignatureCache . get ( instr ) ;
222+ let instructionSignature = context . instructionSignatureCache . get ( instr ) ;
219223 if ( instructionSignature == null ) {
220224 instructionSignature = computeSignatureForInstruction ( state . env , instr ) ;
221- instructionSignatureCache . set ( instr , instructionSignature ) ;
225+ context . instructionSignatureCache . set ( instr , instructionSignature ) ;
222226 }
223- const effects = applySignature (
224- state ,
225- instructionSignature ,
226- instr ,
227- effectInstructionValueCache ,
228- ) ;
227+ const effects = applySignature ( context , state , instructionSignature , instr ) ;
229228 instr . effects = effects ;
230229 }
230+ const terminal = block . terminal ;
231+ if ( terminal . kind === 'try' && terminal . handlerBinding != null ) {
232+ context . catchHandlers . set ( terminal . handler , terminal . handlerBinding ) ;
233+ } else if ( terminal . kind === 'maybe-throw' ) {
234+ const handlerParam = context . catchHandlers . get ( terminal . handler ) ;
235+ if ( handlerParam != null ) {
236+ for ( const instr of block . instructions ) {
237+ if (
238+ instr . value . kind === 'CallExpression' ||
239+ instr . value . kind === 'MethodCall'
240+ ) {
241+ /**
242+ * Many instructions can error, but only calls can throw their result as the error
243+ * itself. For example, `c = a.b` can throw if `a` is nullish, but the thrown value
244+ * is an error object synthesized by the JS runtime. Whereas `throwsInput(x)` can
245+ * throw (effectively) the result of the call.
246+ */
247+ state . appendAlias ( handlerParam , instr . lvalue ) ;
248+ }
249+ }
250+ }
251+ }
231252}
232253
233254/**
@@ -240,10 +261,10 @@ function inferBlock(
240261 * This phase may also emit errors, for example MutateLocal on a frozen value is invalid.
241262 */
242263function applySignature (
264+ context : Context ,
243265 state : InferenceState ,
244266 signature : InstructionSignature ,
245267 instruction : Instruction ,
246- effectInstructionValueCache : Map < AliasingEffect , InstructionValue > ,
247268) : Array < AliasingEffect > | null {
248269 const effects : Array < AliasingEffect > = [ ] ;
249270 /**
@@ -305,14 +326,7 @@ function applySignature(
305326 }
306327
307328 for ( const effect of signature . effects ) {
308- applyEffect (
309- state ,
310- effect ,
311- instruction ,
312- effectInstructionValueCache ,
313- aliased ,
314- effects ,
315- ) ;
329+ applyEffect ( context , state , effect , instruction , aliased , effects ) ;
316330 }
317331 if ( DEBUG ) {
318332 console . log (
@@ -334,10 +348,10 @@ function applySignature(
334348}
335349
336350function applyEffect (
351+ context : Context ,
337352 state : InferenceState ,
338353 effect : AliasingEffect ,
339354 instruction : Instruction ,
340- effectInstructionValueCache : Map < AliasingEffect , InstructionValue > ,
341355 aliased : Set < IdentifierId > ,
342356 effects : Array < AliasingEffect > ,
343357) : void {
@@ -353,14 +367,14 @@ function applyEffect(
353367 break ;
354368 }
355369 case 'Create' : {
356- let value = effectInstructionValueCache . get ( effect ) ;
370+ let value = context . effectInstructionValueCache . get ( effect ) ;
357371 if ( value == null ) {
358372 value = {
359373 kind : 'ObjectExpression' ,
360374 properties : [ ] ,
361375 loc : effect . into . loc ,
362376 } ;
363- effectInstructionValueCache . set ( effect , value ) ;
377+ context . effectInstructionValueCache . set ( effect , value ) ;
364378 }
365379 state . initialize ( value , {
366380 kind : effect . value ,
@@ -385,14 +399,14 @@ function applyEffect(
385399 }
386400 case 'CreateFrom' : {
387401 const kind = state . kind ( effect . from ) . kind ;
388- let value = effectInstructionValueCache . get ( effect ) ;
402+ let value = context . effectInstructionValueCache . get ( effect ) ;
389403 if ( value == null ) {
390404 value = {
391405 kind : 'ObjectExpression' ,
392406 properties : [ ] ,
393407 loc : effect . into . loc ,
394408 } ;
395- effectInstructionValueCache . set ( effect , value ) ;
409+ context . effectInstructionValueCache . set ( effect , value ) ;
396410 }
397411 state . initialize ( value , {
398412 kind,
@@ -456,14 +470,14 @@ function applyEffect(
456470 state . define ( effect . into , effect . function ) ;
457471 for ( const capture of effect . captures ) {
458472 applyEffect (
473+ context ,
459474 state ,
460475 {
461476 kind : 'Capture' ,
462477 from : capture ,
463478 into : effect . into ,
464479 } ,
465480 instruction ,
466- effectInstructionValueCache ,
467481 aliased ,
468482 effects ,
469483 ) ;
@@ -531,29 +545,29 @@ function applyEffect(
531545 from : effect . from ,
532546 into : effect . into ,
533547 } ) ;
534- let value = effectInstructionValueCache . get ( effect ) ;
548+ let value = context . effectInstructionValueCache . get ( effect ) ;
535549 if ( value == null ) {
536550 value = {
537551 kind : 'Primitive' ,
538552 value : undefined ,
539553 loc : effect . from . loc ,
540554 } ;
541- effectInstructionValueCache . set ( effect , value ) ;
555+ context . effectInstructionValueCache . set ( effect , value ) ;
542556 }
543557 state . initialize ( value , { kind : fromKind , reason : new Set ( [ ] ) } ) ;
544558 state . define ( effect . into , value ) ;
545559 break ;
546560 }
547561 case ValueKind . Global :
548562 case ValueKind . Primitive : {
549- let value = effectInstructionValueCache . get ( effect ) ;
563+ let value = context . effectInstructionValueCache . get ( effect ) ;
550564 if ( value == null ) {
551565 value = {
552566 kind : 'Primitive' ,
553567 value : undefined ,
554568 loc : effect . from . loc ,
555569 } ;
556- effectInstructionValueCache . set ( effect , value ) ;
570+ context . effectInstructionValueCache . set ( effect , value ) ;
557571 }
558572 state . initialize ( value , { kind : fromKind , reason : new Set ( [ ] ) } ) ;
559573 state . define ( effect . into , value ) ;
@@ -605,10 +619,10 @@ function applyEffect(
605619 }
606620 for ( const signatureEffect of signatureEffects ) {
607621 applyEffect (
622+ context ,
608623 state ,
609624 signatureEffect ,
610625 instruction ,
611- effectInstructionValueCache ,
612626 aliased ,
613627 effects ,
614628 ) ;
@@ -632,10 +646,10 @@ function applyEffect(
632646 }
633647 for ( const signatureEffect of signatureEffects ) {
634648 applyEffect (
649+ context ,
635650 state ,
636651 signatureEffect ,
637652 instruction ,
638- effectInstructionValueCache ,
639653 aliased ,
640654 effects ,
641655 ) ;
@@ -653,10 +667,10 @@ function applyEffect(
653667 ) ;
654668 for ( const legacyEffect of legacyEffects ) {
655669 applyEffect (
670+ context ,
656671 state ,
657672 legacyEffect ,
658673 instruction ,
659- effectInstructionValueCache ,
660674 aliased ,
661675 effects ,
662676 ) ;
@@ -666,14 +680,14 @@ function applyEffect(
666680 console . log ( 'default effects' ) ;
667681 }
668682 applyEffect (
683+ context ,
669684 state ,
670685 {
671686 kind : 'Create' ,
672687 into : effect . into ,
673688 value : ValueKind . Mutable ,
674689 } ,
675690 instruction ,
676- effectInstructionValueCache ,
677691 aliased ,
678692 effects ,
679693 ) ;
@@ -691,23 +705,23 @@ function applyEffect(
691705 const operand = arg . kind === 'Identifier' ? arg : arg . place ;
692706 if ( operand !== effect . function || effect . mutatesFunction ) {
693707 applyEffect (
708+ context ,
694709 state ,
695710 {
696711 kind : 'MutateTransitiveConditionally' ,
697712 value : operand ,
698713 } ,
699714 instruction ,
700- effectInstructionValueCache ,
701715 aliased ,
702716 effects ,
703717 ) ;
704718 }
705719 applyEffect (
720+ context ,
706721 state ,
707722 // OK: recording information flow
708723 { kind : 'Alias' , from : operand , into : effect . into } ,
709724 instruction ,
710- effectInstructionValueCache ,
711725 aliased ,
712726 effects ,
713727 ) ;
@@ -725,6 +739,7 @@ function applyEffect(
725739 continue ;
726740 }
727741 applyEffect (
742+ context ,
728743 state ,
729744 {
730745 /*
@@ -736,7 +751,6 @@ function applyEffect(
736751 into : other ,
737752 } ,
738753 instruction ,
739- effectInstructionValueCache ,
740754 aliased ,
741755 effects ,
742756 ) ;
@@ -984,7 +998,6 @@ class InferenceState {
984998 | 'MutateTransitiveConditionally' ,
985999 place : Place ,
9861000 ) : 'none' | 'mutate' | 'mutate-frozen' | 'mutate-global' {
987- // TODO: consider handling of function expressions by looking at their effects
9881001 const kind = this . kind ( place ) . kind ;
9891002 switch ( variant ) {
9901003 case 'MutateConditionally' :
@@ -1348,18 +1361,6 @@ function computeSignatureForInstruction(
13481361 effects . push ( { kind : 'Assign' , from : value . value , into : lvalue } ) ;
13491362 break ;
13501363 }
1351- case 'PostfixUpdate' :
1352- case 'PrefixUpdate' : {
1353- effects . push ( {
1354- kind : 'Create' ,
1355- into : lvalue ,
1356- value : ValueKind . Primitive ,
1357- } ) ;
1358- CompilerError . throwTodo ( {
1359- reason : `Handle ${ value . kind } in new inference` ,
1360- loc : instr . loc ,
1361- } ) ;
1362- }
13631364 case 'ObjectMethod' :
13641365 case 'FunctionExpression' : {
13651366 /**
@@ -1570,6 +1571,15 @@ function computeSignatureForInstruction(
15701571 effects . push ( { kind : 'Assign' , from : value . value , into : lvalue } ) ;
15711572 break ;
15721573 }
1574+ case 'PostfixUpdate' :
1575+ case 'PrefixUpdate' : {
1576+ effects . push ( {
1577+ kind : 'Create' ,
1578+ into : lvalue ,
1579+ value : ValueKind . Primitive ,
1580+ } ) ;
1581+ break ;
1582+ }
15731583 case 'StoreGlobal' : {
15741584 CompilerError . throwTodo ( {
15751585 reason : `Handle StoreGlobal in new inference` ,
@@ -1677,15 +1687,28 @@ function computeEffectsForLegacySignature(
16771687 break ;
16781688 }
16791689 case Effect . ConditionallyMutateIterator : {
1680- effects . push ( {
1681- kind : 'Capture' ,
1682- from : place ,
1683- into : lvalue ,
1684- } ) ;
1685- effects . push ( {
1686- kind : 'MutateTransitiveConditionally' ,
1687- value : place ,
1688- } ) ;
1690+ if (
1691+ isArrayType ( place . identifier ) ||
1692+ isSetType ( place . identifier ) ||
1693+ isMapType ( place . identifier )
1694+ ) {
1695+ effects . push ( {
1696+ kind : 'Capture' ,
1697+ from : place ,
1698+ into : lvalue ,
1699+ } ) ;
1700+ } else {
1701+ effects . push ( {
1702+ kind : 'Capture' ,
1703+ from : place ,
1704+ into : lvalue ,
1705+ } ) ;
1706+ captures . push ( place ) ;
1707+ effects . push ( {
1708+ kind : 'MutateTransitiveConditionally' ,
1709+ value : place ,
1710+ } ) ;
1711+ }
16891712 break ;
16901713 }
16911714 case Effect . Freeze : {
0 commit comments