2 use rustc_const_eval::util::CallKind;
3 use rustc_data_structures::fx::FxHashSet;
4 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
6 use rustc_hir::def_id::DefId;
7 use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
8 use rustc_infer::infer::TyCtxtInferExt;
9 use rustc_infer::traits::ObligationCause;
10 use rustc_middle::mir::{
11 self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
12 FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
13 ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
15 use rustc_middle::ty::{self, subst::Subst, suggest_constraining_type_params, PredicateKind, Ty};
16 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
17 use rustc_span::symbol::sym;
18 use rustc_span::{BytePos, Span};
19 use rustc_trait_selection::infer::InferCtxtExt;
20 use rustc_trait_selection::traits::TraitEngineExt as _;
22 use crate::borrow_set::TwoPhaseActivation;
23 use crate::borrowck_errors;
25 use crate::diagnostics::find_all_local_uses;
27 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
28 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
32 explain_borrow::{BorrowExplanation, LaterUseKind},
33 IncludingDowncast, RegionName, RegionNameSource, UseSpans,
38 /// Index of the "move out" that we found. The `MoveData` can
39 /// then tell us where the move occurred.
42 /// `true` if we traversed a back edge while walking from the point
43 /// of error to the move site.
44 traversed_back_edge: bool,
47 /// Which case a StorageDeadOrDrop is for.
48 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
49 enum StorageDeadOrDrop<'tcx> {
55 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
56 pub(crate) fn report_use_of_moved_or_uninitialized(
59 desired_action: InitializationRequiringAction,
60 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
64 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
65 moved_place={:?} used_place={:?} span={:?} mpi={:?}",
66 location, desired_action, moved_place, used_place, span, mpi
70 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
71 let span = use_spans.args_or_use();
73 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
75 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
76 move_site_vec, use_spans
78 let move_out_indices: Vec<_> =
79 move_site_vec.iter().map(|move_site| move_site.moi).collect();
81 if move_out_indices.is_empty() {
82 let root_place = PlaceRef { projection: &[], ..used_place };
84 if !self.uninitialized_error_reported.insert(root_place) {
86 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
93 match self.describe_place_with_options(used_place, IncludingDowncast(true)) {
94 Some(name) => format!("`{}`", name),
95 None => "value".to_owned(),
97 let mut err = self.cannot_act_on_uninitialized_variable(
99 desired_action.as_noun(),
101 .describe_place_with_options(moved_place, IncludingDowncast(true))
102 .unwrap_or_else(|| "_".to_owned()),
104 err.span_label(span, format!("use of possibly-uninitialized {}", item_msg));
106 use_spans.var_span_label_path_only(
108 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
111 self.buffer_error(err);
113 if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
114 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
116 "report_use_of_moved_or_uninitialized place: error suppressed \
124 let is_partial_move = move_site_vec.iter().any(|move_site| {
125 let move_out = self.move_data.moves[(*move_site).moi];
126 let moved_place = &self.move_data.move_paths[move_out.path].place;
127 // `*(_1)` where `_1` is a `Box` is actually a move out.
128 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
129 && self.body.local_decls[moved_place.local].ty.is_box();
132 && used_place != moved_place.as_ref()
133 && used_place.is_prefix_of(moved_place.as_ref())
136 let partial_str = if is_partial_move { "partial " } else { "" };
137 let partially_str = if is_partial_move { "partially " } else { "" };
139 let mut err = self.cannot_act_on_moved_value(
141 desired_action.as_noun(),
143 self.describe_place_with_options(moved_place, IncludingDowncast(true)),
146 let reinit_spans = maybe_reinitialized_locations
150 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
153 .collect::<Vec<Span>>();
155 let reinits = maybe_reinitialized_locations.len();
157 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
158 } else if reinits > 1 {
160 MultiSpan::from_spans(reinit_spans),
162 format!("these {} reinitializations might get skipped", reinits)
165 "these 3 reinitializations and {} other{} might get skipped",
167 if reinits == 4 { "" } else { "s" }
173 self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
175 let mut is_loop_move = false;
176 let mut in_pattern = false;
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 self.explain_captures(
208 maybe_reinitialized_locations.is_empty(),
211 if let (UseSpans::PatUse(span), []) =
212 (move_spans, &maybe_reinitialized_locations[..])
214 if maybe_reinitialized_locations.is_empty() {
215 err.span_suggestion_verbose(
218 "borrow this field in the pattern to avoid moving {}",
219 self.describe_place(moved_place.as_ref())
220 .map(|n| format!("`{}`", n))
221 .unwrap_or_else(|| "the value".to_string())
224 Applicability::MachineApplicable,
231 use_spans.var_span_label_path_only(
233 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
240 "value {} here after {}move",
241 desired_action.as_verb_in_past_tense(),
247 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
248 let needs_note = match ty.kind() {
249 ty::Closure(id, _) => {
250 let tables = self.infcx.tcx.typeck(id.expect_local());
251 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
253 tables.closure_kind_origins().get(hir_id).is_none()
258 let mpi = self.move_data.moves[move_out_indices[0]].path;
259 let place = &self.move_data.move_paths[mpi].place;
260 let ty = place.ty(self.body, self.infcx.tcx).ty;
262 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
263 if is_loop_move & !in_pattern {
264 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
265 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
266 err.span_suggestion_verbose(
269 "consider creating a fresh reborrow of {} here",
270 self.describe_place(moved_place)
271 .map(|n| format!("`{}`", n))
272 .unwrap_or_else(|| "the mutable reference".to_string()),
274 "&mut *".to_string(),
275 Applicability::MachineApplicable,
281 self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
282 let note_msg = match opt_name {
283 Some(ref name) => format!("`{}`", name),
284 None => "value".to_owned(),
286 if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) {
287 // Suppress the next suggestion since we don't want to put more bounds onto
288 // something that already has `Fn`-like bounds (or is a closure), so we can't
291 self.suggest_adding_copy_bounds(&mut err, ty, span);
295 let span = if let Some(local) = place.as_local() {
296 Some(self.body.local_decls[local].source_info.span)
300 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
303 if let UseSpans::FnSelfUse {
304 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
309 "{} occurs due to deref coercion to `{}`",
310 desired_action.as_noun(),
314 // Check first whether the source is accessible (issue #87060)
315 if self.infcx.tcx.sess.source_map().span_to_snippet(deref_target).is_ok() {
316 err.span_note(deref_target, "deref defined here");
320 self.buffer_move_error(move_out_indices, (used_place, err));
324 fn suggest_borrow_fn_like(
326 err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
328 move_sites: &[MoveSite],
331 let tcx = self.infcx.tcx;
333 // Find out if the predicates show that the type is a Fn or FnMut
334 let find_fn_kind_from_did = |predicates: &[(ty::Predicate<'tcx>, Span)], substs| {
335 predicates.iter().find_map(|(pred, _)| {
336 let pred = if let Some(substs) = substs {
337 pred.subst(tcx, substs).kind().skip_binder()
339 pred.kind().skip_binder()
341 if let ty::PredicateKind::Trait(pred) = pred && pred.self_ty() == ty {
342 if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
343 return Some(hir::Mutability::Not);
344 } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
345 return Some(hir::Mutability::Mut);
352 // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
353 // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
354 // These types seem reasonably opaque enough that they could be substituted with their
355 // borrowed variants in a function body when we see a move error.
356 let borrow_level = match ty.kind() {
357 ty::Param(_) => find_fn_kind_from_did(
358 tcx.explicit_predicates_of(self.mir_def_id().to_def_id()).predicates,
361 ty::Opaque(did, substs) => {
362 find_fn_kind_from_did(tcx.explicit_item_bounds(*did), Some(*substs))
364 ty::Closure(_, substs) => match substs.as_closure().kind() {
365 ty::ClosureKind::Fn => Some(hir::Mutability::Not),
366 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
372 let Some(borrow_level) = borrow_level else { return false; };
373 let sugg = move_sites
376 let move_out = self.move_data.moves[(*move_site).moi];
377 let moved_place = &self.move_data.move_paths[move_out.path].place;
378 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
379 let move_span = move_spans.args_or_use();
380 let suggestion = if borrow_level == hir::Mutability::Mut {
385 (move_span.shrink_to_lo(), suggestion)
388 err.multipart_suggestion_verbose(
390 "consider {}borrowing {value_name}",
391 if borrow_level == hir::Mutability::Mut { "mutably " } else { "" }
394 Applicability::MaybeIncorrect,
399 fn suggest_adding_copy_bounds(
401 err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
405 let tcx = self.infcx.tcx;
406 let generics = tcx.generics_of(self.mir_def_id());
408 let Some(hir_generics) = tcx
409 .typeck_root_def_id(self.mir_def_id().to_def_id())
411 .and_then(|def_id| tcx.hir().get_generics(def_id))
413 // Try to find predicates on *generic params* that would allow copying `ty`
414 let predicates: Result<Vec<_>, _> = tcx.infer_ctxt().enter(|infcx| {
415 let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
417 let copy_did = infcx.tcx.lang_items().copy_trait().unwrap();
418 let cause = ObligationCause::new(
421 rustc_infer::traits::ObligationCauseCode::MiscObligation,
423 fulfill_cx.register_bound(
426 // Erase any region vids from the type, which may not be resolved
427 infcx.tcx.erase_regions(ty),
431 // Select all, including ambiguous predicates
432 let errors = fulfill_cx.select_all_or_error(&infcx);
434 // Only emit suggestion if all required predicates are on generic
437 .map(|err| match err.obligation.predicate.kind().skip_binder() {
438 PredicateKind::Trait(predicate) => match predicate.self_ty().kind() {
439 ty::Param(param_ty) => Ok((
440 generics.type_param(param_ty, tcx),
441 predicate.trait_ref.print_only_trait_path().to_string(),
450 if let Ok(predicates) = predicates {
451 suggest_constraining_type_params(
457 .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
462 pub(crate) fn report_move_out_while_borrowed(
465 (place, span): (Place<'tcx>, Span),
466 borrow: &BorrowData<'tcx>,
469 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
470 location, place, span, borrow
472 let value_msg = self.describe_any_place(place.as_ref());
473 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
475 let borrow_spans = self.retrieve_borrow_spans(borrow);
476 let borrow_span = borrow_spans.args_or_use();
478 let move_spans = self.move_spans(place.as_ref(), location);
479 let span = move_spans.args_or_use();
482 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
483 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
484 err.span_label(span, format!("move out of {} occurs here", value_msg));
486 borrow_spans.var_span_label_path_only(
488 format!("borrow occurs due to use{}", borrow_spans.describe()),
491 move_spans.var_span_label(
493 format!("move occurs due to use{}", move_spans.describe()),
497 self.explain_why_borrow_contains_point(location, borrow, None)
498 .add_explanation_to_diagnostic(
507 self.buffer_error(err);
510 pub(crate) fn report_use_while_mutably_borrowed(
513 (place, _span): (Place<'tcx>, Span),
514 borrow: &BorrowData<'tcx>,
515 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
516 let borrow_spans = self.retrieve_borrow_spans(borrow);
517 let borrow_span = borrow_spans.args_or_use();
519 // Conflicting borrows are reported separately, so only check for move
521 let use_spans = self.move_spans(place.as_ref(), location);
522 let span = use_spans.var_or_use();
524 // 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
525 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
526 let mut err = self.cannot_use_when_mutably_borrowed(
528 &self.describe_any_place(place.as_ref()),
530 &self.describe_any_place(borrow.borrowed_place.as_ref()),
533 borrow_spans.var_span_label(
536 let place = &borrow.borrowed_place;
537 let desc_place = self.describe_any_place(place.as_ref());
538 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
543 self.explain_why_borrow_contains_point(location, borrow, None)
544 .add_explanation_to_diagnostic(
556 pub(crate) fn report_conflicting_borrow(
559 (place, span): (Place<'tcx>, Span),
560 gen_borrow_kind: BorrowKind,
561 issued_borrow: &BorrowData<'tcx>,
562 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
563 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
564 let issued_span = issued_spans.args_or_use();
566 let borrow_spans = self.borrow_spans(span, location);
567 let span = borrow_spans.args_or_use();
569 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
575 let (desc_place, msg_place, msg_borrow, union_type_name) =
576 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
578 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
579 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
581 // FIXME: supply non-"" `opt_via` when appropriate
582 let first_borrow_desc;
583 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
584 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
585 first_borrow_desc = "mutable ";
586 self.cannot_reborrow_already_borrowed(
598 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
599 first_borrow_desc = "immutable ";
600 self.cannot_reborrow_already_borrowed(
613 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
614 first_borrow_desc = "first ";
615 let mut err = self.cannot_mutably_borrow_multiply(
623 self.suggest_split_at_mut_if_applicable(
626 issued_borrow.borrowed_place,
631 (BorrowKind::Unique, BorrowKind::Unique) => {
632 first_borrow_desc = "first ";
633 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
636 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
637 if let Some(immutable_section_description) =
638 self.classify_immutable_section(issued_borrow.assigned_place)
640 let mut err = self.cannot_mutate_in_immutable_section(
644 immutable_section_description,
647 borrow_spans.var_span_label(
650 "borrow occurs due to use of {}{}",
652 borrow_spans.describe(),
659 first_borrow_desc = "immutable ";
660 self.cannot_reborrow_already_borrowed(
674 (BorrowKind::Unique, _) => {
675 first_borrow_desc = "first ";
676 self.cannot_uniquely_borrow_by_one_closure(
688 (BorrowKind::Shared, BorrowKind::Unique) => {
689 first_borrow_desc = "first ";
690 self.cannot_reborrow_already_uniquely_borrowed(
703 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
704 first_borrow_desc = "first ";
705 self.cannot_reborrow_already_uniquely_borrowed(
718 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
721 BorrowKind::Mut { .. }
724 | BorrowKind::Shallow,
728 if issued_spans == borrow_spans {
729 borrow_spans.var_span_label(
731 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
732 gen_borrow_kind.describe_mutability(),
735 let borrow_place = &issued_borrow.borrowed_place;
736 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
737 issued_spans.var_span_label(
740 "first borrow occurs due to use of {}{}",
742 issued_spans.describe(),
744 issued_borrow.kind.describe_mutability(),
747 borrow_spans.var_span_label(
750 "second borrow occurs due to use of {}{}",
752 borrow_spans.describe(),
754 gen_borrow_kind.describe_mutability(),
758 if union_type_name != "" {
760 "{} is a field of the union `{}`, so it overlaps the field {}",
761 msg_place, union_type_name, msg_borrow,
765 explanation.add_explanation_to_diagnostic(
772 Some((issued_span, span)),
775 self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation);
780 #[instrument(level = "debug", skip(self, err))]
781 fn suggest_using_local_if_applicable(
783 err: &mut Diagnostic,
785 issued_borrow: &BorrowData<'tcx>,
786 explanation: BorrowExplanation,
788 let used_in_call = matches!(
790 BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
793 debug!("not later used in call");
798 if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
805 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
808 issued_borrow.reserve_location
810 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
812 let inner_param_location = location;
813 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
814 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
817 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
819 "`inner_param_location` {:?} is not for an assignment: {:?}",
820 inner_param_location, inner_param_stmt
824 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
825 let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
826 let Either::Right(term) = self.body.stmt_at(loc) else {
827 debug!("{:?} is a statement, so it can't be a call", loc);
830 let TerminatorKind::Call { args, .. } = &term.kind else {
831 debug!("not a call: {:?}", term);
834 debug!("checking call args for uses of inner_param: {:?}", args);
835 if args.contains(&Operand::Move(inner_param)) {
841 debug!("no uses of inner_param found as a by-move call arg");
844 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
846 let inner_call_span = inner_call_term.source_info.span;
847 let outer_call_span = match use_span {
849 None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span,
851 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
852 // FIXME: This stops the suggestion in some cases where it should be emitted.
853 // Fix the spans for those cases so it's emitted correctly.
855 "outer span {:?} does not strictly contain inner span {:?}",
856 outer_call_span, inner_call_span
863 "try adding a local storing this{}...",
864 if use_span.is_some() { "" } else { " argument" }
870 "...and then using that local {}",
871 if use_span.is_some() { "here" } else { "as the argument to this call" }
876 fn suggest_split_at_mut_if_applicable(
878 err: &mut Diagnostic,
880 borrowed_place: Place<'tcx>,
882 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
883 (&place.projection[..], &borrowed_place.projection[..])
886 "consider using `.split_at_mut(position)` or similar method to obtain \
887 two mutable non-overlapping sub-slices",
892 /// Returns the description of the root place for a conflicting borrow and the full
893 /// descriptions of the places that caused the conflict.
895 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
896 /// attempted while a shared borrow is live, then this function will return:
900 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
901 /// a shared borrow of another field `x.y`, then this function will return:
903 /// ("x", "x.z", "x.y")
905 /// In the more complex union case, where the union is a field of a struct, then if a mutable
906 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
907 /// another field `x.u.y`, then this function will return:
909 /// ("x.u", "x.u.z", "x.u.y")
911 /// This is used when creating error messages like below:
914 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
915 /// mutable (via `a.u.s.b`) [E0502]
917 pub(crate) fn describe_place_for_conflicting_borrow(
919 first_borrowed_place: Place<'tcx>,
920 second_borrowed_place: Place<'tcx>,
921 ) -> (String, String, String, String) {
922 // Define a small closure that we can use to check if the type of a place
924 let union_ty = |place_base| {
925 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
926 // using a type annotation in the closure argument instead leads to a lifetime error.
927 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
928 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
931 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
932 // code duplication (particularly around returning an empty description in the failure
936 // If we have a conflicting borrow of the same place, then we don't want to add
937 // an extraneous "via x.y" to our diagnostics, so filter out this case.
938 first_borrowed_place != second_borrowed_place
941 // We're going to want to traverse the first borrowed place to see if we can find
942 // field access to a union. If we find that, then we will keep the place of the
943 // union being accessed and the field that was being accessed so we can check the
944 // second borrowed place for the same union and an access to a different field.
945 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
947 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
948 return Some((place_base, field));
955 .and_then(|(target_base, target_field)| {
956 // With the place of a union and a field access into it, we traverse the second
957 // borrowed place and look for an access to a different field of the same union.
958 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
959 if let ProjectionElem::Field(field, _) = elem {
960 if let Some(union_ty) = union_ty(place_base) {
961 if field != target_field && place_base == target_base {
963 self.describe_any_place(place_base),
964 self.describe_any_place(first_borrowed_place.as_ref()),
965 self.describe_any_place(second_borrowed_place.as_ref()),
966 union_ty.to_string(),
975 // If we didn't find a field access into a union, or both places match, then
976 // only return the description of the first place.
978 self.describe_any_place(first_borrowed_place.as_ref()),
986 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
988 /// This means that some data referenced by `borrow` needs to live
989 /// past the point where the StorageDeadOrDrop of `place` occurs.
990 /// This is usually interpreted as meaning that `place` has too
991 /// short a lifetime. (But sometimes it is more useful to report
992 /// it as a more direct conflict between the execution of a
993 /// `Drop::drop` with an aliasing borrow.)
994 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
997 borrow: &BorrowData<'tcx>,
998 place_span: (Place<'tcx>, Span),
999 kind: Option<WriteKind>,
1002 "report_borrowed_value_does_not_live_long_enough(\
1003 {:?}, {:?}, {:?}, {:?}\
1005 location, borrow, place_span, kind
1008 let drop_span = place_span.1;
1010 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1012 let borrow_spans = self.retrieve_borrow_spans(borrow);
1013 let borrow_span = borrow_spans.var_or_use_path_span();
1015 assert!(root_place.projection.is_empty());
1016 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1018 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1020 if self.access_place_error_reported.contains(&(
1021 Place { local: root_place.local, projection: root_place_projection },
1025 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1031 self.access_place_error_reported.insert((
1032 Place { local: root_place.local, projection: root_place_projection },
1036 let borrowed_local = borrow.borrowed_place.local;
1037 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1039 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1040 self.buffer_error(err);
1044 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1045 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1047 // If a borrow of path `B` conflicts with drop of `D` (and
1048 // we're not in the uninteresting case where `B` is a
1049 // prefix of `D`), then report this as a more interesting
1050 // destructor conflict.
1051 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1052 self.report_borrow_conflicts_with_destructor(
1053 location, borrow, place_span, kind, dropped_ty,
1059 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1061 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1062 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1065 "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
1066 place_desc, explanation
1068 let err = match (place_desc, explanation) {
1069 // If the outlives constraint comes from inside the closure,
1074 // Box::new(|| y) as Box<Fn() -> &'static i32>
1076 // then just use the normal error. The closure isn't escaping
1077 // and `move` will not help here.
1080 BorrowExplanation::MustBeValidFor {
1082 category @ (ConstraintCategory::Return(_)
1083 | ConstraintCategory::CallArgument
1084 | ConstraintCategory::OpaqueType),
1085 from_closure: false,
1090 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1091 .report_escaping_closure_capture(
1097 &format!("`{}`", name),
1101 BorrowExplanation::MustBeValidFor {
1102 category: ConstraintCategory::Assignment,
1103 from_closure: false,
1107 RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
1113 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1114 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1122 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1132 self.buffer_error(err);
1135 fn report_local_value_does_not_live_long_enough(
1139 borrow: &BorrowData<'tcx>,
1141 borrow_spans: UseSpans<'tcx>,
1142 explanation: BorrowExplanation,
1143 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1145 "report_local_value_does_not_live_long_enough(\
1146 {:?}, {:?}, {:?}, {:?}, {:?}\
1148 location, name, borrow, drop_span, borrow_spans
1151 let borrow_span = borrow_spans.var_or_use_path_span();
1152 if let BorrowExplanation::MustBeValidFor {
1156 from_closure: false,
1160 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1165 opt_place_desc.as_ref(),
1171 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1173 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1174 let region_name = annotation.emit(self, &mut err);
1178 format!("`{}` would have to be valid for `{}`...", name, region_name),
1181 let fn_hir_id = self.mir_hir_id();
1185 "...but `{}` will be dropped here, when the {} returns",
1190 .opt_name(fn_hir_id)
1191 .map(|name| format!("function `{}`", name))
1192 .unwrap_or_else(|| {
1196 .typeck(self.mir_def_id())
1197 .node_type(fn_hir_id)
1200 ty::Closure(..) => "enclosing closure",
1201 ty::Generator(..) => "enclosing generator",
1202 kind => bug!("expected closure or generator, found {:?}", kind),
1210 "functions cannot return a borrow to data owned within the function's scope, \
1211 functions can only return borrows to data passed as arguments",
1214 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1215 references-and-borrowing.html#dangling-references>",
1218 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1220 explanation.add_explanation_to_diagnostic(
1231 err.span_label(borrow_span, "borrowed value does not live long enough");
1232 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1234 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1236 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1238 explanation.add_explanation_to_diagnostic(
1252 fn report_borrow_conflicts_with_destructor(
1255 borrow: &BorrowData<'tcx>,
1256 (place, drop_span): (Place<'tcx>, Span),
1257 kind: Option<WriteKind>,
1258 dropped_ty: Ty<'tcx>,
1261 "report_borrow_conflicts_with_destructor(\
1262 {:?}, {:?}, ({:?}, {:?}), {:?}\
1264 location, borrow, place, drop_span, kind,
1267 let borrow_spans = self.retrieve_borrow_spans(borrow);
1268 let borrow_span = borrow_spans.var_or_use();
1270 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1272 let what_was_dropped = match self.describe_place(place.as_ref()) {
1273 Some(name) => format!("`{}`", name),
1274 None => String::from("temporary value"),
1277 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1278 Some(borrowed) => format!(
1279 "here, drop of {D} needs exclusive access to `{B}`, \
1280 because the type `{T}` implements the `Drop` trait",
1281 D = what_was_dropped,
1286 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1287 D = what_was_dropped,
1291 err.span_label(drop_span, label);
1293 // Only give this note and suggestion if they could be relevant.
1295 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1297 BorrowExplanation::UsedLater { .. }
1298 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1299 err.note("consider using a `let` binding to create a longer lived value");
1304 explanation.add_explanation_to_diagnostic(
1314 self.buffer_error(err);
1317 fn report_thread_local_value_does_not_live_long_enough(
1321 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1323 "report_thread_local_value_does_not_live_long_enough(\
1326 drop_span, borrow_span
1329 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1333 "thread-local variables cannot be borrowed beyond the end of the function",
1335 err.span_label(drop_span, "end of enclosing function is here");
1340 fn report_temporary_value_does_not_live_long_enough(
1343 borrow: &BorrowData<'tcx>,
1345 borrow_spans: UseSpans<'tcx>,
1347 explanation: BorrowExplanation,
1348 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1350 "report_temporary_value_does_not_live_long_enough(\
1351 {:?}, {:?}, {:?}, {:?}\
1353 location, borrow, drop_span, proper_span
1356 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1359 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1370 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1371 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1372 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1375 BorrowExplanation::UsedLater(..)
1376 | BorrowExplanation::UsedLaterInLoop(..)
1377 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1378 // Only give this note and suggestion if it could be relevant.
1379 err.note("consider using a `let` binding to create a longer lived value");
1383 explanation.add_explanation_to_diagnostic(
1393 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1395 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1400 fn try_report_cannot_return_reference_to_local(
1402 borrow: &BorrowData<'tcx>,
1405 category: ConstraintCategory,
1406 opt_place_desc: Option<&String>,
1407 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1408 let return_kind = match category {
1409 ConstraintCategory::Return(_) => "return",
1410 ConstraintCategory::Yield => "yield",
1414 // FIXME use a better heuristic than Spans
1415 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1421 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1422 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1423 match self.body.local_kind(local) {
1424 LocalKind::ReturnPointer | LocalKind::Temp => {
1425 bug!("temporary or return pointer with a name")
1427 LocalKind::Var => "local variable ",
1429 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1431 "variable captured by `move` "
1433 LocalKind::Arg => "function parameter ",
1439 format!("{}`{}`", local_kind, place_desc),
1440 format!("`{}` is borrowed here", place_desc),
1444 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1445 let local = root_place.local;
1446 match self.body.local_kind(local) {
1447 LocalKind::ReturnPointer | LocalKind::Temp => {
1448 ("temporary value".to_string(), "temporary value created here".to_string())
1451 "function parameter".to_string(),
1452 "function parameter borrowed here".to_string(),
1455 ("local binding".to_string(), "local binding introduced here".to_string())
1460 let mut err = self.cannot_return_reference_to_local(
1467 if return_span != borrow_span {
1468 err.span_label(borrow_span, note);
1470 let tcx = self.infcx.tcx;
1471 let ty_params = ty::List::empty();
1473 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1474 let return_ty = tcx.erase_regions(return_ty);
1477 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1480 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1481 .must_apply_modulo_regions()
1483 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1484 err.span_suggestion_hidden(
1486 "use `.collect()` to allocate the iterator",
1487 format!("{}{}", snippet, ".collect::<Vec<_>>()"),
1488 Applicability::MaybeIncorrect,
1498 fn report_escaping_closure_capture(
1500 use_span: UseSpans<'tcx>,
1502 fr_name: &RegionName,
1503 category: ConstraintCategory,
1504 constraint_span: Span,
1506 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1507 let tcx = self.infcx.tcx;
1508 let args_span = use_span.args_or_use();
1510 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1512 if string.starts_with("async ") {
1513 let pos = args_span.lo() + BytePos(6);
1514 (args_span.with_lo(pos).with_hi(pos), "move ".to_string())
1515 } else if string.starts_with("async|") {
1516 let pos = args_span.lo() + BytePos(5);
1517 (args_span.with_lo(pos).with_hi(pos), " move".to_string())
1519 (args_span.shrink_to_lo(), "move ".to_string())
1522 Err(_) => (args_span, "move |<args>| <body>".to_string()),
1524 let kind = match use_span.generator_kind() {
1525 Some(generator_kind) => match generator_kind {
1526 GeneratorKind::Async(async_kind) => match async_kind {
1527 AsyncGeneratorKind::Block => "async block",
1528 AsyncGeneratorKind::Closure => "async closure",
1529 _ => bug!("async block/closure expected, but async function found."),
1531 GeneratorKind::Gen => "generator",
1537 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1538 err.span_suggestion_verbose(
1541 "to force the {} to take ownership of {} (and any \
1542 other referenced variables), use the `move` keyword",
1546 Applicability::MachineApplicable,
1550 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1551 let msg = format!("{} is returned here", kind);
1552 err.span_note(constraint_span, &msg);
1554 ConstraintCategory::CallArgument => {
1555 fr_name.highlight_region_name(&mut err);
1556 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1558 "async blocks are not executed immediately and must either take a \
1559 reference or ownership of outside variables they use",
1562 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1563 err.span_note(constraint_span, &msg);
1567 "report_escaping_closure_capture called with unexpected constraint \
1576 fn report_escaping_data(
1579 name: &Option<String>,
1583 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1584 let tcx = self.infcx.tcx;
1586 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1589 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1593 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1596 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1598 if let Some(name) = name {
1601 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1606 format!("reference escapes the {} body here", escapes_from),
1613 fn get_moved_indexes(
1617 ) -> (Vec<MoveSite>, Vec<Location>) {
1618 fn predecessor_locations<'a>(
1619 body: &'a mir::Body<'_>,
1621 ) -> impl Iterator<Item = Location> + 'a {
1622 if location.statement_index == 0 {
1623 let predecessors = body.predecessors()[location.block].to_vec();
1624 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1626 Either::Right(std::iter::once(Location {
1627 statement_index: location.statement_index - 1,
1633 let mut mpis = vec![mpi];
1634 let move_paths = &self.move_data.move_paths;
1635 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1637 let mut stack = Vec::new();
1638 let mut back_edge_stack = Vec::new();
1640 predecessor_locations(self.body, location).for_each(|predecessor| {
1641 if location.dominates(predecessor, &self.dominators) {
1642 back_edge_stack.push(predecessor)
1644 stack.push(predecessor);
1648 let mut reached_start = false;
1650 /* Check if the mpi is initialized as an argument */
1651 let mut is_argument = false;
1652 for arg in self.body.args_iter() {
1653 let path = self.move_data.rev_lookup.find_local(arg);
1654 if mpis.contains(&path) {
1659 let mut visited = FxHashSet::default();
1660 let mut move_locations = FxHashSet::default();
1661 let mut reinits = vec![];
1662 let mut result = vec![];
1664 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1666 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1667 location, is_back_edge
1670 if !visited.insert(location) {
1676 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1677 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1678 // this analysis only tries to find moves explicitly
1679 // written by the user, so we ignore the move-outs
1680 // created by `StorageDead` and at the beginning
1683 // If we are found a use of a.b.c which was in error, then we want to look for
1684 // moves not only of a.b.c but also a.b and a.
1686 // Note that the moves data already includes "parent" paths, so we don't have to
1687 // worry about the other case: that is, if there is a move of a.b.c, it is already
1688 // marked as a move of a.b and a as well, so we will generate the correct errors
1690 for moi in &self.move_data.loc_map[location] {
1691 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1692 let path = self.move_data.moves[*moi].path;
1693 if mpis.contains(&path) {
1695 "report_use_of_moved_or_uninitialized: found {:?}",
1696 move_paths[path].place
1698 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1699 move_locations.insert(location);
1701 // Strictly speaking, we could continue our DFS here. There may be
1702 // other moves that can reach the point of error. But it is kind of
1703 // confusing to highlight them.
1711 // drop(a); // <-- current point of error
1714 // Because we stop the DFS here, we only highlight `let c = a`,
1715 // and not `let b = a`. We will of course also report an error at
1716 // `let c = a` which highlights `let b = a` as the move.
1723 let mut any_match = false;
1724 for ii in &self.move_data.init_loc_map[location] {
1725 let init = self.move_data.inits[*ii];
1727 InitKind::Deep | InitKind::NonPanicPathOnly => {
1728 if mpis.contains(&init.path) {
1732 InitKind::Shallow => {
1733 if mpi == init.path {
1740 reinits.push(location);
1746 while let Some(location) = stack.pop() {
1747 if dfs_iter(&mut result, location, false) {
1751 let mut has_predecessor = false;
1752 predecessor_locations(self.body, location).for_each(|predecessor| {
1753 if location.dominates(predecessor, &self.dominators) {
1754 back_edge_stack.push(predecessor)
1756 stack.push(predecessor);
1758 has_predecessor = true;
1761 if !has_predecessor {
1762 reached_start = true;
1765 if (is_argument || !reached_start) && result.is_empty() {
1766 /* Process back edges (moves in future loop iterations) only if
1767 the move path is definitely initialized upon loop entry,
1768 to avoid spurious "in previous iteration" errors.
1769 During DFS, if there's a path from the error back to the start
1770 of the function with no intervening init or move, then the
1771 move path may be uninitialized at loop entry.
1773 while let Some(location) = back_edge_stack.pop() {
1774 if dfs_iter(&mut result, location, true) {
1778 predecessor_locations(self.body, location)
1779 .for_each(|predecessor| back_edge_stack.push(predecessor));
1783 // Check if we can reach these reinits from a move location.
1784 let reinits_reachable = reinits
1787 let mut visited = FxHashSet::default();
1788 let mut stack = vec![*reinit];
1789 while let Some(location) = stack.pop() {
1790 if !visited.insert(location) {
1793 if move_locations.contains(&location) {
1796 stack.extend(predecessor_locations(self.body, location));
1800 .collect::<Vec<Location>>();
1801 (result, reinits_reachable)
1804 pub(crate) fn report_illegal_mutation_of_borrowed(
1807 (place, span): (Place<'tcx>, Span),
1808 loan: &BorrowData<'tcx>,
1810 let loan_spans = self.retrieve_borrow_spans(loan);
1811 let loan_span = loan_spans.args_or_use();
1813 let descr_place = self.describe_any_place(place.as_ref());
1814 if loan.kind == BorrowKind::Shallow {
1815 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1816 let mut err = self.cannot_mutate_in_immutable_section(
1823 loan_spans.var_span_label(
1825 format!("borrow occurs due to use{}", loan_spans.describe()),
1826 loan.kind.describe_mutability(),
1829 self.buffer_error(err);
1835 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
1837 loan_spans.var_span_label(
1839 format!("borrow occurs due to use{}", loan_spans.describe()),
1840 loan.kind.describe_mutability(),
1843 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
1853 self.explain_deref_coercion(loan, &mut err);
1855 self.buffer_error(err);
1858 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
1859 let tcx = self.infcx.tcx;
1861 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
1862 Some((method_did, method_substs)),
1864 &self.body[loan.reserve_location.block].terminator,
1865 rustc_const_eval::util::find_self_call(
1868 loan.assigned_place.local,
1869 loan.reserve_location.block,
1872 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
1874 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
1875 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
1878 if let Some(Ok(instance)) = deref_target {
1879 let deref_target_ty = instance.ty(tcx, self.param_env);
1881 "borrow occurs due to deref coercion to `{}`",
1884 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
1890 /// Reports an illegal reassignment; for example, an assignment to
1891 /// (part of) a non-`mut` local that occurs potentially after that
1892 /// local has already been initialized. `place` is the path being
1893 /// assigned; `err_place` is a place providing a reason why
1894 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
1895 /// assignment to `x.f`).
1896 pub(crate) fn report_illegal_reassignment(
1898 _location: Location,
1899 (place, span): (Place<'tcx>, Span),
1900 assigned_span: Span,
1901 err_place: Place<'tcx>,
1903 let (from_arg, local_decl, local_name) = match err_place.as_local() {
1905 self.body.local_kind(local) == LocalKind::Arg,
1906 Some(&self.body.local_decls[local]),
1907 self.local_names[local],
1909 None => (false, None, None),
1912 // If root local is initialized immediately (everything apart from let
1913 // PATTERN;) then make the error refer to that local, rather than the
1914 // place being assigned later.
1915 let (place_description, assigned_span) = match local_decl {
1918 Some(box LocalInfo::User(
1919 ClearCrossCrate::Clear
1920 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
1921 opt_match_place: None,
1925 | Some(box LocalInfo::StaticRef { .. })
1929 | None => (self.describe_any_place(place.as_ref()), assigned_span),
1930 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
1933 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
1934 let msg = if from_arg {
1935 "cannot assign to immutable argument"
1937 "cannot assign twice to immutable variable"
1939 if span != assigned_span && !from_arg {
1940 err.span_label(assigned_span, format!("first assignment to {}", place_description));
1942 if let Some(decl) = local_decl
1943 && let Some(name) = local_name
1944 && decl.can_be_made_mutable()
1946 err.span_suggestion(
1947 decl.source_info.span,
1948 "consider making this binding mutable",
1949 format!("mut {}", name),
1950 Applicability::MachineApplicable,
1953 err.span_label(span, msg);
1954 self.buffer_error(err);
1957 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
1958 let tcx = self.infcx.tcx;
1959 match place.last_projection() {
1960 None => StorageDeadOrDrop::LocalStorageDead,
1961 Some((place_base, elem)) => {
1962 // FIXME(spastorino) make this iterate
1963 let base_access = self.classify_drop_access_kind(place_base);
1965 ProjectionElem::Deref => match base_access {
1966 StorageDeadOrDrop::LocalStorageDead
1967 | StorageDeadOrDrop::BoxedStorageDead => {
1969 place_base.ty(self.body, tcx).ty.is_box(),
1970 "Drop of value behind a reference or raw pointer"
1972 StorageDeadOrDrop::BoxedStorageDead
1974 StorageDeadOrDrop::Destructor(_) => base_access,
1976 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
1977 let base_ty = place_base.ty(self.body, tcx).ty;
1978 match base_ty.kind() {
1979 ty::Adt(def, _) if def.has_dtor(tcx) => {
1980 // Report the outermost adt with a destructor
1982 StorageDeadOrDrop::Destructor(_) => base_access,
1983 StorageDeadOrDrop::LocalStorageDead
1984 | StorageDeadOrDrop::BoxedStorageDead => {
1985 StorageDeadOrDrop::Destructor(base_ty)
1992 ProjectionElem::ConstantIndex { .. }
1993 | ProjectionElem::Subslice { .. }
1994 | ProjectionElem::Index(_) => base_access,
2000 /// Describe the reason for the fake borrow that was assigned to `place`.
2001 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2002 use rustc_middle::mir::visit::Visitor;
2003 struct FakeReadCauseFinder<'tcx> {
2005 cause: Option<FakeReadCause>,
2007 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2008 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2010 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2011 if *place == self.place =>
2013 self.cause = Some(*cause);
2019 let mut visitor = FakeReadCauseFinder { place, cause: None };
2020 visitor.visit_body(&self.body);
2021 match visitor.cause {
2022 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2023 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2028 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2029 /// borrow of local value that does not live long enough.
2030 fn annotate_argument_and_return_for_borrow(
2032 borrow: &BorrowData<'tcx>,
2033 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2034 // Define a fallback for when we can't match a closure.
2036 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2040 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2042 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2043 self.mir_def_id().to_def_id(),
2044 self.infcx.tcx.fn_sig(self.mir_def_id()),
2051 // In order to determine whether we need to annotate, we need to check whether the reserve
2052 // place was an assignment into a temporary.
2054 // If it was, we check whether or not that temporary is eventually assigned into the return
2055 // place. If it was, we can add annotations about the function's return type and arguments
2056 // and it'll make sense.
2057 let location = borrow.reserve_location;
2058 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2059 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2060 &self.body[location.block].statements.get(location.statement_index)
2062 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2063 // Check that the initial assignment of the reserve location is into a temporary.
2064 let mut target = match reservation.as_local() {
2065 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2069 // Next, look through the rest of the block, checking if we are assigning the
2070 // `target` (that is, the place that contains our borrow) to anything.
2071 let mut annotated_closure = None;
2072 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2074 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2077 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2078 if let Some(assigned_to) = place.as_local() {
2080 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2084 // Check if our `target` was captured by a closure.
2085 if let Rvalue::Aggregate(
2086 box AggregateKind::Closure(def_id, substs),
2090 for operand in operands {
2091 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2095 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2099 // Find the local from the operand.
2100 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2104 if assigned_from_local != target {
2108 // If a closure captured our `target` and then assigned
2109 // into a place then we should annotate the closure in
2110 // case it ends up being assigned into the return place.
2112 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2114 "annotate_argument_and_return_for_borrow: \
2115 annotated_closure={:?} assigned_from_local={:?} \
2117 annotated_closure, assigned_from_local, assigned_to
2120 if assigned_to == mir::RETURN_PLACE {
2121 // If it was assigned directly into the return place, then
2123 return annotated_closure;
2125 // Otherwise, update the target.
2126 target = assigned_to;
2130 // If none of our closure's operands matched, then skip to the next
2135 // Otherwise, look at other types of assignment.
2136 let assigned_from = match rvalue {
2137 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2138 Rvalue::Use(operand) => match operand {
2139 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2147 "annotate_argument_and_return_for_borrow: \
2148 assigned_from={:?}",
2152 // Find the local from the rvalue.
2153 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2155 "annotate_argument_and_return_for_borrow: \
2156 assigned_from_local={:?}",
2157 assigned_from_local,
2160 // Check if our local matches the target - if so, we've assigned our
2161 // borrow to a new place.
2162 if assigned_from_local != target {
2166 // If we assigned our `target` into a new place, then we should
2167 // check if it was the return place.
2169 "annotate_argument_and_return_for_borrow: \
2170 assigned_from_local={:?} assigned_to={:?}",
2171 assigned_from_local, assigned_to
2173 if assigned_to == mir::RETURN_PLACE {
2174 // If it was then return the annotated closure if there was one,
2175 // else, annotate this function.
2176 return annotated_closure.or_else(fallback);
2179 // If we didn't assign into the return place, then we just update
2181 target = assigned_to;
2186 // Check the terminator if we didn't find anything in the statements.
2187 let terminator = &self.body[location.block].terminator();
2189 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2192 if let TerminatorKind::Call { destination: Some((place, _)), args, .. } =
2195 if let Some(assigned_to) = place.as_local() {
2197 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2200 for operand in args {
2201 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2205 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2209 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2211 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2212 assigned_from_local,
2215 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2216 return annotated_closure.or_else(fallback);
2224 // If we haven't found an assignment into the return place, then we need not add
2226 debug!("annotate_argument_and_return_for_borrow: none found");
2230 /// Annotate the first argument and return type of a function signature if they are
2235 sig: ty::PolyFnSig<'tcx>,
2236 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2237 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2238 let is_closure = self.infcx.tcx.is_closure(did);
2239 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?);
2240 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2242 // We need to work out which arguments to highlight. We do this by looking
2243 // at the return type, where there are three cases:
2245 // 1. If there are named arguments, then we should highlight the return type and
2246 // highlight any of the arguments that are also references with that lifetime.
2247 // If there are no arguments that have the same lifetime as the return type,
2248 // then don't highlight anything.
2249 // 2. The return type is a reference with an anonymous lifetime. If this is
2250 // the case, then we can take advantage of (and teach) the lifetime elision
2253 // We know that an error is being reported. So the arguments and return type
2254 // must satisfy the elision rules. Therefore, if there is a single argument
2255 // then that means the return type and first (and only) argument have the same
2256 // lifetime and the borrow isn't meeting that, we can highlight the argument
2259 // If there are multiple arguments then the first argument must be self (else
2260 // it would not satisfy the elision rules), so we can highlight self and the
2262 // 3. The return type is not a reference. In this case, we don't highlight
2264 let return_ty = sig.output();
2265 match return_ty.skip_binder().kind() {
2266 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2267 // This is case 1 from above, return type is a named reference so we need to
2268 // search for relevant arguments.
2269 let mut arguments = Vec::new();
2270 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2271 if let ty::Ref(argument_region, _, _) = argument.kind() {
2272 if argument_region == return_region {
2273 // Need to use the `rustc_middle::ty` types to compare against the
2274 // `return_region`. Then use the `rustc_hir` type to get only
2275 // the lifetime span.
2276 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2277 // With access to the lifetime, we can get
2279 arguments.push((*argument, lifetime.span));
2281 bug!("ty type is a ref but hir type is not");
2287 // We need to have arguments. This shouldn't happen, but it's worth checking.
2288 if arguments.is_empty() {
2292 // We use a mix of the HIR and the Ty types to get information
2293 // as the HIR doesn't have full types for closure arguments.
2294 let return_ty = sig.output().skip_binder();
2295 let mut return_span = fn_decl.output.span();
2296 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2297 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2298 return_span = lifetime.span;
2302 Some(AnnotatedBorrowFnSignature::NamedFunction {
2308 ty::Ref(_, _, _) if is_closure => {
2309 // This is case 2 from above but only for closures, return type is anonymous
2310 // reference so we select
2311 // the first argument.
2312 let argument_span = fn_decl.inputs.first()?.span;
2313 let argument_ty = sig.inputs().skip_binder().first()?;
2315 // Closure arguments are wrapped in a tuple, so we need to get the first
2317 if let ty::Tuple(elems) = argument_ty.kind() {
2318 let &argument_ty = elems.first()?;
2319 if let ty::Ref(_, _, _) = argument_ty.kind() {
2320 return Some(AnnotatedBorrowFnSignature::Closure {
2329 ty::Ref(_, _, _) => {
2330 // This is also case 2 from above but for functions, return type is still an
2331 // anonymous reference so we select the first argument.
2332 let argument_span = fn_decl.inputs.first()?.span;
2333 let argument_ty = *sig.inputs().skip_binder().first()?;
2335 let return_span = fn_decl.output.span();
2336 let return_ty = sig.output().skip_binder();
2338 // We expect the first argument to be a reference.
2339 match argument_ty.kind() {
2340 ty::Ref(_, _, _) => {}
2344 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2352 // This is case 3 from above, return type is not a reference so don't highlight
2361 enum AnnotatedBorrowFnSignature<'tcx> {
2363 arguments: Vec<(Ty<'tcx>, Span)>,
2364 return_ty: Ty<'tcx>,
2368 argument_ty: Ty<'tcx>,
2369 argument_span: Span,
2370 return_ty: Ty<'tcx>,
2374 argument_ty: Ty<'tcx>,
2375 argument_span: Span,
2379 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2380 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2382 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2384 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2387 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2390 cx.get_region_name_for_ty(argument_ty, 0)
2392 &AnnotatedBorrowFnSignature::AnonymousFunction {
2398 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2399 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2401 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2402 let types_equal = return_ty_name == argument_ty_name;
2407 if types_equal { "also " } else { "" },
2413 "argument and return type have the same lifetime due to lifetime elision rules",
2416 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2417 lifetime-syntax.html#lifetime-elision>",
2420 cx.get_region_name_for_ty(return_ty, 0)
2422 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2423 // Region of return type and arguments checked to be the same earlier.
2424 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2425 for (_, argument_span) in arguments {
2426 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2429 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2432 "use data from the highlighted arguments which match the `{}` lifetime of \