@@ -74,6 +74,8 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
74
74
capturedEntryWrite ( bb , i , v )
75
75
) and
76
76
certain = true
77
+ or
78
+ capturedCallWrite ( _, bb , i , v ) and certain = false
77
79
}
78
80
79
81
predicate variableRead ( BasicBlock bb , int i , SourceVariable v , boolean certain ) {
@@ -99,6 +101,8 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
99
101
certain = false
100
102
)
101
103
or
104
+ capturedCallRead ( _, bb , i , v ) and certain = false
105
+ or
102
106
capturedExitRead ( bb , i , v ) and certain = false
103
107
}
104
108
}
@@ -145,6 +149,35 @@ private predicate variableReadActual(BasicBlock bb, int i, Variable v) {
145
149
)
146
150
}
147
151
152
+ /**
153
+ * Holds if captured variable `v` is written directly inside `scope`,
154
+ * or inside a (transitively) nested scope of `scope`.
155
+ */
156
+ pragma [ noinline]
157
+ private predicate hasCapturedWrite ( Variable v , Cfg:: CfgScope scope ) {
158
+ any ( VariableWriteAccess write | write .getVariable ( ) = v and scope = write .getEnclosingCallable * ( ) )
159
+ .isCapture ( )
160
+ }
161
+
162
+ /**
163
+ * Holds if `v` is read inside basic block `bb` at index `i`, which is in the
164
+ * immediate outer scope of `scope`.
165
+ */
166
+ pragma [ noinline]
167
+ private predicate variableReadActualInOuterScope (
168
+ BasicBlock bb , int i , Variable v , Cfg:: CfgScope scope
169
+ ) {
170
+ variableReadActual ( bb , i , v ) and bb .getScope ( ) = scope .getEnclosingCallable ( )
171
+ }
172
+
173
+ pragma [ noinline]
174
+ private predicate hasVariableReadWithCapturedWrite (
175
+ BasicBlock bb , int i , Variable v , Cfg:: CfgScope scope
176
+ ) {
177
+ hasCapturedWrite ( v , scope ) and
178
+ variableReadActualInOuterScope ( bb , i , v , scope )
179
+ }
180
+
148
181
private predicate adjacentDefReachesRead (
149
182
Definition def , BasicBlock bb1 , int i1 , BasicBlock bb2 , int i2
150
183
) {
@@ -223,6 +256,40 @@ private predicate readsCapturedVariable(BasicBlock bb, Variable v) {
223
256
getCapturedVariableAccess ( bb , v ) instanceof VariableReadAccess
224
257
}
225
258
259
+ /**
260
+ * Holds if captured variable `v` is read directly inside `scope`,
261
+ * or inside a (transitively) nested scope of `scope`.
262
+ */
263
+ pragma [ noinline]
264
+ private predicate hasCapturedRead ( Variable v , Cfg:: CfgScope scope ) {
265
+ any ( VariableReadAccess read | read .getVariable ( ) = v and scope = read .getEnclosingCallable * ( ) )
266
+ .isCapture ( )
267
+ }
268
+
269
+ /**
270
+ * Holds if `v` is written inside basic block `bb` at index `i`, which is in
271
+ * the immediate outer scope of `scope`.
272
+ */
273
+ pragma [ noinline]
274
+ private predicate variableWriteInOuterScope ( BasicBlock bb , int i , Variable v , Cfg:: CfgScope scope ) {
275
+ SsaInput:: variableWrite ( bb , i , v , _) and scope .getEnclosingCallable ( ) = bb .getScope ( )
276
+ }
277
+
278
+ /**
279
+ * Holds if the call `call` at index `i` in basic block `bb` may reach
280
+ * a callable that reads captured variable `v`.
281
+ */
282
+ private predicate capturedCallRead ( CallExprBase call , BasicBlock bb , int i , Variable v ) {
283
+ exists ( Cfg:: CfgScope scope |
284
+ hasCapturedRead ( v , scope ) and
285
+ (
286
+ variableWriteInOuterScope ( bb , any ( int j | j < i ) , v , scope ) or
287
+ variableWriteInOuterScope ( bb .getAPredecessor + ( ) , _, v , scope )
288
+ ) and
289
+ call = bb .getNode ( i ) .getAstNode ( )
290
+ )
291
+ }
292
+
226
293
/**
227
294
* Holds if a pseudo read of captured variable `v` should be inserted
228
295
* at index `i` in exit block `bb`.
@@ -245,6 +312,20 @@ private module Cached {
245
312
i = - 1
246
313
}
247
314
315
+ /**
316
+ * Holds if the call `call` at index `i` in basic block `bb` may reach a callable
317
+ * that writes captured variable `v`.
318
+ */
319
+ cached
320
+ predicate capturedCallWrite ( CallExprBase call , BasicBlock bb , int i , Variable v ) {
321
+ call = bb .getNode ( i ) .getAstNode ( ) and
322
+ exists ( Cfg:: CfgScope scope |
323
+ hasVariableReadWithCapturedWrite ( bb , any ( int j | j > i ) , v , scope )
324
+ or
325
+ hasVariableReadWithCapturedWrite ( bb .getASuccessor + ( ) , _, v , scope )
326
+ )
327
+ }
328
+
248
329
/**
249
330
* Holds if `v` is written at index `i` in basic block `bb`, and the corresponding
250
331
* AST write access is `write`.
0 commit comments