@@ -310,6 +310,9 @@ function applySignature(
310
310
console . log (
311
311
prettyFormat ( state . debugAbstractValue ( state . kind ( instruction . lvalue ) ) ) ,
312
312
) ;
313
+ console . log (
314
+ effects . map ( effect => ` ${ printAliasingEffect ( effect ) } ` ) . join ( '\n' ) ,
315
+ ) ;
313
316
}
314
317
if (
315
318
! ( state . isDefined ( instruction . lvalue ) && state . kind ( instruction . lvalue ) )
@@ -403,8 +406,8 @@ function applyEffect(
403
406
break ;
404
407
}
405
408
default : {
406
- // TODO: should this be Alias??
407
409
effects . push ( {
410
+ // OK: recording information flow
408
411
kind : 'Alias' ,
409
412
from : effect . from ,
410
413
into : effect . into ,
@@ -446,6 +449,7 @@ function applyEffect(
446
449
}
447
450
break ;
448
451
}
452
+ case 'Alias' :
449
453
case 'Capture' : {
450
454
/*
451
455
* Capture describes potential information flow: storing a pointer to one value
@@ -493,7 +497,7 @@ function applyEffect(
493
497
}
494
498
break ;
495
499
}
496
- case 'Alias ' : {
500
+ case 'Assign ' : {
497
501
/*
498
502
* Alias represents potential pointer aliasing. If the type is a global,
499
503
* a primitive (copy-on-write semantics) then we can prune the effect
@@ -549,12 +553,6 @@ function applyEffect(
549
553
}
550
554
case 'Apply' : {
551
555
const functionValues = state . values ( effect . function ) ;
552
- console . log ( 'function is ' + printPlace ( effect . function ) ) ;
553
- console . log (
554
- functionValues . length +
555
- ' ' +
556
- functionValues . map ( v => v . kind ) . join ( ', ' ) ,
557
- ) ;
558
556
if (
559
557
functionValues . length === 1 &&
560
558
functionValues [ 0 ] . kind === 'FunctionExpression'
@@ -567,15 +565,18 @@ function applyEffect(
567
565
state . env ,
568
566
functionValues [ 0 ] ,
569
567
) ;
570
- console . log (
571
- `constructed alias signature:\n${ printAliasingSignature ( signature ) } ` ,
572
- ) ;
568
+ if ( DEBUG ) {
569
+ console . log (
570
+ `constructed alias signature:\n${ printAliasingSignature ( signature ) } ` ,
571
+ ) ;
572
+ }
573
573
const signatureEffects = computeEffectsForSignature (
574
574
state . env ,
575
575
signature ,
576
576
effect . into ,
577
577
effect . receiver ,
578
578
effect . args ,
579
+ functionValues [ 0 ] . loweredFunc . func . context ,
579
580
) ;
580
581
if ( signatureEffects != null ) {
581
582
if ( DEBUG ) {
@@ -680,17 +681,9 @@ function applyEffect(
680
681
effects ,
681
682
) ;
682
683
}
683
- /*
684
- * TODO: this should be Alias, since the function could be identity.
685
- * Ie local mutation of the result could change the input.
686
- * But if we emit multiple Alias calls, currently the last one will win
687
- * when we update the inferencestate in applySignature. So we may need to group
688
- * them here, or coalesce them in applySignature
689
- *
690
- * maybe make `from: Place | Array<Place>`
691
- */
692
684
applyEffect (
693
685
state ,
686
+ // OK: recording information flow
694
687
{ kind : 'Alias' , from : operand , into : effect . into } ,
695
688
instruction ,
696
689
effectInstructionValueCache ,
@@ -713,6 +706,10 @@ function applyEffect(
713
706
applyEffect (
714
707
state ,
715
708
{
709
+ /*
710
+ * OK: a function might store one operand into another,
711
+ * but it can't force one to alias another
712
+ */
716
713
kind : 'Capture' ,
717
714
from : operand ,
718
715
into : other ,
@@ -1310,7 +1307,8 @@ function computeSignatureForInstruction(
1310
1307
from : value . value ,
1311
1308
into : value . object ,
1312
1309
} ) ;
1313
- effects . push ( { kind : 'Alias' , from : value . value , into : lvalue } ) ;
1310
+ // OK: lvalues are assigned to
1311
+ effects . push ( { kind : 'Assign' , from : value . value , into : lvalue } ) ;
1314
1312
break ;
1315
1313
}
1316
1314
case 'PostfixUpdate' :
@@ -1479,34 +1477,34 @@ function computeSignatureForInstruction(
1479
1477
into : patternLValue ,
1480
1478
} ) ;
1481
1479
}
1482
- effects . push ( { kind : 'Alias ' , from : value . value , into : lvalue } ) ;
1480
+ effects . push ( { kind : 'Assign ' , from : value . value , into : lvalue } ) ;
1483
1481
break ;
1484
1482
}
1485
1483
case 'LoadContext' : {
1486
- effects . push ( { kind : 'Alias ' , from : value . place , into : lvalue } ) ;
1484
+ effects . push ( { kind : 'Assign ' , from : value . place , into : lvalue } ) ;
1487
1485
break ;
1488
1486
}
1489
1487
case 'StoreContext' : {
1490
1488
effects . push ( { kind : 'Mutate' , value : value . lvalue . place } ) ;
1491
1489
effects . push ( {
1492
- kind : 'Alias ' ,
1490
+ kind : 'Assign ' ,
1493
1491
from : value . value ,
1494
1492
into : value . lvalue . place ,
1495
1493
} ) ;
1496
- effects . push ( { kind : 'Alias ' , from : value . value , into : lvalue } ) ;
1494
+ effects . push ( { kind : 'Assign ' , from : value . value , into : lvalue } ) ;
1497
1495
break ;
1498
1496
}
1499
1497
case 'LoadLocal' : {
1500
- effects . push ( { kind : 'Alias ' , from : value . place , into : lvalue } ) ;
1498
+ effects . push ( { kind : 'Assign ' , from : value . place , into : lvalue } ) ;
1501
1499
break ;
1502
1500
}
1503
1501
case 'StoreLocal' : {
1504
1502
effects . push ( {
1505
- kind : 'Alias ' ,
1503
+ kind : 'Assign ' ,
1506
1504
from : value . value ,
1507
1505
into : value . lvalue . place ,
1508
1506
} ) ;
1509
- effects . push ( { kind : 'Alias ' , from : value . value , into : lvalue } ) ;
1507
+ effects . push ( { kind : 'Assign ' , from : value . value , into : lvalue } ) ;
1510
1508
break ;
1511
1509
}
1512
1510
case 'StoreGlobal' : {
@@ -1516,7 +1514,7 @@ function computeSignatureForInstruction(
1516
1514
} ) ;
1517
1515
}
1518
1516
case 'TypeCastExpression' : {
1519
- effects . push ( { kind : 'Alias ' , from : value . value , into : lvalue } ) ;
1517
+ effects . push ( { kind : 'Assign ' , from : value . value , into : lvalue } ) ;
1520
1518
break ;
1521
1519
}
1522
1520
case 'LoadGlobal' : {
@@ -1772,6 +1770,8 @@ function computeEffectsForSignature(
1772
1770
lvalue : Place ,
1773
1771
receiver : Place ,
1774
1772
args : Array < Place | SpreadPattern | Hole > ,
1773
+ // Used for signatures constructed dynamically which reference context variables
1774
+ context : Array < Place > = [ ] ,
1775
1775
) : Array < AliasingEffect > | null {
1776
1776
if (
1777
1777
// Not enough args
@@ -1816,6 +1816,15 @@ function computeEffectsForSignature(
1816
1816
}
1817
1817
}
1818
1818
1819
+ /*
1820
+ * Signatures constructed dynamically from function expressions will reference values
1821
+ * other than their receiver/args/etc. We populate the substitution table with these
1822
+ * values so that we can still exit for unpopulated substitutions
1823
+ */
1824
+ for ( const operand of context ) {
1825
+ substitutions . set ( operand . identifier . id , [ operand ] ) ;
1826
+ }
1827
+
1819
1828
const effects : Array < AliasingEffect > = [ ] ;
1820
1829
for ( const signatureTemporary of signature . temporaries ) {
1821
1830
const temp = createTemporaryPlace ( env , receiver . loc ) ;
@@ -1825,6 +1834,7 @@ function computeEffectsForSignature(
1825
1834
// Apply substitutions
1826
1835
for ( const effect of signature . effects ) {
1827
1836
switch ( effect . kind ) {
1837
+ case 'Assign' :
1828
1838
case 'ImmutableCapture' :
1829
1839
case 'Alias' :
1830
1840
case 'CreateFrom' :
@@ -2068,18 +2078,32 @@ export type AliasingEffect =
2068
2078
*/
2069
2079
| { kind : 'MutateTransitiveConditionally' ; value : Place }
2070
2080
/**
2071
- * Records indirect aliasing from flow from `from` to `into`. Local mutation (Mutate vs MutateTransitive)
2072
- * of `into` will *not* affect `from`.
2081
+ * Records information flow from `from` to `into` in cases where local mutation of the destination
2082
+ * will *not* mutate the source:
2083
+ *
2084
+ * - Capture a -> b and Mutate(b) X=> (does not imply) Mutate(a)
2085
+ * - Capture a -> b and MutateTransitive(b) => (does imply) Mutate(a)
2073
2086
*
2074
- * Example: `x[0] = y[1] `. Information from y (from) is aliased into x (into) , but there is not a
2075
- * direct aliasing of y as x .
2087
+ * Example: `array.push(item) `. Information from item is captured into array , but there is not a
2088
+ * direct aliasing, and local mutations of array will not modify item .
2076
2089
*/
2077
2090
| { kind : 'Capture' ; from : Place ; into : Place }
2078
2091
/**
2079
- * Records direct aliasing of `from` as `into`. Local mutation (Mutate vs MutateTransitive)
2080
- * of `into` *will* affect `from`.
2092
+ * Records information flow from `from` to `into` in cases where local mutation of the destination
2093
+ * *will* mutate the source:
2094
+ *
2095
+ * - Alias a -> b and Mutate(b) => (does imply) Mutate(a)
2096
+ * - Alias a -> b and MutateTransitive(b) => (does imply) Mutate(a)
2097
+ *
2098
+ * Example: `c = identity(a)`. We don't know what `identity()` returns so we can't use Assign.
2099
+ * But we have to assume that it _could_ be returning its input, such that a local mutation of
2100
+ * c could be mutating a.
2081
2101
*/
2082
2102
| { kind : 'Alias' ; from : Place ; into : Place }
2103
+ /**
2104
+ * Records direct assignment: `into = from`.
2105
+ */
2106
+ | { kind : 'Assign' ; from : Place ; into : Place }
2083
2107
/**
2084
2108
* Creates a value of the given type at the given place
2085
2109
*/
0 commit comments