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+
1120use super :: * ;
2121
3122use crate :: framework:: { Analysis , Results , ResultsCursor } ;
@@ -14,16 +133,6 @@ use rustc_middle::ty::{self, Ty, TypeSuperVisitable, TypeVisitable};
14133use core:: ops:: ControlFlow ;
15134use either:: Either ;
16135
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-
27136#[ derive( Copy , Clone , Debug ) ]
28137enum NodeKind {
29138 // 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> {
237346
238347 self . dep_graph . add_edge ( src_node_idx, node_idx, ( ) ) ;
239348 }
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+ }
241353 }
242354 }
243355
@@ -272,7 +384,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
272384 }
273385 None => { }
274386 } ,
275- _ => { }
387+ _ => {
388+ // FIXME Need to also create edges for `Local`s that correspond to `NodeKind::LocalWithRefs` here
389+ }
276390 }
277391
278392 self . super_place ( place, context, location)
@@ -382,6 +496,7 @@ where
382496 borrows_to_locals
383497 }
384498
499+ // FIXME Account for cycles in the graph!
385500 fn dfs_for_node (
386501 node_idx : NodeIndex ,
387502 borrows_to_locals : & mut FxHashMap < Local , Vec < Local > > ,
0 commit comments