@@ -217,7 +217,7 @@ export default function inferReferenceEffects(
217217 }
218218 queue ( fn . body . entry , initialState ) ;
219219
220- const functionEffects : Array < FunctionEffect > = fn . effects ?? [ ] ;
220+ const functionEffects : Set < FunctionEffect > = fn . effects ?? new Set ( ) ;
221221
222222 while ( queuedStates . size !== 0 ) {
223223 for ( const [ blockId , block ] of fn . body . blocks ) {
@@ -382,7 +382,7 @@ class InferenceState {
382382 place : Place ,
383383 effectKind : Effect ,
384384 reason : ValueReason ,
385- functionEffects : Array < FunctionEffect > ,
385+ functionEffects : Set < FunctionEffect > ,
386386 ) : void {
387387 const values = this . #variables. get ( place . identifier . id ) ;
388388 if ( values === undefined ) {
@@ -422,13 +422,13 @@ class InferenceState {
422422
423423 const functionEffect = this . reference ( place , effectKind , reason ) ;
424424 if ( functionEffect !== null ) {
425- functionEffects . push ( functionEffect ) ;
425+ functionEffects . add ( functionEffect ) ;
426426 }
427427 }
428428
429429 propogateEffect (
430430 value : InstructionValue ,
431- functionEffects : Array < FunctionEffect > ,
431+ functionEffects : Set < FunctionEffect > ,
432432 reason : ValueReason ,
433433 ) : void {
434434 if ( value . kind !== 'FunctionExpression' && value . kind !== 'ObjectMethod' ) {
@@ -443,7 +443,7 @@ class InferenceState {
443443 for ( const effect of effects ) {
444444 if ( effect . kind === 'GlobalMutation' || effect . kind === 'ReactMutation' ) {
445445 // Known effects are always propagated upwards
446- functionEffects . push ( effect ) ;
446+ functionEffects . add ( effect ) ;
447447 } else {
448448 /**
449449 * Contextual effects need to be replayed against the current inference
@@ -467,10 +467,10 @@ class InferenceState {
467467 if ( replayedEffect != null ) {
468468 if ( replayedEffect . kind === 'ContextMutation' ) {
469469 // Case 1, still a context value so propagate the original effect
470- functionEffects . push ( effect ) ;
470+ functionEffects . add ( effect ) ;
471471 } else {
472472 // Case 3, immutable value so propagate the more precise effect
473- functionEffects . push ( replayedEffect ) ;
473+ functionEffects . add ( replayedEffect ) ;
474474 }
475475 } // else case 2, local mutable value so this effect was fine
476476 }
@@ -525,7 +525,7 @@ class InferenceState {
525525 operand ,
526526 Effect . Freeze ,
527527 ValueReason . Other ,
528- [ ] ,
528+ new Set ( ) ,
529529 ) ;
530530 }
531531 }
@@ -981,7 +981,7 @@ function mergeAbstractValues(
981981 */
982982function inferBlock (
983983 env : Environment ,
984- functionEffects : Array < FunctionEffect > ,
984+ functionEffects : Set < FunctionEffect > ,
985985 state : InferenceState ,
986986 block : BasicBlock ,
987987) : void {
@@ -1167,18 +1167,21 @@ function inferBlock(
11671167 functionEffects ,
11681168 ) ;
11691169 } else {
1170- const propEffects : Array < FunctionEffect > = [ ] ;
1170+ const propEffects : Set < FunctionEffect > = new Set ( ) ;
11711171 state . referenceAndRecordEffects (
11721172 attr . place ,
11731173 Effect . Freeze ,
11741174 ValueReason . JsxCaptured ,
11751175 propEffects ,
11761176 ) ;
1177- functionEffects . push (
1178- ...propEffects . filter (
1179- propEffect => propEffect . kind !== 'GlobalMutation' ,
1180- ) ,
1181- ) ;
1177+
1178+ for ( const effect of propEffects ) {
1179+ if ( effect . kind === 'GlobalMutation' ) {
1180+ functionEffects . delete ( effect ) ;
1181+ } else {
1182+ functionEffects . add ( effect ) ;
1183+ }
1184+ }
11821185 }
11831186 }
11841187
@@ -1278,7 +1281,7 @@ function inferBlock(
12781281 operand ,
12791282 operand . effect === Effect . Unknown ? Effect . Read : operand . effect ,
12801283 ValueReason . Other ,
1281- [ ] ,
1284+ new Set ( ) ,
12821285 ) ;
12831286 hasMutableOperand ||= isMutableEffect ( operand . effect , operand . loc ) ;
12841287
@@ -1311,9 +1314,9 @@ function inferBlock(
13111314 value . kind === 'FunctionExpression' ) &&
13121315 value . loweredFunc . func . effects !== null
13131316 ) {
1314- instrValue . loweredFunc . func . effects ??= [ ] ;
1315- instrValue . loweredFunc . func . effects . push (
1316- ... value . loweredFunc . func . effects ,
1317+ instrValue . loweredFunc . func . effects ??= new Set ( ) ;
1318+ value . loweredFunc . func . effects . forEach ( e =>
1319+ instrValue . loweredFunc . func . effects ! . add ( e ) ,
13171320 ) ;
13181321 }
13191322 }
@@ -1357,7 +1360,7 @@ function inferBlock(
13571360 let hasCaptureArgument = false ;
13581361 let isUseEffect = isEffectHook ( instrValue . callee . identifier ) ;
13591362 for ( let i = 0 ; i < instrValue . args . length ; i ++ ) {
1360- const argumentEffects : Array < FunctionEffect > = [ ] ;
1363+ const argumentEffects : Set < FunctionEffect > = new Set ( ) ;
13611364 const arg = instrValue . args [ i ] ;
13621365 const place = arg . kind === 'Identifier' ? arg : arg . place ;
13631366 if ( effects !== null ) {
@@ -1379,12 +1382,11 @@ function inferBlock(
13791382 * Join the effects of the argument with the effects of the enclosing function,
13801383 * unless the we're detecting a global mutation inside a useEffect hook
13811384 */
1382- functionEffects . push (
1383- ...argumentEffects . filter (
1384- argEffect =>
1385- ! isUseEffect || i !== 0 || argEffect . kind !== 'GlobalMutation' ,
1386- ) ,
1387- ) ;
1385+ for ( const effect of argumentEffects ) {
1386+ if ( ! isUseEffect || i !== 0 || effect . kind !== 'GlobalMutation' ) {
1387+ functionEffects . add ( effect ) ;
1388+ }
1389+ }
13881390 hasCaptureArgument ||= place . effect === Effect . Capture ;
13891391 }
13901392 if ( signature !== null ) {
@@ -1482,7 +1484,7 @@ function inferBlock(
14821484 let hasCaptureArgument = false ;
14831485 let isUseEffect = isEffectHook ( instrValue . property . identifier ) ;
14841486 for ( let i = 0 ; i < instrValue . args . length ; i ++ ) {
1485- const argumentEffects : Array < FunctionEffect > = [ ] ;
1487+ const argumentEffects : Set < FunctionEffect > = new Set ( ) ;
14861488 const arg = instrValue . args [ i ] ;
14871489 const place = arg . kind === 'Identifier' ? arg : arg . place ;
14881490 if ( effects !== null ) {
@@ -1508,12 +1510,11 @@ function inferBlock(
15081510 * Join the effects of the argument with the effects of the enclosing function,
15091511 * unless the we're detecting a global mutation inside a useEffect hook
15101512 */
1511- functionEffects . push (
1512- ...argumentEffects . filter (
1513- argEffect =>
1514- ! isUseEffect || i !== 0 || argEffect . kind !== 'GlobalMutation' ,
1515- ) ,
1516- ) ;
1513+ for ( const effect of argumentEffects ) {
1514+ if ( ! isUseEffect || i !== 0 || effect . kind !== 'GlobalMutation' ) {
1515+ functionEffects . add ( effect ) ;
1516+ }
1517+ }
15171518 hasCaptureArgument ||= place . effect === Effect . Capture ;
15181519 }
15191520 if ( signature !== null ) {
@@ -1703,14 +1704,14 @@ function inferBlock(
17031704 val ,
17041705 Effect . Freeze ,
17051706 ValueReason . Other ,
1706- [ ] ,
1707+ new Set ( ) ,
17071708 ) ;
17081709 } else {
17091710 state . referenceAndRecordEffects (
17101711 val ,
17111712 Effect . Read ,
17121713 ValueReason . Other ,
1713- [ ] ,
1714+ new Set ( ) ,
17141715 ) ;
17151716 }
17161717 }
@@ -1735,7 +1736,7 @@ function inferBlock(
17351736 instrValue . place ,
17361737 effect ,
17371738 ValueReason . Other ,
1738- [ ] ,
1739+ new Set ( ) ,
17391740 ) ;
17401741 lvalue . effect = Effect . ConditionallyMutate ;
17411742 // direct aliasing: `a = b`;
@@ -1822,7 +1823,7 @@ function inferBlock(
18221823 instrValue . value ,
18231824 effect ,
18241825 ValueReason . Other ,
1825- [ ] ,
1826+ new Set ( ) ,
18261827 ) ;
18271828
18281829 const lvalue = instr . lvalue ;
@@ -1867,7 +1868,7 @@ function inferBlock(
18671868 const lvalue = instr . lvalue ;
18681869 lvalue . effect = Effect . Store ;
18691870
1870- functionEffects . push ( {
1871+ functionEffects . add ( {
18711872 kind : 'GlobalMutation' ,
18721873 error : {
18731874 reason :
0 commit comments