Skip to content

Commit 70b281f

Browse files
committed
fix module doc comment
1 parent 9fb6c16 commit 70b281f

File tree

1 file changed

+55
-42
lines changed

1 file changed

+55
-42
lines changed

compiler/rustc_mir_dataflow/src/impls/live_borrows.rs

+55-42
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! issues. Exposed raw pointers (i.e. those cast to `usize`) and function calls would make this simple
44
//! analysis unsound, so we have to handle them as follows:
55
//!
6+
//! ```text
67
//! * exposed pointers (i.e. a cast of a raw pointer to `usize`)
78
//! => These count towards the liveness of the `Local` that is behind the raw pointer, e.g. for
89
//! `_5 = _4 as usize` (where `_4 = AddressOf(_3)`), we keep `_3` alive on any use site of _5.
@@ -28,6 +29,7 @@
2829
//! Let `Locals2` be the set of `Local`s that need to be kept alive due to the borrows corresponding to `Op2`
2930
//! Then we need to start tracking all `Local`s in `Locals2` for the borrow corresponding to `Op1`, since the `Place`
3031
//! corresponding to `Op2` might be moved into `Op1` in then call.
32+
//! ```
3133
//!
3234
//! As an example for what this analysis does:
3335
//!
@@ -67,20 +69,19 @@
6769
//! yield 3; // live locals: [`Local4`]
6870
//!
6971
//! takes_and_returns_ref(bar3_ref); // live locals: [`Local4`]
70-
//! }
72+
//! }
7173
//! }
7274
//! ```
7375
//!
7476
//! Following is a description of the algorithm:
77+
//! ```text
7578
//! 1. build a dependency graph that relates `Local`s based on their borrowing relationships.
7679
//! As an example if we have something like this (in a simplified MIR representation):
7780
//!
78-
//! ```ignore
7981
//! _4 = Bar {}
8082
//! _5 = Ref(_4)
8183
//! ...
8284
//! _10 = f(_5)
83-
//! ```
8485
//!
8586
//! Then we add edges from `_5` to `_4` and `_4` to `_5` (for an explanation of why the edge
8687
//! from `_4` to `_5` is necessary see the comment in `handle_ravlue_or_ptr`) and from
@@ -95,6 +96,7 @@
9596
//! in that range we traverse our dependency graph and look for nodes that correspond to borrowed
9697
//! `Local`s ("leaf nodes"). In our example we would find an edge from `_5` to `_4`, which is a leaf
9798
//! node and hence we keep `_4` live over that range.
99+
//! ```
98100
//!
99101
//! There are some corner cases we need to look out for to make this analysis sound. Let's look
100102
//! at each of the three steps in more detail and elaborate how these steps deal with these corner
@@ -104,66 +106,67 @@
104106
//!
105107
//! The `Node`s in the dependency graph include data values of type `NodeKind`. `NodeKind` has
106108
//! three variants: `Local`, `Borrow` and `LocalWithRefs`.
109+
//! ```text
107110
//! * `NodeKind::Local` is used for `Local`s that are borrowed somewhere (`_4` in our example), but aren't
108111
//! themselves references or pointers.
109112
//! * `NodeKind::Borrow` is used for `Local`s that correspond to borrows (`_5` in our example). We equate
110113
//! re-borrows with the `Node` that corresponds to the original borrow.
111114
//! * `NodeKind::LocalWithRefs` is used for `Local`s that aren't themselves refs/ptrs, but contain
112115
//! `Local`s that correspond to refs/ptrs or other `Local`s with `Node`s of kind `NodeKind::LocalWithRef`s.
113116
//! `LocalWithRefs` is also used for exposed pointers.
114-
//! Let's look at an example:
117+
//! ```
115118
//!
116-
//! ```ignore
117-
//! _4 = Bar {}
118-
//! _5 = Ref(_4)
119-
//! _6 = Foo(..)(move _5)
120-
//! ...
121-
//! _7 = (_6.0)
122-
//! ```
119+
//! Let's look at an example:
123120
//!
124-
//! In this example `_6` would be given `NodeKind::LocalWithRefs` and our graph would look
125-
//! as follows:
121+
//! ```text
122+
//! _4 = Bar {}
123+
//! _5 = Ref(_4)
124+
//! _6 = Foo(..)(move _5)
125+
//! ...
126+
//! _7 = (_6.0)
126127
//!
127-
//! `_7` (NodeKind::Borrow) <-> `_6` (NodeKind::LocalWithRefs) <-> `_5` (NodeKind::Borrow) <-> `_4` (NodeKind::Local)
128+
//! In this example `_6` would be given `NodeKind::LocalWithRefs` and our graph would look
129+
//! as follows:
128130
//!
129-
//! On the one hand we need to treat `Local`s with `Node`s of kind `NodeKind::LocalWithRefs` similarly
130-
//! to how we treat `Local`s with `Node`s of kind `NodeKind::Local`, in the sense that if they are
131-
//! borrowed we want to keep them live over the live range of the borrow. But on the other hand we
132-
//! want to also treat them like `Local`s with `Node`s of kind `NodeKind::Borrow` as they ultimately
133-
//! could also contain references or pointers that refer to other `Local`s. So we want a
134-
//! path in the graph from a `NodeKind::LocalWithRef`s node to the `NodeKind::Local` nodes, whose borrows
135-
//! they might contain.
131+
//! `_7` (NodeKind::Borrow) <-> `_6` (NodeKind::LocalWithRefs) <-> `_5` (NodeKind::Borrow) <-> `_4` (NodeKind::Local)
136132
//!
137-
//! Additionally `NodeKind::LocalWithRefs` is also used for raw pointers that are cast to
138-
//! `usize`:
133+
//! On the one hand we need to treat `Local`s with `Node`s of kind `NodeKind::LocalWithRefs` similarly
134+
//! to how we treat `Local`s with `Node`s of kind `NodeKind::Local`, in the sense that if they are
135+
//! borrowed we want to keep them live over the live range of the borrow. But on the other hand we
136+
//! want to also treat them like `Local`s with `Node`s of kind `NodeKind::Borrow` as they ultimately
137+
//! could also contain references or pointers that refer to other `Local`s. So we want a
138+
//! path in the graph from a `NodeKind::LocalWithRef`s node to the `NodeKind::Local` nodes, whose borrows
139+
//! they might contain.
139140
//!
140-
//! ```ignore
141-
//! _4 = Bar {}
142-
//! _5 = AddressOf(_4)
143-
//! _6 = _5 as usize
144-
//! _7 = Aggregate(..) (move _6)
145-
//! _8 = (_7.0)
146-
//! ```
141+
//! Additionally `NodeKind::LocalWithRefs` is also used for raw pointers that are cast to
142+
//! `usize`:
147143
//!
148-
//! In this example our graph would have the following edges:
149-
//! * `_5` (Borrow) <-> `_4` (Local)
150-
//! * `_6` (LocalWithRefs) <-> `_5` (Borrow)
151-
//! * `_7` (LocalWithRefs) <-> `_6` (LocalWithRefs)
152-
//! * `_8` (LocalWithRefs) <-> `_7` (LocalWithRefs)
144+
//! _4 = Bar {}
145+
//! _5 = AddressOf(_4)
146+
//! _6 = _5 as usize
147+
//! _7 = Aggregate(..) (move _6)
148+
//! _8 = (_7.0)
153149
//!
154-
//! We also have to be careful about dealing with `Terminator`s. Whenever we pass references,
155-
//! pointers or `Local`s with `NodeKind::LocalWithRefs` to a `TerminatorKind::Call` or
156-
//! `TerminatorKind::Yield`, the destination `Place` or resume place, resp., might contain
157-
//! these references, pointers or `LocalWithRefs`, hence we have to be conservative
158-
//! and keep the `destination` `Local` and `resume_arg` `Local` live.
150+
//! In this example our graph would have the following edges:
151+
//! * `_5` (Borrow) <-> `_4` (Local)
152+
//! * `_6` (LocalWithRefs) <-> `_5` (Borrow)
153+
//! * `_7` (LocalWithRefs) <-> `_6` (LocalWithRefs)
154+
//! * `_8` (LocalWithRefs) <-> `_7` (LocalWithRefs)
155+
//!
156+
//! We also have to be careful about dealing with `Terminator`s. Whenever we pass references,
157+
//! pointers or `Local`s with `NodeKind::LocalWithRefs` to a `TerminatorKind::Call` or
158+
//! `TerminatorKind::Yield`, the destination `Place` or resume place, resp., might contain
159+
//! these references, pointers or `LocalWithRefs`, hence we have to be conservative
160+
//! and keep the `destination` `Local` and `resume_arg` `Local` live.
161+
//! ```
159162
//!
160163
//! 2. Liveness analysis for borrows
161164
//!
162165
//! We perform a standard liveness analysis on any outstanding references, pointers or `LocalWithRefs`
163166
//! So we `gen` at any use site, which are either direct uses of these `Local`s or projections that contain
164167
//! these `Local`s. So e.g.:
165168
//!
166-
//! ```ignore
169+
//! ```text
167170
//! 1. _3 = Foo {}
168171
//! 2. _4 = Bar {}
169172
//! 3. _5 = Ref(_3)
@@ -173,36 +176,41 @@
173176
//! 7. _9 = (_8.0)
174177
//! 8. _10 = const 5
175178
//! 9. (_7.0) = move _10
176-
//! ```
177179
//!
178180
//! * `_5` is live from stmt 3 to stmt 9
179181
//! * `_6` is live from stmt 4 to stmt 7
180182
//! * `_7` is a `Local` of kind `LocalWithRefs` so needs to be taken into account in the
181183
//! analyis. It's live from stmt 5 to stmt 9
182184
//! * `_8` is a `Local` of kind `LocalWithRefs`. It's live from 6. to 7.
183185
//! * `_9` is a `Local` of kind `LocalWithRefs`. It's live at 7.
186+
//! ```
184187
//!
185188
//! 3. Determining which `Local`s are borrowed
186189
//!
187190
//! Let's use our last example again. The dependency graph for that example looks as follows:
188191
//!
192+
//! ```text
189193
//! `_5` (Borrow) <-> `_3` (Local)
190194
//! `_6` (Borrow) <-> `_4` (Local)
191195
//! `_7` (LocalWithRef) <-> `_5` (Borrow)
192196
//! `_8` (LocalWithRef) -> `_6` (Borrow)
193197
//! `_9` (LocalWithRef) <-> `_8` (LocalWithRef)
194198
//! `_7` (LocalWithRef) <-> `_10` (Local)
199+
//! ```
195200
//!
196201
//! We then construct a strongly connected components graph from the dependency graph, yielding:
197202
//!
203+
//! ```text
198204
//! SCC1: [_3, _5, _7, _10]
199205
//! SCC2: [_4, _6, _8, _9]
206+
//! ```
200207
//!
201208
//! Now for each statement in the `Body` we check which refs/ptrs or `LocalWithRefs` are live at that statement
202209
//! and then perform a depth-first search in the scc graph, collecting all `Local`s that need to be kept alive
203210
//! (`Local`s that have `Node`s in the graph of either `NodeKind::Local` or `NodeKind::LocalWithRefs`).
204211
//! So at each of those statements we have the following `Local`s that are live due to borrows:
205212
//!
213+
//! ```text
206214
//! 1. {}
207215
//! 2. {}
208216
//! 3. {_3}
@@ -212,12 +220,14 @@
212220
//! 7. {_3, _4, _7, _8, _9}
213221
//! 8. {_3, _7}
214222
//! 9. {_3, _7, _10}
223+
//! ```
215224
//!
216225
//! Ensuring soundness in all cases requires us to be more conservative (i.e. keeping more `Local`s alive) than necessary
217226
//! in most situations. To eliminate all the unnecessary `Local`s we use the fact that the analysis performed by
218227
//! `MaybeBorrowedLocals` functions as an upper bound for which `Local`s need to be kept alive. Hence we take the intersection
219228
//! of the two analyses at each statement. The results of `MaybeBorrowedLocals` for our example are:
220229
//!
230+
//! ```text
221231
//! 1. {}
222232
//! 2. {}
223233
//! 3. {_3}
@@ -227,9 +237,11 @@
227237
//! 7. {_3, _4,}
228238
//! 8. {_3, _4}
229239
//! 9. {_3, _4}
240+
//! ```
230241
//!
231242
//! Taking the intersection hence yields:
232243
//!
244+
//! ```text
233245
//! 1. {}
234246
//! 2. {}
235247
//! 3. {_3}
@@ -239,6 +251,7 @@
239251
//! 7. {_3, _4}
240252
//! 8. {_3}
241253
//! 9. {_3}
254+
//! ```
242255
243256
use super::*;
244257

0 commit comments

Comments
 (0)