1
+ //! This module defines a more fine-grained analysis for `Local`s that are live due
2
+ //! to outstanding references or raw pointers than `MaybeBorrowedLocals`.
3
+ //!
4
+ //! The analysis consists of three steps:
5
+ //! 1. build a dependency graph that relates `Local`s based on their borrowing relationship.
6
+ //! As an example if we have something like this (in a simplified MIR representation):
7
+ //!
8
+ //! ```ignore(rust)
9
+ //! _4 = Bar {}
10
+ //! _5 = Ref(_4)
11
+ //! ```
12
+ //!
13
+ //! Then we add an edge from `_5` to `_4`.
14
+ //! 2. perform a liveness analysis for borrowed `Local`s.
15
+ //! Continuing our example from step 1, if we later have a use of `_5`, `_5` is
16
+ //! live at least from its definition to that use of it.
17
+ //! 3. Combine the two analyses from step 1 and 2. For any `Local` that corresponds
18
+ //! to a borrow (`_5` in our example), we want to keep the `Local` (`_4`), which is actually
19
+ //! borrowed through it, live over the range at which the borrow is live. Hence for any point
20
+ //! in that range we traverse our dependency graph and look for leaf nodes. In our example
21
+ //! we would find an edge from `_5` to `_4`, which is a leaf node and hence we keep `_4` live
22
+ //! over that range.
23
+ //!
24
+ //! There are some corner cases we need to look out for to make this analysis sound. Let's look
25
+ //! at each of the three steps in more detail and elaborate how these steps deal with these corner
26
+ //! cases.
27
+ //!
28
+ //! 1. Dependency Graph
29
+ //!
30
+ //! The `Node`s in the dependency graph include data values of type `NodeKind`. `NodeKind` has
31
+ //! three variants: `Local`, `Borrow` and `LocalWithRefs`.
32
+ //! * `NodeKind::Local` is used for `Local`s that are borrowed somewhere (`_4` in our example)
33
+ //! * `NodeKind::Borrow` is used for `Local`s that correspond to borrows (`_5` in our example) and
34
+ //! also `Local`s that result from re-borrows.
35
+ //! * `NodeKind::LocalWithRefs` is used for `Local`s that aren't themselves borrows, but contain
36
+ //! borrowed `Local`s. We want to keep these `Local`s live and also any of the references/pointers
37
+ //! they might contain. Let's look at an example:
38
+ //!
39
+ //! ```ignore(rust)
40
+ //! _4 = Bar {}
41
+ //! _5 = Ref(_4)
42
+ //! _6 = Aggregate(..)(move _5)
43
+ //! ...
44
+ //! _7 = (_6.0)
45
+ //! ```
46
+ //!
47
+ //! In this example `_6` would be given `NodeKind::LocalWithRefs` and our graph would look
48
+ //! as follows:
49
+ //!
50
+ //! `_7 (NodeKind::Borrow) -> `_6` (NodeKind::LocalWithRefs) -> `_5` (NodeKind::Borrow) -> `_4` (NodeKind::Local)
51
+ //!
52
+ //! In addition to keeping `_6` alive over the range of `_7` we also keep `_4` alive (leaf node).
53
+ //!
54
+ //! Additionally `NodeKind::LocalWithRefs` is also used for raw pointers that are cast to
55
+ //! `usize`:
56
+ //!
57
+ //! ```ignore(rust)
58
+ //! _4 = Bar {}
59
+ //! _5 = AddressOf(_4)
60
+ //! _6 = _5 as usize
61
+ //! _7 = Aggregate(..) (move _6)
62
+ //! _8 = (_7.0)
63
+ //! ```
64
+ //!
65
+ //! In this example our graph would have the following edges:
66
+ //! * `_5` (Borrow) -> `_4` (Local)
67
+ //! * `_6` (LocalWithRefs) -> `_5` (Borrow)
68
+ //! * `_7` (LocalWithRefs) -> `_6` (LocalWithRefs)
69
+ //! * `_8` (LocalWithRefs) -> `_7` (LocalWithRefs) (FIXME this one is currently not being done)
70
+ //!
71
+ //! We also have to be careful when dealing with `Terminator`s. Whenever we pass references,
72
+ //! pointers or `Local`s with `NodeKind::LocalWithRefs` (FIXME currently not done) to
73
+ //! a `TerminatorKind::Call` or `TerminatorKind::Yield` and the destination `Place` or resume place, resp.,
74
+ //! contains references/pointers or generic parameters we have to be careful and treat the
75
+ //! `Local`s corresponding to the `Place`s as `NodeKind::LocalWithRef`s.
76
+ //!
77
+ //! 2. Liveness analysis for borrows
78
+ //!
79
+ //! We perform a standard liveness analysis on any outstanding references, pointers or `Local`s
80
+ //! with `NodeKind::LocalWithRefs`. So we `gen` at any use site, which are either direct uses
81
+ //! of these `Local`s or projections that contain these `Local`s. So e.g.:
82
+ //!
83
+ //! ```ignore(rust)
84
+ //! 1. _3 = Foo {}
85
+ //! 2. _4 = Bar {}
86
+ //! 3. _5 = Ref(_3)
87
+ //! 4. _6 = Ref(_4)
88
+ //! 5. _7 = Aggregate(..)(move _5)
89
+ //! 6. _8 = Call(..)(move _6) (assume _8 contains no refs/ptrs or generic params)
90
+ //! 7. _9 = (_8.0)
91
+ //! 8. (_7.0) = _9
92
+ //! ```
93
+ //!
94
+ //! * `_5` is live from stmt 3 to stmt 5
95
+ //! * `_6` is live from stmt 4 to stmt 6
96
+ //! * `_7` is a `Local` of kind `LocalWithRef` so needs to be taken into account in the
97
+ //! analyis. It's live from stmt 5 to stmt 8
98
+ //!
99
+ //! 3. Determining which `Local`s are borrowed
100
+ //!
101
+ //! Let's use our last example again. The dependency graph for that example looks as follows:
102
+ //!
103
+ //! `_5` (Borrow) -> `_3` (Local)
104
+ //! `_6` (Borrow) -> `_4` (Local)
105
+ //! `_7` (LocalWithRef) -> `_5` (Borrow)
106
+ //! `_7` (LocalWithRef) -> `_9` (Local)
107
+ //!
108
+ //! So at each of those statements we have the following `Local`s that are live due to borrows:
109
+ //!
110
+ //! 1. {}
111
+ //! 2. {}
112
+ //! 3. {_3}
113
+ //! 4. {_3, _4}
114
+ //! 5. {_3, _4, _7}
115
+ //! 6. {_3, _4, _7}
116
+ //! 7. {_3, _7}
117
+ //! 8. {_3, _7}
118
+ //!
119
+
1
120
use super :: * ;
2
121
3
122
use crate :: framework:: { Analysis , Results , ResultsCursor } ;
@@ -14,16 +133,6 @@ use rustc_middle::ty::{self, Ty, TypeSuperVisitable, TypeVisitable};
14
133
use core:: ops:: ControlFlow ;
15
134
use either:: Either ;
16
135
17
- /// This module defines a more fine-grained analysis for `Local`s that are live due
18
- /// to outstanding references or raw pointers.
19
- /// The idea behind the analysis is that we first build a dependency graph between
20
- /// `Local`s corresponding to references or pointers and the `Local`s that are borrowed.
21
- /// This is done by the `BorrowDependencies` struct.
22
- /// As a second step we perform a liveness analysis for references and pointers, which is
23
- /// done by `LiveBorrows`.
24
- /// Finally we combine the results of the liveness analysis and the dependency graph to
25
- /// infer which borrowed locals need to be live at a given `Location`.
26
-
27
136
#[ derive( Copy , Clone , Debug ) ]
28
137
enum NodeKind {
29
138
// An node corresponding to the place of the borrowed place (`_4` in this case) in
@@ -237,7 +346,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
237
346
238
347
self . dep_graph . add_edge ( src_node_idx, node_idx, ( ) ) ;
239
348
}
240
- _ => self . super_rvalue ( rvalue, location) ,
349
+ _ => {
350
+ // FIXME Need to also create edges for `Local`s that correspond to `NodeKind::LocalWithRefs` here
351
+ self . super_rvalue ( rvalue, location)
352
+ }
241
353
}
242
354
}
243
355
@@ -272,7 +384,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
272
384
}
273
385
None => { }
274
386
} ,
275
- _ => { }
387
+ _ => {
388
+ // FIXME Need to also create edges for `Local`s that correspond to `NodeKind::LocalWithRefs` here
389
+ }
276
390
}
277
391
278
392
self . super_place ( place, context, location)
@@ -382,6 +496,7 @@ where
382
496
borrows_to_locals
383
497
}
384
498
499
+ // FIXME Account for cycles in the graph!
385
500
fn dfs_for_node (
386
501
node_idx : NodeIndex ,
387
502
borrows_to_locals : & mut FxHashMap < Local , Vec < Local > > ,
0 commit comments