2 use rustc_const_eval::util::{CallDesugaringKind, CallKind};
3 use rustc_data_structures::fx::FxHashSet;
4 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
6 use rustc_hir::def_id::DefId;
7 use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
8 use rustc_middle::mir::{
9 self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
10 FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
11 ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
13 use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
14 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
15 use rustc_span::symbol::sym;
16 use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP};
17 use rustc_trait_selection::infer::InferCtxtExt;
19 use crate::borrow_set::TwoPhaseActivation;
20 use crate::borrowck_errors;
22 use crate::diagnostics::find_all_local_uses;
24 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
25 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
29 explain_borrow::{BorrowExplanation, LaterUseKind},
30 IncludingDowncast, RegionName, RegionNameSource, UseSpans,
35 /// Index of the "move out" that we found. The `MoveData` can
36 /// then tell us where the move occurred.
39 /// `true` if we traversed a back edge while walking from the point
40 /// of error to the move site.
41 traversed_back_edge: bool,
44 /// Which case a StorageDeadOrDrop is for.
45 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
46 enum StorageDeadOrDrop<'tcx> {
52 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
53 pub(crate) fn report_use_of_moved_or_uninitialized(
56 desired_action: InitializationRequiringAction,
57 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
61 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
62 moved_place={:?} used_place={:?} span={:?} mpi={:?}",
63 location, desired_action, moved_place, used_place, span, mpi
67 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
68 let span = use_spans.args_or_use();
70 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
72 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
73 move_site_vec, use_spans
75 let move_out_indices: Vec<_> =
76 move_site_vec.iter().map(|move_site| move_site.moi).collect();
78 if move_out_indices.is_empty() {
79 let root_place = PlaceRef { projection: &[], ..used_place };
81 if !self.uninitialized_error_reported.insert(root_place) {
83 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
90 match self.describe_place_with_options(used_place, IncludingDowncast(true)) {
91 Some(name) => format!("`{}`", name),
92 None => "value".to_owned(),
94 let mut err = self.cannot_act_on_uninitialized_variable(
96 desired_action.as_noun(),
98 .describe_place_with_options(moved_place, IncludingDowncast(true))
99 .unwrap_or_else(|| "_".to_owned()),
101 err.span_label(span, format!("use of possibly-uninitialized {}", item_msg));
103 use_spans.var_span_label_path_only(
105 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
108 self.buffer_error(err);
110 if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
111 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
113 "report_use_of_moved_or_uninitialized place: error suppressed \
121 let is_partial_move = move_site_vec.iter().any(|move_site| {
122 let move_out = self.move_data.moves[(*move_site).moi];
123 let moved_place = &self.move_data.move_paths[move_out.path].place;
124 // `*(_1)` where `_1` is a `Box` is actually a move out.
125 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
126 && self.body.local_decls[moved_place.local].ty.is_box();
129 && used_place != moved_place.as_ref()
130 && used_place.is_prefix_of(moved_place.as_ref())
133 let partial_str = if is_partial_move { "partial " } else { "" };
134 let partially_str = if is_partial_move { "partially " } else { "" };
136 let mut err = self.cannot_act_on_moved_value(
138 desired_action.as_noun(),
140 self.describe_place_with_options(moved_place, IncludingDowncast(true)),
143 let reinit_spans = maybe_reinitialized_locations
147 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
150 .collect::<Vec<Span>>();
151 let reinits = maybe_reinitialized_locations.len();
153 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
154 } else if reinits > 1 {
156 MultiSpan::from_spans(reinit_spans),
158 format!("these {} reinitializations might get skipped", reinits)
161 "these 3 reinitializations and {} other{} might get skipped",
163 if reinits == 4 { "" } else { "s" }
169 self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
171 let mut is_loop_move = false;
172 let mut in_pattern = false;
174 for move_site in &move_site_vec {
175 let move_out = self.move_data.moves[(*move_site).moi];
176 let moved_place = &self.move_data.move_paths[move_out.path].place;
178 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
179 let move_span = move_spans.args_or_use();
181 let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
183 let loop_message = if location == move_out.source || move_site.traversed_back_edge {
184 ", in previous iteration of loop"
189 if location == move_out.source {
193 if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
194 let place_name = self
195 .describe_place(moved_place.as_ref())
196 .map(|n| format!("`{}`", n))
197 .unwrap_or_else(|| "value".to_owned());
199 CallKind::FnCall { fn_trait_id, .. }
200 if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() =>
205 "{} {}moved due to this call{}",
206 place_name, partially_str, loop_message
211 "this value implements `FnOnce`, which causes it to be moved when called",
214 CallKind::Operator { self_arg, .. } => {
215 let self_arg = self_arg.unwrap();
219 "{} {}moved due to usage in operator{}",
220 place_name, partially_str, loop_message
223 if self.fn_self_span_reported.insert(fn_span) {
225 // Check whether the source is accessible
231 .span_to_snippet(self_arg.span)
238 "calling this operator moves the left-hand side",
242 CallKind::Normal { self_arg, desugaring, is_option_or_result } => {
243 let self_arg = self_arg.unwrap();
244 if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
248 "{} {}moved due to this implicit call to `.into_iter()`{}",
249 place_name, partially_str, loop_message
252 let sess = self.infcx.tcx.sess;
253 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
254 // If we have a `&mut` ref, we need to reborrow.
255 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
256 // If we are in a loop this will be suggested later.
258 err.span_suggestion_verbose(
259 move_span.shrink_to_lo(),
261 "consider creating a fresh reborrow of {} here",
262 self.describe_place(moved_place.as_ref())
263 .map(|n| format!("`{}`", n))
265 || "the mutable reference".to_string()
268 "&mut *".to_string(),
269 Applicability::MachineApplicable,
272 } else if let Ok(snippet) =
273 sess.source_map().span_to_snippet(move_span)
277 "consider borrowing to avoid moving into the for loop",
278 format!("&{}", snippet),
279 Applicability::MaybeIncorrect,
286 "{} {}moved due to this method call{}",
287 place_name, partially_str, loop_message
291 if is_option_or_result && maybe_reinitialized_locations.is_empty() {
292 err.span_suggestion_verbose(
293 fn_call_span.shrink_to_lo(),
294 "consider calling `.as_ref()` to borrow the type's contents",
295 "as_ref().".to_string(),
296 Applicability::MachineApplicable,
299 // Avoid pointing to the same function in multiple different
301 if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span)
305 &format!("this function takes ownership of the receiver `self`, which moves {}", place_name)
309 // Other desugarings takes &self, which cannot cause a move
315 format!("value {}moved{} here{}", partially_str, move_msg, loop_message),
317 // If the move error occurs due to a loop, don't show
318 // another message for the same span
319 if loop_message.is_empty() {
320 move_spans.var_span_label(
323 "variable {}moved due to use{}",
325 move_spans.describe()
332 if let (UseSpans::PatUse(span), []) =
333 (move_spans, &maybe_reinitialized_locations[..])
335 if maybe_reinitialized_locations.is_empty() {
336 err.span_suggestion_verbose(
339 "borrow this field in the pattern to avoid moving {}",
340 self.describe_place(moved_place.as_ref())
341 .map(|n| format!("`{}`", n))
342 .unwrap_or_else(|| "the value".to_string())
345 Applicability::MachineApplicable,
352 use_spans.var_span_label_path_only(
354 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
361 "value {} here after {}move",
362 desired_action.as_verb_in_past_tense(),
368 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
369 let needs_note = match ty.kind() {
370 ty::Closure(id, _) => {
371 let tables = self.infcx.tcx.typeck(id.expect_local());
372 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
374 tables.closure_kind_origins().get(hir_id).is_none()
379 let mpi = self.move_data.moves[move_out_indices[0]].path;
380 let place = &self.move_data.move_paths[mpi].place;
381 let ty = place.ty(self.body, self.infcx.tcx).ty;
383 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
384 if is_loop_move & !in_pattern {
385 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
386 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
387 err.span_suggestion_verbose(
390 "consider creating a fresh reborrow of {} here",
391 self.describe_place(moved_place)
392 .map(|n| format!("`{}`", n))
393 .unwrap_or_else(|| "the mutable reference".to_string()),
395 "&mut *".to_string(),
396 Applicability::MachineApplicable,
403 self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
404 let note_msg = match opt_name {
405 Some(ref name) => format!("`{}`", name),
406 None => "value".to_owned(),
408 if let ty::Param(param_ty) = ty.kind() {
409 let tcx = self.infcx.tcx;
410 let generics = tcx.generics_of(self.mir_def_id());
411 let param = generics.type_param(¶m_ty, tcx);
412 if let Some(generics) = tcx
413 .typeck_root_def_id(self.mir_def_id().to_def_id())
415 .and_then(|def_id| tcx.hir().get_generics(def_id))
417 suggest_constraining_type_param(
427 let span = if let Some(local) = place.as_local() {
428 let decl = &self.body.local_decls[local];
429 Some(decl.source_info.span)
433 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
436 if let UseSpans::FnSelfUse {
437 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
442 "{} occurs due to deref coercion to `{}`",
443 desired_action.as_noun(),
447 // Check first whether the source is accessible (issue #87060)
448 if self.infcx.tcx.sess.source_map().span_to_snippet(deref_target).is_ok() {
449 err.span_note(deref_target, "deref defined here");
453 self.buffer_move_error(move_out_indices, (used_place, err));
457 pub(crate) fn report_move_out_while_borrowed(
460 (place, span): (Place<'tcx>, Span),
461 borrow: &BorrowData<'tcx>,
464 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
465 location, place, span, borrow
467 let value_msg = self.describe_any_place(place.as_ref());
468 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
470 let borrow_spans = self.retrieve_borrow_spans(borrow);
471 let borrow_span = borrow_spans.args_or_use();
473 let move_spans = self.move_spans(place.as_ref(), location);
474 let span = move_spans.args_or_use();
477 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
478 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
479 err.span_label(span, format!("move out of {} occurs here", value_msg));
481 borrow_spans.var_span_label_path_only(
483 format!("borrow occurs due to use{}", borrow_spans.describe()),
486 move_spans.var_span_label(
488 format!("move occurs due to use{}", move_spans.describe()),
492 self.explain_why_borrow_contains_point(location, borrow, None)
493 .add_explanation_to_diagnostic(
502 self.buffer_error(err);
505 pub(crate) fn report_use_while_mutably_borrowed(
508 (place, _span): (Place<'tcx>, Span),
509 borrow: &BorrowData<'tcx>,
510 ) -> DiagnosticBuilder<'cx, ErrorReported> {
511 let borrow_spans = self.retrieve_borrow_spans(borrow);
512 let borrow_span = borrow_spans.args_or_use();
514 // Conflicting borrows are reported separately, so only check for move
516 let use_spans = self.move_spans(place.as_ref(), location);
517 let span = use_spans.var_or_use();
519 // 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
520 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
521 let mut err = self.cannot_use_when_mutably_borrowed(
523 &self.describe_any_place(place.as_ref()),
525 &self.describe_any_place(borrow.borrowed_place.as_ref()),
528 borrow_spans.var_span_label(
531 let place = &borrow.borrowed_place;
532 let desc_place = self.describe_any_place(place.as_ref());
533 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
538 self.explain_why_borrow_contains_point(location, borrow, None)
539 .add_explanation_to_diagnostic(
551 pub(crate) fn report_conflicting_borrow(
554 (place, span): (Place<'tcx>, Span),
555 gen_borrow_kind: BorrowKind,
556 issued_borrow: &BorrowData<'tcx>,
557 ) -> DiagnosticBuilder<'cx, ErrorReported> {
558 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
559 let issued_span = issued_spans.args_or_use();
561 let borrow_spans = self.borrow_spans(span, location);
562 let span = borrow_spans.args_or_use();
564 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
570 let (desc_place, msg_place, msg_borrow, union_type_name) =
571 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
573 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
574 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
576 // FIXME: supply non-"" `opt_via` when appropriate
577 let first_borrow_desc;
578 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
579 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
580 first_borrow_desc = "mutable ";
581 self.cannot_reborrow_already_borrowed(
593 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
594 first_borrow_desc = "immutable ";
595 self.cannot_reborrow_already_borrowed(
608 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
609 first_borrow_desc = "first ";
610 let mut err = self.cannot_mutably_borrow_multiply(
618 self.suggest_split_at_mut_if_applicable(
621 issued_borrow.borrowed_place,
626 (BorrowKind::Unique, BorrowKind::Unique) => {
627 first_borrow_desc = "first ";
628 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
631 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
632 if let Some(immutable_section_description) =
633 self.classify_immutable_section(issued_borrow.assigned_place)
635 let mut err = self.cannot_mutate_in_immutable_section(
639 immutable_section_description,
642 borrow_spans.var_span_label(
645 "borrow occurs due to use of {}{}",
647 borrow_spans.describe(),
654 first_borrow_desc = "immutable ";
655 self.cannot_reborrow_already_borrowed(
669 (BorrowKind::Unique, _) => {
670 first_borrow_desc = "first ";
671 self.cannot_uniquely_borrow_by_one_closure(
683 (BorrowKind::Shared, BorrowKind::Unique) => {
684 first_borrow_desc = "first ";
685 self.cannot_reborrow_already_uniquely_borrowed(
698 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
699 first_borrow_desc = "first ";
700 self.cannot_reborrow_already_uniquely_borrowed(
713 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
716 BorrowKind::Mut { .. }
719 | BorrowKind::Shallow,
723 if issued_spans == borrow_spans {
724 borrow_spans.var_span_label(
726 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
727 gen_borrow_kind.describe_mutability(),
730 let borrow_place = &issued_borrow.borrowed_place;
731 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
732 issued_spans.var_span_label(
735 "first borrow occurs due to use of {}{}",
737 issued_spans.describe(),
739 issued_borrow.kind.describe_mutability(),
742 borrow_spans.var_span_label(
745 "second borrow occurs due to use of {}{}",
747 borrow_spans.describe(),
749 gen_borrow_kind.describe_mutability(),
753 if union_type_name != "" {
755 "{} is a field of the union `{}`, so it overlaps the field {}",
756 msg_place, union_type_name, msg_borrow,
760 explanation.add_explanation_to_diagnostic(
767 Some((issued_span, span)),
770 self.suggest_using_local_if_applicable(
782 #[instrument(level = "debug", skip(self, err))]
783 fn suggest_using_local_if_applicable(
785 err: &mut Diagnostic,
787 (place, span): (Place<'tcx>, Span),
788 gen_borrow_kind: BorrowKind,
789 issued_borrow: &BorrowData<'tcx>,
790 explanation: BorrowExplanation,
793 matches!(explanation, BorrowExplanation::UsedLater(LaterUseKind::Call, _call_span, _));
795 debug!("not later used in call");
800 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
803 issued_borrow.reserve_location
805 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
807 let inner_param_location = location;
808 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
809 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
812 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
814 "`inner_param_location` {:?} is not for an assignment: {:?}",
815 inner_param_location, inner_param_stmt
819 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
820 let Some((inner_call_loc,inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
821 let Either::Right(term) = self.body.stmt_at(loc) else {
822 debug!("{:?} is a statement, so it can't be a call", loc);
825 let TerminatorKind::Call { args, .. } = &term.kind else {
826 debug!("not a call: {:?}", term);
829 debug!("checking call args for uses of inner_param: {:?}", args);
830 if args.contains(&Operand::Move(inner_param)) {
836 debug!("no uses of inner_param found as a by-move call arg");
839 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
841 let inner_call_span = inner_call_term.source_info.span;
842 let outer_call_span = outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span;
843 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
844 // FIXME: This stops the suggestion in some cases where it should be emitted.
845 // Fix the spans for those cases so it's emitted correctly.
847 "outer span {:?} does not strictly contain inner span {:?}",
848 outer_call_span, inner_call_span
852 err.span_help(inner_call_span, "try adding a local storing this argument...");
853 err.span_help(outer_call_span, "...and then using that local as the argument to this call");
856 fn suggest_split_at_mut_if_applicable(
858 err: &mut Diagnostic,
860 borrowed_place: Place<'tcx>,
862 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
863 (&place.projection[..], &borrowed_place.projection[..])
866 "consider using `.split_at_mut(position)` or similar method to obtain \
867 two mutable non-overlapping sub-slices",
872 /// Returns the description of the root place for a conflicting borrow and the full
873 /// descriptions of the places that caused the conflict.
875 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
876 /// attempted while a shared borrow is live, then this function will return:
880 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
881 /// a shared borrow of another field `x.y`, then this function will return:
883 /// ("x", "x.z", "x.y")
885 /// In the more complex union case, where the union is a field of a struct, then if a mutable
886 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
887 /// another field `x.u.y`, then this function will return:
889 /// ("x.u", "x.u.z", "x.u.y")
891 /// This is used when creating error messages like below:
894 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
895 /// mutable (via `a.u.s.b`) [E0502]
897 pub(crate) fn describe_place_for_conflicting_borrow(
899 first_borrowed_place: Place<'tcx>,
900 second_borrowed_place: Place<'tcx>,
901 ) -> (String, String, String, String) {
902 // Define a small closure that we can use to check if the type of a place
904 let union_ty = |place_base| {
905 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
906 // using a type annotation in the closure argument instead leads to a lifetime error.
907 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
908 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
911 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
912 // code duplication (particularly around returning an empty description in the failure
916 // If we have a conflicting borrow of the same place, then we don't want to add
917 // an extraneous "via x.y" to our diagnostics, so filter out this case.
918 first_borrowed_place != second_borrowed_place
921 // We're going to want to traverse the first borrowed place to see if we can find
922 // field access to a union. If we find that, then we will keep the place of the
923 // union being accessed and the field that was being accessed so we can check the
924 // second borrowed place for the same union and an access to a different field.
925 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
927 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
928 return Some((place_base, field));
935 .and_then(|(target_base, target_field)| {
936 // With the place of a union and a field access into it, we traverse the second
937 // borrowed place and look for an access to a different field of the same union.
938 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
939 if let ProjectionElem::Field(field, _) = elem {
940 if let Some(union_ty) = union_ty(place_base) {
941 if field != target_field && place_base == target_base {
943 self.describe_any_place(place_base),
944 self.describe_any_place(first_borrowed_place.as_ref()),
945 self.describe_any_place(second_borrowed_place.as_ref()),
946 union_ty.to_string(),
955 // If we didn't find a field access into a union, or both places match, then
956 // only return the description of the first place.
958 self.describe_any_place(first_borrowed_place.as_ref()),
966 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
968 /// This means that some data referenced by `borrow` needs to live
969 /// past the point where the StorageDeadOrDrop of `place` occurs.
970 /// This is usually interpreted as meaning that `place` has too
971 /// short a lifetime. (But sometimes it is more useful to report
972 /// it as a more direct conflict between the execution of a
973 /// `Drop::drop` with an aliasing borrow.)
974 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
977 borrow: &BorrowData<'tcx>,
978 place_span: (Place<'tcx>, Span),
979 kind: Option<WriteKind>,
982 "report_borrowed_value_does_not_live_long_enough(\
983 {:?}, {:?}, {:?}, {:?}\
985 location, borrow, place_span, kind
988 let drop_span = place_span.1;
990 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
992 let borrow_spans = self.retrieve_borrow_spans(borrow);
993 let borrow_span = borrow_spans.var_or_use_path_span();
995 assert!(root_place.projection.is_empty());
996 let proper_span = self.body.local_decls[root_place.local].source_info.span;
998 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1000 if self.access_place_error_reported.contains(&(
1001 Place { local: root_place.local, projection: root_place_projection },
1005 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1011 self.access_place_error_reported.insert((
1012 Place { local: root_place.local, projection: root_place_projection },
1016 let borrowed_local = borrow.borrowed_place.local;
1017 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1019 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1020 self.buffer_error(err);
1024 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1025 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1027 // If a borrow of path `B` conflicts with drop of `D` (and
1028 // we're not in the uninteresting case where `B` is a
1029 // prefix of `D`), then report this as a more interesting
1030 // destructor conflict.
1031 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1032 self.report_borrow_conflicts_with_destructor(
1033 location, borrow, place_span, kind, dropped_ty,
1039 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1041 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1042 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1045 "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
1046 place_desc, explanation
1048 let err = match (place_desc, explanation) {
1049 // If the outlives constraint comes from inside the closure,
1054 // Box::new(|| y) as Box<Fn() -> &'static i32>
1056 // then just use the normal error. The closure isn't escaping
1057 // and `move` will not help here.
1060 BorrowExplanation::MustBeValidFor {
1062 category @ (ConstraintCategory::Return(_)
1063 | ConstraintCategory::CallArgument
1064 | ConstraintCategory::OpaqueType),
1065 from_closure: false,
1070 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1071 .report_escaping_closure_capture(
1077 &format!("`{}`", name),
1081 BorrowExplanation::MustBeValidFor {
1082 category: ConstraintCategory::Assignment,
1083 from_closure: false,
1087 RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
1093 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1094 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1102 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1112 self.buffer_error(err);
1115 fn report_local_value_does_not_live_long_enough(
1119 borrow: &BorrowData<'tcx>,
1121 borrow_spans: UseSpans<'tcx>,
1122 explanation: BorrowExplanation,
1123 ) -> DiagnosticBuilder<'cx, ErrorReported> {
1125 "report_local_value_does_not_live_long_enough(\
1126 {:?}, {:?}, {:?}, {:?}, {:?}\
1128 location, name, borrow, drop_span, borrow_spans
1131 let borrow_span = borrow_spans.var_or_use_path_span();
1132 if let BorrowExplanation::MustBeValidFor {
1136 from_closure: false,
1140 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1145 opt_place_desc.as_ref(),
1151 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1153 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1154 let region_name = annotation.emit(self, &mut err);
1158 format!("`{}` would have to be valid for `{}`...", name, region_name),
1161 let fn_hir_id = self.mir_hir_id();
1165 "...but `{}` will be dropped here, when the {} returns",
1170 .opt_name(fn_hir_id)
1171 .map(|name| format!("function `{}`", name))
1172 .unwrap_or_else(|| {
1176 .typeck(self.mir_def_id())
1177 .node_type(fn_hir_id)
1180 ty::Closure(..) => "enclosing closure",
1181 ty::Generator(..) => "enclosing generator",
1182 kind => bug!("expected closure or generator, found {:?}", kind),
1190 "functions cannot return a borrow to data owned within the function's scope, \
1191 functions can only return borrows to data passed as arguments",
1194 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1195 references-and-borrowing.html#dangling-references>",
1198 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1200 explanation.add_explanation_to_diagnostic(
1211 err.span_label(borrow_span, "borrowed value does not live long enough");
1212 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1214 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1216 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1218 explanation.add_explanation_to_diagnostic(
1232 fn report_borrow_conflicts_with_destructor(
1235 borrow: &BorrowData<'tcx>,
1236 (place, drop_span): (Place<'tcx>, Span),
1237 kind: Option<WriteKind>,
1238 dropped_ty: Ty<'tcx>,
1241 "report_borrow_conflicts_with_destructor(\
1242 {:?}, {:?}, ({:?}, {:?}), {:?}\
1244 location, borrow, place, drop_span, kind,
1247 let borrow_spans = self.retrieve_borrow_spans(borrow);
1248 let borrow_span = borrow_spans.var_or_use();
1250 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1252 let what_was_dropped = match self.describe_place(place.as_ref()) {
1253 Some(name) => format!("`{}`", name),
1254 None => String::from("temporary value"),
1257 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1258 Some(borrowed) => format!(
1259 "here, drop of {D} needs exclusive access to `{B}`, \
1260 because the type `{T}` implements the `Drop` trait",
1261 D = what_was_dropped,
1266 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1267 D = what_was_dropped,
1271 err.span_label(drop_span, label);
1273 // Only give this note and suggestion if they could be relevant.
1275 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1277 BorrowExplanation::UsedLater { .. }
1278 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1279 err.note("consider using a `let` binding to create a longer lived value");
1284 explanation.add_explanation_to_diagnostic(
1294 self.buffer_error(err);
1297 fn report_thread_local_value_does_not_live_long_enough(
1301 ) -> DiagnosticBuilder<'cx, ErrorReported> {
1303 "report_thread_local_value_does_not_live_long_enough(\
1306 drop_span, borrow_span
1309 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1313 "thread-local variables cannot be borrowed beyond the end of the function",
1315 err.span_label(drop_span, "end of enclosing function is here");
1320 fn report_temporary_value_does_not_live_long_enough(
1323 borrow: &BorrowData<'tcx>,
1325 borrow_spans: UseSpans<'tcx>,
1327 explanation: BorrowExplanation,
1328 ) -> DiagnosticBuilder<'cx, ErrorReported> {
1330 "report_temporary_value_does_not_live_long_enough(\
1331 {:?}, {:?}, {:?}, {:?}\
1333 location, borrow, drop_span, proper_span
1336 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1339 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1350 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1351 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1352 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1355 BorrowExplanation::UsedLater(..)
1356 | BorrowExplanation::UsedLaterInLoop(..)
1357 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1358 // Only give this note and suggestion if it could be relevant.
1359 err.note("consider using a `let` binding to create a longer lived value");
1363 explanation.add_explanation_to_diagnostic(
1373 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1375 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1380 fn try_report_cannot_return_reference_to_local(
1382 borrow: &BorrowData<'tcx>,
1385 category: ConstraintCategory,
1386 opt_place_desc: Option<&String>,
1387 ) -> Option<DiagnosticBuilder<'cx, ErrorReported>> {
1388 let return_kind = match category {
1389 ConstraintCategory::Return(_) => "return",
1390 ConstraintCategory::Yield => "yield",
1394 // FIXME use a better heuristic than Spans
1395 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1401 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1402 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1403 match self.body.local_kind(local) {
1404 LocalKind::ReturnPointer | LocalKind::Temp => {
1405 bug!("temporary or return pointer with a name")
1407 LocalKind::Var => "local variable ",
1409 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1411 "variable captured by `move` "
1413 LocalKind::Arg => "function parameter ",
1419 format!("{}`{}`", local_kind, place_desc),
1420 format!("`{}` is borrowed here", place_desc),
1424 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1425 let local = root_place.local;
1426 match self.body.local_kind(local) {
1427 LocalKind::ReturnPointer | LocalKind::Temp => {
1428 ("temporary value".to_string(), "temporary value created here".to_string())
1431 "function parameter".to_string(),
1432 "function parameter borrowed here".to_string(),
1435 ("local binding".to_string(), "local binding introduced here".to_string())
1440 let mut err = self.cannot_return_reference_to_local(
1447 if return_span != borrow_span {
1448 err.span_label(borrow_span, note);
1450 let tcx = self.infcx.tcx;
1451 let ty_params = ty::List::empty();
1453 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1454 let return_ty = tcx.erase_regions(return_ty);
1457 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1460 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1461 .must_apply_modulo_regions()
1463 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1464 err.span_suggestion_hidden(
1466 "use `.collect()` to allocate the iterator",
1467 format!("{}{}", snippet, ".collect::<Vec<_>>()"),
1468 Applicability::MaybeIncorrect,
1478 fn report_escaping_closure_capture(
1480 use_span: UseSpans<'tcx>,
1482 fr_name: &RegionName,
1483 category: ConstraintCategory,
1484 constraint_span: Span,
1486 ) -> DiagnosticBuilder<'cx, ErrorReported> {
1487 let tcx = self.infcx.tcx;
1488 let args_span = use_span.args_or_use();
1490 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1492 if string.starts_with("async ") {
1493 let pos = args_span.lo() + BytePos(6);
1494 (args_span.with_lo(pos).with_hi(pos), "move ".to_string())
1495 } else if string.starts_with("async|") {
1496 let pos = args_span.lo() + BytePos(5);
1497 (args_span.with_lo(pos).with_hi(pos), " move".to_string())
1499 (args_span.shrink_to_lo(), "move ".to_string())
1502 Err(_) => (args_span, "move |<args>| <body>".to_string()),
1504 let kind = match use_span.generator_kind() {
1505 Some(generator_kind) => match generator_kind {
1506 GeneratorKind::Async(async_kind) => match async_kind {
1507 AsyncGeneratorKind::Block => "async block",
1508 AsyncGeneratorKind::Closure => "async closure",
1509 _ => bug!("async block/closure expected, but async function found."),
1511 GeneratorKind::Gen => "generator",
1517 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1518 err.span_suggestion_verbose(
1521 "to force the {} to take ownership of {} (and any \
1522 other referenced variables), use the `move` keyword",
1526 Applicability::MachineApplicable,
1530 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1531 let msg = format!("{} is returned here", kind);
1532 err.span_note(constraint_span, &msg);
1534 ConstraintCategory::CallArgument => {
1535 fr_name.highlight_region_name(&mut err);
1536 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1538 "async blocks are not executed immediately and must either take a \
1539 reference or ownership of outside variables they use",
1542 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1543 err.span_note(constraint_span, &msg);
1547 "report_escaping_closure_capture called with unexpected constraint \
1556 fn report_escaping_data(
1559 name: &Option<String>,
1563 ) -> DiagnosticBuilder<'cx, ErrorReported> {
1564 let tcx = self.infcx.tcx;
1566 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1569 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1573 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1576 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1578 if let Some(name) = name {
1581 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1586 format!("reference escapes the {} body here", escapes_from),
1593 fn get_moved_indexes(
1597 ) -> (Vec<MoveSite>, Vec<Location>) {
1598 fn predecessor_locations<'a>(
1599 body: &'a mir::Body<'_>,
1601 ) -> impl Iterator<Item = Location> + 'a {
1602 if location.statement_index == 0 {
1603 let predecessors = body.predecessors()[location.block].to_vec();
1604 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1606 Either::Right(std::iter::once(Location {
1607 statement_index: location.statement_index - 1,
1613 let mut mpis = vec![mpi];
1614 let move_paths = &self.move_data.move_paths;
1615 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1617 let mut stack = Vec::new();
1618 let mut back_edge_stack = Vec::new();
1620 predecessor_locations(self.body, location).for_each(|predecessor| {
1621 if location.dominates(predecessor, &self.dominators) {
1622 back_edge_stack.push(predecessor)
1624 stack.push(predecessor);
1628 let mut reached_start = false;
1630 /* Check if the mpi is initialized as an argument */
1631 let mut is_argument = false;
1632 for arg in self.body.args_iter() {
1633 let path = self.move_data.rev_lookup.find_local(arg);
1634 if mpis.contains(&path) {
1639 let mut visited = FxHashSet::default();
1640 let mut move_locations = FxHashSet::default();
1641 let mut reinits = vec![];
1642 let mut result = vec![];
1644 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1646 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1647 location, is_back_edge
1650 if !visited.insert(location) {
1656 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1657 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1658 // this analysis only tries to find moves explicitly
1659 // written by the user, so we ignore the move-outs
1660 // created by `StorageDead` and at the beginning
1663 // If we are found a use of a.b.c which was in error, then we want to look for
1664 // moves not only of a.b.c but also a.b and a.
1666 // Note that the moves data already includes "parent" paths, so we don't have to
1667 // worry about the other case: that is, if there is a move of a.b.c, it is already
1668 // marked as a move of a.b and a as well, so we will generate the correct errors
1670 for moi in &self.move_data.loc_map[location] {
1671 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1672 let path = self.move_data.moves[*moi].path;
1673 if mpis.contains(&path) {
1675 "report_use_of_moved_or_uninitialized: found {:?}",
1676 move_paths[path].place
1678 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1679 move_locations.insert(location);
1681 // Strictly speaking, we could continue our DFS here. There may be
1682 // other moves that can reach the point of error. But it is kind of
1683 // confusing to highlight them.
1691 // drop(a); // <-- current point of error
1694 // Because we stop the DFS here, we only highlight `let c = a`,
1695 // and not `let b = a`. We will of course also report an error at
1696 // `let c = a` which highlights `let b = a` as the move.
1703 let mut any_match = false;
1704 for ii in &self.move_data.init_loc_map[location] {
1705 let init = self.move_data.inits[*ii];
1707 InitKind::Deep | InitKind::NonPanicPathOnly => {
1708 if mpis.contains(&init.path) {
1712 InitKind::Shallow => {
1713 if mpi == init.path {
1720 reinits.push(location);
1726 while let Some(location) = stack.pop() {
1727 if dfs_iter(&mut result, location, false) {
1731 let mut has_predecessor = false;
1732 predecessor_locations(self.body, location).for_each(|predecessor| {
1733 if location.dominates(predecessor, &self.dominators) {
1734 back_edge_stack.push(predecessor)
1736 stack.push(predecessor);
1738 has_predecessor = true;
1741 if !has_predecessor {
1742 reached_start = true;
1745 if (is_argument || !reached_start) && result.is_empty() {
1746 /* Process back edges (moves in future loop iterations) only if
1747 the move path is definitely initialized upon loop entry,
1748 to avoid spurious "in previous iteration" errors.
1749 During DFS, if there's a path from the error back to the start
1750 of the function with no intervening init or move, then the
1751 move path may be uninitialized at loop entry.
1753 while let Some(location) = back_edge_stack.pop() {
1754 if dfs_iter(&mut result, location, true) {
1758 predecessor_locations(self.body, location)
1759 .for_each(|predecessor| back_edge_stack.push(predecessor));
1763 // Check if we can reach these reinits from a move location.
1764 let reinits_reachable = reinits
1767 let mut visited = FxHashSet::default();
1768 let mut stack = vec![*reinit];
1769 while let Some(location) = stack.pop() {
1770 if !visited.insert(location) {
1773 if move_locations.contains(&location) {
1776 stack.extend(predecessor_locations(self.body, location));
1780 .collect::<Vec<Location>>();
1781 (result, reinits_reachable)
1784 pub(crate) fn report_illegal_mutation_of_borrowed(
1787 (place, span): (Place<'tcx>, Span),
1788 loan: &BorrowData<'tcx>,
1790 let loan_spans = self.retrieve_borrow_spans(loan);
1791 let loan_span = loan_spans.args_or_use();
1793 let descr_place = self.describe_any_place(place.as_ref());
1794 if loan.kind == BorrowKind::Shallow {
1795 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1796 let mut err = self.cannot_mutate_in_immutable_section(
1803 loan_spans.var_span_label(
1805 format!("borrow occurs due to use{}", loan_spans.describe()),
1806 loan.kind.describe_mutability(),
1809 self.buffer_error(err);
1815 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
1817 loan_spans.var_span_label(
1819 format!("borrow occurs due to use{}", loan_spans.describe()),
1820 loan.kind.describe_mutability(),
1823 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
1833 self.explain_deref_coercion(loan, &mut err);
1835 self.buffer_error(err);
1838 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
1839 let tcx = self.infcx.tcx;
1841 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
1842 Some((method_did, method_substs)),
1844 &self.body[loan.reserve_location.block].terminator,
1845 rustc_const_eval::util::find_self_call(
1848 loan.assigned_place.local,
1849 loan.reserve_location.block,
1852 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
1854 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
1855 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
1858 if let Some(Ok(instance)) = deref_target {
1859 let deref_target_ty = instance.ty(tcx, self.param_env);
1861 "borrow occurs due to deref coercion to `{}`",
1864 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
1870 /// Reports an illegal reassignment; for example, an assignment to
1871 /// (part of) a non-`mut` local that occurs potentially after that
1872 /// local has already been initialized. `place` is the path being
1873 /// assigned; `err_place` is a place providing a reason why
1874 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
1875 /// assignment to `x.f`).
1876 pub(crate) fn report_illegal_reassignment(
1878 _location: Location,
1879 (place, span): (Place<'tcx>, Span),
1880 assigned_span: Span,
1881 err_place: Place<'tcx>,
1883 let (from_arg, local_decl, local_name) = match err_place.as_local() {
1885 self.body.local_kind(local) == LocalKind::Arg,
1886 Some(&self.body.local_decls[local]),
1887 self.local_names[local],
1889 None => (false, None, None),
1892 // If root local is initialized immediately (everything apart from let
1893 // PATTERN;) then make the error refer to that local, rather than the
1894 // place being assigned later.
1895 let (place_description, assigned_span) = match local_decl {
1898 Some(box LocalInfo::User(
1899 ClearCrossCrate::Clear
1900 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
1901 opt_match_place: None,
1905 | Some(box LocalInfo::StaticRef { .. })
1909 | None => (self.describe_any_place(place.as_ref()), assigned_span),
1910 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
1913 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
1914 let msg = if from_arg {
1915 "cannot assign to immutable argument"
1917 "cannot assign twice to immutable variable"
1919 if span != assigned_span {
1921 err.span_label(assigned_span, format!("first assignment to {}", place_description));
1924 if let Some(decl) = local_decl {
1925 if let Some(name) = local_name {
1926 if decl.can_be_made_mutable() {
1927 err.span_suggestion(
1928 decl.source_info.span,
1929 "consider making this binding mutable",
1930 format!("mut {}", name),
1931 Applicability::MachineApplicable,
1936 err.span_label(span, msg);
1937 self.buffer_error(err);
1940 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
1941 let tcx = self.infcx.tcx;
1942 match place.last_projection() {
1943 None => StorageDeadOrDrop::LocalStorageDead,
1944 Some((place_base, elem)) => {
1945 // FIXME(spastorino) make this iterate
1946 let base_access = self.classify_drop_access_kind(place_base);
1948 ProjectionElem::Deref => match base_access {
1949 StorageDeadOrDrop::LocalStorageDead
1950 | StorageDeadOrDrop::BoxedStorageDead => {
1952 place_base.ty(self.body, tcx).ty.is_box(),
1953 "Drop of value behind a reference or raw pointer"
1955 StorageDeadOrDrop::BoxedStorageDead
1957 StorageDeadOrDrop::Destructor(_) => base_access,
1959 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
1960 let base_ty = place_base.ty(self.body, tcx).ty;
1961 match base_ty.kind() {
1962 ty::Adt(def, _) if def.has_dtor(tcx) => {
1963 // Report the outermost adt with a destructor
1965 StorageDeadOrDrop::Destructor(_) => base_access,
1966 StorageDeadOrDrop::LocalStorageDead
1967 | StorageDeadOrDrop::BoxedStorageDead => {
1968 StorageDeadOrDrop::Destructor(base_ty)
1975 ProjectionElem::ConstantIndex { .. }
1976 | ProjectionElem::Subslice { .. }
1977 | ProjectionElem::Index(_) => base_access,
1983 /// Describe the reason for the fake borrow that was assigned to `place`.
1984 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
1985 use rustc_middle::mir::visit::Visitor;
1986 struct FakeReadCauseFinder<'tcx> {
1988 cause: Option<FakeReadCause>,
1990 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
1991 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
1993 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
1994 if *place == self.place =>
1996 self.cause = Some(*cause);
2002 let mut visitor = FakeReadCauseFinder { place, cause: None };
2003 visitor.visit_body(&self.body);
2004 match visitor.cause {
2005 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2006 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2011 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2012 /// borrow of local value that does not live long enough.
2013 fn annotate_argument_and_return_for_borrow(
2015 borrow: &BorrowData<'tcx>,
2016 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2017 // Define a fallback for when we can't match a closure.
2019 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2023 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2025 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2026 self.mir_def_id().to_def_id(),
2027 self.infcx.tcx.fn_sig(self.mir_def_id()),
2034 // In order to determine whether we need to annotate, we need to check whether the reserve
2035 // place was an assignment into a temporary.
2037 // If it was, we check whether or not that temporary is eventually assigned into the return
2038 // place. If it was, we can add annotations about the function's return type and arguments
2039 // and it'll make sense.
2040 let location = borrow.reserve_location;
2041 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2042 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2043 &self.body[location.block].statements.get(location.statement_index)
2045 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2046 // Check that the initial assignment of the reserve location is into a temporary.
2047 let mut target = match reservation.as_local() {
2048 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2052 // Next, look through the rest of the block, checking if we are assigning the
2053 // `target` (that is, the place that contains our borrow) to anything.
2054 let mut annotated_closure = None;
2055 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2057 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2060 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2061 if let Some(assigned_to) = place.as_local() {
2063 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2067 // Check if our `target` was captured by a closure.
2068 if let Rvalue::Aggregate(
2069 box AggregateKind::Closure(def_id, substs),
2073 for operand in operands {
2074 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2078 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2082 // Find the local from the operand.
2083 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2087 if assigned_from_local != target {
2091 // If a closure captured our `target` and then assigned
2092 // into a place then we should annotate the closure in
2093 // case it ends up being assigned into the return place.
2095 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2097 "annotate_argument_and_return_for_borrow: \
2098 annotated_closure={:?} assigned_from_local={:?} \
2100 annotated_closure, assigned_from_local, assigned_to
2103 if assigned_to == mir::RETURN_PLACE {
2104 // If it was assigned directly into the return place, then
2106 return annotated_closure;
2108 // Otherwise, update the target.
2109 target = assigned_to;
2113 // If none of our closure's operands matched, then skip to the next
2118 // Otherwise, look at other types of assignment.
2119 let assigned_from = match rvalue {
2120 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2121 Rvalue::Use(operand) => match operand {
2122 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2130 "annotate_argument_and_return_for_borrow: \
2131 assigned_from={:?}",
2135 // Find the local from the rvalue.
2136 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2138 "annotate_argument_and_return_for_borrow: \
2139 assigned_from_local={:?}",
2140 assigned_from_local,
2143 // Check if our local matches the target - if so, we've assigned our
2144 // borrow to a new place.
2145 if assigned_from_local != target {
2149 // If we assigned our `target` into a new place, then we should
2150 // check if it was the return place.
2152 "annotate_argument_and_return_for_borrow: \
2153 assigned_from_local={:?} assigned_to={:?}",
2154 assigned_from_local, assigned_to
2156 if assigned_to == mir::RETURN_PLACE {
2157 // If it was then return the annotated closure if there was one,
2158 // else, annotate this function.
2159 return annotated_closure.or_else(fallback);
2162 // If we didn't assign into the return place, then we just update
2164 target = assigned_to;
2169 // Check the terminator if we didn't find anything in the statements.
2170 let terminator = &self.body[location.block].terminator();
2172 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2175 if let TerminatorKind::Call { destination: Some((place, _)), args, .. } =
2178 if let Some(assigned_to) = place.as_local() {
2180 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2183 for operand in args {
2184 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2188 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2192 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2194 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2195 assigned_from_local,
2198 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2199 return annotated_closure.or_else(fallback);
2207 // If we haven't found an assignment into the return place, then we need not add
2209 debug!("annotate_argument_and_return_for_borrow: none found");
2213 /// Annotate the first argument and return type of a function signature if they are
2218 sig: ty::PolyFnSig<'tcx>,
2219 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2220 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2221 let is_closure = self.infcx.tcx.is_closure(did);
2222 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?);
2223 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2225 // We need to work out which arguments to highlight. We do this by looking
2226 // at the return type, where there are three cases:
2228 // 1. If there are named arguments, then we should highlight the return type and
2229 // highlight any of the arguments that are also references with that lifetime.
2230 // If there are no arguments that have the same lifetime as the return type,
2231 // then don't highlight anything.
2232 // 2. The return type is a reference with an anonymous lifetime. If this is
2233 // the case, then we can take advantage of (and teach) the lifetime elision
2236 // We know that an error is being reported. So the arguments and return type
2237 // must satisfy the elision rules. Therefore, if there is a single argument
2238 // then that means the return type and first (and only) argument have the same
2239 // lifetime and the borrow isn't meeting that, we can highlight the argument
2242 // If there are multiple arguments then the first argument must be self (else
2243 // it would not satisfy the elision rules), so we can highlight self and the
2245 // 3. The return type is not a reference. In this case, we don't highlight
2247 let return_ty = sig.output();
2248 match return_ty.skip_binder().kind() {
2249 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2250 // This is case 1 from above, return type is a named reference so we need to
2251 // search for relevant arguments.
2252 let mut arguments = Vec::new();
2253 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2254 if let ty::Ref(argument_region, _, _) = argument.kind() {
2255 if argument_region == return_region {
2256 // Need to use the `rustc_middle::ty` types to compare against the
2257 // `return_region`. Then use the `rustc_hir` type to get only
2258 // the lifetime span.
2259 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2260 // With access to the lifetime, we can get
2262 arguments.push((*argument, lifetime.span));
2264 bug!("ty type is a ref but hir type is not");
2270 // We need to have arguments. This shouldn't happen, but it's worth checking.
2271 if arguments.is_empty() {
2275 // We use a mix of the HIR and the Ty types to get information
2276 // as the HIR doesn't have full types for closure arguments.
2277 let return_ty = sig.output().skip_binder();
2278 let mut return_span = fn_decl.output.span();
2279 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2280 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2281 return_span = lifetime.span;
2285 Some(AnnotatedBorrowFnSignature::NamedFunction {
2291 ty::Ref(_, _, _) if is_closure => {
2292 // This is case 2 from above but only for closures, return type is anonymous
2293 // reference so we select
2294 // the first argument.
2295 let argument_span = fn_decl.inputs.first()?.span;
2296 let argument_ty = sig.inputs().skip_binder().first()?;
2298 // Closure arguments are wrapped in a tuple, so we need to get the first
2300 if let ty::Tuple(elems) = argument_ty.kind() {
2301 let &argument_ty = elems.first()?;
2302 if let ty::Ref(_, _, _) = argument_ty.kind() {
2303 return Some(AnnotatedBorrowFnSignature::Closure {
2312 ty::Ref(_, _, _) => {
2313 // This is also case 2 from above but for functions, return type is still an
2314 // anonymous reference so we select the first argument.
2315 let argument_span = fn_decl.inputs.first()?.span;
2316 let argument_ty = *sig.inputs().skip_binder().first()?;
2318 let return_span = fn_decl.output.span();
2319 let return_ty = sig.output().skip_binder();
2321 // We expect the first argument to be a reference.
2322 match argument_ty.kind() {
2323 ty::Ref(_, _, _) => {}
2327 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2335 // This is case 3 from above, return type is not a reference so don't highlight
2344 enum AnnotatedBorrowFnSignature<'tcx> {
2346 arguments: Vec<(Ty<'tcx>, Span)>,
2347 return_ty: Ty<'tcx>,
2351 argument_ty: Ty<'tcx>,
2352 argument_span: Span,
2353 return_ty: Ty<'tcx>,
2357 argument_ty: Ty<'tcx>,
2358 argument_span: Span,
2362 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2363 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2365 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2367 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2370 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2373 cx.get_region_name_for_ty(argument_ty, 0)
2375 &AnnotatedBorrowFnSignature::AnonymousFunction {
2381 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2382 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2384 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2385 let types_equal = return_ty_name == argument_ty_name;
2390 if types_equal { "also " } else { "" },
2396 "argument and return type have the same lifetime due to lifetime elision rules",
2399 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2400 lifetime-syntax.html#lifetime-elision>",
2403 cx.get_region_name_for_ty(return_ty, 0)
2405 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2406 // Region of return type and arguments checked to be the same earlier.
2407 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2408 for (_, argument_span) in arguments {
2409 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2412 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2415 "use data from the highlighted arguments which match the `{}` lifetime of \