1#![allow(rustc::diagnostic_outside_of_impl)]
4#![allow(rustc::untranslatable_diagnostic)]
5
6use std::assert_matches::assert_matches;
7
8use rustc_errors::{Applicability, Diag, EmissionGuarantee};
9use rustc_hir as hir;
10use rustc_hir::intravisit::Visitor;
11use rustc_infer::infer::NllRegionVariableOrigin;
12use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
13use rustc_middle::mir::{
14 Body, CallSource, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location,
15 Operand, Place, Rvalue, Statement, StatementKind, TerminatorKind,
16};
17use rustc_middle::ty::adjustment::PointerCoercion;
18use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
19use rustc_span::{DesugaringKind, Span, kw, sym};
20use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
21use rustc_trait_selection::error_reporting::traits::call_kind::CallKind;
22use tracing::{debug, instrument};
23
24use super::{RegionName, UseSpans, find_use};
25use crate::borrow_set::BorrowData;
26use crate::constraints::OutlivesConstraint;
27use crate::nll::ConstraintDescription;
28use crate::region_infer::{BlameConstraint, Cause};
29use crate::{MirBorrowckCtxt, WriteKind};
30
31#[derive(Debug)]
32pub(crate) enum BorrowExplanation<'tcx> {
33 UsedLater(Local, LaterUseKind, Span, Option<Span>),
34 UsedLaterInLoop(LaterUseKind, Span, Option<Span>),
35 UsedLaterWhenDropped {
36 drop_loc: Location,
37 dropped_local: Local,
38 should_note_order: bool,
39 },
40 MustBeValidFor {
41 category: ConstraintCategory<'tcx>,
42 from_closure: bool,
43 span: Span,
44 region_name: RegionName,
45 opt_place_desc: Option<String>,
46 path: Vec<OutlivesConstraint<'tcx>>,
47 },
48 Unexplained,
49}
50
51#[derive(Clone, Copy, Debug)]
52pub(crate) enum LaterUseKind {
53 TraitCapture,
54 ClosureCapture,
55 Call,
56 FakeLetRead,
57 Other,
58}
59
60impl<'tcx> BorrowExplanation<'tcx> {
61 pub(crate) fn is_explained(&self) -> bool {
62 !matches!(self, BorrowExplanation::Unexplained)
63 }
64 pub(crate) fn add_explanation_to_diagnostic<G: EmissionGuarantee>(
65 &self,
66 cx: &MirBorrowckCtxt<'_, '_, 'tcx>,
67 err: &mut Diag<'_, G>,
68 borrow_desc: &str,
69 borrow_span: Option<Span>,
70 multiple_borrow_span: Option<(Span, Span)>,
71 ) {
72 let tcx = cx.infcx.tcx;
73 let body = cx.body;
74
75 if let Some(span) = borrow_span {
76 let def_id = body.source.def_id();
77 if let Some(node) = tcx.hir_get_if_local(def_id)
78 && let Some(body_id) = node.body_id()
79 {
80 let body = tcx.hir_body(body_id);
81 let mut expr_finder = FindExprBySpan::new(span, tcx);
82 expr_finder.visit_expr(body.value);
83 if let Some(mut expr) = expr_finder.result {
84 while let hir::ExprKind::AddrOf(_, _, inner)
85 | hir::ExprKind::Unary(hir::UnOp::Deref, inner)
86 | hir::ExprKind::Field(inner, _)
87 | hir::ExprKind::MethodCall(_, inner, _, _)
88 | hir::ExprKind::Index(inner, _, _) = &expr.kind
89 {
90 expr = inner;
91 }
92 if let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind
93 && let [hir::PathSegment { ident, args: None, .. }] = p.segments
94 && let hir::def::Res::Local(hir_id) = p.res
95 && let hir::Node::Pat(pat) = tcx.hir_node(hir_id)
96 {
97 if !ident.span.in_external_macro(tcx.sess.source_map()) {
98 err.span_label(pat.span, format!("binding `{ident}` declared here"));
99 }
100 }
101 }
102 }
103 }
104 match *self {
105 BorrowExplanation::UsedLater(
106 dropped_local,
107 later_use_kind,
108 var_or_use_span,
109 path_span,
110 ) => {
111 let message = match later_use_kind {
112 LaterUseKind::TraitCapture => "captured here by trait object",
113 LaterUseKind::ClosureCapture => "captured here by closure",
114 LaterUseKind::Call => "used by call",
115 LaterUseKind::FakeLetRead => "stored here",
116 LaterUseKind::Other => "used here",
117 };
118 let local_decl = &body.local_decls[dropped_local];
119
120 if let &LocalInfo::IfThenRescopeTemp { if_then } = local_decl.local_info()
121 && let Some((_, hir::Node::Expr(expr))) = tcx.hir_parent_iter(if_then).next()
122 && let hir::ExprKind::If(cond, conseq, alt) = expr.kind
123 && let hir::ExprKind::Let(&hir::LetExpr {
124 span: _,
125 pat,
126 init,
127 ty: None,
129 recovered: _,
130 }) = cond.kind
131 && pat.span.can_be_used_for_suggestions()
132 && let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span)
133 {
134 suggest_rewrite_if_let(tcx, expr, &pat, init, conseq, alt, err);
135 } else if path_span.is_none_or(|path_span| path_span == var_or_use_span) {
136 if borrow_span.is_none_or(|sp| !sp.overlaps(var_or_use_span)) {
139 err.span_label(
140 var_or_use_span,
141 format!("{borrow_desc}borrow later {message}"),
142 );
143 }
144 } else {
145 let path_span = path_span.unwrap();
147 assert_matches!(later_use_kind, LaterUseKind::ClosureCapture);
149 if !borrow_span.is_some_and(|sp| sp.overlaps(var_or_use_span)) {
150 let path_label = "used here by closure";
151 let capture_kind_label = message;
152 err.span_label(
153 var_or_use_span,
154 format!("{borrow_desc}borrow later {capture_kind_label}"),
155 );
156 err.span_label(path_span, path_label);
157 }
158 }
159 }
160 BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span, path_span) => {
161 let message = match later_use_kind {
162 LaterUseKind::TraitCapture => {
163 "borrow captured here by trait object, in later iteration of loop"
164 }
165 LaterUseKind::ClosureCapture => {
166 "borrow captured here by closure, in later iteration of loop"
167 }
168 LaterUseKind::Call => "borrow used by call, in later iteration of loop",
169 LaterUseKind::FakeLetRead => "borrow later stored here",
170 LaterUseKind::Other => "borrow used here, in later iteration of loop",
171 };
172 if path_span.map(|path_span| path_span == var_or_use_span).unwrap_or(true) {
175 err.span_label(var_or_use_span, format!("{borrow_desc}{message}"));
176 } else {
177 let path_span = path_span.unwrap();
179 assert_matches!(later_use_kind, LaterUseKind::ClosureCapture);
181 if borrow_span.map(|sp| !sp.overlaps(var_or_use_span)).unwrap_or(true) {
182 let path_label = "used here by closure";
183 let capture_kind_label = message;
184 err.span_label(
185 var_or_use_span,
186 format!("{borrow_desc}borrow later {capture_kind_label}"),
187 );
188 err.span_label(path_span, path_label);
189 }
190 }
191 }
192 BorrowExplanation::UsedLaterWhenDropped {
193 drop_loc,
194 dropped_local,
195 should_note_order,
196 } => {
197 let local_decl = &body.local_decls[dropped_local];
198 let mut ty = local_decl.ty;
199 if local_decl.source_info.span.desugaring_kind() == Some(DesugaringKind::ForLoop) {
200 if let ty::Adt(adt, args) = local_decl.ty.kind() {
201 if tcx.is_diagnostic_item(sym::Option, adt.did()) {
202 ty = args.type_at(0);
204 }
205 }
206 }
207 let (dtor_desc, type_desc) = match ty.kind() {
208 ty::Adt(adt, _args) if adt.has_dtor(tcx) && !adt.is_box() => {
211 ("`Drop` code", format!("type `{}`", tcx.def_path_str(adt.did())))
212 }
213
214 ty::Closure(..) => ("destructor", "closure".to_owned()),
217 ty::Coroutine(..) => ("destructor", "coroutine".to_owned()),
218
219 _ => ("destructor", format!("type `{}`", local_decl.ty)),
220 };
221
222 match cx.local_name(dropped_local) {
223 Some(local_name) if !local_decl.from_compiler_desugaring() => {
224 let message = format!(
225 "{borrow_desc}borrow might be used here, when `{local_name}` is dropped \
226 and runs the {dtor_desc} for {type_desc}",
227 );
228 err.span_label(body.source_info(drop_loc).span, message);
229
230 if should_note_order {
231 err.note(
232 "values in a scope are dropped \
233 in the opposite order they are defined",
234 );
235 }
236 }
237 _ => {
238 err.span_label(
239 local_decl.source_info.span,
240 format!(
241 "a temporary with access to the {borrow_desc}borrow \
242 is created here ...",
243 ),
244 );
245 let message = format!(
246 "... and the {borrow_desc}borrow might be used here, \
247 when that temporary is dropped \
248 and runs the {dtor_desc} for {type_desc}",
249 );
250 err.span_label(body.source_info(drop_loc).span, message);
251
252 struct FindLetExpr<'hir> {
253 span: Span,
254 result: Option<(Span, &'hir hir::Pat<'hir>, &'hir hir::Expr<'hir>)>,
255 tcx: TyCtxt<'hir>,
256 }
257
258 impl<'hir> rustc_hir::intravisit::Visitor<'hir> for FindLetExpr<'hir> {
259 type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
260 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
261 self.tcx
262 }
263 fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
264 if let hir::ExprKind::If(cond, _conseq, _alt)
265 | hir::ExprKind::Loop(
266 &hir::Block {
267 expr:
268 Some(&hir::Expr {
269 kind: hir::ExprKind::If(cond, _conseq, _alt),
270 ..
271 }),
272 ..
273 },
274 _,
275 hir::LoopSource::While,
276 _,
277 ) = expr.kind
278 && let hir::ExprKind::Let(hir::LetExpr {
279 init: let_expr_init,
280 span: let_expr_span,
281 pat: let_expr_pat,
282 ..
283 }) = cond.kind
284 && let_expr_init.span.contains(self.span)
285 {
286 self.result =
287 Some((*let_expr_span, let_expr_pat, let_expr_init))
288 } else {
289 hir::intravisit::walk_expr(self, expr);
290 }
291 }
292 }
293
294 if let &LocalInfo::IfThenRescopeTemp { if_then } = local_decl.local_info()
295 && let hir::Node::Expr(expr) = tcx.hir_node(if_then)
296 && let hir::ExprKind::If(cond, conseq, alt) = expr.kind
297 && let hir::ExprKind::Let(&hir::LetExpr {
298 span: _,
299 pat,
300 init,
301 ty: None,
304 recovered: _,
305 }) = cond.kind
306 && pat.span.can_be_used_for_suggestions()
307 && let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span)
308 {
309 suggest_rewrite_if_let(tcx, expr, &pat, init, conseq, alt, err);
310 } else if let Some((old, new)) = multiple_borrow_span
311 && let def_id = body.source.def_id()
312 && let Some(node) = tcx.hir_get_if_local(def_id)
313 && let Some(body_id) = node.body_id()
314 && let hir_body = tcx.hir_body(body_id)
315 && let mut expr_finder = (FindLetExpr { span: old, result: None, tcx })
316 && let Some((let_expr_span, let_expr_pat, let_expr_init)) = {
317 expr_finder.visit_expr(hir_body.value);
318 expr_finder.result
319 }
320 && !let_expr_span.contains(new)
321 {
322 if let_expr_pat
326 .walk_short(|pat| !matches!(pat.kind, hir::PatKind::Binding(..)))
327 {
328 if let Ok(pat_snippet) =
329 tcx.sess.source_map().span_to_snippet(let_expr_pat.span)
330 && let Ok(init_snippet) =
331 tcx.sess.source_map().span_to_snippet(let_expr_init.span)
332 {
333 err.span_suggestion_verbose(
334 let_expr_span,
335 "consider using the `matches!` macro",
336 format!("matches!({init_snippet}, {pat_snippet})"),
337 Applicability::MaybeIncorrect,
338 );
339 } else {
340 err.note("consider using the `matches!` macro");
341 }
342 }
343 } else if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() {
344 let sp = info.span.find_ancestor_not_from_macro().unwrap_or(info.span);
345 if info.tail_result_is_ignored {
346 if !multiple_borrow_span.is_some_and(|(old, new)| {
349 old.to(info.span.shrink_to_hi()).contains(new)
350 }) {
351 err.span_suggestion_verbose(
352 sp.shrink_to_hi(),
353 "consider adding semicolon after the expression so its \
354 temporaries are dropped sooner, before the local variables \
355 declared by the block are dropped",
356 ";",
357 Applicability::MaybeIncorrect,
358 );
359 }
360 } else {
361 err.note(
362 "the temporary is part of an expression at the end of a \
363 block;\nconsider forcing this temporary to be dropped sooner, \
364 before the block's local variables are dropped",
365 );
366 err.multipart_suggestion(
367 "for example, you could save the expression's value in a new \
368 local variable `x` and then make `x` be the expression at the \
369 end of the block",
370 vec![
371 (sp.shrink_to_lo(), "let x = ".to_string()),
372 (sp.shrink_to_hi(), "; x".to_string()),
373 ],
374 Applicability::MaybeIncorrect,
375 );
376 };
377 }
378 }
379 }
380 }
381 BorrowExplanation::MustBeValidFor {
382 category,
383 span,
384 ref region_name,
385 ref opt_place_desc,
386 from_closure: _,
387 ref path,
388 } => {
389 region_name.highlight_region_name(err);
390
391 if let Some(desc) = opt_place_desc {
392 err.span_label(
393 span,
394 format!(
395 "{}requires that `{desc}` is borrowed for `{region_name}`",
396 category.description(),
397 ),
398 );
399 } else {
400 err.span_label(
401 span,
402 format!(
403 "{}requires that {borrow_desc}borrow lasts for `{region_name}`",
404 category.description(),
405 ),
406 );
407 };
408
409 cx.add_placeholder_from_predicate_note(err, &path);
410 cx.add_sized_or_copy_bound_info(err, category, &path);
411
412 if let ConstraintCategory::Cast {
413 is_implicit_coercion: true,
414 unsize_to: Some(unsize_ty),
415 } = category
416 {
417 self.add_object_lifetime_default_note(tcx, err, unsize_ty);
418 }
419
420 let mut preds = path
421 .iter()
422 .filter_map(|constraint| match constraint.category {
423 ConstraintCategory::Predicate(pred) if !pred.is_dummy() => Some(pred),
424 _ => None,
425 })
426 .collect::<Vec<Span>>();
427 preds.sort();
428 preds.dedup();
429 if !preds.is_empty() {
430 let s = if preds.len() == 1 { "" } else { "s" };
431 err.span_note(
432 preds,
433 format!(
434 "requirement{s} that the value outlives `{region_name}` introduced here"
435 ),
436 );
437 }
438
439 self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name);
440 }
441 _ => {}
442 }
443 }
444
445 fn add_object_lifetime_default_note<G: EmissionGuarantee>(
446 &self,
447 tcx: TyCtxt<'tcx>,
448 err: &mut Diag<'_, G>,
449 unsize_ty: Ty<'tcx>,
450 ) {
451 if let ty::Adt(def, args) = unsize_ty.kind() {
452 let generics = tcx.generics_of(def.did());
455
456 let mut has_dyn = false;
457 let mut failed = false;
458
459 let elaborated_args =
460 std::iter::zip(*args, &generics.own_params).map(|(arg, param)| {
461 if let Some(ty::Dynamic(obj, _)) = arg.as_type().map(Ty::kind) {
462 let default = tcx.object_lifetime_default(param.def_id);
463
464 let re_static = tcx.lifetimes.re_static;
465
466 let implied_region = match default {
467 ObjectLifetimeDefault::Empty => re_static,
469 ObjectLifetimeDefault::Ambiguous => {
470 failed = true;
471 re_static
472 }
473 ObjectLifetimeDefault::Param(param_def_id) => {
474 let index = generics.param_def_id_to_index[¶m_def_id] as usize;
475 args.get(index).and_then(|arg| arg.as_region()).unwrap_or_else(
476 || {
477 failed = true;
478 re_static
479 },
480 )
481 }
482 ObjectLifetimeDefault::Static => re_static,
483 };
484
485 has_dyn = true;
486
487 Ty::new_dynamic(tcx, obj, implied_region).into()
488 } else {
489 arg
490 }
491 });
492 let elaborated_ty = Ty::new_adt(tcx, *def, tcx.mk_args_from_iter(elaborated_args));
493
494 if has_dyn && !failed {
495 err.note(format!(
496 "due to object lifetime defaults, `{unsize_ty}` actually means `{elaborated_ty}`"
497 ));
498 }
499 }
500 }
501
502 fn add_lifetime_bound_suggestion_to_diagnostic<G: EmissionGuarantee>(
503 &self,
504 err: &mut Diag<'_, G>,
505 category: &ConstraintCategory<'tcx>,
506 span: Span,
507 region_name: &RegionName,
508 ) {
509 if !span.is_desugaring(DesugaringKind::OpaqueTy) {
510 return;
511 }
512 if let ConstraintCategory::OpaqueType = category {
513 let suggestable_name =
514 if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime };
515
516 let msg = format!(
517 "you can add a bound to the {}to make it last less than `'static` and match `{region_name}`",
518 category.description(),
519 );
520
521 err.span_suggestion_verbose(
522 span.shrink_to_hi(),
523 msg,
524 format!(" + {suggestable_name}"),
525 Applicability::Unspecified,
526 );
527 }
528 }
529}
530
531fn suggest_rewrite_if_let<G: EmissionGuarantee>(
532 tcx: TyCtxt<'_>,
533 expr: &hir::Expr<'_>,
534 pat: &str,
535 init: &hir::Expr<'_>,
536 conseq: &hir::Expr<'_>,
537 alt: Option<&hir::Expr<'_>>,
538 err: &mut Diag<'_, G>,
539) {
540 let source_map = tcx.sess.source_map();
541 err.span_note(
542 source_map.end_point(conseq.span),
543 "lifetimes for temporaries generated in `if let`s have been shortened in Edition 2024 so that they are dropped here instead",
544 );
545 if expr.span.can_be_used_for_suggestions() && conseq.span.can_be_used_for_suggestions() {
546 let needs_block = if let Some(hir::Node::Expr(expr)) =
547 alt.and_then(|alt| tcx.hir_parent_iter(alt.hir_id).next()).map(|(_, node)| node)
548 {
549 matches!(expr.kind, hir::ExprKind::If(..))
550 } else {
551 false
552 };
553 let mut sugg = vec![
554 (
555 expr.span.shrink_to_lo().between(init.span),
556 if needs_block { "{ match ".into() } else { "match ".into() },
557 ),
558 (conseq.span.shrink_to_lo(), format!(" {{ {pat} => ")),
559 ];
560 let expr_end = expr.span.shrink_to_hi();
561 let mut expr_end_code;
562 if let Some(alt) = alt {
563 sugg.push((conseq.span.between(alt.span), " _ => ".into()));
564 expr_end_code = "}".to_string();
565 } else {
566 expr_end_code = " _ => {} }".into();
567 }
568 expr_end_code.push('}');
569 sugg.push((expr_end, expr_end_code));
570 err.multipart_suggestion(
571 "consider rewriting the `if` into `match` which preserves the extended lifetime",
572 sugg,
573 Applicability::MaybeIncorrect,
574 );
575 }
576}
577
578impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
579 fn free_region_constraint_info(
580 &self,
581 borrow_region: RegionVid,
582 outlived_region: RegionVid,
583 ) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<OutlivesConstraint<'tcx>>)
584 {
585 let (blame_constraint, path) = self.regioncx.best_blame_constraint(
586 borrow_region,
587 NllRegionVariableOrigin::FreeRegion,
588 outlived_region,
589 );
590 let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;
591
592 let outlived_fr_name = self.give_region_a_name(outlived_region);
593
594 (category, from_closure, cause.span, outlived_fr_name, path)
595 }
596
597 #[instrument(level = "debug", skip(self))]
611 pub(crate) fn explain_why_borrow_contains_point(
612 &self,
613 location: Location,
614 borrow: &BorrowData<'tcx>,
615 kind_place: Option<(WriteKind, Place<'tcx>)>,
616 ) -> BorrowExplanation<'tcx> {
617 let regioncx = &self.regioncx;
618 let body: &Body<'_> = self.body;
619 let tcx = self.infcx.tcx;
620
621 let borrow_region_vid = borrow.region;
622 debug!(?borrow_region_vid);
623
624 let mut region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);
625 debug!(?region_sub);
626
627 let mut use_location = location;
628 let mut use_in_later_iteration_of_loop = false;
629
630 if region_sub == borrow_region_vid {
631 if let Some(loop_terminator_location) =
636 regioncx.find_loop_terminator_location(borrow.region, body)
637 {
638 region_sub = self
639 .regioncx
640 .find_sub_region_live_at(borrow_region_vid, loop_terminator_location);
641 debug!("explain_why_borrow_contains_point: region_sub in loop={:?}", region_sub);
642 use_location = loop_terminator_location;
643 use_in_later_iteration_of_loop = true;
644 }
645 }
646
647 let is_local_boring = |local| {
655 if let Some(polonius_diagnostics) = self.polonius_diagnostics {
656 polonius_diagnostics.boring_nll_locals.contains(&local)
657 } else {
658 assert!(!tcx.sess.opts.unstable_opts.polonius.is_next_enabled());
659
660 false
662 }
663 };
664 match find_use::find(body, regioncx, tcx, region_sub, use_location) {
665 Some(Cause::LiveVar(local, location)) if !is_local_boring(local) => {
666 let span = body.source_info(location).span;
667 let spans = self
668 .move_spans(Place::from(local).as_ref(), location)
669 .or_else(|| self.borrow_spans(span, location));
670
671 if use_in_later_iteration_of_loop {
672 let (later_use_kind, var_or_use_span, path_span) =
673 self.later_use_kind(borrow, spans, use_location);
674 BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span, path_span)
675 } else {
676 let (later_use_kind, var_or_use_span, path_span) =
680 self.later_use_kind(borrow, spans, location);
681 BorrowExplanation::UsedLater(
682 borrow.borrowed_place.local,
683 later_use_kind,
684 var_or_use_span,
685 path_span,
686 )
687 }
688 }
689
690 Some(Cause::DropVar(local, location)) if !is_local_boring(local) => {
691 let mut should_note_order = false;
692 if self.local_name(local).is_some()
693 && let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place
694 && let Some(borrowed_local) = place.as_local()
695 && self.local_name(borrowed_local).is_some()
696 && local != borrowed_local
697 {
698 should_note_order = true;
699 }
700
701 BorrowExplanation::UsedLaterWhenDropped {
702 drop_loc: location,
703 dropped_local: local,
704 should_note_order,
705 }
706 }
707
708 Some(Cause::LiveVar(..) | Cause::DropVar(..)) | None => {
709 if let Some(region) = self.to_error_region_vid(borrow_region_vid) {
712 let (category, from_closure, span, region_name, path) =
713 self.free_region_constraint_info(borrow_region_vid, region);
714 if let Some(region_name) = region_name {
715 let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref());
716 BorrowExplanation::MustBeValidFor {
717 category,
718 from_closure,
719 span,
720 region_name,
721 opt_place_desc,
722 path,
723 }
724 } else {
725 debug!("Could not generate a region name");
726 BorrowExplanation::Unexplained
727 }
728 } else {
729 debug!("Could not generate an error region vid");
730 BorrowExplanation::Unexplained
731 }
732 }
733 }
734 }
735
736 #[instrument(level = "debug", skip(self))]
741 fn later_use_kind(
742 &self,
743 borrow: &BorrowData<'tcx>,
744 use_spans: UseSpans<'tcx>,
745 location: Location,
746 ) -> (LaterUseKind, Span, Option<Span>) {
747 match use_spans {
748 UseSpans::ClosureUse { capture_kind_span, path_span, .. } => {
749 (LaterUseKind::ClosureCapture, capture_kind_span, Some(path_span))
751 }
752 UseSpans::FnSelfUse {
755 var_span: span,
756 kind: CallKind::Normal { desugaring: None, .. },
757 ..
758 } if span
759 .overlaps(self.body.local_decls[borrow.assigned_place.local].source_info.span) =>
760 {
761 if let TerminatorKind::Call { func, call_source: CallSource::Normal, .. } =
762 &self.body.basic_blocks[location.block].terminator().kind
763 {
764 let function_span = match func {
766 Operand::Constant(c) => c.span,
767 Operand::Copy(place) | Operand::Move(place) => {
768 if let Some(l) = place.as_local() {
769 let local_decl = &self.body.local_decls[l];
770 if self.local_name(l).is_none() {
771 local_decl.source_info.span
772 } else {
773 span
774 }
775 } else {
776 span
777 }
778 }
779 };
780 (LaterUseKind::Call, function_span, None)
781 } else {
782 (LaterUseKind::Other, span, None)
783 }
784 }
785 UseSpans::PatUse(span)
786 | UseSpans::OtherUse(span)
787 | UseSpans::FnSelfUse { var_span: span, .. } => {
788 let block = &self.body.basic_blocks[location.block];
789
790 let kind = if let Some(&Statement {
791 kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), place)),
792 ..
793 }) = block.statements.get(location.statement_index)
794 {
795 if let Some(l) = place.as_local()
796 && let local_decl = &self.body.local_decls[l]
797 && local_decl.ty.is_closure()
798 {
799 LaterUseKind::ClosureCapture
800 } else {
801 LaterUseKind::FakeLetRead
802 }
803 } else if self.was_captured_by_trait_object(borrow) {
804 LaterUseKind::TraitCapture
805 } else if location.statement_index == block.statements.len() {
806 if let TerminatorKind::Call { func, call_source: CallSource::Normal, .. } =
807 &block.terminator().kind
808 {
809 let function_span = match func {
811 Operand::Constant(c) => c.span,
812 Operand::Copy(place) | Operand::Move(place) => {
813 if let Some(l) = place.as_local() {
814 let local_decl = &self.body.local_decls[l];
815 if self.local_name(l).is_none() {
816 local_decl.source_info.span
817 } else {
818 span
819 }
820 } else {
821 span
822 }
823 }
824 };
825 return (LaterUseKind::Call, function_span, None);
826 } else {
827 LaterUseKind::Other
828 }
829 } else {
830 LaterUseKind::Other
831 };
832
833 (kind, span, None)
834 }
835 }
836 }
837
838 fn was_captured_by_trait_object(&self, borrow: &BorrowData<'tcx>) -> bool {
842 let location = borrow.reserve_location;
844 let block = &self.body[location.block];
845 let stmt = block.statements.get(location.statement_index);
846 debug!("was_captured_by_trait_object: location={:?} stmt={:?}", location, stmt);
847
848 let mut queue = vec![location];
852 let mut target =
853 if let Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) = stmt {
854 if let Some(local) = place.as_local() {
855 local
856 } else {
857 return false;
858 }
859 } else {
860 return false;
861 };
862
863 debug!("was_captured_by_trait: target={:?} queue={:?}", target, queue);
864 while let Some(current_location) = queue.pop() {
865 debug!("was_captured_by_trait: target={:?}", target);
866 let block = &self.body[current_location.block];
867 let is_terminator = current_location.statement_index == block.statements.len();
869 if !is_terminator {
870 let stmt = &block.statements[current_location.statement_index];
871 debug!("was_captured_by_trait_object: stmt={:?}", stmt);
872
873 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
875 let Some(into) = place.local_or_deref_local() else {
876 queue.push(current_location.successor_within_block());
878 continue;
879 };
880
881 match rvalue {
882 Rvalue::Use(operand) => match operand {
885 Operand::Copy(place) | Operand::Move(place) => {
886 if let Some(from) = place.as_local() {
887 if from == target {
888 target = into;
889 }
890 }
891 }
892 _ => {}
893 },
894 Rvalue::Cast(
897 CastKind::PointerCoercion(PointerCoercion::Unsize, _),
898 operand,
899 ty,
900 ) => {
901 match operand {
902 Operand::Copy(place) | Operand::Move(place) => {
903 if let Some(from) = place.as_local() {
904 if from == target {
905 debug!("was_captured_by_trait_object: ty={:?}", ty);
906 return match ty.kind() {
908 ty::Ref(_, ty, _) if ty.is_trait() => true,
910 _ if ty.boxed_ty().is_some_and(Ty::is_trait) => {
912 true
913 }
914
915 _ if ty.is_trait() => true,
917 _ => false,
919 };
920 }
921 }
922 return false;
923 }
924 _ => return false,
925 }
926 }
927 _ => {}
928 }
929 }
930
931 queue.push(current_location.successor_within_block());
933 } else {
934 let terminator = block.terminator();
936 debug!("was_captured_by_trait_object: terminator={:?}", terminator);
937
938 if let TerminatorKind::Call { destination, target: Some(block), args, .. } =
939 &terminator.kind
940 && let Some(dest) = destination.as_local()
941 {
942 debug!(
943 "was_captured_by_trait_object: target={:?} dest={:?} args={:?}",
944 target, dest, args
945 );
946 let found_target = args.iter().any(|arg| {
948 if let Operand::Move(place) = arg.node {
949 if let Some(potential) = place.as_local() {
950 potential == target
951 } else {
952 false
953 }
954 } else {
955 false
956 }
957 });
958
959 if found_target {
961 target = dest;
962 queue.push(block.start_location());
963 }
964 }
965 }
966
967 debug!("was_captured_by_trait: queue={:?}", queue);
968 }
969
970 false
972 }
973}