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::intravisit::{walk_block, walk_expr, Visitor};
10 use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
11 use rustc_infer::infer::TyCtxtInferExt;
12 use rustc_infer::traits::ObligationCause;
13 use rustc_middle::mir::tcx::PlaceTy;
14 use rustc_middle::mir::{
15 self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
16 FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
17 ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
19 use rustc_middle::ty::{self, subst::Subst, suggest_constraining_type_params, PredicateKind, Ty};
20 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
21 use rustc_span::def_id::LocalDefId;
22 use rustc_span::hygiene::DesugaringKind;
23 use rustc_span::symbol::sym;
24 use rustc_span::{BytePos, Span, Symbol};
25 use rustc_trait_selection::infer::InferCtxtExt;
26 use rustc_trait_selection::traits::TraitEngineExt as _;
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;
34 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
35 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
39 explain_borrow::{BorrowExplanation, LaterUseKind},
40 DescribePlaceOpt, RegionName, RegionNameSource, UseSpans,
45 /// Index of the "move out" that we found. The `MoveData` can
46 /// then tell us where the move occurred.
49 /// `true` if we traversed a back edge while walking from the point
50 /// of error to the move site.
51 traversed_back_edge: bool,
54 /// Which case a StorageDeadOrDrop is for.
55 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
56 enum StorageDeadOrDrop<'tcx> {
62 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
63 pub(crate) fn report_use_of_moved_or_uninitialized(
66 desired_action: InitializationRequiringAction,
67 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
71 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
72 moved_place={:?} used_place={:?} span={:?} mpi={:?}",
73 location, desired_action, moved_place, used_place, span, mpi
77 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
78 let span = use_spans.args_or_use();
80 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
82 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
83 move_site_vec, use_spans
85 let move_out_indices: Vec<_> =
86 move_site_vec.iter().map(|move_site| move_site.moi).collect();
88 if move_out_indices.is_empty() {
89 let root_place = PlaceRef { projection: &[], ..used_place };
91 if !self.uninitialized_error_reported.insert(root_place) {
93 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
99 let err = self.report_use_of_uninitialized(
107 self.buffer_error(err);
109 if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
110 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
112 "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}",
119 let is_partial_move = move_site_vec.iter().any(|move_site| {
120 let move_out = self.move_data.moves[(*move_site).moi];
121 let moved_place = &self.move_data.move_paths[move_out.path].place;
122 // `*(_1)` where `_1` is a `Box` is actually a move out.
123 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
124 && self.body.local_decls[moved_place.local].ty.is_box();
127 && used_place != moved_place.as_ref()
128 && used_place.is_prefix_of(moved_place.as_ref())
131 let partial_str = if is_partial_move { "partial " } else { "" };
132 let partially_str = if is_partial_move { "partially " } else { "" };
134 let mut err = self.cannot_act_on_moved_value(
136 desired_action.as_noun(),
138 self.describe_place_with_options(
140 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
144 let reinit_spans = maybe_reinitialized_locations
148 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
151 .collect::<Vec<Span>>();
153 let reinits = maybe_reinitialized_locations.len();
155 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
156 } else if reinits > 1 {
158 MultiSpan::from_spans(reinit_spans),
160 format!("these {} reinitializations might get skipped", reinits)
163 "these 3 reinitializations and {} other{} might get skipped",
165 if reinits == 4 { "" } else { "s" }
171 self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
173 let mut is_loop_move = false;
174 let mut in_pattern = false;
176 for move_site in &move_site_vec {
177 let move_out = self.move_data.moves[(*move_site).moi];
178 let moved_place = &self.move_data.move_paths[move_out.path].place;
180 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
181 let move_span = move_spans.args_or_use();
183 let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
185 let loop_message = if location == move_out.source || move_site.traversed_back_edge {
186 ", in previous iteration of loop"
191 if location == move_out.source {
195 self.explain_captures(
206 maybe_reinitialized_locations.is_empty(),
209 if let (UseSpans::PatUse(span), []) =
210 (move_spans, &maybe_reinitialized_locations[..])
212 if maybe_reinitialized_locations.is_empty() {
213 err.span_suggestion_verbose(
216 "borrow this field in the pattern to avoid moving {}",
217 self.describe_place(moved_place.as_ref())
218 .map(|n| format!("`{}`", n))
219 .unwrap_or_else(|| "the value".to_string())
222 Applicability::MachineApplicable,
229 use_spans.var_span_label_path_only(
231 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
238 "value {} here after {}move",
239 desired_action.as_verb_in_past_tense(),
245 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
246 let needs_note = match ty.kind() {
247 ty::Closure(id, _) => {
248 let tables = self.infcx.tcx.typeck(id.expect_local());
249 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
251 tables.closure_kind_origins().get(hir_id).is_none()
256 let mpi = self.move_data.moves[move_out_indices[0]].path;
257 let place = &self.move_data.move_paths[mpi].place;
258 let ty = place.ty(self.body, self.infcx.tcx).ty;
260 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
261 // Same for if we're in a loop, see #101119.
262 if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
263 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
264 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
265 err.span_suggestion_verbose(
268 "consider creating a fresh reborrow of {} here",
269 self.describe_place(moved_place)
270 .map(|n| format!("`{}`", n))
271 .unwrap_or_else(|| "the mutable reference".to_string()),
274 Applicability::MachineApplicable,
279 let opt_name = self.describe_place_with_options(
281 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
283 let note_msg = match opt_name {
284 Some(ref name) => format!("`{}`", name),
285 None => "value".to_owned(),
287 if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) {
288 // Suppress the next suggestion since we don't want to put more bounds onto
289 // something that already has `Fn`-like bounds (or is a closure), so we can't
292 self.suggest_adding_copy_bounds(&mut err, ty, span);
296 let span = if let Some(local) = place.as_local() {
297 Some(self.body.local_decls[local].source_info.span)
301 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
304 if let UseSpans::FnSelfUse {
305 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
310 "{} occurs due to deref coercion to `{}`",
311 desired_action.as_noun(),
315 // Check first whether the source is accessible (issue #87060)
316 if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) {
317 err.span_note(deref_target, "deref defined here");
321 self.buffer_move_error(move_out_indices, (used_place, err));
325 fn report_use_of_uninitialized(
328 used_place: PlaceRef<'tcx>,
329 moved_place: PlaceRef<'tcx>,
330 desired_action: InitializationRequiringAction,
332 use_spans: UseSpans<'tcx>,
333 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
334 // We need all statements in the body where the binding was assigned to to later find all
335 // the branching code paths where the binding *wasn't* assigned to.
336 let inits = &self.move_data.init_path_map[mpi];
337 let move_path = &self.move_data.move_paths[mpi];
338 let decl_span = self.body.local_decls[move_path.place.local].source_info.span;
339 let mut spans = vec![];
340 for init_idx in inits {
341 let init = &self.move_data.inits[*init_idx];
342 let span = init.span(&self.body);
343 if !span.is_dummy() {
348 let (name, desc) = match self.describe_place_with_options(
350 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
352 Some(name) => (format!("`{name}`"), format!("`{name}` ")),
353 None => ("the variable".to_string(), String::new()),
355 let path = match self.describe_place_with_options(
357 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
359 Some(name) => format!("`{name}`"),
360 None => "value".to_string(),
363 // We use the statements were the binding was initialized, and inspect the HIR to look
364 // for the branching codepaths that aren't covered, to point at them.
365 let map = self.infcx.tcx.hir();
366 let body_id = map.body_owned_by(self.mir_def_id());
367 let body = map.body(body_id);
369 let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] };
370 visitor.visit_body(&body);
372 let isnt_initialized = if let InitializationRequiringAction::PartialAssignment
373 | InitializationRequiringAction::Assignment = desired_action
375 // The same error is emitted for bindings that are *sometimes* initialized and the ones
376 // that are *partially* initialized by assigning to a field of an uninitialized
377 // binding. We differentiate between them for more accurate wording here.
378 "isn't fully initialized"
382 // We filter these to avoid misleading wording in cases like the following,
383 // where `x` has an `init`, but it is in the same place we're looking at:
389 // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
394 .any(|sp| span < sp && !sp.contains(span))
401 "is possibly-uninitialized"
404 let used = desired_action.as_general_verb_in_past_tense();
406 struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}");
407 use_spans.var_span_label_path_only(
409 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
412 if let InitializationRequiringAction::PartialAssignment
413 | InitializationRequiringAction::Assignment = desired_action
416 "partial initialization isn't supported, fully initialize the binding with a \
417 default value and mutate it, or use `std::mem::MaybeUninit`",
420 err.span_label(span, format!("{path} {used} here but it {isnt_initialized}"));
422 let mut shown = false;
423 for (sp, label) in visitor.errors {
424 if sp < span && !sp.overlaps(span) {
425 // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention
426 // match arms coming after the primary span because they aren't relevant:
430 // _ if { x = 2; true } => {}
435 // _ => {} // We don't want to point to this.
438 err.span_label(sp, &label);
444 if *sp < span && !sp.overlaps(span) {
445 err.span_label(*sp, "binding initialized here in some conditions");
449 err.span_label(decl_span, "binding declared here but left uninitialized");
453 fn suggest_borrow_fn_like(
455 err: &mut Diagnostic,
457 move_sites: &[MoveSite],
460 let tcx = self.infcx.tcx;
462 // Find out if the predicates show that the type is a Fn or FnMut
463 let find_fn_kind_from_did =
464 |predicates: ty::EarlyBinder<&[(ty::Predicate<'tcx>, Span)]>, substs| {
465 predicates.0.iter().find_map(|(pred, _)| {
466 let pred = if let Some(substs) = substs {
467 predicates.rebind(*pred).subst(tcx, substs).kind().skip_binder()
469 pred.kind().skip_binder()
471 if let ty::PredicateKind::Trait(pred) = pred && pred.self_ty() == ty {
472 if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
473 return Some(hir::Mutability::Not);
474 } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
475 return Some(hir::Mutability::Mut);
482 // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
483 // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
484 // These types seem reasonably opaque enough that they could be substituted with their
485 // borrowed variants in a function body when we see a move error.
486 let borrow_level = match ty.kind() {
487 ty::Param(_) => find_fn_kind_from_did(
488 tcx.bound_explicit_predicates_of(self.mir_def_id().to_def_id())
489 .map_bound(|p| p.predicates),
492 ty::Opaque(did, substs) => {
493 find_fn_kind_from_did(tcx.bound_explicit_item_bounds(*did), Some(*substs))
495 ty::Closure(_, substs) => match substs.as_closure().kind() {
496 ty::ClosureKind::Fn => Some(hir::Mutability::Not),
497 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
503 let Some(borrow_level) = borrow_level else { return false; };
504 let sugg = move_sites
507 let move_out = self.move_data.moves[(*move_site).moi];
508 let moved_place = &self.move_data.move_paths[move_out.path].place;
509 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
510 let move_span = move_spans.args_or_use();
511 let suggestion = if borrow_level == hir::Mutability::Mut {
516 (move_span.shrink_to_lo(), suggestion)
519 err.multipart_suggestion_verbose(
521 "consider {}borrowing {value_name}",
522 if borrow_level == hir::Mutability::Mut { "mutably " } else { "" }
525 Applicability::MaybeIncorrect,
530 fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
531 let tcx = self.infcx.tcx;
532 let generics = tcx.generics_of(self.mir_def_id());
534 let Some(hir_generics) = tcx
535 .typeck_root_def_id(self.mir_def_id().to_def_id())
537 .and_then(|def_id| tcx.hir().get_generics(def_id))
539 // Try to find predicates on *generic params* that would allow copying `ty`
540 let predicates: Result<Vec<_>, _> = tcx.infer_ctxt().enter(|infcx| {
541 let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
543 let copy_did = infcx.tcx.lang_items().copy_trait().unwrap();
544 let cause = ObligationCause::new(
547 rustc_infer::traits::ObligationCauseCode::MiscObligation,
549 fulfill_cx.register_bound(
552 // Erase any region vids from the type, which may not be resolved
553 infcx.tcx.erase_regions(ty),
557 // Select all, including ambiguous predicates
558 let errors = fulfill_cx.select_all_or_error(&infcx);
560 // Only emit suggestion if all required predicates are on generic
563 .map(|err| match err.obligation.predicate.kind().skip_binder() {
564 PredicateKind::Trait(predicate) => match predicate.self_ty().kind() {
565 ty::Param(param_ty) => Ok((
566 generics.type_param(param_ty, tcx),
567 predicate.trait_ref.print_only_trait_path().to_string(),
576 if let Ok(predicates) = predicates {
577 suggest_constraining_type_params(
583 .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
588 pub(crate) fn report_move_out_while_borrowed(
591 (place, span): (Place<'tcx>, Span),
592 borrow: &BorrowData<'tcx>,
595 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
596 location, place, span, borrow
598 let value_msg = self.describe_any_place(place.as_ref());
599 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
601 let borrow_spans = self.retrieve_borrow_spans(borrow);
602 let borrow_span = borrow_spans.args_or_use();
604 let move_spans = self.move_spans(place.as_ref(), location);
605 let span = move_spans.args_or_use();
608 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
609 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
610 err.span_label(span, format!("move out of {} occurs here", value_msg));
612 borrow_spans.var_span_label_path_only(
614 format!("borrow occurs due to use{}", borrow_spans.describe()),
617 move_spans.var_span_label(
619 format!("move occurs due to use{}", move_spans.describe()),
623 self.explain_why_borrow_contains_point(location, borrow, None)
624 .add_explanation_to_diagnostic(
633 self.buffer_error(err);
636 pub(crate) fn report_use_while_mutably_borrowed(
639 (place, _span): (Place<'tcx>, Span),
640 borrow: &BorrowData<'tcx>,
641 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
642 let borrow_spans = self.retrieve_borrow_spans(borrow);
643 let borrow_span = borrow_spans.args_or_use();
645 // Conflicting borrows are reported separately, so only check for move
647 let use_spans = self.move_spans(place.as_ref(), location);
648 let span = use_spans.var_or_use();
650 // 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
651 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
652 let mut err = self.cannot_use_when_mutably_borrowed(
654 &self.describe_any_place(place.as_ref()),
656 &self.describe_any_place(borrow.borrowed_place.as_ref()),
659 borrow_spans.var_span_label(
662 let place = &borrow.borrowed_place;
663 let desc_place = self.describe_any_place(place.as_ref());
664 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
669 self.explain_why_borrow_contains_point(location, borrow, None)
670 .add_explanation_to_diagnostic(
682 pub(crate) fn report_conflicting_borrow(
685 (place, span): (Place<'tcx>, Span),
686 gen_borrow_kind: BorrowKind,
687 issued_borrow: &BorrowData<'tcx>,
688 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
689 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
690 let issued_span = issued_spans.args_or_use();
692 let borrow_spans = self.borrow_spans(span, location);
693 let span = borrow_spans.args_or_use();
695 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
701 let (desc_place, msg_place, msg_borrow, union_type_name) =
702 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
704 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
705 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
707 // FIXME: supply non-"" `opt_via` when appropriate
708 let first_borrow_desc;
709 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
710 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
711 first_borrow_desc = "mutable ";
712 self.cannot_reborrow_already_borrowed(
724 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
725 first_borrow_desc = "immutable ";
726 self.cannot_reborrow_already_borrowed(
739 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
740 first_borrow_desc = "first ";
741 let mut err = self.cannot_mutably_borrow_multiply(
749 self.suggest_split_at_mut_if_applicable(
752 issued_borrow.borrowed_place,
757 (BorrowKind::Unique, BorrowKind::Unique) => {
758 first_borrow_desc = "first ";
759 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
762 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
763 if let Some(immutable_section_description) =
764 self.classify_immutable_section(issued_borrow.assigned_place)
766 let mut err = self.cannot_mutate_in_immutable_section(
770 immutable_section_description,
773 borrow_spans.var_span_label(
776 "borrow occurs due to use of {}{}",
778 borrow_spans.describe(),
785 first_borrow_desc = "immutable ";
786 self.cannot_reborrow_already_borrowed(
800 (BorrowKind::Unique, _) => {
801 first_borrow_desc = "first ";
802 self.cannot_uniquely_borrow_by_one_closure(
814 (BorrowKind::Shared, BorrowKind::Unique) => {
815 first_borrow_desc = "first ";
816 self.cannot_reborrow_already_uniquely_borrowed(
829 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
830 first_borrow_desc = "first ";
831 self.cannot_reborrow_already_uniquely_borrowed(
844 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
847 BorrowKind::Mut { .. }
850 | BorrowKind::Shallow,
854 if issued_spans == borrow_spans {
855 borrow_spans.var_span_label(
857 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
858 gen_borrow_kind.describe_mutability(),
861 let borrow_place = &issued_borrow.borrowed_place;
862 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
863 issued_spans.var_span_label(
866 "first borrow occurs due to use of {}{}",
868 issued_spans.describe(),
870 issued_borrow.kind.describe_mutability(),
873 borrow_spans.var_span_label(
876 "second borrow occurs due to use of {}{}",
878 borrow_spans.describe(),
880 gen_borrow_kind.describe_mutability(),
884 if union_type_name != "" {
886 "{} is a field of the union `{}`, so it overlaps the field {}",
887 msg_place, union_type_name, msg_borrow,
891 explanation.add_explanation_to_diagnostic(
898 Some((issued_span, span)),
901 self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation);
906 #[instrument(level = "debug", skip(self, err))]
907 fn suggest_using_local_if_applicable(
909 err: &mut Diagnostic,
911 issued_borrow: &BorrowData<'tcx>,
912 explanation: BorrowExplanation<'tcx>,
914 let used_in_call = matches!(
916 BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
919 debug!("not later used in call");
924 if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
931 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
934 issued_borrow.reserve_location
936 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
938 let inner_param_location = location;
939 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
940 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
943 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
945 "`inner_param_location` {:?} is not for an assignment: {:?}",
946 inner_param_location, inner_param_stmt
950 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
951 let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
952 let Either::Right(term) = self.body.stmt_at(loc) else {
953 debug!("{:?} is a statement, so it can't be a call", loc);
956 let TerminatorKind::Call { args, .. } = &term.kind else {
957 debug!("not a call: {:?}", term);
960 debug!("checking call args for uses of inner_param: {:?}", args);
961 if args.contains(&Operand::Move(inner_param)) {
967 debug!("no uses of inner_param found as a by-move call arg");
970 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
972 let inner_call_span = inner_call_term.source_info.span;
973 let outer_call_span = match use_span {
975 None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span,
977 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
978 // FIXME: This stops the suggestion in some cases where it should be emitted.
979 // Fix the spans for those cases so it's emitted correctly.
981 "outer span {:?} does not strictly contain inner span {:?}",
982 outer_call_span, inner_call_span
989 "try adding a local storing this{}...",
990 if use_span.is_some() { "" } else { " argument" }
996 "...and then using that local {}",
997 if use_span.is_some() { "here" } else { "as the argument to this call" }
1002 fn suggest_split_at_mut_if_applicable(
1004 err: &mut Diagnostic,
1006 borrowed_place: Place<'tcx>,
1008 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
1009 (&place.projection[..], &borrowed_place.projection[..])
1012 "consider using `.split_at_mut(position)` or similar method to obtain \
1013 two mutable non-overlapping sub-slices",
1018 /// Returns the description of the root place for a conflicting borrow and the full
1019 /// descriptions of the places that caused the conflict.
1021 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
1022 /// attempted while a shared borrow is live, then this function will return:
1027 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
1028 /// a shared borrow of another field `x.y`, then this function will return:
1030 /// ("x", "x.z", "x.y")
1033 /// In the more complex union case, where the union is a field of a struct, then if a mutable
1034 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
1035 /// another field `x.u.y`, then this function will return:
1037 /// ("x.u", "x.u.z", "x.u.y")
1040 /// This is used when creating error messages like below:
1043 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
1044 /// mutable (via `a.u.s.b`) [E0502]
1046 pub(crate) fn describe_place_for_conflicting_borrow(
1048 first_borrowed_place: Place<'tcx>,
1049 second_borrowed_place: Place<'tcx>,
1050 ) -> (String, String, String, String) {
1051 // Define a small closure that we can use to check if the type of a place
1053 let union_ty = |place_base| {
1054 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
1055 // using a type annotation in the closure argument instead leads to a lifetime error.
1056 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
1057 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
1060 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
1061 // code duplication (particularly around returning an empty description in the failure
1065 // If we have a conflicting borrow of the same place, then we don't want to add
1066 // an extraneous "via x.y" to our diagnostics, so filter out this case.
1067 first_borrowed_place != second_borrowed_place
1070 // We're going to want to traverse the first borrowed place to see if we can find
1071 // field access to a union. If we find that, then we will keep the place of the
1072 // union being accessed and the field that was being accessed so we can check the
1073 // second borrowed place for the same union and an access to a different field.
1074 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
1076 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
1077 return Some((place_base, field));
1084 .and_then(|(target_base, target_field)| {
1085 // With the place of a union and a field access into it, we traverse the second
1086 // borrowed place and look for an access to a different field of the same union.
1087 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1088 if let ProjectionElem::Field(field, _) = elem {
1089 if let Some(union_ty) = union_ty(place_base) {
1090 if field != target_field && place_base == target_base {
1092 self.describe_any_place(place_base),
1093 self.describe_any_place(first_borrowed_place.as_ref()),
1094 self.describe_any_place(second_borrowed_place.as_ref()),
1095 union_ty.to_string(),
1103 .unwrap_or_else(|| {
1104 // If we didn't find a field access into a union, or both places match, then
1105 // only return the description of the first place.
1107 self.describe_any_place(first_borrowed_place.as_ref()),
1115 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1117 /// This means that some data referenced by `borrow` needs to live
1118 /// past the point where the StorageDeadOrDrop of `place` occurs.
1119 /// This is usually interpreted as meaning that `place` has too
1120 /// short a lifetime. (But sometimes it is more useful to report
1121 /// it as a more direct conflict between the execution of a
1122 /// `Drop::drop` with an aliasing borrow.)
1123 #[instrument(level = "debug", skip(self))]
1124 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1127 borrow: &BorrowData<'tcx>,
1128 place_span: (Place<'tcx>, Span),
1129 kind: Option<WriteKind>,
1131 let drop_span = place_span.1;
1133 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1135 let borrow_spans = self.retrieve_borrow_spans(borrow);
1136 let borrow_span = borrow_spans.var_or_use_path_span();
1138 assert!(root_place.projection.is_empty());
1139 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1141 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1143 if self.access_place_error_reported.contains(&(
1144 Place { local: root_place.local, projection: root_place_projection },
1148 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1154 self.access_place_error_reported.insert((
1155 Place { local: root_place.local, projection: root_place_projection },
1159 let borrowed_local = borrow.borrowed_place.local;
1160 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1162 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1163 self.buffer_error(err);
1167 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1168 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1170 // If a borrow of path `B` conflicts with drop of `D` (and
1171 // we're not in the uninteresting case where `B` is a
1172 // prefix of `D`), then report this as a more interesting
1173 // destructor conflict.
1174 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1175 self.report_borrow_conflicts_with_destructor(
1176 location, borrow, place_span, kind, dropped_ty,
1182 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1184 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1185 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1187 debug!(?place_desc, ?explanation);
1189 let err = match (place_desc, explanation) {
1190 // If the outlives constraint comes from inside the closure,
1195 // Box::new(|| y) as Box<Fn() -> &'static i32>
1197 // then just use the normal error. The closure isn't escaping
1198 // and `move` will not help here.
1201 BorrowExplanation::MustBeValidFor {
1203 category @ (ConstraintCategory::Return(_)
1204 | ConstraintCategory::CallArgument(_)
1205 | ConstraintCategory::OpaqueType),
1206 from_closure: false,
1211 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1212 .report_escaping_closure_capture(
1218 &format!("`{}`", name),
1222 BorrowExplanation::MustBeValidFor {
1223 category: ConstraintCategory::Assignment,
1224 from_closure: false,
1227 source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name),
1233 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1234 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1242 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1252 self.buffer_error(err);
1255 fn report_local_value_does_not_live_long_enough(
1259 borrow: &BorrowData<'tcx>,
1261 borrow_spans: UseSpans<'tcx>,
1262 explanation: BorrowExplanation<'tcx>,
1263 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1265 "report_local_value_does_not_live_long_enough(\
1266 {:?}, {:?}, {:?}, {:?}, {:?}\
1268 location, name, borrow, drop_span, borrow_spans
1271 let borrow_span = borrow_spans.var_or_use_path_span();
1272 if let BorrowExplanation::MustBeValidFor {
1276 from_closure: false,
1280 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1285 opt_place_desc.as_ref(),
1291 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1293 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1294 let region_name = annotation.emit(self, &mut err);
1298 format!("`{}` would have to be valid for `{}`...", name, region_name),
1301 let fn_hir_id = self.mir_hir_id();
1305 "...but `{}` will be dropped here, when the {} returns",
1310 .opt_name(fn_hir_id)
1311 .map(|name| format!("function `{}`", name))
1312 .unwrap_or_else(|| {
1316 .typeck(self.mir_def_id())
1317 .node_type(fn_hir_id)
1320 ty::Closure(..) => "enclosing closure",
1321 ty::Generator(..) => "enclosing generator",
1322 kind => bug!("expected closure or generator, found {:?}", kind),
1330 "functions cannot return a borrow to data owned within the function's scope, \
1331 functions can only return borrows to data passed as arguments",
1334 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1335 references-and-borrowing.html#dangling-references>",
1338 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1340 explanation.add_explanation_to_diagnostic(
1351 err.span_label(borrow_span, "borrowed value does not live long enough");
1352 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1354 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1356 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1358 explanation.add_explanation_to_diagnostic(
1372 fn report_borrow_conflicts_with_destructor(
1375 borrow: &BorrowData<'tcx>,
1376 (place, drop_span): (Place<'tcx>, Span),
1377 kind: Option<WriteKind>,
1378 dropped_ty: Ty<'tcx>,
1381 "report_borrow_conflicts_with_destructor(\
1382 {:?}, {:?}, ({:?}, {:?}), {:?}\
1384 location, borrow, place, drop_span, kind,
1387 let borrow_spans = self.retrieve_borrow_spans(borrow);
1388 let borrow_span = borrow_spans.var_or_use();
1390 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1392 let what_was_dropped = match self.describe_place(place.as_ref()) {
1393 Some(name) => format!("`{}`", name),
1394 None => String::from("temporary value"),
1397 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1398 Some(borrowed) => format!(
1399 "here, drop of {D} needs exclusive access to `{B}`, \
1400 because the type `{T}` implements the `Drop` trait",
1401 D = what_was_dropped,
1406 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1407 D = what_was_dropped,
1411 err.span_label(drop_span, label);
1413 // Only give this note and suggestion if they could be relevant.
1415 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1417 BorrowExplanation::UsedLater { .. }
1418 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1419 err.note("consider using a `let` binding to create a longer lived value");
1424 explanation.add_explanation_to_diagnostic(
1434 self.buffer_error(err);
1437 fn report_thread_local_value_does_not_live_long_enough(
1441 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1443 "report_thread_local_value_does_not_live_long_enough(\
1446 drop_span, borrow_span
1449 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1453 "thread-local variables cannot be borrowed beyond the end of the function",
1455 err.span_label(drop_span, "end of enclosing function is here");
1460 #[instrument(level = "debug", skip(self))]
1461 fn report_temporary_value_does_not_live_long_enough(
1464 borrow: &BorrowData<'tcx>,
1466 borrow_spans: UseSpans<'tcx>,
1468 explanation: BorrowExplanation<'tcx>,
1469 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1470 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1473 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1484 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1485 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1486 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1489 BorrowExplanation::UsedLater(..)
1490 | BorrowExplanation::UsedLaterInLoop(..)
1491 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1492 // Only give this note and suggestion if it could be relevant.
1493 let sm = self.infcx.tcx.sess.source_map();
1494 let mut suggested = false;
1495 let msg = "consider using a `let` binding to create a longer lived value";
1497 /// We check that there's a single level of block nesting to ensure always correct
1498 /// suggestions. If we don't, then we only provide a free-form message to avoid
1499 /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`.
1500 /// We could expand the analysis to suggest hoising all of the relevant parts of
1501 /// the users' code to make the code compile, but that could be too much.
1502 struct NestedStatementVisitor {
1508 impl<'tcx> Visitor<'tcx> for NestedStatementVisitor {
1509 fn visit_block(&mut self, block: &hir::Block<'tcx>) {
1511 walk_block(self, block);
1514 fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) {
1515 if self.span == expr.span {
1516 self.found = self.current;
1518 walk_expr(self, expr);
1521 let source_info = self.body.source_info(location);
1522 if let Some(scope) = self.body.source_scopes.get(source_info.scope)
1523 && let ClearCrossCrate::Set(scope_data) = &scope.local_data
1524 && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root)
1525 && let Some(id) = node.body_id()
1526 && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
1528 for stmt in block.stmts {
1529 let mut visitor = NestedStatementVisitor {
1534 visitor.visit_stmt(stmt);
1535 if visitor.found == 0
1536 && stmt.span.contains(proper_span)
1537 && let Some(p) = sm.span_to_margin(stmt.span)
1538 && let Ok(s) = sm.span_to_snippet(proper_span)
1540 let addition = format!("let binding = {};\n{}", s, " ".repeat(p));
1541 err.multipart_suggestion_verbose(
1544 (stmt.span.shrink_to_lo(), addition),
1545 (proper_span, "binding".to_string()),
1547 Applicability::MaybeIncorrect,
1560 explanation.add_explanation_to_diagnostic(
1570 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1572 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1577 fn try_report_cannot_return_reference_to_local(
1579 borrow: &BorrowData<'tcx>,
1582 category: ConstraintCategory<'tcx>,
1583 opt_place_desc: Option<&String>,
1584 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1585 let return_kind = match category {
1586 ConstraintCategory::Return(_) => "return",
1587 ConstraintCategory::Yield => "yield",
1591 // FIXME use a better heuristic than Spans
1592 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1598 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1599 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1600 match self.body.local_kind(local) {
1601 LocalKind::ReturnPointer | LocalKind::Temp => {
1602 bug!("temporary or return pointer with a name")
1604 LocalKind::Var => "local variable ",
1606 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1608 "variable captured by `move` "
1610 LocalKind::Arg => "function parameter ",
1616 format!("{}`{}`", local_kind, place_desc),
1617 format!("`{}` is borrowed here", place_desc),
1621 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1622 let local = root_place.local;
1623 match self.body.local_kind(local) {
1624 LocalKind::ReturnPointer | LocalKind::Temp => {
1625 ("temporary value".to_string(), "temporary value created here".to_string())
1628 "function parameter".to_string(),
1629 "function parameter borrowed here".to_string(),
1632 ("local binding".to_string(), "local binding introduced here".to_string())
1637 let mut err = self.cannot_return_reference_to_local(
1644 if return_span != borrow_span {
1645 err.span_label(borrow_span, note);
1647 let tcx = self.infcx.tcx;
1648 let ty_params = ty::List::empty();
1650 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1651 let return_ty = tcx.erase_regions(return_ty);
1654 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
1657 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1658 .must_apply_modulo_regions()
1660 err.span_suggestion_hidden(
1661 return_span.shrink_to_hi(),
1662 "use `.collect()` to allocate the iterator",
1663 ".collect::<Vec<_>>()",
1664 Applicability::MaybeIncorrect,
1672 fn report_escaping_closure_capture(
1674 use_span: UseSpans<'tcx>,
1676 fr_name: &RegionName,
1677 category: ConstraintCategory<'tcx>,
1678 constraint_span: Span,
1680 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1681 let tcx = self.infcx.tcx;
1682 let args_span = use_span.args_or_use();
1684 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1686 if string.starts_with("async ") {
1687 let pos = args_span.lo() + BytePos(6);
1688 (args_span.with_lo(pos).with_hi(pos), "move ")
1689 } else if string.starts_with("async|") {
1690 let pos = args_span.lo() + BytePos(5);
1691 (args_span.with_lo(pos).with_hi(pos), " move")
1693 (args_span.shrink_to_lo(), "move ")
1696 Err(_) => (args_span, "move |<args>| <body>"),
1698 let kind = match use_span.generator_kind() {
1699 Some(generator_kind) => match generator_kind {
1700 GeneratorKind::Async(async_kind) => match async_kind {
1701 AsyncGeneratorKind::Block => "async block",
1702 AsyncGeneratorKind::Closure => "async closure",
1703 _ => bug!("async block/closure expected, but async function found."),
1705 GeneratorKind::Gen => "generator",
1711 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1712 err.span_suggestion_verbose(
1715 "to force the {} to take ownership of {} (and any \
1716 other referenced variables), use the `move` keyword",
1720 Applicability::MachineApplicable,
1724 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1725 let msg = format!("{} is returned here", kind);
1726 err.span_note(constraint_span, &msg);
1728 ConstraintCategory::CallArgument(_) => {
1729 fr_name.highlight_region_name(&mut err);
1730 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1732 "async blocks are not executed immediately and must either take a \
1733 reference or ownership of outside variables they use",
1736 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1737 err.span_note(constraint_span, &msg);
1741 "report_escaping_closure_capture called with unexpected constraint \
1750 fn report_escaping_data(
1753 name: &Option<String>,
1757 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1758 let tcx = self.infcx.tcx;
1760 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1763 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1767 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1770 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1772 if let Some(name) = name {
1775 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1780 format!("reference escapes the {} body here", escapes_from),
1787 fn get_moved_indexes(
1791 ) -> (Vec<MoveSite>, Vec<Location>) {
1792 fn predecessor_locations<'tcx, 'a>(
1793 body: &'a mir::Body<'tcx>,
1795 ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
1796 if location.statement_index == 0 {
1797 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
1798 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1800 Either::Right(std::iter::once(Location {
1801 statement_index: location.statement_index - 1,
1807 let mut mpis = vec![mpi];
1808 let move_paths = &self.move_data.move_paths;
1809 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1811 let mut stack = Vec::new();
1812 let mut back_edge_stack = Vec::new();
1814 predecessor_locations(self.body, location).for_each(|predecessor| {
1815 if location.dominates(predecessor, &self.dominators) {
1816 back_edge_stack.push(predecessor)
1818 stack.push(predecessor);
1822 let mut reached_start = false;
1824 /* Check if the mpi is initialized as an argument */
1825 let mut is_argument = false;
1826 for arg in self.body.args_iter() {
1827 let path = self.move_data.rev_lookup.find_local(arg);
1828 if mpis.contains(&path) {
1833 let mut visited = FxHashSet::default();
1834 let mut move_locations = FxHashSet::default();
1835 let mut reinits = vec![];
1836 let mut result = vec![];
1838 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1840 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1841 location, is_back_edge
1844 if !visited.insert(location) {
1850 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1851 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1852 // this analysis only tries to find moves explicitly
1853 // written by the user, so we ignore the move-outs
1854 // created by `StorageDead` and at the beginning
1857 // If we are found a use of a.b.c which was in error, then we want to look for
1858 // moves not only of a.b.c but also a.b and a.
1860 // Note that the moves data already includes "parent" paths, so we don't have to
1861 // worry about the other case: that is, if there is a move of a.b.c, it is already
1862 // marked as a move of a.b and a as well, so we will generate the correct errors
1864 for moi in &self.move_data.loc_map[location] {
1865 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1866 let path = self.move_data.moves[*moi].path;
1867 if mpis.contains(&path) {
1869 "report_use_of_moved_or_uninitialized: found {:?}",
1870 move_paths[path].place
1872 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1873 move_locations.insert(location);
1875 // Strictly speaking, we could continue our DFS here. There may be
1876 // other moves that can reach the point of error. But it is kind of
1877 // confusing to highlight them.
1885 // drop(a); // <-- current point of error
1888 // Because we stop the DFS here, we only highlight `let c = a`,
1889 // and not `let b = a`. We will of course also report an error at
1890 // `let c = a` which highlights `let b = a` as the move.
1897 let mut any_match = false;
1898 for ii in &self.move_data.init_loc_map[location] {
1899 let init = self.move_data.inits[*ii];
1901 InitKind::Deep | InitKind::NonPanicPathOnly => {
1902 if mpis.contains(&init.path) {
1906 InitKind::Shallow => {
1907 if mpi == init.path {
1914 reinits.push(location);
1920 while let Some(location) = stack.pop() {
1921 if dfs_iter(&mut result, location, false) {
1925 let mut has_predecessor = false;
1926 predecessor_locations(self.body, location).for_each(|predecessor| {
1927 if location.dominates(predecessor, &self.dominators) {
1928 back_edge_stack.push(predecessor)
1930 stack.push(predecessor);
1932 has_predecessor = true;
1935 if !has_predecessor {
1936 reached_start = true;
1939 if (is_argument || !reached_start) && result.is_empty() {
1940 /* Process back edges (moves in future loop iterations) only if
1941 the move path is definitely initialized upon loop entry,
1942 to avoid spurious "in previous iteration" errors.
1943 During DFS, if there's a path from the error back to the start
1944 of the function with no intervening init or move, then the
1945 move path may be uninitialized at loop entry.
1947 while let Some(location) = back_edge_stack.pop() {
1948 if dfs_iter(&mut result, location, true) {
1952 predecessor_locations(self.body, location)
1953 .for_each(|predecessor| back_edge_stack.push(predecessor));
1957 // Check if we can reach these reinits from a move location.
1958 let reinits_reachable = reinits
1961 let mut visited = FxHashSet::default();
1962 let mut stack = vec![*reinit];
1963 while let Some(location) = stack.pop() {
1964 if !visited.insert(location) {
1967 if move_locations.contains(&location) {
1970 stack.extend(predecessor_locations(self.body, location));
1974 .collect::<Vec<Location>>();
1975 (result, reinits_reachable)
1978 pub(crate) fn report_illegal_mutation_of_borrowed(
1981 (place, span): (Place<'tcx>, Span),
1982 loan: &BorrowData<'tcx>,
1984 let loan_spans = self.retrieve_borrow_spans(loan);
1985 let loan_span = loan_spans.args_or_use();
1987 let descr_place = self.describe_any_place(place.as_ref());
1988 if loan.kind == BorrowKind::Shallow {
1989 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1990 let mut err = self.cannot_mutate_in_immutable_section(
1997 loan_spans.var_span_label(
1999 format!("borrow occurs due to use{}", loan_spans.describe()),
2000 loan.kind.describe_mutability(),
2003 self.buffer_error(err);
2009 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
2011 loan_spans.var_span_label(
2013 format!("borrow occurs due to use{}", loan_spans.describe()),
2014 loan.kind.describe_mutability(),
2017 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
2027 self.explain_deref_coercion(loan, &mut err);
2029 self.buffer_error(err);
2032 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
2033 let tcx = self.infcx.tcx;
2035 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
2036 Some((method_did, method_substs)),
2038 &self.body[loan.reserve_location.block].terminator,
2039 rustc_const_eval::util::find_self_call(
2042 loan.assigned_place.local,
2043 loan.reserve_location.block,
2046 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
2048 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
2049 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
2052 if let Some(Ok(instance)) = deref_target {
2053 let deref_target_ty = instance.ty(tcx, self.param_env);
2055 "borrow occurs due to deref coercion to `{}`",
2058 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
2064 /// Reports an illegal reassignment; for example, an assignment to
2065 /// (part of) a non-`mut` local that occurs potentially after that
2066 /// local has already been initialized. `place` is the path being
2067 /// assigned; `err_place` is a place providing a reason why
2068 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
2069 /// assignment to `x.f`).
2070 pub(crate) fn report_illegal_reassignment(
2072 _location: Location,
2073 (place, span): (Place<'tcx>, Span),
2074 assigned_span: Span,
2075 err_place: Place<'tcx>,
2077 let (from_arg, local_decl, local_name) = match err_place.as_local() {
2079 self.body.local_kind(local) == LocalKind::Arg,
2080 Some(&self.body.local_decls[local]),
2081 self.local_names[local],
2083 None => (false, None, None),
2086 // If root local is initialized immediately (everything apart from let
2087 // PATTERN;) then make the error refer to that local, rather than the
2088 // place being assigned later.
2089 let (place_description, assigned_span) = match local_decl {
2092 Some(box LocalInfo::User(
2093 ClearCrossCrate::Clear
2094 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
2095 opt_match_place: None,
2099 | Some(box LocalInfo::StaticRef { .. })
2103 | None => (self.describe_any_place(place.as_ref()), assigned_span),
2104 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
2107 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
2108 let msg = if from_arg {
2109 "cannot assign to immutable argument"
2111 "cannot assign twice to immutable variable"
2113 if span != assigned_span && !from_arg {
2114 err.span_label(assigned_span, format!("first assignment to {}", place_description));
2116 if let Some(decl) = local_decl
2117 && let Some(name) = local_name
2118 && decl.can_be_made_mutable()
2120 err.span_suggestion(
2121 decl.source_info.span,
2122 "consider making this binding mutable",
2123 format!("mut {}", name),
2124 Applicability::MachineApplicable,
2127 err.span_label(span, msg);
2128 self.buffer_error(err);
2131 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2132 let tcx = self.infcx.tcx;
2133 let (kind, _place_ty) = place.projection.iter().fold(
2134 (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
2135 |(kind, place_ty), &elem| {
2138 ProjectionElem::Deref => match kind {
2139 StorageDeadOrDrop::LocalStorageDead
2140 | StorageDeadOrDrop::BoxedStorageDead => {
2142 place_ty.ty.is_box(),
2143 "Drop of value behind a reference or raw pointer"
2145 StorageDeadOrDrop::BoxedStorageDead
2147 StorageDeadOrDrop::Destructor(_) => kind,
2149 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
2150 match place_ty.ty.kind() {
2151 ty::Adt(def, _) if def.has_dtor(tcx) => {
2152 // Report the outermost adt with a destructor
2154 StorageDeadOrDrop::Destructor(_) => kind,
2155 StorageDeadOrDrop::LocalStorageDead
2156 | StorageDeadOrDrop::BoxedStorageDead => {
2157 StorageDeadOrDrop::Destructor(place_ty.ty)
2164 ProjectionElem::ConstantIndex { .. }
2165 | ProjectionElem::Subslice { .. }
2166 | ProjectionElem::Index(_) => kind,
2168 place_ty.projection_ty(tcx, elem),
2175 /// Describe the reason for the fake borrow that was assigned to `place`.
2176 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2177 use rustc_middle::mir::visit::Visitor;
2178 struct FakeReadCauseFinder<'tcx> {
2180 cause: Option<FakeReadCause>,
2182 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2183 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2185 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2186 if *place == self.place =>
2188 self.cause = Some(*cause);
2194 let mut visitor = FakeReadCauseFinder { place, cause: None };
2195 visitor.visit_body(&self.body);
2196 match visitor.cause {
2197 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2198 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2203 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2204 /// borrow of local value that does not live long enough.
2205 fn annotate_argument_and_return_for_borrow(
2207 borrow: &BorrowData<'tcx>,
2208 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2209 // Define a fallback for when we can't match a closure.
2211 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2215 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2217 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2219 self.infcx.tcx.fn_sig(self.mir_def_id()),
2226 // In order to determine whether we need to annotate, we need to check whether the reserve
2227 // place was an assignment into a temporary.
2229 // If it was, we check whether or not that temporary is eventually assigned into the return
2230 // place. If it was, we can add annotations about the function's return type and arguments
2231 // and it'll make sense.
2232 let location = borrow.reserve_location;
2233 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2234 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2235 &self.body[location.block].statements.get(location.statement_index)
2237 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2238 // Check that the initial assignment of the reserve location is into a temporary.
2239 let mut target = match reservation.as_local() {
2240 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2244 // Next, look through the rest of the block, checking if we are assigning the
2245 // `target` (that is, the place that contains our borrow) to anything.
2246 let mut annotated_closure = None;
2247 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2249 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2252 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2253 if let Some(assigned_to) = place.as_local() {
2255 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2259 // Check if our `target` was captured by a closure.
2260 if let Rvalue::Aggregate(
2261 box AggregateKind::Closure(def_id, substs),
2265 for operand in operands {
2266 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2270 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2274 // Find the local from the operand.
2275 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2279 if assigned_from_local != target {
2283 // If a closure captured our `target` and then assigned
2284 // into a place then we should annotate the closure in
2285 // case it ends up being assigned into the return place.
2287 self.annotate_fn_sig(def_id, substs.as_closure().sig());
2289 "annotate_argument_and_return_for_borrow: \
2290 annotated_closure={:?} assigned_from_local={:?} \
2292 annotated_closure, assigned_from_local, assigned_to
2295 if assigned_to == mir::RETURN_PLACE {
2296 // If it was assigned directly into the return place, then
2298 return annotated_closure;
2300 // Otherwise, update the target.
2301 target = assigned_to;
2305 // If none of our closure's operands matched, then skip to the next
2310 // Otherwise, look at other types of assignment.
2311 let assigned_from = match rvalue {
2312 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2313 Rvalue::Use(operand) => match operand {
2314 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2322 "annotate_argument_and_return_for_borrow: \
2323 assigned_from={:?}",
2327 // Find the local from the rvalue.
2328 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2330 "annotate_argument_and_return_for_borrow: \
2331 assigned_from_local={:?}",
2332 assigned_from_local,
2335 // Check if our local matches the target - if so, we've assigned our
2336 // borrow to a new place.
2337 if assigned_from_local != target {
2341 // If we assigned our `target` into a new place, then we should
2342 // check if it was the return place.
2344 "annotate_argument_and_return_for_borrow: \
2345 assigned_from_local={:?} assigned_to={:?}",
2346 assigned_from_local, assigned_to
2348 if assigned_to == mir::RETURN_PLACE {
2349 // If it was then return the annotated closure if there was one,
2350 // else, annotate this function.
2351 return annotated_closure.or_else(fallback);
2354 // If we didn't assign into the return place, then we just update
2356 target = assigned_to;
2361 // Check the terminator if we didn't find anything in the statements.
2362 let terminator = &self.body[location.block].terminator();
2364 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2367 if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
2370 if let Some(assigned_to) = destination.as_local() {
2372 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2375 for operand in args {
2376 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2380 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2384 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2386 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2387 assigned_from_local,
2390 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2391 return annotated_closure.or_else(fallback);
2399 // If we haven't found an assignment into the return place, then we need not add
2401 debug!("annotate_argument_and_return_for_borrow: none found");
2405 /// Annotate the first argument and return type of a function signature if they are
2410 sig: ty::PolyFnSig<'tcx>,
2411 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2412 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2413 let is_closure = self.infcx.tcx.is_closure(did.to_def_id());
2414 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did);
2415 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2417 // We need to work out which arguments to highlight. We do this by looking
2418 // at the return type, where there are three cases:
2420 // 1. If there are named arguments, then we should highlight the return type and
2421 // highlight any of the arguments that are also references with that lifetime.
2422 // If there are no arguments that have the same lifetime as the return type,
2423 // then don't highlight anything.
2424 // 2. The return type is a reference with an anonymous lifetime. If this is
2425 // the case, then we can take advantage of (and teach) the lifetime elision
2428 // We know that an error is being reported. So the arguments and return type
2429 // must satisfy the elision rules. Therefore, if there is a single argument
2430 // then that means the return type and first (and only) argument have the same
2431 // lifetime and the borrow isn't meeting that, we can highlight the argument
2434 // If there are multiple arguments then the first argument must be self (else
2435 // it would not satisfy the elision rules), so we can highlight self and the
2437 // 3. The return type is not a reference. In this case, we don't highlight
2439 let return_ty = sig.output();
2440 match return_ty.skip_binder().kind() {
2441 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2442 // This is case 1 from above, return type is a named reference so we need to
2443 // search for relevant arguments.
2444 let mut arguments = Vec::new();
2445 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2446 if let ty::Ref(argument_region, _, _) = argument.kind() {
2447 if argument_region == return_region {
2448 // Need to use the `rustc_middle::ty` types to compare against the
2449 // `return_region`. Then use the `rustc_hir` type to get only
2450 // the lifetime span.
2451 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2452 // With access to the lifetime, we can get
2454 arguments.push((*argument, lifetime.span));
2456 bug!("ty type is a ref but hir type is not");
2462 // We need to have arguments. This shouldn't happen, but it's worth checking.
2463 if arguments.is_empty() {
2467 // We use a mix of the HIR and the Ty types to get information
2468 // as the HIR doesn't have full types for closure arguments.
2469 let return_ty = sig.output().skip_binder();
2470 let mut return_span = fn_decl.output.span();
2471 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2472 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2473 return_span = lifetime.span;
2477 Some(AnnotatedBorrowFnSignature::NamedFunction {
2483 ty::Ref(_, _, _) if is_closure => {
2484 // This is case 2 from above but only for closures, return type is anonymous
2485 // reference so we select
2486 // the first argument.
2487 let argument_span = fn_decl.inputs.first()?.span;
2488 let argument_ty = sig.inputs().skip_binder().first()?;
2490 // Closure arguments are wrapped in a tuple, so we need to get the first
2492 if let ty::Tuple(elems) = argument_ty.kind() {
2493 let &argument_ty = elems.first()?;
2494 if let ty::Ref(_, _, _) = argument_ty.kind() {
2495 return Some(AnnotatedBorrowFnSignature::Closure {
2504 ty::Ref(_, _, _) => {
2505 // This is also case 2 from above but for functions, return type is still an
2506 // anonymous reference so we select the first argument.
2507 let argument_span = fn_decl.inputs.first()?.span;
2508 let argument_ty = *sig.inputs().skip_binder().first()?;
2510 let return_span = fn_decl.output.span();
2511 let return_ty = sig.output().skip_binder();
2513 // We expect the first argument to be a reference.
2514 match argument_ty.kind() {
2515 ty::Ref(_, _, _) => {}
2519 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2527 // This is case 3 from above, return type is not a reference so don't highlight
2536 enum AnnotatedBorrowFnSignature<'tcx> {
2538 arguments: Vec<(Ty<'tcx>, Span)>,
2539 return_ty: Ty<'tcx>,
2543 argument_ty: Ty<'tcx>,
2544 argument_span: Span,
2545 return_ty: Ty<'tcx>,
2549 argument_ty: Ty<'tcx>,
2550 argument_span: Span,
2554 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2555 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2557 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2559 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2562 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2565 cx.get_region_name_for_ty(argument_ty, 0)
2567 &AnnotatedBorrowFnSignature::AnonymousFunction {
2573 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2574 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2576 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2577 let types_equal = return_ty_name == argument_ty_name;
2582 if types_equal { "also " } else { "" },
2588 "argument and return type have the same lifetime due to lifetime elision rules",
2591 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2592 lifetime-syntax.html#lifetime-elision>",
2595 cx.get_region_name_for_ty(return_ty, 0)
2597 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2598 // Region of return type and arguments checked to be the same earlier.
2599 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2600 for (_, argument_span) in arguments {
2601 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2604 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2607 "use data from the highlighted arguments which match the `{}` lifetime of \
2618 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr
2619 struct ReferencedStatementsVisitor<'a>(&'a [Span], bool);
2621 impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> {
2622 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
2624 hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => {
2632 /// Given a set of spans representing statements initializing the relevant binding, visit all the
2633 /// function expressions looking for branching code paths that *do not* initialize the binding.
2634 struct ConditionVisitor<'b> {
2637 errors: Vec<(Span, String)>,
2640 impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
2641 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
2643 hir::ExprKind::If(cond, body, None) => {
2644 // `if` expressions with no `else` that initialize the binding might be missing an
2646 let mut v = ReferencedStatementsVisitor(self.spans, false);
2652 "if this `if` condition is `false`, {} is not initialized",
2657 ex.span.shrink_to_hi(),
2658 format!("an `else` arm might be missing here, initializing {}", self.name),
2662 hir::ExprKind::If(cond, body, Some(other)) => {
2663 // `if` expressions where the binding is only initialized in one of the two arms
2664 // might be missing a binding initialization.
2665 let mut a = ReferencedStatementsVisitor(self.spans, false);
2667 let mut b = ReferencedStatementsVisitor(self.spans, false);
2668 b.visit_expr(other);
2670 (true, true) | (false, false) => {}
2672 if other.span.is_desugaring(DesugaringKind::WhileLoop) {
2676 "if this condition isn't met and the `while` loop runs 0 \
2677 times, {} is not initialized",
2683 body.span.shrink_to_hi().until(other.span),
2685 "if the `if` condition is `false` and this `else` arm is \
2686 executed, {} is not initialized",
2696 "if this condition is `true`, {} is not initialized",
2703 hir::ExprKind::Match(e, arms, loop_desugar) => {
2704 // If the binding is initialized in one of the match arms, then the other match
2705 // arms might be missing an initialization.
2706 let results: Vec<bool> = arms
2709 let mut v = ReferencedStatementsVisitor(self.spans, false);
2714 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
2715 for (arm, seen) in arms.iter().zip(results) {
2717 if loop_desugar == hir::MatchSource::ForLoopDesugar {
2721 "if the `for` loop runs 0 times, {} is not initialized",
2725 } else if let Some(guard) = &arm.guard {
2727 arm.pat.span.to(guard.body().span),
2729 "if this pattern and condition are matched, {} is not \
2738 "if this pattern is matched, {} is not initialized",
2747 // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should
2748 // also be accounted for. For now it is fine, as if we don't find *any* relevant
2749 // branching code paths, we point at the places where the binding *is* initialized for
2753 walk_expr(self, ex);