@@ -49,24 +49,20 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
4949 /**
5050 * A variable amenable to SSA construction.
5151 *
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).
5655 */
5756 class SourceVariable extends Variable {
5857 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
6563 // receivers can be borrowed implicitly, cf.
6664 // 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 ( )
7066 )
7167 }
7268 }
@@ -78,6 +74,8 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
7874 capturedEntryWrite ( bb , i , v )
7975 ) and
8076 certain = true
77+ or
78+ capturedCallWrite ( _, bb , i , v ) and certain = false
8179 }
8280
8381 predicate variableRead ( BasicBlock bb , int i , SourceVariable v , boolean certain ) {
@@ -102,6 +100,10 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
102100 v = va .getVariable ( ) and
103101 certain = false
104102 )
103+ or
104+ capturedCallRead ( _, bb , i , v ) and certain = false
105+ or
106+ capturedExitRead ( bb , i , v ) and certain = false
105107 }
106108}
107109
@@ -147,6 +149,35 @@ private predicate variableReadActual(BasicBlock bb, int i, Variable v) {
147149 )
148150}
149151
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+
150181private predicate adjacentDefReachesRead (
151182 Definition def , BasicBlock bb1 , int i1 , BasicBlock bb2 , int i2
152183) {
@@ -207,16 +238,81 @@ private predicate lastRefSkipUncertainReadsExt(DefinitionExt def, BasicBlock bb,
207238 )
208239}
209240
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`. */
211254pragma [ 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 ( )
217290 )
218291}
219292
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+
220316cached
221317private module Cached {
222318 /**
@@ -225,7 +321,7 @@ private module Cached {
225321 */
226322 cached
227323 predicate capturedEntryWrite ( EntryBasicBlock bb , int i , Variable v ) {
228- hasCapturedVariableAccess ( bb .getASuccessor * ( ) , v ) and
324+ readsCapturedVariable ( bb .getASuccessor * ( ) , v ) and
229325 i = - 1
230326 }
231327
0 commit comments