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