@@ -391,16 +391,6 @@ module Flow<InputSig Input> implements OutputSig<Input> {
391391 msg = "ClosureExpr has no body" and not ce .hasBody ( _)
392392 }
393393
394- query predicate closureAliasMustBeLocal ( ClosureExpr ce , Expr access , string msg ) {
395- exists ( BasicBlock bb1 , BasicBlock bb2 |
396- ce .hasAliasedAccess ( access ) and
397- ce .hasCfgNode ( bb1 , _) and
398- access .hasCfgNode ( bb2 , _) and
399- bb1 .getEnclosingCallable ( ) != bb2 .getEnclosingCallable ( ) and
400- msg = "ClosureExpr has non-local alias - these are ignored"
401- )
402- }
403-
404394 private predicate astClosureParent ( Callable closure , Callable parent ) {
405395 exists ( ClosureExpr ce , BasicBlock bb |
406396 ce .hasBody ( closure ) and ce .hasCfgNode ( bb , _) and parent = bb .getEnclosingCallable ( )
@@ -440,7 +430,6 @@ module Flow<InputSig Input> implements OutputSig<Input> {
440430 n = strictcount ( VariableWrite vw | localWriteStep ( vw , msg ) ) or
441431 n = strictcount ( VariableRead vr | uniqueReadVariable ( vr , msg ) ) or
442432 n = strictcount ( ClosureExpr ce | closureMustHaveBody ( ce , msg ) ) or
443- n = strictcount ( ClosureExpr ce , Expr access | closureAliasMustBeLocal ( ce , access , msg ) ) or
444433 n = strictcount ( CapturedVariable v , Callable c | variableAccessAstNesting ( v , c , msg ) ) or
445434 n = strictcount ( Callable c | uniqueCallableLocation ( c , msg ) )
446435 }
@@ -518,10 +507,42 @@ module Flow<InputSig Input> implements OutputSig<Input> {
518507 }
519508
520509 /** Gets the enclosing callable of `ce`. */
521- private Callable closureExprGetCallable ( ClosureExpr ce ) {
510+ private Callable closureExprGetEnclosingCallable ( ClosureExpr ce ) {
522511 exists ( BasicBlock bb | ce .hasCfgNode ( bb , _) and result = bb .getEnclosingCallable ( ) )
523512 }
524513
514+ /** Gets the enclosing callable of `inner`. */
515+ pragma [ nomagic]
516+ private Callable callableGetEnclosingCallable ( Callable inner ) {
517+ exists ( ClosureExpr closure |
518+ closure .hasBody ( inner ) and
519+ result = closureExprGetEnclosingCallable ( closure )
520+ )
521+ }
522+
523+ /**
524+ * Gets a callable that contains a reference to `ce` into which `ce` could be inlined without
525+ * bringing any variables out of scope.
526+ *
527+ * If `ce` was to be inlined into that reference, the resulting callable
528+ * would become the enclosing callable, and thus capture the same variables as `ce`.
529+ * In some sense, we model captured aliases as if this inlining has happened.
530+ */
531+ private Callable closureExprGetAReferencingCallable ( ClosureExpr ce ) {
532+ exists ( Expr expr , BasicBlock bb |
533+ ce .hasAliasedAccess ( expr ) and
534+ expr .hasCfgNode ( bb , _) and
535+ result = bb .getEnclosingCallable ( ) and
536+ // The reference to `ce` is allowed to occur in a more deeply nested context
537+ closureExprGetEnclosingCallable ( ce ) = callableGetEnclosingCallable * ( result )
538+ )
539+ }
540+
541+ /** Gets a callable containing `ce` or one of its aliases. */
542+ private Callable closureExprGetCallable ( ClosureExpr ce ) {
543+ result = [ closureExprGetEnclosingCallable ( ce ) , closureExprGetAReferencingCallable ( ce ) ]
544+ }
545+
525546 /**
526547 * Holds if `v` is available in `c` through capture. This can either be due to
527548 * an explicit variable reference or through the construction of a closure
0 commit comments