]>
Commit | Line | Data |
---|---|---|
9fa01778 XL |
1 | use std::collections::VecDeque; |
2 | ||
3 | use crate::borrow_check::borrow_set::BorrowData; | |
4 | use crate::borrow_check::error_reporting::UseSpans; | |
5 | use crate::borrow_check::nll::region_infer::{Cause, RegionName}; | |
6 | use crate::borrow_check::nll::ConstraintDescription; | |
48663c56 | 7 | use crate::borrow_check::{MirBorrowckCtxt, WriteKind}; |
0bf4aa26 | 8 | use rustc::mir::{ |
dc9dc135 | 9 | CastKind, ConstraintCategory, FakeReadCause, Local, Location, Body, Operand, Place, PlaceBase, |
416331ca | 10 | Rvalue, Statement, StatementKind, TerminatorKind, |
0bf4aa26 | 11 | }; |
9fa01778 | 12 | use rustc::ty::{self, TyCtxt}; |
48663c56 | 13 | use rustc::ty::adjustment::{PointerCast}; |
9fa01778 | 14 | use rustc_data_structures::fx::FxHashSet; |
ff7c6d11 | 15 | use rustc_errors::DiagnosticBuilder; |
0bf4aa26 | 16 | use syntax_pos::Span; |
8faf50e0 XL |
17 | |
18 | mod find_use; | |
ff7c6d11 | 19 | |
9fa01778 | 20 | pub(in crate::borrow_check) enum BorrowExplanation { |
0bf4aa26 XL |
21 | UsedLater(LaterUseKind, Span), |
22 | UsedLaterInLoop(LaterUseKind, Span), | |
23 | UsedLaterWhenDropped { | |
24 | drop_loc: Location, | |
25 | dropped_local: Local, | |
26 | should_note_order: bool, | |
b7449926 | 27 | }, |
0bf4aa26 XL |
28 | MustBeValidFor { |
29 | category: ConstraintCategory, | |
30 | from_closure: bool, | |
31 | span: Span, | |
32 | region_name: RegionName, | |
33 | opt_place_desc: Option<String>, | |
b7449926 | 34 | }, |
0bf4aa26 XL |
35 | Unexplained, |
36 | } | |
37 | ||
38 | #[derive(Clone, Copy)] | |
9fa01778 | 39 | pub(in crate::borrow_check) enum LaterUseKind { |
0bf4aa26 XL |
40 | TraitCapture, |
41 | ClosureCapture, | |
42 | Call, | |
43 | FakeLetRead, | |
44 | Other, | |
45 | } | |
46 | ||
47 | impl BorrowExplanation { | |
9fa01778 | 48 | pub(in crate::borrow_check) fn is_explained(&self) -> bool { |
0731742a XL |
49 | match self { |
50 | BorrowExplanation::Unexplained => false, | |
51 | _ => true, | |
52 | } | |
53 | } | |
dc9dc135 | 54 | pub(in crate::borrow_check) fn add_explanation_to_diagnostic<'tcx>( |
0bf4aa26 | 55 | &self, |
dc9dc135 XL |
56 | tcx: TyCtxt<'tcx>, |
57 | body: &Body<'tcx>, | |
0bf4aa26 XL |
58 | err: &mut DiagnosticBuilder<'_>, |
59 | borrow_desc: &str, | |
532ac7d7 | 60 | borrow_span: Option<Span>, |
0bf4aa26 XL |
61 | ) { |
62 | match *self { | |
63 | BorrowExplanation::UsedLater(later_use_kind, var_or_use_span) => { | |
64 | let message = match later_use_kind { | |
532ac7d7 XL |
65 | LaterUseKind::TraitCapture => "captured here by trait object", |
66 | LaterUseKind::ClosureCapture => "captured here by closure", | |
67 | LaterUseKind::Call => "used by call", | |
68 | LaterUseKind::FakeLetRead => "stored here", | |
69 | LaterUseKind::Other => "used here", | |
0bf4aa26 | 70 | }; |
532ac7d7 XL |
71 | if !borrow_span.map(|sp| sp.overlaps(var_or_use_span)).unwrap_or(false) { |
72 | err.span_label( | |
73 | var_or_use_span, | |
74 | format!("{}borrow later {}", borrow_desc, message), | |
75 | ); | |
76 | } | |
9fa01778 | 77 | } |
0bf4aa26 XL |
78 | BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span) => { |
79 | let message = match later_use_kind { | |
9fa01778 XL |
80 | LaterUseKind::TraitCapture => { |
81 | "borrow captured here by trait object, in later iteration of loop" | |
82 | } | |
83 | LaterUseKind::ClosureCapture => { | |
84 | "borrow captured here by closure, in later iteration of loop" | |
85 | } | |
86 | LaterUseKind::Call => "borrow used by call, in later iteration of loop", | |
0bf4aa26 XL |
87 | LaterUseKind::FakeLetRead => "borrow later stored here", |
88 | LaterUseKind::Other => "borrow used here, in later iteration of loop", | |
89 | }; | |
90 | err.span_label(var_or_use_span, format!("{}{}", borrow_desc, message)); | |
9fa01778 XL |
91 | } |
92 | BorrowExplanation::UsedLaterWhenDropped { | |
93 | drop_loc, | |
94 | dropped_local, | |
95 | should_note_order, | |
96 | } => { | |
dc9dc135 | 97 | let local_decl = &body.local_decls[dropped_local]; |
0bf4aa26 XL |
98 | let (dtor_desc, type_desc) = match local_decl.ty.sty { |
99 | // If type is an ADT that implements Drop, then | |
100 | // simplify output by reporting just the ADT name. | |
9fa01778 XL |
101 | ty::Adt(adt, _substs) if adt.has_dtor(tcx) && !adt.is_box() => ( |
102 | "`Drop` code", | |
532ac7d7 | 103 | format!("type `{}`", tcx.def_path_str(adt.did)), |
9fa01778 | 104 | ), |
0bf4aa26 XL |
105 | |
106 | // Otherwise, just report the whole type (and use | |
107 | // the intentionally fuzzy phrase "destructor") | |
9fa01778 XL |
108 | ty::Closure(..) => ("destructor", "closure".to_owned()), |
109 | ty::Generator(..) => ("destructor", "generator".to_owned()), | |
0bf4aa26 XL |
110 | |
111 | _ => ("destructor", format!("type `{}`", local_decl.ty)), | |
112 | }; | |
113 | ||
114 | match local_decl.name { | |
dc9dc135 | 115 | Some(local_name) if !local_decl.from_compiler_desugaring() => { |
9fa01778 XL |
116 | let message = format!( |
117 | "{B}borrow might be used here, when `{LOC}` is dropped \ | |
118 | and runs the {DTOR} for {TYPE}", | |
119 | B = borrow_desc, | |
120 | LOC = local_name, | |
121 | TYPE = type_desc, | |
122 | DTOR = dtor_desc | |
123 | ); | |
dc9dc135 | 124 | err.span_label(body.source_info(drop_loc).span, message); |
0bf4aa26 XL |
125 | |
126 | if should_note_order { | |
127 | err.note( | |
128 | "values in a scope are dropped \ | |
129 | in the opposite order they are defined", | |
130 | ); | |
131 | } | |
132 | } | |
dc9dc135 | 133 | _ => { |
9fa01778 XL |
134 | err.span_label( |
135 | local_decl.source_info.span, | |
136 | format!( | |
137 | "a temporary with access to the {B}borrow \ | |
138 | is created here ...", | |
139 | B = borrow_desc | |
140 | ), | |
141 | ); | |
142 | let message = format!( | |
143 | "... and the {B}borrow might be used here, \ | |
144 | when that temporary is dropped \ | |
145 | and runs the {DTOR} for {TYPE}", | |
146 | B = borrow_desc, | |
147 | TYPE = type_desc, | |
148 | DTOR = dtor_desc | |
149 | ); | |
dc9dc135 | 150 | err.span_label(body.source_info(drop_loc).span, message); |
0bf4aa26 XL |
151 | |
152 | if let Some(info) = &local_decl.is_block_tail { | |
153 | // FIXME: use span_suggestion instead, highlighting the | |
154 | // whole block tail expression. | |
155 | let msg = if info.tail_result_is_ignored { | |
156 | "The temporary is part of an expression at the end of a block. \ | |
157 | Consider adding semicolon after the expression so its temporaries \ | |
158 | are dropped sooner, before the local variables declared by the \ | |
159 | block are dropped." | |
160 | } else { | |
161 | "The temporary is part of an expression at the end of a block. \ | |
162 | Consider forcing this temporary to be dropped sooner, before \ | |
163 | the block's local variables are dropped. \ | |
164 | For example, you could save the expression's value in a new \ | |
165 | local variable `x` and then make `x` be the expression \ | |
166 | at the end of the block." | |
167 | }; | |
168 | ||
169 | err.note(msg); | |
170 | } | |
171 | } | |
172 | } | |
9fa01778 | 173 | } |
0bf4aa26 XL |
174 | BorrowExplanation::MustBeValidFor { |
175 | category, | |
176 | span, | |
177 | ref region_name, | |
178 | ref opt_place_desc, | |
179 | from_closure: _, | |
180 | } => { | |
181 | region_name.highlight_region_name(err); | |
182 | ||
183 | if let Some(desc) = opt_place_desc { | |
9fa01778 XL |
184 | err.span_label( |
185 | span, | |
186 | format!( | |
187 | "{}requires that `{}` is borrowed for `{}`", | |
188 | category.description(), | |
189 | desc, | |
190 | region_name, | |
191 | ), | |
192 | ); | |
0bf4aa26 | 193 | } else { |
9fa01778 XL |
194 | err.span_label( |
195 | span, | |
196 | format!( | |
197 | "{}requires that {}borrow lasts for `{}`", | |
198 | category.description(), | |
199 | borrow_desc, | |
200 | region_name, | |
201 | ), | |
202 | ); | |
0bf4aa26 | 203 | }; |
9fa01778 XL |
204 | } |
205 | _ => {} | |
0bf4aa26 XL |
206 | } |
207 | } | |
b7449926 XL |
208 | } |
209 | ||
dc9dc135 | 210 | impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { |
0bf4aa26 | 211 | /// Returns structured explanation for *why* the borrow contains the |
48663c56 | 212 | /// point from `location`. This is key for the "3-point errors" |
0531ce1d XL |
213 | /// [described in the NLL RFC][d]. |
214 | /// | |
94b46f34 XL |
215 | /// # Parameters |
216 | /// | |
217 | /// - `borrow`: the borrow in question | |
48663c56 | 218 | /// - `location`: where the borrow occurs |
94b46f34 XL |
219 | /// - `kind_place`: if Some, this describes the statement that triggered the error. |
220 | /// - first half is the kind of write, if any, being performed | |
221 | /// - second half is the place being accessed | |
94b46f34 | 222 | /// |
0531ce1d | 223 | /// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points |
9fa01778 | 224 | pub(in crate::borrow_check) fn explain_why_borrow_contains_point( |
b7449926 | 225 | &self, |
48663c56 | 226 | location: Location, |
0531ce1d | 227 | borrow: &BorrowData<'tcx>, |
94b46f34 | 228 | kind_place: Option<(WriteKind, &Place<'tcx>)>, |
0bf4aa26 | 229 | ) -> BorrowExplanation { |
8faf50e0 | 230 | debug!( |
48663c56 XL |
231 | "explain_why_borrow_contains_point(location={:?}, borrow={:?}, kind_place={:?})", |
232 | location, borrow, kind_place | |
8faf50e0 XL |
233 | ); |
234 | ||
235 | let regioncx = &self.nonlexical_regioncx; | |
dc9dc135 | 236 | let body = self.body; |
0bf4aa26 | 237 | let tcx = self.infcx.tcx; |
ff7c6d11 | 238 | |
a1dfa0c6 | 239 | let borrow_region_vid = borrow.region; |
8faf50e0 XL |
240 | debug!( |
241 | "explain_why_borrow_contains_point: borrow_region_vid={:?}", | |
242 | borrow_region_vid | |
243 | ); | |
94b46f34 | 244 | |
48663c56 | 245 | let region_sub = regioncx.find_sub_region_live_at(borrow_region_vid, location); |
8faf50e0 XL |
246 | debug!( |
247 | "explain_why_borrow_contains_point: region_sub={:?}", | |
248 | region_sub | |
249 | ); | |
94b46f34 | 250 | |
dc9dc135 | 251 | match find_use::find(body, regioncx, tcx, region_sub, location) { |
0bf4aa26 | 252 | Some(Cause::LiveVar(local, location)) => { |
dc9dc135 | 253 | let span = body.source_info(location).span; |
9fa01778 | 254 | let spans = self |
416331ca | 255 | .move_spans(Place::from(local).as_ref(), location) |
b7449926 | 256 | .or_else(|| self.borrow_spans(span, location)); |
0bf4aa26 | 257 | |
48663c56 | 258 | let borrow_location = location; |
9fa01778 | 259 | if self.is_use_in_later_iteration_of_loop(borrow_location, location) { |
0bf4aa26 XL |
260 | let later_use = self.later_use_kind(borrow, spans, location); |
261 | BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1) | |
b7449926 | 262 | } else { |
0bf4aa26 XL |
263 | // Check if the location represents a `FakeRead`, and adapt the error |
264 | // message to the `FakeReadCause` it is from: in particular, | |
265 | // the ones inserted in optimized `let var = <expr>` patterns. | |
266 | let later_use = self.later_use_kind(borrow, spans, location); | |
267 | BorrowExplanation::UsedLater(later_use.0, later_use.1) | |
268 | } | |
b7449926 | 269 | } |
8faf50e0 | 270 | |
9fa01778 XL |
271 | Some(Cause::DropVar(local, location)) => { |
272 | let mut should_note_order = false; | |
dc9dc135 | 273 | if body.local_decls[local].name.is_some() { |
9fa01778 | 274 | if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place { |
416331ca XL |
275 | if let Place { |
276 | base: PlaceBase::Local(borrowed_local), | |
277 | projection: None, | |
278 | } = place { | |
dc9dc135 | 279 | if body.local_decls[*borrowed_local].name.is_some() |
9fa01778 XL |
280 | && local != *borrowed_local |
281 | { | |
282 | should_note_order = true; | |
283 | } | |
284 | } | |
285 | } | |
286 | } | |
287 | ||
288 | BorrowExplanation::UsedLaterWhenDropped { | |
289 | drop_loc: location, | |
290 | dropped_local: local, | |
291 | should_note_order, | |
292 | } | |
b7449926 | 293 | } |
0bf4aa26 | 294 | |
9fa01778 XL |
295 | None => { |
296 | if let Some(region) = regioncx.to_error_region_vid(borrow_region_vid) { | |
297 | let (category, from_closure, span, region_name) = | |
298 | self.nonlexical_regioncx.free_region_constraint_info( | |
dc9dc135 | 299 | self.body, |
48663c56 | 300 | &self.upvars, |
9fa01778 XL |
301 | self.mir_def_id, |
302 | self.infcx, | |
303 | borrow_region_vid, | |
304 | region, | |
305 | ); | |
306 | if let Some(region_name) = region_name { | |
416331ca XL |
307 | let opt_place_desc = |
308 | self.describe_place(borrow.borrowed_place.as_ref()); | |
9fa01778 XL |
309 | BorrowExplanation::MustBeValidFor { |
310 | category, | |
311 | from_closure, | |
312 | span, | |
313 | region_name, | |
314 | opt_place_desc, | |
315 | } | |
316 | } else { | |
48663c56 XL |
317 | debug!("explain_why_borrow_contains_point: \ |
318 | Could not generate a region name"); | |
9fa01778 | 319 | BorrowExplanation::Unexplained |
0731742a XL |
320 | } |
321 | } else { | |
48663c56 XL |
322 | debug!("explain_why_borrow_contains_point: \ |
323 | Could not generate an error region vid"); | |
0731742a | 324 | BorrowExplanation::Unexplained |
0bf4aa26 | 325 | } |
b7449926 | 326 | } |
b7449926 XL |
327 | } |
328 | } | |
8faf50e0 | 329 | |
9fa01778 XL |
330 | /// true if `borrow_location` can reach `use_location` by going through a loop and |
331 | /// `use_location` is also inside of that loop | |
332 | fn is_use_in_later_iteration_of_loop( | |
b7449926 XL |
333 | &self, |
334 | borrow_location: Location, | |
9fa01778 | 335 | use_location: Location, |
b7449926 | 336 | ) -> bool { |
9fa01778 XL |
337 | let back_edge = self.reach_through_backedge(borrow_location, use_location); |
338 | back_edge.map_or(false, |back_edge| { | |
339 | self.can_reach_head_of_loop(use_location, back_edge) | |
340 | }) | |
341 | } | |
b7449926 | 342 | |
9fa01778 XL |
343 | /// Returns the outmost back edge if `from` location can reach `to` location passing through |
344 | /// that back edge | |
345 | fn reach_through_backedge(&self, from: Location, to: Location) -> Option<Location> { | |
346 | let mut visited_locations = FxHashSet::default(); | |
347 | let mut pending_locations = VecDeque::new(); | |
348 | visited_locations.insert(from); | |
349 | pending_locations.push_back(from); | |
350 | debug!("reach_through_backedge: from={:?} to={:?}", from, to,); | |
351 | ||
352 | let mut outmost_back_edge = None; | |
353 | while let Some(location) = pending_locations.pop_front() { | |
354 | debug!( | |
355 | "reach_through_backedge: location={:?} outmost_back_edge={:?} | |
356 | pending_locations={:?} visited_locations={:?}", | |
357 | location, outmost_back_edge, pending_locations, visited_locations | |
358 | ); | |
359 | ||
360 | if location == to && outmost_back_edge.is_some() { | |
361 | // We've managed to reach the use location | |
362 | debug!("reach_through_backedge: found!"); | |
363 | return outmost_back_edge; | |
364 | } | |
b7449926 | 365 | |
dc9dc135 | 366 | let block = &self.body.basic_blocks()[location.block]; |
9fa01778 XL |
367 | |
368 | if location.statement_index < block.statements.len() { | |
369 | let successor = location.successor_within_block(); | |
370 | if visited_locations.insert(successor) { | |
371 | pending_locations.push_back(successor); | |
ff7c6d11 | 372 | } |
b7449926 | 373 | } else { |
9fa01778 XL |
374 | pending_locations.extend( |
375 | block | |
376 | .terminator() | |
377 | .successors() | |
378 | .map(|bb| Location { | |
379 | statement_index: 0, | |
380 | block: *bb, | |
381 | }) | |
382 | .filter(|s| visited_locations.insert(*s)) | |
383 | .map(|s| { | |
384 | if self.is_back_edge(location, s) { | |
385 | match outmost_back_edge { | |
386 | None => { | |
387 | outmost_back_edge = Some(location); | |
388 | } | |
389 | ||
390 | Some(back_edge) | |
391 | if location.dominates(back_edge, &self.dominators) => | |
392 | { | |
393 | outmost_back_edge = Some(location); | |
394 | } | |
395 | ||
396 | Some(_) => {} | |
397 | } | |
398 | } | |
399 | ||
400 | s | |
401 | }), | |
402 | ); | |
ff7c6d11 | 403 | } |
9fa01778 XL |
404 | } |
405 | ||
406 | None | |
407 | } | |
408 | ||
409 | /// true if `from` location can reach `loop_head` location and `loop_head` dominates all the | |
410 | /// intermediate nodes | |
411 | fn can_reach_head_of_loop(&self, from: Location, loop_head: Location) -> bool { | |
412 | self.find_loop_head_dfs(from, loop_head, &mut FxHashSet::default()) | |
413 | } | |
414 | ||
415 | fn find_loop_head_dfs( | |
416 | &self, | |
417 | from: Location, | |
418 | loop_head: Location, | |
419 | visited_locations: &mut FxHashSet<Location>, | |
420 | ) -> bool { | |
421 | visited_locations.insert(from); | |
422 | ||
423 | if from == loop_head { | |
424 | return true; | |
425 | } | |
426 | ||
427 | if loop_head.dominates(from, &self.dominators) { | |
dc9dc135 | 428 | let block = &self.body.basic_blocks()[from.block]; |
9fa01778 XL |
429 | |
430 | if from.statement_index < block.statements.len() { | |
431 | let successor = from.successor_within_block(); | |
432 | ||
433 | if !visited_locations.contains(&successor) | |
434 | && self.find_loop_head_dfs(successor, loop_head, visited_locations) | |
435 | { | |
436 | return true; | |
437 | } | |
438 | } else { | |
439 | for bb in block.terminator().successors() { | |
440 | let successor = Location { | |
441 | statement_index: 0, | |
442 | block: *bb, | |
443 | }; | |
b7449926 | 444 | |
9fa01778 XL |
445 | if !visited_locations.contains(&successor) |
446 | && self.find_loop_head_dfs(successor, loop_head, visited_locations) | |
447 | { | |
448 | return true; | |
449 | } | |
450 | } | |
451 | } | |
ff7c6d11 | 452 | } |
b7449926 XL |
453 | |
454 | false | |
ff7c6d11 | 455 | } |
b7449926 | 456 | |
9fa01778 XL |
457 | /// True if an edge `source -> target` is a backedge -- in other words, if the target |
458 | /// dominates the source. | |
459 | fn is_back_edge(&self, source: Location, target: Location) -> bool { | |
dc9dc135 | 460 | target.dominates(source, &self.body.dominators()) |
9fa01778 XL |
461 | } |
462 | ||
0bf4aa26 XL |
463 | /// Determine how the borrow was later used. |
464 | fn later_use_kind( | |
465 | &self, | |
466 | borrow: &BorrowData<'tcx>, | |
467 | use_spans: UseSpans, | |
9fa01778 | 468 | location: Location, |
0bf4aa26 XL |
469 | ) -> (LaterUseKind, Span) { |
470 | match use_spans { | |
471 | UseSpans::ClosureUse { var_span, .. } => { | |
472 | // Used in a closure. | |
473 | (LaterUseKind::ClosureCapture, var_span) | |
9fa01778 | 474 | } |
0bf4aa26 | 475 | UseSpans::OtherUse(span) => { |
dc9dc135 | 476 | let block = &self.body.basic_blocks()[location.block]; |
0bf4aa26 XL |
477 | |
478 | let kind = if let Some(&Statement { | |
479 | kind: StatementKind::FakeRead(FakeReadCause::ForLet, _), | |
480 | .. | |
9fa01778 XL |
481 | }) = block.statements.get(location.statement_index) |
482 | { | |
0bf4aa26 XL |
483 | LaterUseKind::FakeLetRead |
484 | } else if self.was_captured_by_trait_object(borrow) { | |
485 | LaterUseKind::TraitCapture | |
486 | } else if location.statement_index == block.statements.len() { | |
487 | if let TerminatorKind::Call { | |
9fa01778 XL |
488 | ref func, |
489 | from_hir_call: true, | |
490 | .. | |
491 | } = block.terminator().kind | |
492 | { | |
0bf4aa26 XL |
493 | // Just point to the function, to reduce the chance of overlapping spans. |
494 | let function_span = match func { | |
495 | Operand::Constant(c) => c.span, | |
416331ca XL |
496 | Operand::Copy(Place { |
497 | base: PlaceBase::Local(l), | |
498 | projection: None, | |
499 | }) | | |
500 | Operand::Move(Place { | |
501 | base: PlaceBase::Local(l), | |
502 | projection: None, | |
503 | }) => { | |
dc9dc135 | 504 | let local_decl = &self.body.local_decls[*l]; |
0bf4aa26 XL |
505 | if local_decl.name.is_none() { |
506 | local_decl.source_info.span | |
507 | } else { | |
508 | span | |
509 | } | |
9fa01778 | 510 | } |
0bf4aa26 XL |
511 | _ => span, |
512 | }; | |
513 | return (LaterUseKind::Call, function_span); | |
514 | } else { | |
515 | LaterUseKind::Other | |
516 | } | |
517 | } else { | |
518 | LaterUseKind::Other | |
519 | }; | |
520 | ||
521 | (kind, span) | |
522 | } | |
523 | } | |
524 | } | |
525 | ||
9fa01778 | 526 | /// Checks if a borrowed value was captured by a trait object. We do this by |
0bf4aa26 XL |
527 | /// looking forward in the MIR from the reserve location and checking if we see |
528 | /// a unsized cast to a trait object on our data. | |
529 | fn was_captured_by_trait_object(&self, borrow: &BorrowData<'tcx>) -> bool { | |
530 | // Start at the reserve location, find the place that we want to see cast to a trait object. | |
531 | let location = borrow.reserve_location; | |
dc9dc135 | 532 | let block = &self.body[location.block]; |
0bf4aa26 | 533 | let stmt = block.statements.get(location.statement_index); |
9fa01778 XL |
534 | debug!( |
535 | "was_captured_by_trait_object: location={:?} stmt={:?}", | |
536 | location, stmt | |
537 | ); | |
0bf4aa26 XL |
538 | |
539 | // We make a `queue` vector that has the locations we want to visit. As of writing, this | |
540 | // will only ever have one item at any given time, but by using a vector, we can pop from | |
541 | // it which simplifies the termination logic. | |
542 | let mut queue = vec![location]; | |
543 | let mut target = if let Some(&Statement { | |
416331ca XL |
544 | kind: StatementKind::Assign(Place { |
545 | base: PlaceBase::Local(local), | |
546 | projection: None, | |
547 | }, _), | |
0bf4aa26 | 548 | .. |
9fa01778 XL |
549 | }) = stmt |
550 | { | |
0bf4aa26 XL |
551 | local |
552 | } else { | |
553 | return false; | |
554 | }; | |
555 | ||
9fa01778 XL |
556 | debug!( |
557 | "was_captured_by_trait: target={:?} queue={:?}", | |
558 | target, queue | |
559 | ); | |
0bf4aa26 XL |
560 | while let Some(current_location) = queue.pop() { |
561 | debug!("was_captured_by_trait: target={:?}", target); | |
dc9dc135 | 562 | let block = &self.body[current_location.block]; |
0bf4aa26 XL |
563 | // We need to check the current location to find out if it is a terminator. |
564 | let is_terminator = current_location.statement_index == block.statements.len(); | |
565 | if !is_terminator { | |
566 | let stmt = &block.statements[current_location.statement_index]; | |
567 | debug!("was_captured_by_trait_object: stmt={:?}", stmt); | |
568 | ||
569 | // The only kind of statement that we care about is assignments... | |
9fa01778 | 570 | if let StatementKind::Assign(place, box rvalue) = &stmt.kind { |
416331ca XL |
571 | let into = match place.local_or_deref_local() { |
572 | Some(into) => into, | |
573 | None => { | |
0bf4aa26 XL |
574 | // Continue at the next location. |
575 | queue.push(current_location.successor_within_block()); | |
576 | continue; | |
9fa01778 | 577 | } |
0bf4aa26 XL |
578 | }; |
579 | ||
580 | match rvalue { | |
581 | // If we see a use, we should check whether it is our data, and if so | |
582 | // update the place that we're looking for to that new place. | |
583 | Rvalue::Use(operand) => match operand { | |
416331ca XL |
584 | Operand::Copy(Place { |
585 | base: PlaceBase::Local(from), | |
586 | projection: None, | |
587 | }) | |
588 | | Operand::Move(Place { | |
589 | base: PlaceBase::Local(from), | |
590 | projection: None, | |
591 | }) | |
9fa01778 XL |
592 | if *from == target => |
593 | { | |
416331ca | 594 | target = into; |
9fa01778 XL |
595 | } |
596 | _ => {} | |
0bf4aa26 XL |
597 | }, |
598 | // If we see a unsized cast, then if it is our data we should check | |
599 | // whether it is being cast to a trait object. | |
48663c56 XL |
600 | Rvalue::Cast( |
601 | CastKind::Pointer(PointerCast::Unsize), operand, ty | |
602 | ) => match operand { | |
416331ca XL |
603 | Operand::Copy(Place { |
604 | base: PlaceBase::Local(from), | |
605 | projection: None, | |
606 | }) | |
607 | | Operand::Move(Place { | |
608 | base: PlaceBase::Local(from), | |
609 | projection: None, | |
610 | }) | |
9fa01778 XL |
611 | if *from == target => |
612 | { | |
0bf4aa26 XL |
613 | debug!("was_captured_by_trait_object: ty={:?}", ty); |
614 | // Check the type for a trait object. | |
615 | return match ty.sty { | |
616 | // `&dyn Trait` | |
532ac7d7 | 617 | ty::Ref(_, ty, _) if ty.is_trait() => true, |
0bf4aa26 | 618 | // `Box<dyn Trait>` |
9fa01778 | 619 | _ if ty.is_box() && ty.boxed_ty().is_trait() => true, |
0bf4aa26 XL |
620 | // `dyn Trait` |
621 | _ if ty.is_trait() => true, | |
622 | // Anything else. | |
623 | _ => false, | |
624 | }; | |
9fa01778 | 625 | } |
0bf4aa26 XL |
626 | _ => return false, |
627 | }, | |
9fa01778 | 628 | _ => {} |
0bf4aa26 XL |
629 | } |
630 | } | |
631 | ||
632 | // Continue at the next location. | |
633 | queue.push(current_location.successor_within_block()); | |
634 | } else { | |
635 | // The only thing we need to do for terminators is progress to the next block. | |
636 | let terminator = block.terminator(); | |
637 | debug!("was_captured_by_trait_object: terminator={:?}", terminator); | |
638 | ||
639 | if let TerminatorKind::Call { | |
416331ca XL |
640 | destination: Some((Place { |
641 | base: PlaceBase::Local(dest), | |
642 | projection: None, | |
643 | }, block)), | |
0bf4aa26 XL |
644 | args, |
645 | .. | |
9fa01778 XL |
646 | } = &terminator.kind |
647 | { | |
0bf4aa26 XL |
648 | debug!( |
649 | "was_captured_by_trait_object: target={:?} dest={:?} args={:?}", | |
650 | target, dest, args | |
651 | ); | |
652 | // Check if one of the arguments to this function is the target place. | |
653 | let found_target = args.iter().any(|arg| { | |
416331ca XL |
654 | if let Operand::Move(Place { |
655 | base: PlaceBase::Local(potential), | |
656 | projection: None, | |
657 | }) = arg { | |
0bf4aa26 XL |
658 | *potential == target |
659 | } else { | |
660 | false | |
661 | } | |
662 | }); | |
663 | ||
664 | // If it is, follow this to the next block and update the target. | |
665 | if found_target { | |
666 | target = *dest; | |
667 | queue.push(block.start_location()); | |
668 | } | |
669 | } | |
670 | } | |
671 | ||
672 | debug!("was_captured_by_trait: queue={:?}", queue); | |
673 | } | |
674 | ||
675 | // We didn't find anything and ran out of locations to check. | |
676 | false | |
677 | } | |
678 | } |