rustc_borrowck/diagnostics/
explain_borrow.rs

1//! Print diagnostics to explain why values are borrowed.
2
3#![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                        // FIXME(#101728): enable rewrite when type ascription is stabilized again
128                        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                    // We can use `var_or_use_span` if either `path_span` is not present, or both
137                    // spans are the same.
138                    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                    // path_span must be `Some` as otherwise the if condition is true
146                    let path_span = path_span.unwrap();
147                    // path_span is only present in the case of closure capture
148                    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                // We can use `var_or_use_span` if either `path_span` is not present, or both spans
173                // are the same.
174                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                    // path_span must be `Some` as otherwise the if condition is true
178                    let path_span = path_span.unwrap();
179                    // path_span is only present in the case of closure capture
180                    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                            // in for loop desugaring, only look at the `Some(..)` inner type
203                            ty = args.type_at(0);
204                        }
205                    }
206                }
207                let (dtor_desc, type_desc) = match ty.kind() {
208                    // If type is an ADT that implements Drop, then
209                    // simplify output by reporting just the ADT name.
210                    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                    // Otherwise, just report the whole type (and use
215                    // the intentionally fuzzy phrase "destructor")
216                    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                                // FIXME(#101728): enable rewrite when type ascription is
302                                // stabilized again.
303                                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                            // #133941: The `old` expression is at the conditional part of an
323                            // if/while let expression. Adding a semicolon won't work.
324                            // Instead, try suggesting the `matches!` macro or a temporary.
325                            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                                // #85581: If the first mutable borrow's scope contains
347                                // the second borrow, this suggestion isn't helpful.
348                                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            // We try to elaborate the object lifetime defaults and present those to the user. This
453            // should make it clear where the region constraint is coming from.
454            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                            // This is not entirely precise.
468                            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[&param_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    /// Returns structured explanation for *why* the borrow contains the
598    /// point from `location`. This is key for the "3-point errors"
599    /// [described in the NLL RFC][d].
600    ///
601    /// # Parameters
602    ///
603    /// - `borrow`: the borrow in question
604    /// - `location`: where the borrow occurs
605    /// - `kind_place`: if Some, this describes the statement that triggered the error.
606    ///   - first half is the kind of write, if any, being performed
607    ///   - second half is the place being accessed
608    ///
609    /// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points
610    #[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            // When `region_sub` is the same as `borrow_region_vid` (the location where the borrow
632            // is issued is the same location that invalidates the reference), this is likely a
633            // loop iteration. In this case, try using the loop terminator location in
634            // `find_sub_region_live_at`.
635            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        // NLL doesn't consider boring locals for liveness, and wouldn't encounter a
648        // `Cause::LiveVar` for such a local. Polonius can't avoid computing liveness for boring
649        // locals yet, and will encounter them when trying to explain why a borrow contains a given
650        // point.
651        //
652        // We want to focus on relevant live locals in diagnostics, so when polonius is enabled, we
653        // ensure that we don't emit live boring locals as explanations.
654        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                // Boring locals are never the cause of a borrow explanation in NLLs.
661                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                    // Check if the location represents a `FakeRead`, and adapt the error
677                    // message to the `FakeReadCause` it is from: in particular,
678                    // the ones inserted in optimized `let var = <expr>` patterns.
679                    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                // Here, under NLL: no cause was found. Under polonius: no cause was found, or a
710                // boring local was found, which we ignore like NLLs do to match its diagnostics.
711                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    /// Determine how the borrow was later used.
737    /// First span returned points to the location of the conflicting use
738    /// Second span if `Some` is returned in the case of closures and points
739    /// to the use of the path
740    #[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                // Used in a closure.
750                (LaterUseKind::ClosureCapture, capture_kind_span, Some(path_span))
751            }
752            // In the case that the borrowed value (probably a temporary)
753            // overlaps with the method's receiver, then point at the method.
754            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                    // Just point to the function, to reduce the chance of overlapping spans.
765                    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                        // Just point to the function, to reduce the chance of overlapping spans.
810                        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    /// Checks if a borrowed value was captured by a trait object. We do this by
839    /// looking forward in the MIR from the reserve location and checking if we see
840    /// an unsized cast to a trait object on our data.
841    fn was_captured_by_trait_object(&self, borrow: &BorrowData<'tcx>) -> bool {
842        // Start at the reserve location, find the place that we want to see cast to a trait object.
843        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        // We make a `queue` vector that has the locations we want to visit. As of writing, this
849        // will only ever have one item at any given time, but by using a vector, we can pop from
850        // it which simplifies the termination logic.
851        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            // We need to check the current location to find out if it is a terminator.
868            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                // The only kind of statement that we care about is assignments...
874                if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
875                    let Some(into) = place.local_or_deref_local() else {
876                        // Continue at the next location.
877                        queue.push(current_location.successor_within_block());
878                        continue;
879                    };
880
881                    match rvalue {
882                        // If we see a use, we should check whether it is our data, and if so
883                        // update the place that we're looking for to that new place.
884                        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                        // If we see an unsized cast, then if it is our data we should check
895                        // whether it is being cast to a trait object.
896                        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                                            // Check the type for a trait object.
907                                            return match ty.kind() {
908                                                // `&dyn Trait`
909                                                ty::Ref(_, ty, _) if ty.is_trait() => true,
910                                                // `Box<dyn Trait>`
911                                                _ if ty.boxed_ty().is_some_and(Ty::is_trait) => {
912                                                    true
913                                                }
914
915                                                // `dyn Trait`
916                                                _ if ty.is_trait() => true,
917                                                // Anything else.
918                                                _ => false,
919                                            };
920                                        }
921                                    }
922                                    return false;
923                                }
924                                _ => return false,
925                            }
926                        }
927                        _ => {}
928                    }
929                }
930
931                // Continue at the next location.
932                queue.push(current_location.successor_within_block());
933            } else {
934                // The only thing we need to do for terminators is progress to the next block.
935                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                    // Check if one of the arguments to this function is the target place.
947                    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 it is, follow this to the next block and update the target.
960                    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        // We didn't find anything and ran out of locations to check.
971        false
972    }
973}