@@ -49,24 +49,20 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
49
49
/**
50
50
* A variable amenable to SSA construction.
51
51
*
52
- * All immutable variables are amenable. Mutable variables are restricted
53
- * to those that are not captured by closures, and are not borrowed
54
- * (either explicitly using `& mut`, or (potentially) implicit as borrowed
55
- * receivers in a method call).
52
+ * All immutable variables are amenable. Mutable variables are restricted to
53
+ * those that are not borrowed (either explicitly using `& mut`, or
54
+ * (potentially) implicit as borrowed receivers in a method call).
56
55
*/
57
56
class SourceVariable extends Variable {
58
57
SourceVariable ( ) {
59
- this .isImmutable ( )
60
- or
61
- this .isMutable ( ) and
62
- not this .isCaptured ( ) and
63
- forall ( VariableAccess va | va = this .getAnAccess ( ) |
64
- va instanceof VariableReadAccess and
58
+ this .isMutable ( )
59
+ implies
60
+ not exists ( VariableAccess va | va = this .getAnAccess ( ) |
61
+ va = any ( RefExpr re | re .isMut ( ) ) .getExpr ( )
62
+ or
65
63
// receivers can be borrowed implicitly, cf.
66
64
// https://doc.rust-lang.org/reference/expressions/method-call-expr.html
67
- not va = any ( MethodCallExpr mce ) .getReceiver ( )
68
- or
69
- variableWrite ( va , this )
65
+ va = any ( MethodCallExpr mce ) .getReceiver ( )
70
66
)
71
67
}
72
68
}
@@ -78,6 +74,8 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
78
74
capturedEntryWrite ( bb , i , v )
79
75
) and
80
76
certain = true
77
+ or
78
+ capturedCallWrite ( _, bb , i , v ) and certain = false
81
79
}
82
80
83
81
predicate variableRead ( BasicBlock bb , int i , SourceVariable v , boolean certain ) {
@@ -102,6 +100,10 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
102
100
v = va .getVariable ( ) and
103
101
certain = false
104
102
)
103
+ or
104
+ capturedCallRead ( _, bb , i , v ) and certain = false
105
+ or
106
+ capturedExitRead ( bb , i , v ) and certain = false
105
107
}
106
108
}
107
109
@@ -147,6 +149,35 @@ private predicate variableReadActual(BasicBlock bb, int i, Variable v) {
147
149
)
148
150
}
149
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
+
150
181
private predicate adjacentDefReachesRead (
151
182
Definition def , BasicBlock bb1 , int i1 , BasicBlock bb2 , int i2
152
183
) {
@@ -207,16 +238,81 @@ private predicate lastRefSkipUncertainReadsExt(DefinitionExt def, BasicBlock bb,
207
238
)
208
239
}
209
240
210
- /** Holds if `bb` contains a captured access to variable `v`. */
241
+ private VariableAccess getACapturedVariableAccess ( BasicBlock bb , Variable v ) {
242
+ result = bb .getANode ( ) .getAstNode ( ) and
243
+ result .isCapture ( ) and
244
+ result .getVariable ( ) = v
245
+ }
246
+
247
+ /** Holds if `bb` contains a captured write to variable `v`. */
248
+ pragma [ noinline]
249
+ private predicate writesCapturedVariable ( BasicBlock bb , Variable v ) {
250
+ getACapturedVariableAccess ( bb , v ) instanceof VariableWriteAccess
251
+ }
252
+
253
+ /** Holds if `bb` contains a captured read to variable `v`. */
211
254
pragma [ nomagic]
212
- private predicate hasCapturedVariableAccess ( BasicBlock bb , Variable v ) {
213
- exists ( VariableAccess read |
214
- read = bb .getANode ( ) .getAstNode ( ) and
215
- read .isCapture ( ) and
216
- read .getVariable ( ) = v
255
+ private predicate readsCapturedVariable ( BasicBlock bb , Variable v ) {
256
+ getACapturedVariableAccess ( bb , v ) instanceof VariableReadAccess
257
+ }
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 ( )
217
290
)
218
291
}
219
292
293
+ /**
294
+ * Holds if the call `call` at index `i` in basic block `bb` may reach a callable
295
+ * that writes captured variable `v`.
296
+ */
297
+ predicate capturedCallWrite ( CallExprBase call , BasicBlock bb , int i , Variable v ) {
298
+ call = bb .getNode ( i ) .getAstNode ( ) and
299
+ exists ( Cfg:: CfgScope scope |
300
+ hasVariableReadWithCapturedWrite ( bb , any ( int j | j > i ) , v , scope )
301
+ or
302
+ hasVariableReadWithCapturedWrite ( bb .getASuccessor + ( ) , _, v , scope )
303
+ )
304
+ }
305
+
306
+ /**
307
+ * Holds if a pseudo read of captured variable `v` should be inserted
308
+ * at index `i` in exit block `bb`.
309
+ */
310
+ private predicate capturedExitRead ( AnnotatedExitBasicBlock bb , int i , Variable v ) {
311
+ bb .isNormal ( ) and
312
+ writesCapturedVariable ( bb .getAPredecessor * ( ) , v ) and
313
+ i = bb .length ( )
314
+ }
315
+
220
316
cached
221
317
private module Cached {
222
318
/**
@@ -225,7 +321,7 @@ private module Cached {
225
321
*/
226
322
cached
227
323
predicate capturedEntryWrite ( EntryBasicBlock bb , int i , Variable v ) {
228
- hasCapturedVariableAccess ( bb .getASuccessor * ( ) , v ) and
324
+ readsCapturedVariable ( bb .getASuccessor * ( ) , v ) and
229
325
i = - 1
230
326
}
231
327
0 commit comments