]>
Commit | Line | Data |
---|---|---|
e74abb32 | 1 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
dfeec247 | 2 | use rustc_index::bit_set::HybridBitSet; |
a2a8927a | 3 | use rustc_index::interval::IntervalSet; |
74b04a01 | 4 | use rustc_infer::infer::canonical::QueryRegionConstraints; |
f9f354fc | 5 | use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location}; |
ba9703b0 XL |
6 | use rustc_middle::ty::{Ty, TypeFoldable}; |
7 | use rustc_trait_selection::traits::query::dropck_outlives::DropckOutlivesResult; | |
8 | use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives; | |
94222f64 | 9 | use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; |
b7449926 | 10 | use std::rc::Rc; |
b7449926 | 11 | |
c295e0f8 XL |
12 | use rustc_mir_dataflow::impls::MaybeInitializedPlaces; |
13 | use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex}; | |
14 | use rustc_mir_dataflow::ResultsCursor; | |
60c5eb7d | 15 | |
c295e0f8 | 16 | use crate::{ |
60c5eb7d XL |
17 | region_infer::values::{self, PointIndex, RegionValueElements}, |
18 | type_check::liveness::local_use_map::LocalUseMap, | |
19 | type_check::liveness::polonius, | |
20 | type_check::NormalizeLocation, | |
21 | type_check::TypeChecker, | |
22 | }; | |
23 | ||
b7449926 XL |
24 | /// This is the heart of the liveness computation. For each variable X |
25 | /// that requires a liveness computation, it walks over all the uses | |
26 | /// of X and does a reverse depth-first search ("trace") through the | |
27 | /// MIR. This search stops when we find a definition of that variable. | |
28 | /// The points visited in this search is the USE-LIVE set for the variable; | |
29 | /// of those points is added to all the regions that appear in the variable's | |
30 | /// type. | |
31 | /// | |
32 | /// We then also walks through each *drop* of those variables and does | |
33 | /// another search, stopping when we reach a use or definition. This | |
34 | /// is the DROP-LIVE set of points. Each of the points in the | |
35 | /// DROP-LIVE set are to the liveness sets for regions found in the | |
36 | /// `dropck_outlives` result of the variable's type (in particular, | |
37 | /// this respects `#[may_dangle]` annotations). | |
a2a8927a | 38 | pub(super) fn trace<'mir, 'tcx>( |
dc9dc135 | 39 | typeck: &mut TypeChecker<'_, 'tcx>, |
f9f354fc | 40 | body: &Body<'tcx>, |
b7449926 | 41 | elements: &Rc<RegionValueElements>, |
74b04a01 | 42 | flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, |
b7449926 | 43 | move_data: &MoveData<'tcx>, |
532ac7d7 | 44 | live_locals: Vec<Local>, |
e74abb32 | 45 | polonius_drop_used: Option<Vec<(Local, Location)>>, |
b7449926 XL |
46 | ) { |
47 | debug!("trace()"); | |
48 | ||
dc9dc135 | 49 | let local_use_map = &LocalUseMap::build(&live_locals, elements, body); |
b7449926 XL |
50 | |
51 | let cx = LivenessContext { | |
52 | typeck, | |
dc9dc135 | 53 | body, |
b7449926 XL |
54 | flow_inits, |
55 | elements, | |
56 | local_use_map, | |
57 | move_data, | |
b7449926 XL |
58 | drop_data: FxHashMap::default(), |
59 | }; | |
60 | ||
e74abb32 XL |
61 | let mut results = LivenessResults::new(cx); |
62 | ||
63 | if let Some(drop_used) = polonius_drop_used { | |
64 | results.add_extra_drop_facts(drop_used, live_locals.iter().copied().collect()) | |
65 | } | |
66 | ||
67 | results.compute_for_all_locals(live_locals); | |
b7449926 XL |
68 | } |
69 | ||
70 | /// Contextual state for the type-liveness generator. | |
dc9dc135 | 71 | struct LivenessContext<'me, 'typeck, 'flow, 'tcx> { |
b7449926 | 72 | /// Current type-checker, giving us our inference context etc. |
dc9dc135 | 73 | typeck: &'me mut TypeChecker<'typeck, 'tcx>, |
b7449926 XL |
74 | |
75 | /// Defines the `PointIndex` mapping | |
76 | elements: &'me RegionValueElements, | |
77 | ||
78 | /// MIR we are analyzing. | |
f9f354fc | 79 | body: &'me Body<'tcx>, |
b7449926 XL |
80 | |
81 | /// Mapping to/from the various indices used for initialization tracking. | |
82 | move_data: &'me MoveData<'tcx>, | |
83 | ||
84 | /// Cache for the results of `dropck_outlives` query. | |
85 | drop_data: FxHashMap<Ty<'tcx>, DropData<'tcx>>, | |
86 | ||
87 | /// Results of dataflow tracking which variables (and paths) have been | |
88 | /// initialized. | |
74b04a01 | 89 | flow_inits: &'me mut ResultsCursor<'flow, 'tcx, MaybeInitializedPlaces<'flow, 'tcx>>, |
b7449926 XL |
90 | |
91 | /// Index indicating where each variable is assigned, used, or | |
92 | /// dropped. | |
532ac7d7 | 93 | local_use_map: &'me LocalUseMap, |
b7449926 XL |
94 | } |
95 | ||
96 | struct DropData<'tcx> { | |
97 | dropck_result: DropckOutlivesResult<'tcx>, | |
dc9dc135 | 98 | region_constraint_data: Option<Rc<QueryRegionConstraints<'tcx>>>, |
b7449926 XL |
99 | } |
100 | ||
dc9dc135 XL |
101 | struct LivenessResults<'me, 'typeck, 'flow, 'tcx> { |
102 | cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>, | |
b7449926 XL |
103 | |
104 | /// Set of points that define the current local. | |
0bf4aa26 | 105 | defs: HybridBitSet<PointIndex>, |
b7449926 XL |
106 | |
107 | /// Points where the current variable is "use live" -- meaning | |
108 | /// that there is a future "full use" that may use its value. | |
a2a8927a | 109 | use_live_at: IntervalSet<PointIndex>, |
b7449926 XL |
110 | |
111 | /// Points where the current variable is "drop live" -- meaning | |
112 | /// that there is no future "full use" that may use its value, but | |
113 | /// there is a future drop. | |
a2a8927a | 114 | drop_live_at: IntervalSet<PointIndex>, |
b7449926 XL |
115 | |
116 | /// Locations where drops may occur. | |
117 | drop_locations: Vec<Location>, | |
118 | ||
119 | /// Stack used when doing (reverse) DFS. | |
120 | stack: Vec<PointIndex>, | |
121 | } | |
122 | ||
a2a8927a | 123 | impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { |
dc9dc135 | 124 | fn new(cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>) -> Self { |
b7449926 XL |
125 | let num_points = cx.elements.num_points(); |
126 | LivenessResults { | |
127 | cx, | |
0bf4aa26 | 128 | defs: HybridBitSet::new_empty(num_points), |
a2a8927a XL |
129 | use_live_at: IntervalSet::new(num_points), |
130 | drop_live_at: IntervalSet::new(num_points), | |
b7449926 XL |
131 | drop_locations: vec![], |
132 | stack: vec![], | |
133 | } | |
134 | } | |
135 | ||
532ac7d7 XL |
136 | fn compute_for_all_locals(&mut self, live_locals: Vec<Local>) { |
137 | for local in live_locals { | |
b7449926 | 138 | self.reset_local_state(); |
532ac7d7 XL |
139 | self.add_defs_for(local); |
140 | self.compute_use_live_points_for(local); | |
141 | self.compute_drop_live_points_for(local); | |
b7449926 | 142 | |
dc9dc135 | 143 | let local_ty = self.cx.body.local_decls[local].ty; |
b7449926 XL |
144 | |
145 | if !self.use_live_at.is_empty() { | |
146 | self.cx.add_use_live_facts_for(local_ty, &self.use_live_at); | |
147 | } | |
148 | ||
149 | if !self.drop_live_at.is_empty() { | |
150 | self.cx.add_drop_live_facts_for( | |
151 | local, | |
152 | local_ty, | |
153 | &self.drop_locations, | |
154 | &self.drop_live_at, | |
155 | ); | |
156 | } | |
157 | } | |
158 | } | |
159 | ||
e74abb32 XL |
160 | /// Add extra drop facts needed for Polonius. |
161 | /// | |
162 | /// Add facts for all locals with free regions, since regions may outlive | |
163 | /// the function body only at certain nodes in the CFG. | |
164 | fn add_extra_drop_facts( | |
165 | &mut self, | |
166 | drop_used: Vec<(Local, Location)>, | |
167 | live_locals: FxHashSet<Local>, | |
168 | ) { | |
a2a8927a | 169 | let locations = IntervalSet::new(self.cx.elements.num_points()); |
e74abb32 XL |
170 | |
171 | for (local, location) in drop_used { | |
172 | if !live_locals.contains(&local) { | |
173 | let local_ty = self.cx.body.local_decls[local].ty; | |
94222f64 | 174 | if local_ty.has_free_regions(self.cx.typeck.tcx()) { |
dfeec247 | 175 | self.cx.add_drop_live_facts_for(local, local_ty, &[location], &locations); |
e74abb32 XL |
176 | } |
177 | } | |
178 | } | |
179 | } | |
180 | ||
b7449926 XL |
181 | /// Clear the value of fields that are "per local variable". |
182 | fn reset_local_state(&mut self) { | |
183 | self.defs.clear(); | |
184 | self.use_live_at.clear(); | |
185 | self.drop_live_at.clear(); | |
186 | self.drop_locations.clear(); | |
187 | assert!(self.stack.is_empty()); | |
188 | } | |
189 | ||
190 | /// Adds the definitions of `local` into `self.defs`. | |
532ac7d7 XL |
191 | fn add_defs_for(&mut self, local: Local) { |
192 | for def in self.cx.local_use_map.defs(local) { | |
b7449926 XL |
193 | debug!("- defined at {:?}", def); |
194 | self.defs.insert(def); | |
195 | } | |
196 | } | |
197 | ||
9fa01778 | 198 | /// Computes all points where local is "use live" -- meaning its |
b7449926 | 199 | /// current value may be used later (except by a drop). This is |
532ac7d7 | 200 | /// done by walking backwards from each use of `local` until we |
b7449926 XL |
201 | /// find a `def` of local. |
202 | /// | |
532ac7d7 XL |
203 | /// Requires `add_defs_for(local)` to have been executed. |
204 | fn compute_use_live_points_for(&mut self, local: Local) { | |
205 | debug!("compute_use_live_points_for(local={:?})", local); | |
b7449926 | 206 | |
532ac7d7 | 207 | self.stack.extend(self.cx.local_use_map.uses(local)); |
b7449926 | 208 | while let Some(p) = self.stack.pop() { |
3c0e092e XL |
209 | // We are live in this block from the closest to us of: |
210 | // | |
211 | // * Inclusively, the block start | |
212 | // * Exclusively, the previous definition (if it's in this block) | |
213 | // * Exclusively, the previous live_at setting (an optimization) | |
214 | let block_start = self.cx.elements.to_block_start(p); | |
215 | let previous_defs = self.defs.last_set_in(block_start..=p); | |
216 | let previous_live_at = self.use_live_at.last_set_in(block_start..=p); | |
217 | ||
218 | let exclusive_start = match (previous_defs, previous_live_at) { | |
219 | (Some(a), Some(b)) => Some(std::cmp::max(a, b)), | |
220 | (Some(a), None) | (None, Some(a)) => Some(a), | |
221 | (None, None) => None, | |
222 | }; | |
223 | ||
224 | if let Some(exclusive) = exclusive_start { | |
225 | self.use_live_at.insert_range(exclusive + 1..=p); | |
226 | ||
227 | // If we have a bound after the start of the block, we should | |
228 | // not add the predecessors for this block. | |
b7449926 | 229 | continue; |
3c0e092e XL |
230 | } else { |
231 | // Add all the elements of this block. | |
232 | self.use_live_at.insert_range(block_start..=p); | |
233 | ||
234 | // Then add the predecessors for this block, which are the | |
235 | // terminators of predecessor basic blocks. Push those onto the | |
236 | // stack so that the next iteration(s) will process them. | |
237 | ||
238 | let block = self.cx.elements.to_location(block_start).block; | |
239 | self.stack.extend( | |
240 | self.cx.body.predecessors()[block] | |
241 | .iter() | |
242 | .map(|&pred_bb| self.cx.body.terminator_loc(pred_bb)) | |
243 | .map(|pred_loc| self.cx.elements.point_from_location(pred_loc)), | |
244 | ); | |
b7449926 XL |
245 | } |
246 | } | |
247 | } | |
248 | ||
9fa01778 | 249 | /// Computes all points where local is "drop live" -- meaning its |
b7449926 XL |
250 | /// current value may be dropped later (but not used). This is |
251 | /// done by iterating over the drops of `local` where `local` (or | |
252 | /// some subpart of `local`) is initialized. For each such drop, | |
253 | /// we walk backwards until we find a point where `local` is | |
254 | /// either defined or use-live. | |
255 | /// | |
256 | /// Requires `compute_use_live_points_for` and `add_defs_for` to | |
257 | /// have been executed. | |
532ac7d7 XL |
258 | fn compute_drop_live_points_for(&mut self, local: Local) { |
259 | debug!("compute_drop_live_points_for(local={:?})", local); | |
b7449926 | 260 | |
b7449926 XL |
261 | let mpi = self.cx.move_data.rev_lookup.find_local(local); |
262 | debug!("compute_drop_live_points_for: mpi = {:?}", mpi); | |
263 | ||
264 | // Find the drops where `local` is initialized. | |
532ac7d7 | 265 | for drop_point in self.cx.local_use_map.drops(local) { |
b7449926 | 266 | let location = self.cx.elements.to_location(drop_point); |
dc9dc135 | 267 | debug_assert_eq!(self.cx.body.terminator_loc(location.block), location,); |
b7449926 XL |
268 | |
269 | if self.cx.initialized_at_terminator(location.block, mpi) { | |
270 | if self.drop_live_at.insert(drop_point) { | |
271 | self.drop_locations.push(location); | |
272 | self.stack.push(drop_point); | |
273 | } | |
274 | } | |
275 | } | |
276 | ||
416331ca | 277 | debug!("compute_drop_live_points_for: drop_locations={:?}", self.drop_locations); |
b7449926 XL |
278 | |
279 | // Reverse DFS. But for drops, we do it a bit differently. | |
280 | // The stack only ever stores *terminators of blocks*. Within | |
281 | // a block, we walk back the statements in an inner loop. | |
60c5eb7d | 282 | while let Some(term_point) = self.stack.pop() { |
b7449926 XL |
283 | self.compute_drop_live_points_for_block(mpi, term_point); |
284 | } | |
285 | } | |
286 | ||
287 | /// Executes one iteration of the drop-live analysis loop. | |
288 | /// | |
289 | /// The parameter `mpi` is the `MovePathIndex` of the local variable | |
290 | /// we are currently analyzing. | |
291 | /// | |
292 | /// The point `term_point` represents some terminator in the MIR, | |
293 | /// where the local `mpi` is drop-live on entry to that terminator. | |
294 | /// | |
295 | /// This method adds all drop-live points within the block and -- | |
296 | /// where applicable -- pushes the terminators of preceding blocks | |
297 | /// onto `self.stack`. | |
298 | fn compute_drop_live_points_for_block(&mut self, mpi: MovePathIndex, term_point: PointIndex) { | |
299 | debug!( | |
300 | "compute_drop_live_points_for_block(mpi={:?}, term_point={:?})", | |
301 | self.cx.move_data.move_paths[mpi].place, | |
302 | self.cx.elements.to_location(term_point), | |
303 | ); | |
304 | ||
305 | // We are only invoked with terminators where `mpi` is | |
306 | // drop-live on entry. | |
307 | debug_assert!(self.drop_live_at.contains(term_point)); | |
308 | ||
309 | // Otherwise, scan backwards through the statements in the | |
310 | // block. One of them may be either a definition or use | |
311 | // live point. | |
312 | let term_location = self.cx.elements.to_location(term_point); | |
416331ca | 313 | debug_assert_eq!(self.cx.body.terminator_loc(term_location.block), term_location,); |
b7449926 XL |
314 | let block = term_location.block; |
315 | let entry_point = self.cx.elements.entry_point(term_location.block); | |
316 | for p in (entry_point..term_point).rev() { | |
416331ca | 317 | debug!("compute_drop_live_points_for_block: p = {:?}", self.cx.elements.to_location(p)); |
b7449926 XL |
318 | |
319 | if self.defs.contains(p) { | |
320 | debug!("compute_drop_live_points_for_block: def site"); | |
321 | return; | |
322 | } | |
323 | ||
324 | if self.use_live_at.contains(p) { | |
325 | debug!("compute_drop_live_points_for_block: use-live at {:?}", p); | |
326 | return; | |
327 | } | |
328 | ||
329 | if !self.drop_live_at.insert(p) { | |
330 | debug!("compute_drop_live_points_for_block: already drop-live"); | |
331 | return; | |
332 | } | |
333 | } | |
334 | ||
60c5eb7d | 335 | let body = self.cx.body; |
f9f354fc | 336 | for &pred_block in body.predecessors()[block].iter() { |
416331ca | 337 | debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,); |
b7449926 XL |
338 | |
339 | // Check whether the variable is (at least partially) | |
340 | // initialized at the exit of this predecessor. If so, we | |
341 | // want to enqueue it on our list. If not, go check the | |
342 | // next block. | |
343 | // | |
344 | // Note that we only need to check whether `live_local` | |
345 | // became de-initialized at basic block boundaries. If it | |
346 | // were to become de-initialized within the block, that | |
347 | // would have been a "use-live" transition in the earlier | |
348 | // loop, and we'd have returned already. | |
349 | // | |
350 | // NB. It's possible that the pred-block ends in a call | |
351 | // which stores to the variable; in that case, the | |
352 | // variable may be uninitialized "at exit" because this | |
353 | // call only considers the *unconditional effects* of the | |
354 | // terminator. *But*, in that case, the terminator is also | |
355 | // a *definition* of the variable, in which case we want | |
356 | // to stop the search anyhow. (But see Note 1 below.) | |
357 | if !self.cx.initialized_at_exit(pred_block, mpi) { | |
358 | debug!("compute_drop_live_points_for_block: not initialized"); | |
359 | continue; | |
360 | } | |
361 | ||
dc9dc135 | 362 | let pred_term_loc = self.cx.body.terminator_loc(pred_block); |
b7449926 XL |
363 | let pred_term_point = self.cx.elements.point_from_location(pred_term_loc); |
364 | ||
365 | // If the terminator of this predecessor either *assigns* | |
366 | // our value or is a "normal use", then stop. | |
367 | if self.defs.contains(pred_term_point) { | |
416331ca | 368 | debug!("compute_drop_live_points_for_block: defined at {:?}", pred_term_loc); |
b7449926 XL |
369 | continue; |
370 | } | |
371 | ||
372 | if self.use_live_at.contains(pred_term_point) { | |
416331ca | 373 | debug!("compute_drop_live_points_for_block: use-live at {:?}", pred_term_loc); |
b7449926 XL |
374 | continue; |
375 | } | |
376 | ||
377 | // Otherwise, we are drop-live on entry to the terminator, | |
378 | // so walk it. | |
379 | if self.drop_live_at.insert(pred_term_point) { | |
380 | debug!("compute_drop_live_points_for_block: pushed to stack"); | |
381 | self.stack.push(pred_term_point); | |
382 | } | |
383 | } | |
384 | ||
385 | // Note 1. There is a weird scenario that you might imagine | |
386 | // being problematic here, but which actually cannot happen. | |
387 | // The problem would be if we had a variable that *is* initialized | |
388 | // (but dead) on entry to the terminator, and where the current value | |
389 | // will be dropped in the case of unwind. In that case, we ought to | |
390 | // consider `X` to be drop-live in between the last use and call. | |
391 | // Here is the example: | |
392 | // | |
393 | // ``` | |
394 | // BB0 { | |
395 | // X = ... | |
396 | // use(X); // last use | |
397 | // ... // <-- X ought to be drop-live here | |
398 | // X = call() goto BB1 unwind BB2 | |
399 | // } | |
400 | // | |
401 | // BB1 { | |
402 | // DROP(X) | |
403 | // } | |
404 | // | |
405 | // BB2 { | |
406 | // DROP(X) | |
407 | // } | |
408 | // ``` | |
409 | // | |
410 | // However, the current code would, when walking back from BB2, | |
411 | // simply stop and never explore BB0. This seems bad! But it turns | |
412 | // out this code is flawed anyway -- note that the existing value of | |
413 | // `X` would leak in the case where unwinding did *not* occur. | |
414 | // | |
415 | // What we *actually* generate is a store to a temporary | |
416 | // for the call (`TMP = call()...`) and then a | |
417 | // `DropAndReplace` to swap that with `X` | |
418 | // (`DropAndReplace` has very particular semantics). | |
419 | } | |
420 | } | |
421 | ||
a2a8927a | 422 | impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { |
74b04a01 XL |
423 | /// Returns `true` if the local variable (or some part of it) is initialized at the current |
424 | /// cursor position. Callers should call one of the `seek` methods immediately before to point | |
425 | /// the cursor to the desired location. | |
426 | fn initialized_at_curr_loc(&self, mpi: MovePathIndex) -> bool { | |
427 | let state = self.flow_inits.get(); | |
428 | if state.contains(mpi) { | |
429 | return true; | |
430 | } | |
431 | ||
432 | let move_paths = &self.flow_inits.analysis().move_data().move_paths; | |
433 | move_paths[mpi].find_descendant(&move_paths, |mpi| state.contains(mpi)).is_some() | |
434 | } | |
435 | ||
9fa01778 | 436 | /// Returns `true` if the local variable (or some part of it) is initialized in |
b7449926 XL |
437 | /// the terminator of `block`. We need to check this to determine if a |
438 | /// DROP of some local variable will have an effect -- note that | |
439 | /// drops, as they may unwind, are always terminators. | |
440 | fn initialized_at_terminator(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool { | |
f9f354fc | 441 | self.flow_inits.seek_before_primary_effect(self.body.terminator_loc(block)); |
74b04a01 | 442 | self.initialized_at_curr_loc(mpi) |
b7449926 XL |
443 | } |
444 | ||
9fa01778 | 445 | /// Returns `true` if the path `mpi` (or some part of it) is initialized at |
b7449926 XL |
446 | /// the exit of `block`. |
447 | /// | |
448 | /// **Warning:** Does not account for the result of `Call` | |
449 | /// instructions. | |
450 | fn initialized_at_exit(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool { | |
f9f354fc | 451 | self.flow_inits.seek_after_primary_effect(self.body.terminator_loc(block)); |
74b04a01 | 452 | self.initialized_at_curr_loc(mpi) |
b7449926 XL |
453 | } |
454 | ||
9fa01778 | 455 | /// Stores the result that all regions in `value` are live for the |
b7449926 XL |
456 | /// points `live_at`. |
457 | fn add_use_live_facts_for( | |
458 | &mut self, | |
459 | value: impl TypeFoldable<'tcx>, | |
a2a8927a | 460 | live_at: &IntervalSet<PointIndex>, |
b7449926 XL |
461 | ) { |
462 | debug!("add_use_live_facts_for(value={:?})", value); | |
463 | ||
e1599b0c | 464 | Self::make_all_regions_live(self.elements, &mut self.typeck, value, live_at) |
b7449926 XL |
465 | } |
466 | ||
467 | /// Some variable with type `live_ty` is "drop live" at `location` | |
468 | /// -- i.e., it may be dropped later. This means that *some* of | |
469 | /// the regions in its type must be live at `location`. The | |
470 | /// precise set will depend on the dropck constraints, and in | |
471 | /// particular this takes `#[may_dangle]` into account. | |
472 | fn add_drop_live_facts_for( | |
473 | &mut self, | |
474 | dropped_local: Local, | |
475 | dropped_ty: Ty<'tcx>, | |
476 | drop_locations: &[Location], | |
a2a8927a | 477 | live_at: &IntervalSet<PointIndex>, |
b7449926 XL |
478 | ) { |
479 | debug!( | |
480 | "add_drop_live_constraint(\ | |
481 | dropped_local={:?}, \ | |
482 | dropped_ty={:?}, \ | |
483 | drop_locations={:?}, \ | |
484 | live_at={:?})", | |
485 | dropped_local, | |
486 | dropped_ty, | |
487 | drop_locations, | |
488 | values::location_set_str(self.elements, live_at.iter()), | |
489 | ); | |
490 | ||
491 | let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({ | |
492 | let typeck = &mut self.typeck; | |
493 | move || Self::compute_drop_data(typeck, dropped_ty) | |
494 | }); | |
495 | ||
496 | if let Some(data) = &drop_data.region_constraint_data { | |
497 | for &drop_location in drop_locations { | |
416331ca XL |
498 | self.typeck.push_region_constraints( |
499 | drop_location.to_locations(), | |
500 | ConstraintCategory::Boring, | |
501 | data, | |
502 | ); | |
b7449926 XL |
503 | } |
504 | } | |
505 | ||
506 | drop_data.dropck_result.report_overflows( | |
507 | self.typeck.infcx.tcx, | |
dc9dc135 | 508 | self.body.source_info(*drop_locations.first().unwrap()).span, |
b7449926 XL |
509 | dropped_ty, |
510 | ); | |
511 | ||
512 | // All things in the `outlives` array may be touched by | |
513 | // the destructor and must be live at this point. | |
514 | for &kind in &drop_data.dropck_result.kinds { | |
e1599b0c | 515 | Self::make_all_regions_live(self.elements, &mut self.typeck, kind, live_at); |
416331ca | 516 | |
74b04a01 | 517 | polonius::add_drop_of_var_derefs_origin(&mut self.typeck, dropped_local, &kind); |
b7449926 XL |
518 | } |
519 | } | |
520 | ||
521 | fn make_all_regions_live( | |
522 | elements: &RegionValueElements, | |
dc9dc135 | 523 | typeck: &mut TypeChecker<'_, 'tcx>, |
b7449926 | 524 | value: impl TypeFoldable<'tcx>, |
a2a8927a | 525 | live_at: &IntervalSet<PointIndex>, |
b7449926 XL |
526 | ) { |
527 | debug!("make_all_regions_live(value={:?})", value); | |
528 | debug!( | |
529 | "make_all_regions_live: live_at={}", | |
530 | values::location_set_str(elements, live_at.iter()), | |
531 | ); | |
532 | ||
533 | let tcx = typeck.tcx(); | |
534 | tcx.for_each_free_region(&value, |live_region| { | |
416331ca XL |
535 | let live_region_vid = |
536 | typeck.borrowck_context.universal_regions.to_region_vid(live_region); | |
537 | typeck | |
538 | .borrowck_context | |
b7449926 XL |
539 | .constraints |
540 | .liveness_constraints | |
541 | .add_elements(live_region_vid, live_at); | |
b7449926 XL |
542 | }); |
543 | } | |
544 | ||
545 | fn compute_drop_data( | |
dc9dc135 | 546 | typeck: &mut TypeChecker<'_, 'tcx>, |
b7449926 XL |
547 | dropped_ty: Ty<'tcx>, |
548 | ) -> DropData<'tcx> { | |
549 | debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,); | |
550 | ||
551 | let param_env = typeck.param_env; | |
94222f64 | 552 | let TypeOpOutput { output, constraints, .. } = |
416331ca XL |
553 | param_env.and(DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx).unwrap(); |
554 | ||
94222f64 | 555 | DropData { dropck_result: output, region_constraint_data: constraints } |
b7449926 XL |
556 | } |
557 | } |