2 use rustc_const_eval::util::CallKind;
3 use rustc_data_structures::captures::Captures;
4 use rustc_data_structures::fx::FxHashSet;
6 struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
9 use rustc_hir::def::Res;
10 use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
11 use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem};
12 use rustc_infer::infer::TyCtxtInferExt;
13 use rustc_infer::traits::ObligationCause;
14 use rustc_middle::mir::tcx::PlaceTy;
15 use rustc_middle::mir::{
16 self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
17 FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
18 ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
20 use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty};
21 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
22 use rustc_span::def_id::LocalDefId;
23 use rustc_span::hygiene::DesugaringKind;
24 use rustc_span::symbol::{kw, sym};
25 use rustc_span::{BytePos, Span, Symbol};
26 use rustc_trait_selection::infer::InferCtxtExt;
28 use crate::borrow_set::TwoPhaseActivation;
29 use crate::borrowck_errors;
31 use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
32 use crate::diagnostics::find_all_local_uses;
33 use crate::diagnostics::mutability_errors::mut_borrow_of_mutable_ref;
35 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
36 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
40 explain_borrow::{BorrowExplanation, LaterUseKind},
41 DescribePlaceOpt, RegionName, RegionNameSource, UseSpans,
46 /// Index of the "move out" that we found. The `MoveData` can
47 /// then tell us where the move occurred.
50 /// `true` if we traversed a back edge while walking from the point
51 /// of error to the move site.
52 traversed_back_edge: bool,
55 /// Which case a StorageDeadOrDrop is for.
56 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
57 enum StorageDeadOrDrop<'tcx> {
63 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
64 pub(crate) fn report_use_of_moved_or_uninitialized(
67 desired_action: InitializationRequiringAction,
68 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
72 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
73 moved_place={:?} used_place={:?} span={:?} mpi={:?}",
74 location, desired_action, moved_place, used_place, span, mpi
78 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
79 let span = use_spans.args_or_use();
81 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
83 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
84 move_site_vec, use_spans
86 let move_out_indices: Vec<_> =
87 move_site_vec.iter().map(|move_site| move_site.moi).collect();
89 if move_out_indices.is_empty() {
90 let root_place = PlaceRef { projection: &[], ..used_place };
92 if !self.uninitialized_error_reported.insert(root_place) {
94 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
100 let err = self.report_use_of_uninitialized(
108 self.buffer_error(err);
110 if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
111 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
113 "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}",
120 let is_partial_move = move_site_vec.iter().any(|move_site| {
121 let move_out = self.move_data.moves[(*move_site).moi];
122 let moved_place = &self.move_data.move_paths[move_out.path].place;
123 // `*(_1)` where `_1` is a `Box` is actually a move out.
124 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
125 && self.body.local_decls[moved_place.local].ty.is_box();
128 && used_place != moved_place.as_ref()
129 && used_place.is_prefix_of(moved_place.as_ref())
132 let partial_str = if is_partial_move { "partial " } else { "" };
133 let partially_str = if is_partial_move { "partially " } else { "" };
135 let mut err = self.cannot_act_on_moved_value(
137 desired_action.as_noun(),
139 self.describe_place_with_options(
141 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
145 let reinit_spans = maybe_reinitialized_locations
149 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
152 .collect::<Vec<Span>>();
154 let reinits = maybe_reinitialized_locations.len();
156 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
157 } else if reinits > 1 {
159 MultiSpan::from_spans(reinit_spans),
161 format!("these {reinits} reinitializations might get skipped")
164 "these 3 reinitializations and {} other{} might get skipped",
166 if reinits == 4 { "" } else { "s" }
172 let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
174 let mut is_loop_move = false;
175 let mut in_pattern = false;
176 let mut seen_spans = FxHashSet::default();
178 for move_site in &move_site_vec {
179 let move_out = self.move_data.moves[(*move_site).moi];
180 let moved_place = &self.move_data.move_paths[move_out.path].place;
182 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
183 let move_span = move_spans.args_or_use();
185 let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
187 let loop_message = if location == move_out.source || move_site.traversed_back_edge {
188 ", in previous iteration of loop"
193 if location == move_out.source {
197 if !seen_spans.contains(&move_span) {
199 self.suggest_ref_or_clone(
208 self.explain_captures(
218 maybe_reinitialized_locations.is_empty(),
221 seen_spans.insert(move_span);
224 use_spans.var_path_only_subdiag(&mut err, desired_action);
230 "value {} here after {partial_str}move",
231 desired_action.as_verb_in_past_tense(),
236 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
237 let needs_note = match ty.kind() {
238 ty::Closure(id, _) => {
239 let tables = self.infcx.tcx.typeck(id.expect_local());
240 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
242 tables.closure_kind_origins().get(hir_id).is_none()
247 let mpi = self.move_data.moves[move_out_indices[0]].path;
248 let place = &self.move_data.move_paths[mpi].place;
249 let ty = place.ty(self.body, self.infcx.tcx).ty;
251 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
252 // Same for if we're in a loop, see #101119.
253 if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
254 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
255 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
256 err.span_suggestion_verbose(
259 "consider creating a fresh reborrow of {} here",
260 self.describe_place(moved_place)
261 .map(|n| format!("`{n}`"))
262 .unwrap_or_else(|| "the mutable reference".to_string()),
265 Applicability::MachineApplicable,
270 let opt_name = self.describe_place_with_options(
272 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
274 let note_msg = match opt_name {
275 Some(name) => format!("`{name}`"),
276 None => "value".to_owned(),
278 if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) {
279 // Suppress the next suggestion since we don't want to put more bounds onto
280 // something that already has `Fn`-like bounds (or is a closure), so we can't
283 self.suggest_adding_copy_bounds(&mut err, ty, span);
287 let span = if let Some(local) = place.as_local() {
288 Some(self.body.local_decls[local].source_info.span)
292 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
295 if let UseSpans::FnSelfUse {
296 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
301 "{} occurs due to deref coercion to `{deref_target_ty}`",
302 desired_action.as_noun(),
305 // Check first whether the source is accessible (issue #87060)
306 if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) {
307 err.span_note(deref_target, "deref defined here");
311 self.buffer_move_error(move_out_indices, (used_place, err));
315 fn suggest_ref_or_clone(
319 err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
320 in_pattern: &mut bool,
321 move_spans: UseSpans<'_>,
323 struct ExpressionFinder<'hir> {
325 expr: Option<&'hir hir::Expr<'hir>>,
326 pat: Option<&'hir hir::Pat<'hir>>,
327 parent_pat: Option<&'hir hir::Pat<'hir>>,
329 impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
330 fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
331 if e.span == self.expr_span {
334 hir::intravisit::walk_expr(self, e);
336 fn visit_pat(&mut self, p: &'hir hir::Pat<'hir>) {
337 if p.span == self.expr_span {
340 if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, i, sub) = p.kind {
341 if i.span == self.expr_span || p.span == self.expr_span {
344 // Check if we are in a situation of `ident @ ident` where we want to suggest
345 // `ref ident @ ref ident` or `ref ident @ Struct { ref ident }`.
346 if let Some(subpat) = sub && self.pat.is_none() {
347 self.visit_pat(subpat);
348 if self.pat.is_some() {
349 self.parent_pat = Some(p);
354 hir::intravisit::walk_pat(self, p);
357 let hir = self.infcx.tcx.hir();
358 if let Some(hir::Node::Item(hir::Item {
359 kind: hir::ItemKind::Fn(_, _, body_id),
361 })) = hir.find(self.mir_hir_id())
362 && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id)
364 let place = &self.move_data.move_paths[mpi].place;
365 let span = place.as_local()
366 .map(|local| self.body.local_decls[local].source_info.span);
367 let mut finder = ExpressionFinder {
368 expr_span: move_span,
373 finder.visit_expr(expr);
374 if let Some(span) = span && let Some(expr) = finder.expr {
375 for (_, expr) in hir.parent_iter(expr.hir_id) {
376 if let hir::Node::Expr(expr) = expr {
377 if expr.span.contains(span) {
378 // If the let binding occurs within the same loop, then that
379 // loop isn't relevant, like in the following, the outermost `loop`
380 // doesn't play into `x` being moved.
383 // let x = String::new();
391 if let hir::ExprKind::Loop(.., loop_span) = expr.kind {
392 err.span_label(loop_span, "inside of this loop");
396 let typeck = self.infcx.tcx.typeck(self.mir_def_id());
397 let hir_id = hir.parent_id(expr.hir_id);
398 if let Some(parent) = hir.find(hir_id) {
399 let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
400 && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
401 && let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id)
403 (def_id.as_local(), args, 1)
404 } else if let hir::Node::Expr(parent_expr) = parent
405 && let hir::ExprKind::Call(call, args) = parent_expr.kind
406 && let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind()
408 (def_id.as_local(), args, 0)
412 if let Some(def_id) = def_id
413 && let Some(node) = hir.find(hir.local_def_id_to_hir_id(def_id))
414 && let Some(fn_sig) = node.fn_sig()
415 && let Some(ident) = node.ident()
416 && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
417 && let Some(arg) = fn_sig.decl.inputs.get(pos + offset)
419 let mut span: MultiSpan = arg.span.into();
420 span.push_span_label(
422 "this parameter takes ownership of the value".to_string(),
424 let descr = match node.fn_kind() {
425 Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function",
426 Some(hir::intravisit::FnKind::Method(..)) => "method",
427 Some(hir::intravisit::FnKind::Closure) => "closure",
429 span.push_span_label(
431 format!("in this {descr}"),
436 "consider changing this parameter type in {descr} `{ident}` to \
437 borrow instead if owning the value isn't necessary",
441 let place = &self.move_data.move_paths[mpi].place;
442 let ty = place.ty(self.body, self.infcx.tcx).ty;
443 if let hir::Node::Expr(parent_expr) = parent
444 && let hir::ExprKind::Call(call_expr, _) = parent_expr.kind
445 && let hir::ExprKind::Path(
446 hir::QPath::LangItem(LangItem::IntoIterIntoIter, _, _)
449 // Do not suggest `.clone()` in a `for` loop, we already suggest borrowing.
450 } else if let UseSpans::FnSelfUse {
451 kind: CallKind::Normal { .. },
454 // We already suggest cloning for these cases in `explain_captures`.
456 self.suggest_cloning(err, ty, move_span);
460 if let Some(pat) = finder.pat {
462 let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())];
463 if let Some(pat) = finder.parent_pat {
464 sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string()));
466 err.multipart_suggestion_verbose(
467 "borrow this binding in the pattern to avoid moving the value",
469 Applicability::MachineApplicable,
475 fn report_use_of_uninitialized(
478 used_place: PlaceRef<'tcx>,
479 moved_place: PlaceRef<'tcx>,
480 desired_action: InitializationRequiringAction,
482 use_spans: UseSpans<'tcx>,
483 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
484 // We need all statements in the body where the binding was assigned to to later find all
485 // the branching code paths where the binding *wasn't* assigned to.
486 let inits = &self.move_data.init_path_map[mpi];
487 let move_path = &self.move_data.move_paths[mpi];
488 let decl_span = self.body.local_decls[move_path.place.local].source_info.span;
489 let mut spans = vec![];
490 for init_idx in inits {
491 let init = &self.move_data.inits[*init_idx];
492 let span = init.span(&self.body);
493 if !span.is_dummy() {
498 let (name, desc) = match self.describe_place_with_options(
500 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
502 Some(name) => (format!("`{name}`"), format!("`{name}` ")),
503 None => ("the variable".to_string(), String::new()),
505 let path = match self.describe_place_with_options(
507 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
509 Some(name) => format!("`{name}`"),
510 None => "value".to_string(),
513 // We use the statements were the binding was initialized, and inspect the HIR to look
514 // for the branching codepaths that aren't covered, to point at them.
515 let map = self.infcx.tcx.hir();
516 let body_id = map.body_owned_by(self.mir_def_id());
517 let body = map.body(body_id);
519 let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] };
520 visitor.visit_body(&body);
522 let mut show_assign_sugg = false;
523 let isnt_initialized = if let InitializationRequiringAction::PartialAssignment
524 | InitializationRequiringAction::Assignment = desired_action
526 // The same error is emitted for bindings that are *sometimes* initialized and the ones
527 // that are *partially* initialized by assigning to a field of an uninitialized
528 // binding. We differentiate between them for more accurate wording here.
529 "isn't fully initialized"
530 } else if !spans.iter().any(|i| {
531 // We filter these to avoid misleading wording in cases like the following,
532 // where `x` has an `init`, but it is in the same place we're looking at:
538 // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
543 .any(|sp| span < sp && !sp.contains(span))
545 show_assign_sugg = true;
548 "is possibly-uninitialized"
551 let used = desired_action.as_general_verb_in_past_tense();
553 struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}");
554 use_spans.var_path_only_subdiag(&mut err, desired_action);
556 if let InitializationRequiringAction::PartialAssignment
557 | InitializationRequiringAction::Assignment = desired_action
560 "partial initialization isn't supported, fully initialize the binding with a \
561 default value and mutate it, or use `std::mem::MaybeUninit`",
564 err.span_label(span, format!("{path} {used} here but it {isnt_initialized}"));
566 let mut shown = false;
567 for (sp, label) in visitor.errors {
568 if sp < span && !sp.overlaps(span) {
569 // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention
570 // match arms coming after the primary span because they aren't relevant:
574 // _ if { x = 2; true } => {}
579 // _ => {} // We don't want to point to this.
582 err.span_label(sp, &label);
588 if *sp < span && !sp.overlaps(span) {
589 err.span_label(*sp, "binding initialized here in some conditions");
594 err.span_label(decl_span, "binding declared here but left uninitialized");
595 if show_assign_sugg {
598 sugg_span: Option<Span>,
601 impl<'v> Visitor<'v> for LetVisitor {
602 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
603 if self.sugg_span.is_some() {
606 if let hir::StmtKind::Local(hir::Local {
607 span, ty, init: None, ..
608 }) = &ex.kind && span.contains(self.decl_span) {
609 self.sugg_span = ty.map_or(Some(self.decl_span), |ty| Some(ty.span));
611 hir::intravisit::walk_stmt(self, ex);
615 let mut visitor = LetVisitor { decl_span, sugg_span: None };
616 visitor.visit_body(&body);
617 if let Some(span) = visitor.sugg_span {
618 self.suggest_assign_value(&mut err, moved_place, span);
624 fn suggest_assign_value(
626 err: &mut Diagnostic,
627 moved_place: PlaceRef<'tcx>,
630 let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
631 debug!("ty: {:?}, kind: {:?}", ty, ty.kind());
633 let tcx = self.infcx.tcx;
634 let implements_default = |ty, param_env| {
635 let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else {
638 // Regions are already solved, so we must use a fresh InferCtxt,
639 // but the type has region variables, so erase those.
642 .type_implements_trait(default_trait, [tcx.erase_regions(ty)], param_env)
643 .must_apply_modulo_regions()
646 let assign_value = match ty.kind() {
648 ty::Float(_) => "0.0",
649 ty::Int(_) | ty::Uint(_) => "0",
650 ty::Never | ty::Error(_) => "",
651 ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => "vec![]",
652 ty::Adt(_, _) if implements_default(ty, self.param_env) => "Default::default()",
656 if !assign_value.is_empty() {
657 err.span_suggestion_verbose(
658 sugg_span.shrink_to_hi(),
659 "consider assigning a value",
660 format!(" = {}", assign_value),
661 Applicability::MaybeIncorrect,
666 fn suggest_borrow_fn_like(
668 err: &mut Diagnostic,
670 move_sites: &[MoveSite],
673 let tcx = self.infcx.tcx;
675 // Find out if the predicates show that the type is a Fn or FnMut
676 let find_fn_kind_from_did = |predicates: ty::EarlyBinder<
677 &[(ty::Predicate<'tcx>, Span)],
680 predicates.0.iter().find_map(|(pred, _)| {
681 let pred = if let Some(substs) = substs {
682 predicates.rebind(*pred).subst(tcx, substs).kind().skip_binder()
684 pred.kind().skip_binder()
686 if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred && pred.self_ty() == ty {
687 if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
688 return Some(hir::Mutability::Not);
689 } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
690 return Some(hir::Mutability::Mut);
697 // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
698 // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
699 // These types seem reasonably opaque enough that they could be substituted with their
700 // borrowed variants in a function body when we see a move error.
701 let borrow_level = match ty.kind() {
702 ty::Param(_) => find_fn_kind_from_did(
703 tcx.bound_explicit_predicates_of(self.mir_def_id().to_def_id())
704 .map_bound(|p| p.predicates),
707 ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
708 find_fn_kind_from_did(tcx.bound_explicit_item_bounds(*def_id), Some(*substs))
710 ty::Closure(_, substs) => match substs.as_closure().kind() {
711 ty::ClosureKind::Fn => Some(hir::Mutability::Not),
712 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
718 let Some(borrow_level) = borrow_level else { return false; };
719 let sugg = move_sites
722 let move_out = self.move_data.moves[(*move_site).moi];
723 let moved_place = &self.move_data.move_paths[move_out.path].place;
724 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
725 let move_span = move_spans.args_or_use();
726 let suggestion = borrow_level.ref_prefix_str().to_owned();
727 (move_span.shrink_to_lo(), suggestion)
730 err.multipart_suggestion_verbose(
731 format!("consider {}borrowing {value_name}", borrow_level.mutably_str()),
733 Applicability::MaybeIncorrect,
738 fn suggest_cloning(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
739 let tcx = self.infcx.tcx;
740 // Try to find predicates on *generic params* that would allow copying `ty`
741 let infcx = tcx.infer_ctxt().build();
743 if let Some(clone_trait_def) = tcx.lang_items().clone_trait()
745 .type_implements_trait(
747 [tcx.erase_regions(ty)],
750 .must_apply_modulo_regions()
752 err.span_suggestion_verbose(
754 "consider cloning the value if the performance cost is acceptable",
756 Applicability::MachineApplicable,
761 fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
762 let tcx = self.infcx.tcx;
763 let generics = tcx.generics_of(self.mir_def_id());
765 let Some(hir_generics) = tcx
766 .typeck_root_def_id(self.mir_def_id().to_def_id())
768 .and_then(|def_id| tcx.hir().get_generics(def_id))
770 // Try to find predicates on *generic params* that would allow copying `ty`
771 let infcx = tcx.infer_ctxt().build();
772 let copy_did = infcx.tcx.require_lang_item(LangItem::Copy, Some(span));
773 let cause = ObligationCause::new(
776 rustc_infer::traits::ObligationCauseCode::MiscObligation,
778 let errors = rustc_trait_selection::traits::fully_solve_bound(
782 // Erase any region vids from the type, which may not be resolved
783 infcx.tcx.erase_regions(ty),
787 // Only emit suggestion if all required predicates are on generic
788 let predicates: Result<Vec<_>, _> = errors
790 .map(|err| match err.obligation.predicate.kind().skip_binder() {
791 PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
792 match predicate.self_ty().kind() {
793 ty::Param(param_ty) => Ok((
794 generics.type_param(param_ty, tcx),
795 predicate.trait_ref.print_only_trait_path().to_string(),
804 if let Ok(predicates) = predicates {
805 suggest_constraining_type_params(
811 .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
816 pub(crate) fn report_move_out_while_borrowed(
819 (place, span): (Place<'tcx>, Span),
820 borrow: &BorrowData<'tcx>,
823 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
824 location, place, span, borrow
826 let value_msg = self.describe_any_place(place.as_ref());
827 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
829 let borrow_spans = self.retrieve_borrow_spans(borrow);
830 let borrow_span = borrow_spans.args_or_use();
832 let move_spans = self.move_spans(place.as_ref(), location);
833 let span = move_spans.args_or_use();
835 let mut err = self.cannot_move_when_borrowed(
838 &self.describe_any_place(place.as_ref()),
843 borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow);
845 move_spans.var_span_label(
847 format!("move occurs due to use{}", move_spans.describe()),
851 self.explain_why_borrow_contains_point(location, borrow, None)
852 .add_explanation_to_diagnostic(
861 self.buffer_error(err);
864 pub(crate) fn report_use_while_mutably_borrowed(
867 (place, _span): (Place<'tcx>, Span),
868 borrow: &BorrowData<'tcx>,
869 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
870 let borrow_spans = self.retrieve_borrow_spans(borrow);
871 let borrow_span = borrow_spans.args_or_use();
873 // Conflicting borrows are reported separately, so only check for move
875 let use_spans = self.move_spans(place.as_ref(), location);
876 let span = use_spans.var_or_use();
878 // If the attempted use is in a closure then we do not care about the path span of the place we are currently trying to use
879 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
880 let mut err = self.cannot_use_when_mutably_borrowed(
882 &self.describe_any_place(place.as_ref()),
884 &self.describe_any_place(borrow.borrowed_place.as_ref()),
886 borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| {
887 use crate::session_diagnostics::CaptureVarCause::*;
888 let place = &borrow.borrowed_place;
889 let desc_place = self.describe_any_place(place.as_ref());
891 Some(_) => BorrowUsePlaceGenerator { place: desc_place, var_span },
892 None => BorrowUsePlaceClosure { place: desc_place, var_span },
896 self.explain_why_borrow_contains_point(location, borrow, None)
897 .add_explanation_to_diagnostic(
909 pub(crate) fn report_conflicting_borrow(
912 (place, span): (Place<'tcx>, Span),
913 gen_borrow_kind: BorrowKind,
914 issued_borrow: &BorrowData<'tcx>,
915 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
916 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
917 let issued_span = issued_spans.args_or_use();
919 let borrow_spans = self.borrow_spans(span, location);
920 let span = borrow_spans.args_or_use();
922 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
928 let (desc_place, msg_place, msg_borrow, union_type_name) =
929 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
931 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
932 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
934 // FIXME: supply non-"" `opt_via` when appropriate
935 let first_borrow_desc;
936 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
937 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
938 first_borrow_desc = "mutable ";
939 self.cannot_reborrow_already_borrowed(
951 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
952 first_borrow_desc = "immutable ";
953 let mut err = self.cannot_reborrow_already_borrowed(
964 self.suggest_binding_for_closure_capture_self(
966 issued_borrow.borrowed_place,
972 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
973 first_borrow_desc = "first ";
974 let mut err = self.cannot_mutably_borrow_multiply(
982 self.suggest_split_at_mut_if_applicable(
985 issued_borrow.borrowed_place,
990 (BorrowKind::Unique, BorrowKind::Unique) => {
991 first_borrow_desc = "first ";
992 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
995 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
996 if let Some(immutable_section_description) =
997 self.classify_immutable_section(issued_borrow.assigned_place)
999 let mut err = self.cannot_mutate_in_immutable_section(
1003 immutable_section_description,
1006 borrow_spans.var_span_label(
1009 "borrow occurs due to use of {}{}",
1011 borrow_spans.describe(),
1018 first_borrow_desc = "immutable ";
1019 self.cannot_reborrow_already_borrowed(
1033 (BorrowKind::Unique, _) => {
1034 first_borrow_desc = "first ";
1035 self.cannot_uniquely_borrow_by_one_closure(
1047 (BorrowKind::Shared, BorrowKind::Unique) => {
1048 first_borrow_desc = "first ";
1049 self.cannot_reborrow_already_uniquely_borrowed(
1062 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
1063 first_borrow_desc = "first ";
1064 self.cannot_reborrow_already_uniquely_borrowed(
1077 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
1079 BorrowKind::Shallow,
1080 BorrowKind::Mut { .. }
1081 | BorrowKind::Unique
1082 | BorrowKind::Shared
1083 | BorrowKind::Shallow,
1084 ) => unreachable!(),
1087 if issued_spans == borrow_spans {
1088 borrow_spans.var_span_label(
1090 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
1091 gen_borrow_kind.describe_mutability(),
1094 let borrow_place = &issued_borrow.borrowed_place;
1095 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
1096 issued_spans.var_span_label(
1099 "first borrow occurs due to use of {}{}",
1101 issued_spans.describe(),
1103 issued_borrow.kind.describe_mutability(),
1106 borrow_spans.var_span_label(
1109 "second borrow occurs due to use of {}{}",
1111 borrow_spans.describe(),
1113 gen_borrow_kind.describe_mutability(),
1117 if union_type_name != "" {
1119 "{} is a field of the union `{}`, so it overlaps the field {}",
1120 msg_place, union_type_name, msg_borrow,
1124 explanation.add_explanation_to_diagnostic(
1131 Some((issued_span, span)),
1134 self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation);
1139 #[instrument(level = "debug", skip(self, err))]
1140 fn suggest_using_local_if_applicable(
1142 err: &mut Diagnostic,
1144 issued_borrow: &BorrowData<'tcx>,
1145 explanation: BorrowExplanation<'tcx>,
1147 let used_in_call = matches!(
1149 BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
1152 debug!("not later used in call");
1157 if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
1163 let outer_call_loc =
1164 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
1167 issued_borrow.reserve_location
1169 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
1171 let inner_param_location = location;
1172 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
1173 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
1176 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
1178 "`inner_param_location` {:?} is not for an assignment: {:?}",
1179 inner_param_location, inner_param_stmt
1183 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
1184 let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
1185 let Either::Right(term) = self.body.stmt_at(loc) else {
1186 debug!("{:?} is a statement, so it can't be a call", loc);
1189 let TerminatorKind::Call { args, .. } = &term.kind else {
1190 debug!("not a call: {:?}", term);
1193 debug!("checking call args for uses of inner_param: {:?}", args);
1194 if args.contains(&Operand::Move(inner_param)) {
1200 debug!("no uses of inner_param found as a by-move call arg");
1203 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
1205 let inner_call_span = inner_call_term.source_info.span;
1206 let outer_call_span = match use_span {
1208 None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span,
1210 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
1211 // FIXME: This stops the suggestion in some cases where it should be emitted.
1212 // Fix the spans for those cases so it's emitted correctly.
1214 "outer span {:?} does not strictly contain inner span {:?}",
1215 outer_call_span, inner_call_span
1222 "try adding a local storing this{}...",
1223 if use_span.is_some() { "" } else { " argument" }
1229 "...and then using that local {}",
1230 if use_span.is_some() { "here" } else { "as the argument to this call" }
1235 fn suggest_split_at_mut_if_applicable(
1237 err: &mut Diagnostic,
1239 borrowed_place: Place<'tcx>,
1241 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
1242 (&place.projection[..], &borrowed_place.projection[..])
1245 "consider using `.split_at_mut(position)` or similar method to obtain \
1246 two mutable non-overlapping sub-slices",
1251 fn suggest_binding_for_closure_capture_self(
1253 err: &mut Diagnostic,
1254 borrowed_place: Place<'tcx>,
1255 issued_spans: &UseSpans<'tcx>,
1257 let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
1258 let hir = self.infcx.tcx.hir();
1260 // check whether the borrowed place is capturing `self` by mut reference
1261 let local = borrowed_place.local;
1266 .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) else { return };
1268 struct ExpressionFinder<'hir> {
1270 closure_change_spans: Vec<Span>,
1271 closure_arg_span: Option<Span>,
1273 suggest_arg: String,
1274 hir: rustc_middle::hir::map::Map<'hir>,
1275 closure_local_id: Option<hir::HirId>,
1276 closure_call_changes: Vec<(Span, String)>,
1278 impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
1279 fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
1280 if e.span.contains(self.capture_span) {
1281 if let hir::ExprKind::Closure(&hir::Closure {
1285 fn_decl: hir::FnDecl{ inputs, .. },
1288 let Some(hir::Node::Expr(body )) = self.hir.find(body.hir_id) {
1289 self.suggest_arg = "this: &Self".to_string();
1290 if inputs.len() > 0 {
1291 self.suggest_arg.push_str(", ");
1293 self.in_closure = true;
1294 self.closure_arg_span = fn_arg_span;
1295 self.visit_expr(body);
1296 self.in_closure = false;
1299 if let hir::Expr { kind: hir::ExprKind::Path(path), .. } = e {
1300 if let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path &&
1301 seg.ident.name == kw::SelfLower && self.in_closure {
1302 self.closure_change_spans.push(e.span);
1305 hir::intravisit::walk_expr(self, e);
1308 fn visit_local(&mut self, local: &'hir hir::Local<'hir>) {
1309 if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat &&
1310 let Some(init) = local.init
1312 if let hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure {
1316 init.span.contains(self.capture_span) {
1317 self.closure_local_id = Some(*hir_id);
1320 hir::intravisit::walk_local(self, local);
1323 fn visit_stmt(&mut self, s: &'hir hir::Stmt<'hir>) {
1324 if let hir::StmtKind::Semi(e) = s.kind &&
1325 let hir::ExprKind::Call(hir::Expr { kind: hir::ExprKind::Path(path), ..}, args) = e.kind &&
1326 let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path &&
1327 let Res::Local(hir_id) = seg.res &&
1328 Some(hir_id) == self.closure_local_id {
1329 let (span, arg_str) = if args.len() > 0 {
1330 (args[0].span.shrink_to_lo(), "self, ".to_string())
1332 let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span);
1333 (span, "(self)".to_string())
1335 self.closure_call_changes.push((span, arg_str));
1337 hir::intravisit::walk_stmt(self, s);
1341 if let Some(hir::Node::ImplItem(
1342 hir::ImplItem { kind: hir::ImplItemKind::Fn(_fn_sig, body_id), .. }
1343 )) = hir.find(self.mir_hir_id()) &&
1344 let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) {
1345 let mut finder = ExpressionFinder {
1346 capture_span: *capture_kind_span,
1347 closure_change_spans: vec![],
1348 closure_arg_span: None,
1350 suggest_arg: String::new(),
1351 closure_local_id: None,
1352 closure_call_changes: vec![],
1355 finder.visit_expr(expr);
1357 if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() {
1361 let mut sugg = vec![];
1362 let sm = self.infcx.tcx.sess.source_map();
1364 if let Some(span) = finder.closure_arg_span {
1365 sugg.push((sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg));
1367 for span in finder.closure_change_spans {
1368 sugg.push((span, "this".to_string()));
1371 for (span, suggest) in finder.closure_call_changes {
1372 sugg.push((span, suggest));
1375 err.multipart_suggestion_verbose(
1376 "try explicitly pass `&Self` into the Closure as an argument",
1378 Applicability::MachineApplicable,
1383 /// Returns the description of the root place for a conflicting borrow and the full
1384 /// descriptions of the places that caused the conflict.
1386 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
1387 /// attempted while a shared borrow is live, then this function will return:
1392 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
1393 /// a shared borrow of another field `x.y`, then this function will return:
1395 /// ("x", "x.z", "x.y")
1398 /// In the more complex union case, where the union is a field of a struct, then if a mutable
1399 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
1400 /// another field `x.u.y`, then this function will return:
1402 /// ("x.u", "x.u.z", "x.u.y")
1405 /// This is used when creating error messages like below:
1408 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
1409 /// mutable (via `a.u.s.b`) [E0502]
1411 pub(crate) fn describe_place_for_conflicting_borrow(
1413 first_borrowed_place: Place<'tcx>,
1414 second_borrowed_place: Place<'tcx>,
1415 ) -> (String, String, String, String) {
1416 // Define a small closure that we can use to check if the type of a place
1418 let union_ty = |place_base| {
1419 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
1420 // using a type annotation in the closure argument instead leads to a lifetime error.
1421 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
1422 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
1425 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
1426 // code duplication (particularly around returning an empty description in the failure
1430 // If we have a conflicting borrow of the same place, then we don't want to add
1431 // an extraneous "via x.y" to our diagnostics, so filter out this case.
1432 first_borrowed_place != second_borrowed_place
1435 // We're going to want to traverse the first borrowed place to see if we can find
1436 // field access to a union. If we find that, then we will keep the place of the
1437 // union being accessed and the field that was being accessed so we can check the
1438 // second borrowed place for the same union and an access to a different field.
1439 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
1441 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
1442 return Some((place_base, field));
1449 .and_then(|(target_base, target_field)| {
1450 // With the place of a union and a field access into it, we traverse the second
1451 // borrowed place and look for an access to a different field of the same union.
1452 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1453 if let ProjectionElem::Field(field, _) = elem {
1454 if let Some(union_ty) = union_ty(place_base) {
1455 if field != target_field && place_base == target_base {
1457 self.describe_any_place(place_base),
1458 self.describe_any_place(first_borrowed_place.as_ref()),
1459 self.describe_any_place(second_borrowed_place.as_ref()),
1460 union_ty.to_string(),
1468 .unwrap_or_else(|| {
1469 // If we didn't find a field access into a union, or both places match, then
1470 // only return the description of the first place.
1472 self.describe_any_place(first_borrowed_place.as_ref()),
1480 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1482 /// This means that some data referenced by `borrow` needs to live
1483 /// past the point where the StorageDeadOrDrop of `place` occurs.
1484 /// This is usually interpreted as meaning that `place` has too
1485 /// short a lifetime. (But sometimes it is more useful to report
1486 /// it as a more direct conflict between the execution of a
1487 /// `Drop::drop` with an aliasing borrow.)
1488 #[instrument(level = "debug", skip(self))]
1489 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1492 borrow: &BorrowData<'tcx>,
1493 place_span: (Place<'tcx>, Span),
1494 kind: Option<WriteKind>,
1496 let drop_span = place_span.1;
1498 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1500 let borrow_spans = self.retrieve_borrow_spans(borrow);
1501 let borrow_span = borrow_spans.var_or_use_path_span();
1503 assert!(root_place.projection.is_empty());
1504 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1506 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1508 if self.access_place_error_reported.contains(&(
1509 Place { local: root_place.local, projection: root_place_projection },
1513 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1519 self.access_place_error_reported.insert((
1520 Place { local: root_place.local, projection: root_place_projection },
1524 let borrowed_local = borrow.borrowed_place.local;
1525 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1527 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1528 self.buffer_error(err);
1532 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1533 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1535 // If a borrow of path `B` conflicts with drop of `D` (and
1536 // we're not in the uninteresting case where `B` is a
1537 // prefix of `D`), then report this as a more interesting
1538 // destructor conflict.
1539 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1540 self.report_borrow_conflicts_with_destructor(
1541 location, borrow, place_span, kind, dropped_ty,
1547 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1549 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1550 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1552 debug!(?place_desc, ?explanation);
1554 let err = match (place_desc, explanation) {
1555 // If the outlives constraint comes from inside the closure,
1560 // Box::new(|| y) as Box<Fn() -> &'static i32>
1562 // then just use the normal error. The closure isn't escaping
1563 // and `move` will not help here.
1566 BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _),
1567 ) => self.report_escaping_closure_capture(
1571 name: self.synthesize_region_name(),
1572 source: RegionNameSource::Static,
1574 ConstraintCategory::CallArgument(None),
1576 &format!("`{}`", name),
1581 BorrowExplanation::MustBeValidFor {
1583 category @ (ConstraintCategory::Return(_)
1584 | ConstraintCategory::CallArgument(_)
1585 | ConstraintCategory::OpaqueType),
1586 from_closure: false,
1591 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1592 .report_escaping_closure_capture(
1598 &format!("`{}`", name),
1603 BorrowExplanation::MustBeValidFor {
1604 category: ConstraintCategory::Assignment,
1605 from_closure: false,
1608 source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
1614 ) => self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, span),
1615 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1623 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1633 self.buffer_error(err);
1636 fn report_local_value_does_not_live_long_enough(
1640 borrow: &BorrowData<'tcx>,
1642 borrow_spans: UseSpans<'tcx>,
1643 explanation: BorrowExplanation<'tcx>,
1644 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1646 "report_local_value_does_not_live_long_enough(\
1647 {:?}, {:?}, {:?}, {:?}, {:?}\
1649 location, name, borrow, drop_span, borrow_spans
1652 let borrow_span = borrow_spans.var_or_use_path_span();
1653 if let BorrowExplanation::MustBeValidFor {
1657 from_closure: false,
1661 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1666 opt_place_desc.as_ref(),
1672 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1674 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1675 let region_name = annotation.emit(self, &mut err);
1679 format!("`{}` would have to be valid for `{}`...", name, region_name),
1682 let fn_hir_id = self.mir_hir_id();
1686 "...but `{}` will be dropped here, when the {} returns",
1691 .opt_name(fn_hir_id)
1692 .map(|name| format!("function `{}`", name))
1693 .unwrap_or_else(|| {
1697 .typeck(self.mir_def_id())
1698 .node_type(fn_hir_id)
1701 ty::Closure(..) => "enclosing closure",
1702 ty::Generator(..) => "enclosing generator",
1703 kind => bug!("expected closure or generator, found {:?}", kind),
1711 "functions cannot return a borrow to data owned within the function's scope, \
1712 functions can only return borrows to data passed as arguments",
1715 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1716 references-and-borrowing.html#dangling-references>",
1719 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1721 explanation.add_explanation_to_diagnostic(
1732 err.span_label(borrow_span, "borrowed value does not live long enough");
1733 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1735 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1737 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1739 explanation.add_explanation_to_diagnostic(
1753 fn report_borrow_conflicts_with_destructor(
1756 borrow: &BorrowData<'tcx>,
1757 (place, drop_span): (Place<'tcx>, Span),
1758 kind: Option<WriteKind>,
1759 dropped_ty: Ty<'tcx>,
1762 "report_borrow_conflicts_with_destructor(\
1763 {:?}, {:?}, ({:?}, {:?}), {:?}\
1765 location, borrow, place, drop_span, kind,
1768 let borrow_spans = self.retrieve_borrow_spans(borrow);
1769 let borrow_span = borrow_spans.var_or_use();
1771 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1773 let what_was_dropped = match self.describe_place(place.as_ref()) {
1774 Some(name) => format!("`{}`", name),
1775 None => String::from("temporary value"),
1778 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1779 Some(borrowed) => format!(
1780 "here, drop of {D} needs exclusive access to `{B}`, \
1781 because the type `{T}` implements the `Drop` trait",
1782 D = what_was_dropped,
1787 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1788 D = what_was_dropped,
1792 err.span_label(drop_span, label);
1794 // Only give this note and suggestion if they could be relevant.
1796 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1798 BorrowExplanation::UsedLater { .. }
1799 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1800 err.note("consider using a `let` binding to create a longer lived value");
1805 explanation.add_explanation_to_diagnostic(
1815 self.buffer_error(err);
1818 fn report_thread_local_value_does_not_live_long_enough(
1822 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1824 "report_thread_local_value_does_not_live_long_enough(\
1827 drop_span, borrow_span
1830 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1834 "thread-local variables cannot be borrowed beyond the end of the function",
1836 err.span_label(drop_span, "end of enclosing function is here");
1841 #[instrument(level = "debug", skip(self))]
1842 fn report_temporary_value_does_not_live_long_enough(
1845 borrow: &BorrowData<'tcx>,
1847 borrow_spans: UseSpans<'tcx>,
1849 explanation: BorrowExplanation<'tcx>,
1850 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1851 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1854 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1865 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1866 err.span_label(proper_span, "creates a temporary value which is freed while still in use");
1867 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1870 BorrowExplanation::UsedLater(..)
1871 | BorrowExplanation::UsedLaterInLoop(..)
1872 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1873 // Only give this note and suggestion if it could be relevant.
1874 let sm = self.infcx.tcx.sess.source_map();
1875 let mut suggested = false;
1876 let msg = "consider using a `let` binding to create a longer lived value";
1878 /// We check that there's a single level of block nesting to ensure always correct
1879 /// suggestions. If we don't, then we only provide a free-form message to avoid
1880 /// misleading users in cases like `tests/ui/nll/borrowed-temporary-error.rs`.
1881 /// We could expand the analysis to suggest hoising all of the relevant parts of
1882 /// the users' code to make the code compile, but that could be too much.
1883 struct NestedStatementVisitor {
1889 impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
1890 fn visit_block(&mut self, block: &hir::Block<'tcx>) {
1892 walk_block(self, block);
1895 fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
1896 if self.span == expr.span {
1897 self.found = self.current;
1899 walk_expr(self, expr);
1902 let source_info = self.body.source_info(location);
1903 if let Some(scope) = self.body.source_scopes.get(source_info.scope)
1904 && let ClearCrossCrate::Set(scope_data) = &scope.local_data
1905 && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root)
1906 && let Some(id) = node.body_id()
1907 && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
1909 for stmt in block.stmts {
1910 let mut visitor = NestedStatementVisitor {
1915 visitor.visit_stmt(stmt);
1916 if visitor.found == 0
1917 && stmt.span.contains(proper_span)
1918 && let Some(p) = sm.span_to_margin(stmt.span)
1919 && let Ok(s) = sm.span_to_snippet(proper_span)
1921 let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
1922 err.multipart_suggestion_verbose(
1925 (stmt.span.shrink_to_lo(), addition),
1926 (proper_span, "binding".to_string()),
1928 Applicability::MaybeIncorrect,
1941 explanation.add_explanation_to_diagnostic(
1951 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1953 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1958 fn try_report_cannot_return_reference_to_local(
1960 borrow: &BorrowData<'tcx>,
1963 category: ConstraintCategory<'tcx>,
1964 opt_place_desc: Option<&String>,
1965 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1966 let return_kind = match category {
1967 ConstraintCategory::Return(_) => "return",
1968 ConstraintCategory::Yield => "yield",
1972 // FIXME use a better heuristic than Spans
1973 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1979 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1980 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1981 match self.body.local_kind(local) {
1982 LocalKind::ReturnPointer | LocalKind::Temp => {
1983 bug!("temporary or return pointer with a name")
1985 LocalKind::Var => "local variable ",
1987 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1989 "variable captured by `move` "
1991 LocalKind::Arg => "function parameter ",
1997 format!("{}`{}`", local_kind, place_desc),
1998 format!("`{}` is borrowed here", place_desc),
2002 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
2003 let local = root_place.local;
2004 match self.body.local_kind(local) {
2005 LocalKind::ReturnPointer | LocalKind::Temp => {
2006 ("temporary value".to_string(), "temporary value created here".to_string())
2009 "function parameter".to_string(),
2010 "function parameter borrowed here".to_string(),
2013 ("local binding".to_string(), "local binding introduced here".to_string())
2018 let mut err = self.cannot_return_reference_to_local(
2025 if return_span != borrow_span {
2026 err.span_label(borrow_span, note);
2028 let tcx = self.infcx.tcx;
2030 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
2031 let return_ty = tcx.erase_regions(return_ty);
2034 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
2037 .type_implements_trait(iter_trait, [return_ty], self.param_env)
2038 .must_apply_modulo_regions()
2040 err.span_suggestion_hidden(
2041 return_span.shrink_to_hi(),
2042 "use `.collect()` to allocate the iterator",
2043 ".collect::<Vec<_>>()",
2044 Applicability::MaybeIncorrect,
2052 #[instrument(level = "debug", skip(self))]
2053 fn report_escaping_closure_capture(
2055 use_span: UseSpans<'tcx>,
2057 fr_name: &RegionName,
2058 category: ConstraintCategory<'tcx>,
2059 constraint_span: Span,
2062 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
2063 let tcx = self.infcx.tcx;
2064 let args_span = use_span.args_or_use();
2066 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
2068 if string.starts_with("async ") {
2069 let pos = args_span.lo() + BytePos(6);
2070 (args_span.with_lo(pos).with_hi(pos), "move ")
2071 } else if string.starts_with("async|") {
2072 let pos = args_span.lo() + BytePos(5);
2073 (args_span.with_lo(pos).with_hi(pos), " move")
2075 (args_span.shrink_to_lo(), "move ")
2078 Err(_) => (args_span, "move |<args>| <body>"),
2080 let kind = match use_span.generator_kind() {
2081 Some(generator_kind) => match generator_kind {
2082 GeneratorKind::Async(async_kind) => match async_kind {
2083 AsyncGeneratorKind::Block => "async block",
2084 AsyncGeneratorKind::Closure => "async closure",
2085 _ => bug!("async block/closure expected, but async function found."),
2087 GeneratorKind::Gen => "generator",
2092 let mut err = self.cannot_capture_in_long_lived_closure(
2099 err.span_suggestion_verbose(
2102 "to force the {} to take ownership of {} (and any \
2103 other referenced variables), use the `move` keyword",
2107 Applicability::MachineApplicable,
2111 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
2112 let msg = format!("{} is returned here", kind);
2113 err.span_note(constraint_span, &msg);
2115 ConstraintCategory::CallArgument(_) => {
2116 fr_name.highlight_region_name(&mut err);
2117 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
2119 "async blocks are not executed immediately and must either take a \
2120 reference or ownership of outside variables they use",
2123 let msg = format!("{scope} requires argument type to outlive `{fr_name}`");
2124 err.span_note(constraint_span, &msg);
2128 "report_escaping_closure_capture called with unexpected constraint \
2137 fn report_escaping_data(
2140 name: &Option<String>,
2144 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
2145 let tcx = self.infcx.tcx;
2147 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
2150 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
2154 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
2157 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
2159 if let Some(name) = name {
2162 format!("reference to `{}` escapes the {} body here", name, escapes_from),
2167 format!("reference escapes the {} body here", escapes_from),
2174 fn get_moved_indexes(
2178 ) -> (Vec<MoveSite>, Vec<Location>) {
2179 fn predecessor_locations<'tcx, 'a>(
2180 body: &'a mir::Body<'tcx>,
2182 ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
2183 if location.statement_index == 0 {
2184 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
2185 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
2187 Either::Right(std::iter::once(Location {
2188 statement_index: location.statement_index - 1,
2194 let mut mpis = vec![mpi];
2195 let move_paths = &self.move_data.move_paths;
2196 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
2198 let mut stack = Vec::new();
2199 let mut back_edge_stack = Vec::new();
2201 predecessor_locations(self.body, location).for_each(|predecessor| {
2202 if location.dominates(predecessor, &self.dominators) {
2203 back_edge_stack.push(predecessor)
2205 stack.push(predecessor);
2209 let mut reached_start = false;
2211 /* Check if the mpi is initialized as an argument */
2212 let mut is_argument = false;
2213 for arg in self.body.args_iter() {
2214 let path = self.move_data.rev_lookup.find_local(arg);
2215 if mpis.contains(&path) {
2220 let mut visited = FxHashSet::default();
2221 let mut move_locations = FxHashSet::default();
2222 let mut reinits = vec![];
2223 let mut result = vec![];
2225 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
2227 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
2228 location, is_back_edge
2231 if !visited.insert(location) {
2237 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
2238 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
2239 // this analysis only tries to find moves explicitly
2240 // written by the user, so we ignore the move-outs
2241 // created by `StorageDead` and at the beginning
2244 // If we are found a use of a.b.c which was in error, then we want to look for
2245 // moves not only of a.b.c but also a.b and a.
2247 // Note that the moves data already includes "parent" paths, so we don't have to
2248 // worry about the other case: that is, if there is a move of a.b.c, it is already
2249 // marked as a move of a.b and a as well, so we will generate the correct errors
2251 for moi in &self.move_data.loc_map[location] {
2252 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
2253 let path = self.move_data.moves[*moi].path;
2254 if mpis.contains(&path) {
2256 "report_use_of_moved_or_uninitialized: found {:?}",
2257 move_paths[path].place
2259 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
2260 move_locations.insert(location);
2262 // Strictly speaking, we could continue our DFS here. There may be
2263 // other moves that can reach the point of error. But it is kind of
2264 // confusing to highlight them.
2272 // drop(a); // <-- current point of error
2275 // Because we stop the DFS here, we only highlight `let c = a`,
2276 // and not `let b = a`. We will of course also report an error at
2277 // `let c = a` which highlights `let b = a` as the move.
2284 let mut any_match = false;
2285 for ii in &self.move_data.init_loc_map[location] {
2286 let init = self.move_data.inits[*ii];
2288 InitKind::Deep | InitKind::NonPanicPathOnly => {
2289 if mpis.contains(&init.path) {
2293 InitKind::Shallow => {
2294 if mpi == init.path {
2301 reinits.push(location);
2307 while let Some(location) = stack.pop() {
2308 if dfs_iter(&mut result, location, false) {
2312 let mut has_predecessor = false;
2313 predecessor_locations(self.body, location).for_each(|predecessor| {
2314 if location.dominates(predecessor, &self.dominators) {
2315 back_edge_stack.push(predecessor)
2317 stack.push(predecessor);
2319 has_predecessor = true;
2322 if !has_predecessor {
2323 reached_start = true;
2326 if (is_argument || !reached_start) && result.is_empty() {
2327 /* Process back edges (moves in future loop iterations) only if
2328 the move path is definitely initialized upon loop entry,
2329 to avoid spurious "in previous iteration" errors.
2330 During DFS, if there's a path from the error back to the start
2331 of the function with no intervening init or move, then the
2332 move path may be uninitialized at loop entry.
2334 while let Some(location) = back_edge_stack.pop() {
2335 if dfs_iter(&mut result, location, true) {
2339 predecessor_locations(self.body, location)
2340 .for_each(|predecessor| back_edge_stack.push(predecessor));
2344 // Check if we can reach these reinits from a move location.
2345 let reinits_reachable = reinits
2348 let mut visited = FxHashSet::default();
2349 let mut stack = vec![*reinit];
2350 while let Some(location) = stack.pop() {
2351 if !visited.insert(location) {
2354 if move_locations.contains(&location) {
2357 stack.extend(predecessor_locations(self.body, location));
2361 .collect::<Vec<Location>>();
2362 (result, reinits_reachable)
2365 pub(crate) fn report_illegal_mutation_of_borrowed(
2368 (place, span): (Place<'tcx>, Span),
2369 loan: &BorrowData<'tcx>,
2371 let loan_spans = self.retrieve_borrow_spans(loan);
2372 let loan_span = loan_spans.args_or_use();
2374 let descr_place = self.describe_any_place(place.as_ref());
2375 if loan.kind == BorrowKind::Shallow {
2376 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
2377 let mut err = self.cannot_mutate_in_immutable_section(
2384 loan_spans.var_span_label(
2386 format!("borrow occurs due to use{}", loan_spans.describe()),
2387 loan.kind.describe_mutability(),
2390 self.buffer_error(err);
2396 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
2398 loan_spans.var_span_label(
2400 format!("borrow occurs due to use{}", loan_spans.describe()),
2401 loan.kind.describe_mutability(),
2404 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
2414 self.explain_deref_coercion(loan, &mut err);
2416 self.buffer_error(err);
2419 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
2420 let tcx = self.infcx.tcx;
2422 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
2423 Some((method_did, method_substs)),
2425 &self.body[loan.reserve_location.block].terminator,
2426 rustc_const_eval::util::find_self_call(
2429 loan.assigned_place.local,
2430 loan.reserve_location.block,
2433 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
2435 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
2436 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
2439 if let Some(Ok(instance)) = deref_target {
2440 let deref_target_ty = instance.ty(tcx, self.param_env);
2442 "borrow occurs due to deref coercion to `{}`",
2445 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
2451 /// Reports an illegal reassignment; for example, an assignment to
2452 /// (part of) a non-`mut` local that occurs potentially after that
2453 /// local has already been initialized. `place` is the path being
2454 /// assigned; `err_place` is a place providing a reason why
2455 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
2456 /// assignment to `x.f`).
2457 pub(crate) fn report_illegal_reassignment(
2459 _location: Location,
2460 (place, span): (Place<'tcx>, Span),
2461 assigned_span: Span,
2462 err_place: Place<'tcx>,
2464 let (from_arg, local_decl, local_name) = match err_place.as_local() {
2466 self.body.local_kind(local) == LocalKind::Arg,
2467 Some(&self.body.local_decls[local]),
2468 self.local_names[local],
2470 None => (false, None, None),
2473 // If root local is initialized immediately (everything apart from let
2474 // PATTERN;) then make the error refer to that local, rather than the
2475 // place being assigned later.
2476 let (place_description, assigned_span) = match local_decl {
2479 Some(box LocalInfo::User(
2480 ClearCrossCrate::Clear
2481 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
2482 opt_match_place: None,
2486 | Some(box LocalInfo::StaticRef { .. })
2490 | None => (self.describe_any_place(place.as_ref()), assigned_span),
2491 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
2494 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
2495 let msg = if from_arg {
2496 "cannot assign to immutable argument"
2498 "cannot assign twice to immutable variable"
2500 if span != assigned_span && !from_arg {
2501 err.span_label(assigned_span, format!("first assignment to {}", place_description));
2503 if let Some(decl) = local_decl
2504 && let Some(name) = local_name
2505 && decl.can_be_made_mutable()
2507 err.span_suggestion(
2508 decl.source_info.span,
2509 "consider making this binding mutable",
2510 format!("mut {}", name),
2511 Applicability::MachineApplicable,
2514 err.span_label(span, msg);
2515 self.buffer_error(err);
2518 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2519 let tcx = self.infcx.tcx;
2520 let (kind, _place_ty) = place.projection.iter().fold(
2521 (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
2522 |(kind, place_ty), &elem| {
2525 ProjectionElem::Deref => match kind {
2526 StorageDeadOrDrop::LocalStorageDead
2527 | StorageDeadOrDrop::BoxedStorageDead => {
2529 place_ty.ty.is_box(),
2530 "Drop of value behind a reference or raw pointer"
2532 StorageDeadOrDrop::BoxedStorageDead
2534 StorageDeadOrDrop::Destructor(_) => kind,
2536 ProjectionElem::OpaqueCast { .. }
2537 | ProjectionElem::Field(..)
2538 | ProjectionElem::Downcast(..) => {
2539 match place_ty.ty.kind() {
2540 ty::Adt(def, _) if def.has_dtor(tcx) => {
2541 // Report the outermost adt with a destructor
2543 StorageDeadOrDrop::Destructor(_) => kind,
2544 StorageDeadOrDrop::LocalStorageDead
2545 | StorageDeadOrDrop::BoxedStorageDead => {
2546 StorageDeadOrDrop::Destructor(place_ty.ty)
2553 ProjectionElem::ConstantIndex { .. }
2554 | ProjectionElem::Subslice { .. }
2555 | ProjectionElem::Index(_) => kind,
2557 place_ty.projection_ty(tcx, elem),
2564 /// Describe the reason for the fake borrow that was assigned to `place`.
2565 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2566 use rustc_middle::mir::visit::Visitor;
2567 struct FakeReadCauseFinder<'tcx> {
2569 cause: Option<FakeReadCause>,
2571 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2572 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2574 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2575 if *place == self.place =>
2577 self.cause = Some(*cause);
2583 let mut visitor = FakeReadCauseFinder { place, cause: None };
2584 visitor.visit_body(&self.body);
2585 match visitor.cause {
2586 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2587 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2592 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2593 /// borrow of local value that does not live long enough.
2594 fn annotate_argument_and_return_for_borrow(
2596 borrow: &BorrowData<'tcx>,
2597 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2598 // Define a fallback for when we can't match a closure.
2600 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2604 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2606 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2608 self.infcx.tcx.fn_sig(self.mir_def_id()),
2615 // In order to determine whether we need to annotate, we need to check whether the reserve
2616 // place was an assignment into a temporary.
2618 // If it was, we check whether or not that temporary is eventually assigned into the return
2619 // place. If it was, we can add annotations about the function's return type and arguments
2620 // and it'll make sense.
2621 let location = borrow.reserve_location;
2622 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2623 if let Some(Statement { kind: StatementKind::Assign(box (reservation, _)), .. }) =
2624 &self.body[location.block].statements.get(location.statement_index)
2626 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2627 // Check that the initial assignment of the reserve location is into a temporary.
2628 let mut target = match reservation.as_local() {
2629 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2633 // Next, look through the rest of the block, checking if we are assigning the
2634 // `target` (that is, the place that contains our borrow) to anything.
2635 let mut annotated_closure = None;
2636 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2638 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2641 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2642 if let Some(assigned_to) = place.as_local() {
2644 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2648 // Check if our `target` was captured by a closure.
2649 if let Rvalue::Aggregate(
2650 box AggregateKind::Closure(def_id, substs),
2654 for operand in operands {
2655 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2659 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2663 // Find the local from the operand.
2664 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2668 if assigned_from_local != target {
2672 // If a closure captured our `target` and then assigned
2673 // into a place then we should annotate the closure in
2674 // case it ends up being assigned into the return place.
2676 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2678 "annotate_argument_and_return_for_borrow: \
2679 annotated_closure={:?} assigned_from_local={:?} \
2681 annotated_closure, assigned_from_local, assigned_to
2684 if assigned_to == mir::RETURN_PLACE {
2685 // If it was assigned directly into the return place, then
2687 return annotated_closure;
2689 // Otherwise, update the target.
2690 target = assigned_to;
2694 // If none of our closure's operands matched, then skip to the next
2699 // Otherwise, look at other types of assignment.
2700 let assigned_from = match rvalue {
2701 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2702 Rvalue::Use(operand) => match operand {
2703 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2711 "annotate_argument_and_return_for_borrow: \
2712 assigned_from={:?}",
2716 // Find the local from the rvalue.
2717 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2719 "annotate_argument_and_return_for_borrow: \
2720 assigned_from_local={:?}",
2721 assigned_from_local,
2724 // Check if our local matches the target - if so, we've assigned our
2725 // borrow to a new place.
2726 if assigned_from_local != target {
2730 // If we assigned our `target` into a new place, then we should
2731 // check if it was the return place.
2733 "annotate_argument_and_return_for_borrow: \
2734 assigned_from_local={:?} assigned_to={:?}",
2735 assigned_from_local, assigned_to
2737 if assigned_to == mir::RETURN_PLACE {
2738 // If it was then return the annotated closure if there was one,
2739 // else, annotate this function.
2740 return annotated_closure.or_else(fallback);
2743 // If we didn't assign into the return place, then we just update
2745 target = assigned_to;
2750 // Check the terminator if we didn't find anything in the statements.
2751 let terminator = &self.body[location.block].terminator();
2753 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2756 if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
2759 if let Some(assigned_to) = destination.as_local() {
2761 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2764 for operand in args {
2765 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2769 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2773 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2775 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2776 assigned_from_local,
2779 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2780 return annotated_closure.or_else(fallback);
2788 // If we haven't found an assignment into the return place, then we need not add
2790 debug!("annotate_argument_and_return_for_borrow: none found");
2794 /// Annotate the first argument and return type of a function signature if they are
2799 sig: ty::PolyFnSig<'tcx>,
2800 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2801 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2802 let is_closure = self.infcx.tcx.is_closure(did.to_def_id());
2803 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
2804 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2806 // We need to work out which arguments to highlight. We do this by looking
2807 // at the return type, where there are three cases:
2809 // 1. If there are named arguments, then we should highlight the return type and
2810 // highlight any of the arguments that are also references with that lifetime.
2811 // If there are no arguments that have the same lifetime as the return type,
2812 // then don't highlight anything.
2813 // 2. The return type is a reference with an anonymous lifetime. If this is
2814 // the case, then we can take advantage of (and teach) the lifetime elision
2817 // We know that an error is being reported. So the arguments and return type
2818 // must satisfy the elision rules. Therefore, if there is a single argument
2819 // then that means the return type and first (and only) argument have the same
2820 // lifetime and the borrow isn't meeting that, we can highlight the argument
2823 // If there are multiple arguments then the first argument must be self (else
2824 // it would not satisfy the elision rules), so we can highlight self and the
2826 // 3. The return type is not a reference. In this case, we don't highlight
2828 let return_ty = sig.output();
2829 match return_ty.skip_binder().kind() {
2830 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2831 // This is case 1 from above, return type is a named reference so we need to
2832 // search for relevant arguments.
2833 let mut arguments = Vec::new();
2834 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2835 if let ty::Ref(argument_region, _, _) = argument.kind() {
2836 if argument_region == return_region {
2837 // Need to use the `rustc_middle::ty` types to compare against the
2838 // `return_region`. Then use the `rustc_hir` type to get only
2839 // the lifetime span.
2840 if let hir::TyKind::Ref(lifetime, _) = &fn_decl.inputs[index].kind {
2841 // With access to the lifetime, we can get
2843 arguments.push((*argument, lifetime.ident.span));
2845 bug!("ty type is a ref but hir type is not");
2851 // We need to have arguments. This shouldn't happen, but it's worth checking.
2852 if arguments.is_empty() {
2856 // We use a mix of the HIR and the Ty types to get information
2857 // as the HIR doesn't have full types for closure arguments.
2858 let return_ty = sig.output().skip_binder();
2859 let mut return_span = fn_decl.output.span();
2860 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2861 if let hir::TyKind::Ref(lifetime, _) = ty.kind {
2862 return_span = lifetime.ident.span;
2866 Some(AnnotatedBorrowFnSignature::NamedFunction {
2872 ty::Ref(_, _, _) if is_closure => {
2873 // This is case 2 from above but only for closures, return type is anonymous
2874 // reference so we select
2875 // the first argument.
2876 let argument_span = fn_decl.inputs.first()?.span;
2877 let argument_ty = sig.inputs().skip_binder().first()?;
2879 // Closure arguments are wrapped in a tuple, so we need to get the first
2881 if let ty::Tuple(elems) = argument_ty.kind() {
2882 let &argument_ty = elems.first()?;
2883 if let ty::Ref(_, _, _) = argument_ty.kind() {
2884 return Some(AnnotatedBorrowFnSignature::Closure {
2893 ty::Ref(_, _, _) => {
2894 // This is also case 2 from above but for functions, return type is still an
2895 // anonymous reference so we select the first argument.
2896 let argument_span = fn_decl.inputs.first()?.span;
2897 let argument_ty = *sig.inputs().skip_binder().first()?;
2899 let return_span = fn_decl.output.span();
2900 let return_ty = sig.output().skip_binder();
2902 // We expect the first argument to be a reference.
2903 match argument_ty.kind() {
2904 ty::Ref(_, _, _) => {}
2908 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2916 // This is case 3 from above, return type is not a reference so don't highlight
2925 enum AnnotatedBorrowFnSignature<'tcx> {
2927 arguments: Vec<(Ty<'tcx>, Span)>,
2928 return_ty: Ty<'tcx>,
2932 argument_ty: Ty<'tcx>,
2933 argument_span: Span,
2934 return_ty: Ty<'tcx>,
2938 argument_ty: Ty<'tcx>,
2939 argument_span: Span,
2943 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2944 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2946 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2948 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2951 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2954 cx.get_region_name_for_ty(argument_ty, 0)
2956 &AnnotatedBorrowFnSignature::AnonymousFunction {
2962 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2963 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2965 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2966 let types_equal = return_ty_name == argument_ty_name;
2971 if types_equal { "also " } else { "" },
2977 "argument and return type have the same lifetime due to lifetime elision rules",
2980 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2981 lifetime-syntax.html#lifetime-elision>",
2984 cx.get_region_name_for_ty(return_ty, 0)
2986 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2987 // Region of return type and arguments checked to be the same earlier.
2988 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2989 for (_, argument_span) in arguments {
2990 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2993 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2996 "use data from the highlighted arguments which match the `{}` lifetime of \
3007 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr
3008 struct ReferencedStatementsVisitor<'a>(&'a [Span], bool);
3010 impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> {
3011 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
3013 hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => {
3021 /// Given a set of spans representing statements initializing the relevant binding, visit all the
3022 /// function expressions looking for branching code paths that *do not* initialize the binding.
3023 struct ConditionVisitor<'b> {
3026 errors: Vec<(Span, String)>,
3029 impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
3030 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
3032 hir::ExprKind::If(cond, body, None) => {
3033 // `if` expressions with no `else` that initialize the binding might be missing an
3035 let mut v = ReferencedStatementsVisitor(self.spans, false);
3041 "if this `if` condition is `false`, {} is not initialized",
3046 ex.span.shrink_to_hi(),
3047 format!("an `else` arm might be missing here, initializing {}", self.name),
3051 hir::ExprKind::If(cond, body, Some(other)) => {
3052 // `if` expressions where the binding is only initialized in one of the two arms
3053 // might be missing a binding initialization.
3054 let mut a = ReferencedStatementsVisitor(self.spans, false);
3056 let mut b = ReferencedStatementsVisitor(self.spans, false);
3057 b.visit_expr(other);
3059 (true, true) | (false, false) => {}
3061 if other.span.is_desugaring(DesugaringKind::WhileLoop) {
3065 "if this condition isn't met and the `while` loop runs 0 \
3066 times, {} is not initialized",
3072 body.span.shrink_to_hi().until(other.span),
3074 "if the `if` condition is `false` and this `else` arm is \
3075 executed, {} is not initialized",
3085 "if this condition is `true`, {} is not initialized",
3092 hir::ExprKind::Match(e, arms, loop_desugar) => {
3093 // If the binding is initialized in one of the match arms, then the other match
3094 // arms might be missing an initialization.
3095 let results: Vec<bool> = arms
3098 let mut v = ReferencedStatementsVisitor(self.spans, false);
3103 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
3104 for (arm, seen) in arms.iter().zip(results) {
3106 if loop_desugar == hir::MatchSource::ForLoopDesugar {
3110 "if the `for` loop runs 0 times, {} is not initialized",
3114 } else if let Some(guard) = &arm.guard {
3116 arm.pat.span.to(guard.body().span),
3118 "if this pattern and condition are matched, {} is not \
3127 "if this pattern is matched, {} is not initialized",
3136 // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should
3137 // also be accounted for. For now it is fine, as if we don't find *any* relevant
3138 // branching code paths, we point at the places where the binding *is* initialized for
3142 walk_expr(self, ex);