@@ -140,7 +140,7 @@ export function inferMutationAliasingRanges(
140140 } else if ( effect . kind === 'CreateFunction' ) {
141141 state . create ( effect . into , {
142142 kind : 'Function' ,
143- function : effect . function . loweredFunc . func ,
143+ effect,
144144 } ) ;
145145 } else if ( effect . kind === 'CreateFrom' ) {
146146 state . createFrom ( index ++ , effect . from , effect . into ) ;
@@ -155,7 +155,7 @@ export function inferMutationAliasingRanges(
155155 * invariant here.
156156 */
157157 if ( ! state . nodes . has ( effect . into . identifier ) ) {
158- state . create ( effect . into , { kind : 'Object ' } ) ;
158+ state . create ( effect . into , { kind : 'Assign ' } ) ;
159159 }
160160 state . assign ( index ++ , effect . from , effect . into ) ;
161161 } else if ( effect . kind === 'Alias' ) {
@@ -465,35 +465,112 @@ export function inferMutationAliasingRanges(
465465 }
466466 }
467467
468+ const tracked : Array < Place > = [ ] ;
469+ for ( const param of [ ...fn . params , ...fn . context , fn . returns ] ) {
470+ const place = param . kind === 'Identifier' ? param : param . place ;
471+ tracked . push ( place ) ;
472+ }
473+
474+ const returned : Set < Node > = new Set ( ) ;
475+ const queue : Array < Node > = [ state . nodes . get ( fn . returns . identifier ) ! ] ;
476+ const seen : Set < Node > = new Set ( ) ;
477+ while ( queue . length !== 0 ) {
478+ const node = queue . pop ( ) ! ;
479+ if ( seen . has ( node ) ) {
480+ continue ;
481+ }
482+ seen . add ( node ) ;
483+ for ( const id of node . aliases . keys ( ) ) {
484+ queue . push ( state . nodes . get ( id ) ! ) ;
485+ }
486+ for ( const id of node . createdFrom . keys ( ) ) {
487+ queue . push ( state . nodes . get ( id ) ! ) ;
488+ }
489+ if ( node . id . id === fn . returns . identifier . id ) {
490+ continue ;
491+ }
492+ switch ( node . value . kind ) {
493+ case 'Assign' :
494+ case 'CreateFrom' : {
495+ break ;
496+ }
497+ case 'Phi' :
498+ case 'Object' :
499+ case 'Function' : {
500+ returned . add ( node ) ;
501+ break ;
502+ }
503+ default : {
504+ assertExhaustive (
505+ node . value ,
506+ `Unexpected node value kind '${ ( node . value as any ) . kind } '` ,
507+ ) ;
508+ }
509+ }
510+ }
511+ const returnedValues = [ ...returned ] ;
512+ if (
513+ returnedValues . length === 1 &&
514+ returnedValues [ 0 ] . value . kind === 'Object' &&
515+ tracked . some ( place => place . identifier . id === returnedValues [ 0 ] . id . id )
516+ ) {
517+ const from = tracked . find (
518+ place => place . identifier . id === returnedValues [ 0 ] . id . id ,
519+ ) ! ;
520+ functionEffects . push ( {
521+ kind : 'Assign' ,
522+ from,
523+ into : fn . returns ,
524+ } ) ;
525+ } else if (
526+ returnedValues . length === 1 &&
527+ returnedValues [ 0 ] . value . kind === 'Function'
528+ ) {
529+ const outerContext = new Set ( fn . context . map ( p => p . identifier . id ) ) ;
530+ const effect = returnedValues [ 0 ] . value . effect ;
531+ functionEffects . push ( {
532+ kind : 'CreateFunction' ,
533+ function : {
534+ ...effect . function ,
535+ loweredFunc : {
536+ func : {
537+ ...effect . function . loweredFunc . func ,
538+ context : effect . function . loweredFunc . func . context . filter ( p =>
539+ outerContext . has ( p . identifier . id ) ,
540+ ) ,
541+ } ,
542+ } ,
543+ } ,
544+ captures : effect . captures . filter ( p => outerContext . has ( p . identifier . id ) ) ,
545+ into : fn . returns ,
546+ } ) ;
547+ } else {
548+ const returns = fn . returns . identifier ;
549+ functionEffects . push ( {
550+ kind : 'Create' ,
551+ into : fn . returns ,
552+ value : isPrimitiveType ( returns )
553+ ? ValueKind . Primitive
554+ : isJsxType ( returns . type )
555+ ? ValueKind . Frozen
556+ : ValueKind . Mutable ,
557+ reason : ValueReason . KnownReturnSignature ,
558+ } ) ;
559+ }
560+
468561 /**
469562 * Part 3
470563 * Finish populating the externally visible effects. Above we bubble-up the side effects
471564 * (MutateFrozen/MutableGlobal/Impure/Render) as well as mutations of context variables.
472565 * Here we populate an effect to create the return value as well as populating alias/capture
473566 * effects for how data flows between the params, context vars, and return.
474567 */
475- const returns = fn . returns . identifier ;
476- functionEffects . push ( {
477- kind : 'Create' ,
478- into : fn . returns ,
479- value : isPrimitiveType ( returns )
480- ? ValueKind . Primitive
481- : isJsxType ( returns . type )
482- ? ValueKind . Frozen
483- : ValueKind . Mutable ,
484- reason : ValueReason . KnownReturnSignature ,
485- } ) ;
486568 /**
487569 * Determine precise data-flow effects by simulating transitive mutations of the params/
488570 * captures and seeing what other params/context variables are affected. Anything that
489571 * would be transitively mutated needs a capture relationship.
490572 */
491- const tracked : Array < Place > = [ ] ;
492573 const ignoredErrors = new CompilerError ( ) ;
493- for ( const param of [ ...fn . params , ...fn . context , fn . returns ] ) {
494- const place = param . kind === 'Identifier' ? param : param . place ;
495- tracked . push ( place ) ;
496- }
497574 for ( const into of tracked ) {
498575 const mutationIndex = index ++ ;
499576 state . mutate (
@@ -572,9 +649,14 @@ type Node = {
572649 local : { kind : MutationKind ; loc : SourceLocation } | null ;
573650 lastMutated : number ;
574651 value :
652+ | { kind : 'Assign' }
653+ | { kind : 'CreateFrom' }
575654 | { kind : 'Object' }
576655 | { kind : 'Phi' }
577- | { kind : 'Function' ; function : HIRFunction } ;
656+ | {
657+ kind : 'Function' ;
658+ effect : Extract < AliasingEffect , { kind : 'CreateFunction' } > ;
659+ } ;
578660} ;
579661class AliasingState {
580662 nodes : Map < Identifier , Node > = new Map ( ) ;
@@ -594,7 +676,7 @@ class AliasingState {
594676 }
595677
596678 createFrom ( index : number , from : Place , into : Place ) : void {
597- this . create ( into , { kind : 'Object ' } ) ;
679+ this . create ( into , { kind : 'CreateFrom ' } ) ;
598680 const fromNode = this . nodes . get ( from . identifier ) ;
599681 const toNode = this . nodes . get ( into . identifier ) ;
600682 if ( fromNode == null || toNode == null ) {
@@ -644,7 +726,10 @@ class AliasingState {
644726 continue ;
645727 }
646728 if ( node . value . kind === 'Function' ) {
647- appendFunctionErrors ( errors , node . value . function ) ;
729+ appendFunctionErrors (
730+ errors ,
731+ node . value . effect . function . loweredFunc . func ,
732+ ) ;
648733 }
649734 for ( const [ alias , when ] of node . createdFrom ) {
650735 if ( when >= index ) {
@@ -704,7 +789,10 @@ class AliasingState {
704789 node . transitive == null &&
705790 node . local == null
706791 ) {
707- appendFunctionErrors ( errors , node . value . function ) ;
792+ appendFunctionErrors (
793+ errors ,
794+ node . value . effect . function . loweredFunc . func ,
795+ ) ;
708796 }
709797 if ( transitive ) {
710798 if ( node . transitive == null || node . transitive . kind < kind ) {
0 commit comments