@@ -140,7 +140,7 @@ export function inferMutationAliasingRanges(
140
140
} else if ( effect . kind === 'CreateFunction' ) {
141
141
state . create ( effect . into , {
142
142
kind : 'Function' ,
143
- function : effect . function . loweredFunc . func ,
143
+ effect,
144
144
} ) ;
145
145
} else if ( effect . kind === 'CreateFrom' ) {
146
146
state . createFrom ( index ++ , effect . from , effect . into ) ;
@@ -155,7 +155,7 @@ export function inferMutationAliasingRanges(
155
155
* invariant here.
156
156
*/
157
157
if ( ! state . nodes . has ( effect . into . identifier ) ) {
158
- state . create ( effect . into , { kind : 'Object ' } ) ;
158
+ state . create ( effect . into , { kind : 'Assign ' } ) ;
159
159
}
160
160
state . assign ( index ++ , effect . from , effect . into ) ;
161
161
} else if ( effect . kind === 'Alias' ) {
@@ -465,35 +465,112 @@ export function inferMutationAliasingRanges(
465
465
}
466
466
}
467
467
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
+
468
561
/**
469
562
* Part 3
470
563
* Finish populating the externally visible effects. Above we bubble-up the side effects
471
564
* (MutateFrozen/MutableGlobal/Impure/Render) as well as mutations of context variables.
472
565
* Here we populate an effect to create the return value as well as populating alias/capture
473
566
* effects for how data flows between the params, context vars, and return.
474
567
*/
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
- } ) ;
486
568
/**
487
569
* Determine precise data-flow effects by simulating transitive mutations of the params/
488
570
* captures and seeing what other params/context variables are affected. Anything that
489
571
* would be transitively mutated needs a capture relationship.
490
572
*/
491
- const tracked : Array < Place > = [ ] ;
492
573
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
- }
497
574
for ( const into of tracked ) {
498
575
const mutationIndex = index ++ ;
499
576
state . mutate (
@@ -572,9 +649,14 @@ type Node = {
572
649
local : { kind : MutationKind ; loc : SourceLocation } | null ;
573
650
lastMutated : number ;
574
651
value :
652
+ | { kind : 'Assign' }
653
+ | { kind : 'CreateFrom' }
575
654
| { kind : 'Object' }
576
655
| { kind : 'Phi' }
577
- | { kind : 'Function' ; function : HIRFunction } ;
656
+ | {
657
+ kind : 'Function' ;
658
+ effect : Extract < AliasingEffect , { kind : 'CreateFunction' } > ;
659
+ } ;
578
660
} ;
579
661
class AliasingState {
580
662
nodes : Map < Identifier , Node > = new Map ( ) ;
@@ -594,7 +676,7 @@ class AliasingState {
594
676
}
595
677
596
678
createFrom ( index : number , from : Place , into : Place ) : void {
597
- this . create ( into , { kind : 'Object ' } ) ;
679
+ this . create ( into , { kind : 'CreateFrom ' } ) ;
598
680
const fromNode = this . nodes . get ( from . identifier ) ;
599
681
const toNode = this . nodes . get ( into . identifier ) ;
600
682
if ( fromNode == null || toNode == null ) {
@@ -644,7 +726,10 @@ class AliasingState {
644
726
continue ;
645
727
}
646
728
if ( node . value . kind === 'Function' ) {
647
- appendFunctionErrors ( errors , node . value . function ) ;
729
+ appendFunctionErrors (
730
+ errors ,
731
+ node . value . effect . function . loweredFunc . func ,
732
+ ) ;
648
733
}
649
734
for ( const [ alias , when ] of node . createdFrom ) {
650
735
if ( when >= index ) {
@@ -704,7 +789,10 @@ class AliasingState {
704
789
node . transitive == null &&
705
790
node . local == null
706
791
) {
707
- appendFunctionErrors ( errors , node . value . function ) ;
792
+ appendFunctionErrors (
793
+ errors ,
794
+ node . value . effect . function . loweredFunc . func ,
795
+ ) ;
708
796
}
709
797
if ( transitive ) {
710
798
if ( node . transitive == null || node . transitive . kind < kind ) {
0 commit comments