2 use rustc_data_structures::fx::FxHashSet;
3 use rustc_errors::{Applicability, DiagnosticBuilder};
5 use rustc_hir::def_id::DefId;
6 use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
7 use rustc_middle::mir::{
8 self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
9 FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
10 ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
12 use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
13 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
14 use rustc_span::symbol::sym;
15 use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP};
16 use rustc_trait_selection::infer::InferCtxtExt;
18 use crate::borrow_set::TwoPhaseActivation;
19 use crate::borrowck_errors;
21 use crate::diagnostics::find_all_local_uses;
23 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
24 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
28 explain_borrow::{BorrowExplanation, LaterUseKind},
29 FnSelfUseKind, IncludingDowncast, RegionName, RegionNameSource, UseSpans,
34 /// Index of the "move out" that we found. The `MoveData` can
35 /// then tell us where the move occurred.
38 /// `true` if we traversed a back edge while walking from the point
39 /// of error to the move site.
40 traversed_back_edge: bool,
43 /// Which case a StorageDeadOrDrop is for.
44 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
45 enum StorageDeadOrDrop<'tcx> {
51 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
52 pub(crate) fn report_use_of_moved_or_uninitialized(
55 desired_action: InitializationRequiringAction,
56 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
60 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
61 moved_place={:?} used_place={:?} span={:?} mpi={:?}",
62 location, desired_action, moved_place, used_place, span, mpi
66 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
67 let span = use_spans.args_or_use();
69 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
71 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
72 move_site_vec, use_spans
74 let move_out_indices: Vec<_> =
75 move_site_vec.iter().map(|move_site| move_site.moi).collect();
77 if move_out_indices.is_empty() {
78 let root_place = PlaceRef { projection: &[], ..used_place };
80 if !self.uninitialized_error_reported.insert(root_place) {
82 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
89 match self.describe_place_with_options(used_place, IncludingDowncast(true)) {
90 Some(name) => format!("`{}`", name),
91 None => "value".to_owned(),
93 let mut err = self.cannot_act_on_uninitialized_variable(
95 desired_action.as_noun(),
97 .describe_place_with_options(moved_place, IncludingDowncast(true))
98 .unwrap_or_else(|| "_".to_owned()),
100 err.span_label(span, format!("use of possibly-uninitialized {}", item_msg));
102 use_spans.var_span_label_path_only(
104 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
107 self.buffer_error(err);
109 if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
110 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
112 "report_use_of_moved_or_uninitialized place: error suppressed \
120 let is_partial_move = move_site_vec.iter().any(|move_site| {
121 let move_out = self.move_data.moves[(*move_site).moi];
122 let moved_place = &self.move_data.move_paths[move_out.path].place;
123 // `*(_1)` where `_1` is a `Box` is actually a move out.
124 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
125 && self.body.local_decls[moved_place.local].ty.is_box();
128 && used_place != moved_place.as_ref()
129 && used_place.is_prefix_of(moved_place.as_ref())
132 let partial_str = if is_partial_move { "partial " } else { "" };
133 let partially_str = if is_partial_move { "partially " } else { "" };
135 let mut err = self.cannot_act_on_moved_value(
137 desired_action.as_noun(),
139 self.describe_place_with_options(moved_place, IncludingDowncast(true)),
142 let reinit_spans = maybe_reinitialized_locations
146 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
149 .collect::<Vec<Span>>();
150 let reinits = maybe_reinitialized_locations.len();
152 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
153 } else if reinits > 1 {
155 MultiSpan::from_spans(reinit_spans),
157 format!("these {} reinitializations might get skipped", reinits)
160 "these 3 reinitializations and {} other{} might get skipped",
162 if reinits == 4 { "" } else { "s" }
168 self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
170 let mut is_loop_move = false;
171 let mut in_pattern = false;
173 for move_site in &move_site_vec {
174 let move_out = self.move_data.moves[(*move_site).moi];
175 let moved_place = &self.move_data.move_paths[move_out.path].place;
177 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
178 let move_span = move_spans.args_or_use();
180 let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
182 let loop_message = if location == move_out.source || move_site.traversed_back_edge {
183 ", in previous iteration of loop"
188 if location == move_out.source {
192 if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
193 let place_name = self
194 .describe_place(moved_place.as_ref())
195 .map(|n| format!("`{}`", n))
196 .unwrap_or_else(|| "value".to_owned());
198 FnSelfUseKind::FnOnceCall => {
202 "{} {}moved due to this call{}",
203 place_name, partially_str, loop_message
208 "this value implements `FnOnce`, which causes it to be moved when called",
211 FnSelfUseKind::Operator { self_arg } => {
215 "{} {}moved due to usage in operator{}",
216 place_name, partially_str, loop_message
219 if self.fn_self_span_reported.insert(fn_span) {
221 // Check whether the source is accessible
227 .span_to_snippet(self_arg.span)
234 "calling this operator moves the left-hand side",
238 FnSelfUseKind::Normal {
243 if implicit_into_iter {
247 "{} {}moved due to this implicit call to `.into_iter()`{}",
248 place_name, partially_str, loop_message
251 let sess = self.infcx.tcx.sess;
252 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
253 // If we have a `&mut` ref, we need to reborrow.
254 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
255 // If we are in a loop this will be suggested later.
257 err.span_suggestion_verbose(
258 move_span.shrink_to_lo(),
260 "consider creating a fresh reborrow of {} here",
261 self.describe_place(moved_place.as_ref())
262 .map(|n| format!("`{}`", n))
264 || "the mutable reference".to_string()
267 "&mut *".to_string(),
268 Applicability::MachineApplicable,
271 } else if let Ok(snippet) =
272 sess.source_map().span_to_snippet(move_span)
276 "consider borrowing to avoid moving into the for loop",
277 format!("&{}", snippet),
278 Applicability::MaybeIncorrect,
285 "{} {}moved due to this method call{}",
286 place_name, partially_str, loop_message
290 if is_option_or_result && maybe_reinitialized_locations.is_empty() {
291 err.span_suggestion_verbose(
292 fn_call_span.shrink_to_lo(),
293 "consider calling `.as_ref()` to borrow the type's contents",
294 "as_ref().".to_string(),
295 Applicability::MachineApplicable,
298 // Avoid pointing to the same function in multiple different
300 if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span)
304 &format!("this function takes ownership of the receiver `self`, which moves {}", place_name)
308 // Deref::deref takes &self, which cannot cause a move
309 FnSelfUseKind::DerefCoercion { .. } => unreachable!(),
314 format!("value {}moved{} here{}", partially_str, move_msg, loop_message),
316 // If the move error occurs due to a loop, don't show
317 // another message for the same span
318 if loop_message.is_empty() {
319 move_spans.var_span_label(
322 "variable {}moved due to use{}",
324 move_spans.describe()
331 if let (UseSpans::PatUse(span), []) =
332 (move_spans, &maybe_reinitialized_locations[..])
334 if maybe_reinitialized_locations.is_empty() {
335 err.span_suggestion_verbose(
338 "borrow this field in the pattern to avoid moving {}",
339 self.describe_place(moved_place.as_ref())
340 .map(|n| format!("`{}`", n))
341 .unwrap_or_else(|| "the value".to_string())
344 Applicability::MachineApplicable,
351 use_spans.var_span_label_path_only(
353 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
360 "value {} here after {}move",
361 desired_action.as_verb_in_past_tense(),
367 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
368 let needs_note = match ty.kind() {
369 ty::Closure(id, _) => {
370 let tables = self.infcx.tcx.typeck(id.expect_local());
371 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
373 tables.closure_kind_origins().get(hir_id).is_none()
378 let mpi = self.move_data.moves[move_out_indices[0]].path;
379 let place = &self.move_data.move_paths[mpi].place;
380 let ty = place.ty(self.body, self.infcx.tcx).ty;
382 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
383 if is_loop_move & !in_pattern {
384 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
385 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
386 err.span_suggestion_verbose(
389 "consider creating a fresh reborrow of {} here",
390 self.describe_place(moved_place)
391 .map(|n| format!("`{}`", n))
392 .unwrap_or_else(|| "the mutable reference".to_string()),
394 "&mut *".to_string(),
395 Applicability::MachineApplicable,
402 self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
403 let note_msg = match opt_name {
404 Some(ref name) => format!("`{}`", name),
405 None => "value".to_owned(),
407 if let ty::Param(param_ty) = ty.kind() {
408 let tcx = self.infcx.tcx;
409 let generics = tcx.generics_of(self.mir_def_id());
410 let param = generics.type_param(¶m_ty, tcx);
411 if let Some(generics) = tcx
412 .typeck_root_def_id(self.mir_def_id().to_def_id())
414 .and_then(|def_id| tcx.hir().get_generics(def_id))
416 suggest_constraining_type_param(
426 let span = if let Some(local) = place.as_local() {
427 let decl = &self.body.local_decls[local];
428 Some(decl.source_info.span)
432 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
435 if let UseSpans::FnSelfUse {
436 kind: FnSelfUseKind::DerefCoercion { deref_target, deref_target_ty },
441 "{} occurs due to deref coercion to `{}`",
442 desired_action.as_noun(),
446 // Check first whether the source is accessible (issue #87060)
447 if self.infcx.tcx.sess.source_map().span_to_snippet(deref_target).is_ok() {
448 err.span_note(deref_target, "deref defined here");
452 self.buffer_move_error(move_out_indices, (used_place, err));
456 pub(crate) fn report_move_out_while_borrowed(
459 (place, span): (Place<'tcx>, Span),
460 borrow: &BorrowData<'tcx>,
463 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
464 location, place, span, borrow
466 let value_msg = self.describe_any_place(place.as_ref());
467 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
469 let borrow_spans = self.retrieve_borrow_spans(borrow);
470 let borrow_span = borrow_spans.args_or_use();
472 let move_spans = self.move_spans(place.as_ref(), location);
473 let span = move_spans.args_or_use();
476 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
477 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
478 err.span_label(span, format!("move out of {} occurs here", value_msg));
480 borrow_spans.var_span_label_path_only(
482 format!("borrow occurs due to use{}", borrow_spans.describe()),
485 move_spans.var_span_label(
487 format!("move occurs due to use{}", move_spans.describe()),
491 self.explain_why_borrow_contains_point(location, borrow, None)
492 .add_explanation_to_diagnostic(
501 self.buffer_error(err);
504 pub(crate) fn report_use_while_mutably_borrowed(
507 (place, _span): (Place<'tcx>, Span),
508 borrow: &BorrowData<'tcx>,
509 ) -> DiagnosticBuilder<'cx> {
510 let borrow_spans = self.retrieve_borrow_spans(borrow);
511 let borrow_span = borrow_spans.args_or_use();
513 // Conflicting borrows are reported separately, so only check for move
515 let use_spans = self.move_spans(place.as_ref(), location);
516 let span = use_spans.var_or_use();
518 // 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
519 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
520 let mut err = self.cannot_use_when_mutably_borrowed(
522 &self.describe_any_place(place.as_ref()),
524 &self.describe_any_place(borrow.borrowed_place.as_ref()),
527 borrow_spans.var_span_label(
530 let place = &borrow.borrowed_place;
531 let desc_place = self.describe_any_place(place.as_ref());
532 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
537 self.explain_why_borrow_contains_point(location, borrow, None)
538 .add_explanation_to_diagnostic(
550 pub(crate) fn report_conflicting_borrow(
553 (place, span): (Place<'tcx>, Span),
554 gen_borrow_kind: BorrowKind,
555 issued_borrow: &BorrowData<'tcx>,
556 ) -> DiagnosticBuilder<'cx> {
557 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
558 let issued_span = issued_spans.args_or_use();
560 let borrow_spans = self.borrow_spans(span, location);
561 let span = borrow_spans.args_or_use();
563 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
569 let (desc_place, msg_place, msg_borrow, union_type_name) =
570 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
572 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
573 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
575 // FIXME: supply non-"" `opt_via` when appropriate
576 let first_borrow_desc;
577 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
578 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
579 first_borrow_desc = "mutable ";
580 self.cannot_reborrow_already_borrowed(
592 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
593 first_borrow_desc = "immutable ";
594 self.cannot_reborrow_already_borrowed(
607 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
608 first_borrow_desc = "first ";
609 let mut err = self.cannot_mutably_borrow_multiply(
617 self.suggest_split_at_mut_if_applicable(
620 issued_borrow.borrowed_place,
625 (BorrowKind::Unique, BorrowKind::Unique) => {
626 first_borrow_desc = "first ";
627 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
630 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
631 if let Some(immutable_section_description) =
632 self.classify_immutable_section(issued_borrow.assigned_place)
634 let mut err = self.cannot_mutate_in_immutable_section(
638 immutable_section_description,
641 borrow_spans.var_span_label(
644 "borrow occurs due to use of {}{}",
646 borrow_spans.describe(),
653 first_borrow_desc = "immutable ";
654 self.cannot_reborrow_already_borrowed(
668 (BorrowKind::Unique, _) => {
669 first_borrow_desc = "first ";
670 self.cannot_uniquely_borrow_by_one_closure(
682 (BorrowKind::Shared, BorrowKind::Unique) => {
683 first_borrow_desc = "first ";
684 self.cannot_reborrow_already_uniquely_borrowed(
697 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
698 first_borrow_desc = "first ";
699 self.cannot_reborrow_already_uniquely_borrowed(
712 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
715 BorrowKind::Mut { .. }
718 | BorrowKind::Shallow,
722 if issued_spans == borrow_spans {
723 borrow_spans.var_span_label(
725 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
726 gen_borrow_kind.describe_mutability(),
729 let borrow_place = &issued_borrow.borrowed_place;
730 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
731 issued_spans.var_span_label(
734 "first borrow occurs due to use of {}{}",
736 issued_spans.describe(),
738 issued_borrow.kind.describe_mutability(),
741 borrow_spans.var_span_label(
744 "second borrow occurs due to use of {}{}",
746 borrow_spans.describe(),
748 gen_borrow_kind.describe_mutability(),
752 if union_type_name != "" {
754 "{} is a field of the union `{}`, so it overlaps the field {}",
755 msg_place, union_type_name, msg_borrow,
759 explanation.add_explanation_to_diagnostic(
766 Some((issued_span, span)),
769 self.suggest_using_local_if_applicable(
781 #[instrument(level = "debug", skip(self, err))]
782 fn suggest_using_local_if_applicable(
784 err: &mut DiagnosticBuilder<'_>,
786 (place, span): (Place<'tcx>, Span),
787 gen_borrow_kind: BorrowKind,
788 issued_borrow: &BorrowData<'tcx>,
789 explanation: BorrowExplanation,
792 matches!(explanation, BorrowExplanation::UsedLater(LaterUseKind::Call, _call_span, _));
794 debug!("not later used in call");
799 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
802 issued_borrow.reserve_location
804 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
806 let inner_param_location = location;
807 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
808 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
811 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
813 "`inner_param_location` {:?} is not for an assignment: {:?}",
814 inner_param_location, inner_param_stmt
818 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
819 let Some((inner_call_loc,inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
820 let Either::Right(term) = self.body.stmt_at(loc) else {
821 debug!("{:?} is a statement, so it can't be a call", loc);
824 let TerminatorKind::Call { args, .. } = &term.kind else {
825 debug!("not a call: {:?}", term);
828 debug!("checking call args for uses of inner_param: {:?}", args);
829 if args.contains(&Operand::Move(inner_param)) {
835 debug!("no uses of inner_param found as a by-move call arg");
838 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
840 let inner_call_span = inner_call_term.source_info.span;
841 let outer_call_span = outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span;
842 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
843 // FIXME: This stops the suggestion in some cases where it should be emitted.
844 // Fix the spans for those cases so it's emitted correctly.
846 "outer span {:?} does not strictly contain inner span {:?}",
847 outer_call_span, inner_call_span
851 err.span_help(inner_call_span, "try adding a local storing this argument...");
852 err.span_help(outer_call_span, "...and then using that local as the argument to this call");
855 fn suggest_split_at_mut_if_applicable(
857 err: &mut DiagnosticBuilder<'_>,
859 borrowed_place: Place<'tcx>,
861 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
862 (&place.projection[..], &borrowed_place.projection[..])
865 "consider using `.split_at_mut(position)` or similar method to obtain \
866 two mutable non-overlapping sub-slices",
871 /// Returns the description of the root place for a conflicting borrow and the full
872 /// descriptions of the places that caused the conflict.
874 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
875 /// attempted while a shared borrow is live, then this function will return:
879 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
880 /// a shared borrow of another field `x.y`, then this function will return:
882 /// ("x", "x.z", "x.y")
884 /// In the more complex union case, where the union is a field of a struct, then if a mutable
885 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
886 /// another field `x.u.y`, then this function will return:
888 /// ("x.u", "x.u.z", "x.u.y")
890 /// This is used when creating error messages like below:
893 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
894 /// mutable (via `a.u.s.b`) [E0502]
896 pub(crate) fn describe_place_for_conflicting_borrow(
898 first_borrowed_place: Place<'tcx>,
899 second_borrowed_place: Place<'tcx>,
900 ) -> (String, String, String, String) {
901 // Define a small closure that we can use to check if the type of a place
903 let union_ty = |place_base| {
904 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
905 // using a type annotation in the closure argument instead leads to a lifetime error.
906 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
907 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
910 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
911 // code duplication (particularly around returning an empty description in the failure
915 // If we have a conflicting borrow of the same place, then we don't want to add
916 // an extraneous "via x.y" to our diagnostics, so filter out this case.
917 first_borrowed_place != second_borrowed_place
920 // We're going to want to traverse the first borrowed place to see if we can find
921 // field access to a union. If we find that, then we will keep the place of the
922 // union being accessed and the field that was being accessed so we can check the
923 // second borrowed place for the same union and an access to a different field.
924 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
926 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
927 return Some((place_base, field));
934 .and_then(|(target_base, target_field)| {
935 // With the place of a union and a field access into it, we traverse the second
936 // borrowed place and look for an access to a different field of the same union.
937 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
938 if let ProjectionElem::Field(field, _) = elem {
939 if let Some(union_ty) = union_ty(place_base) {
940 if field != target_field && place_base == target_base {
942 self.describe_any_place(place_base),
943 self.describe_any_place(first_borrowed_place.as_ref()),
944 self.describe_any_place(second_borrowed_place.as_ref()),
945 union_ty.to_string(),
954 // If we didn't find a field access into a union, or both places match, then
955 // only return the description of the first place.
957 self.describe_any_place(first_borrowed_place.as_ref()),
965 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
967 /// This means that some data referenced by `borrow` needs to live
968 /// past the point where the StorageDeadOrDrop of `place` occurs.
969 /// This is usually interpreted as meaning that `place` has too
970 /// short a lifetime. (But sometimes it is more useful to report
971 /// it as a more direct conflict between the execution of a
972 /// `Drop::drop` with an aliasing borrow.)
973 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
976 borrow: &BorrowData<'tcx>,
977 place_span: (Place<'tcx>, Span),
978 kind: Option<WriteKind>,
981 "report_borrowed_value_does_not_live_long_enough(\
982 {:?}, {:?}, {:?}, {:?}\
984 location, borrow, place_span, kind
987 let drop_span = place_span.1;
989 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
991 let borrow_spans = self.retrieve_borrow_spans(borrow);
992 let borrow_span = borrow_spans.var_or_use_path_span();
994 assert!(root_place.projection.is_empty());
995 let proper_span = self.body.local_decls[root_place.local].source_info.span;
997 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
999 if self.access_place_error_reported.contains(&(
1000 Place { local: root_place.local, projection: root_place_projection },
1004 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1010 self.access_place_error_reported.insert((
1011 Place { local: root_place.local, projection: root_place_projection },
1015 let borrowed_local = borrow.borrowed_place.local;
1016 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1018 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1019 self.buffer_error(err);
1023 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1024 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1026 // If a borrow of path `B` conflicts with drop of `D` (and
1027 // we're not in the uninteresting case where `B` is a
1028 // prefix of `D`), then report this as a more interesting
1029 // destructor conflict.
1030 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1031 self.report_borrow_conflicts_with_destructor(
1032 location, borrow, place_span, kind, dropped_ty,
1038 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1040 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1041 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1044 "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
1045 place_desc, explanation
1047 let err = match (place_desc, explanation) {
1048 // If the outlives constraint comes from inside the closure,
1053 // Box::new(|| y) as Box<Fn() -> &'static i32>
1055 // then just use the normal error. The closure isn't escaping
1056 // and `move` will not help here.
1059 BorrowExplanation::MustBeValidFor {
1061 category @ (ConstraintCategory::Return(_)
1062 | ConstraintCategory::CallArgument
1063 | ConstraintCategory::OpaqueType),
1064 from_closure: false,
1069 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1070 .report_escaping_closure_capture(
1076 &format!("`{}`", name),
1080 BorrowExplanation::MustBeValidFor {
1081 category: ConstraintCategory::Assignment,
1082 from_closure: false,
1086 RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
1092 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1093 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1101 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1111 self.buffer_error(err);
1114 fn report_local_value_does_not_live_long_enough(
1118 borrow: &BorrowData<'tcx>,
1120 borrow_spans: UseSpans<'tcx>,
1121 explanation: BorrowExplanation,
1122 ) -> DiagnosticBuilder<'cx> {
1124 "report_local_value_does_not_live_long_enough(\
1125 {:?}, {:?}, {:?}, {:?}, {:?}\
1127 location, name, borrow, drop_span, borrow_spans
1130 let borrow_span = borrow_spans.var_or_use_path_span();
1131 if let BorrowExplanation::MustBeValidFor {
1135 from_closure: false,
1139 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1144 opt_place_desc.as_ref(),
1150 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1152 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1153 let region_name = annotation.emit(self, &mut err);
1157 format!("`{}` would have to be valid for `{}`...", name, region_name),
1160 let fn_hir_id = self.mir_hir_id();
1164 "...but `{}` will be dropped here, when the {} returns",
1169 .opt_name(fn_hir_id)
1170 .map(|name| format!("function `{}`", name))
1171 .unwrap_or_else(|| {
1175 .typeck(self.mir_def_id())
1176 .node_type(fn_hir_id)
1179 ty::Closure(..) => "enclosing closure",
1180 ty::Generator(..) => "enclosing generator",
1181 kind => bug!("expected closure or generator, found {:?}", kind),
1189 "functions cannot return a borrow to data owned within the function's scope, \
1190 functions can only return borrows to data passed as arguments",
1193 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1194 references-and-borrowing.html#dangling-references>",
1197 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1199 explanation.add_explanation_to_diagnostic(
1210 err.span_label(borrow_span, "borrowed value does not live long enough");
1211 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1213 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1215 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1217 explanation.add_explanation_to_diagnostic(
1231 fn report_borrow_conflicts_with_destructor(
1234 borrow: &BorrowData<'tcx>,
1235 (place, drop_span): (Place<'tcx>, Span),
1236 kind: Option<WriteKind>,
1237 dropped_ty: Ty<'tcx>,
1240 "report_borrow_conflicts_with_destructor(\
1241 {:?}, {:?}, ({:?}, {:?}), {:?}\
1243 location, borrow, place, drop_span, kind,
1246 let borrow_spans = self.retrieve_borrow_spans(borrow);
1247 let borrow_span = borrow_spans.var_or_use();
1249 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1251 let what_was_dropped = match self.describe_place(place.as_ref()) {
1252 Some(name) => format!("`{}`", name),
1253 None => String::from("temporary value"),
1256 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1257 Some(borrowed) => format!(
1258 "here, drop of {D} needs exclusive access to `{B}`, \
1259 because the type `{T}` implements the `Drop` trait",
1260 D = what_was_dropped,
1265 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1266 D = what_was_dropped,
1270 err.span_label(drop_span, label);
1272 // Only give this note and suggestion if they could be relevant.
1274 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1276 BorrowExplanation::UsedLater { .. }
1277 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1278 err.note("consider using a `let` binding to create a longer lived value");
1283 explanation.add_explanation_to_diagnostic(
1293 self.buffer_error(err);
1296 fn report_thread_local_value_does_not_live_long_enough(
1300 ) -> DiagnosticBuilder<'cx> {
1302 "report_thread_local_value_does_not_live_long_enough(\
1305 drop_span, borrow_span
1308 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1312 "thread-local variables cannot be borrowed beyond the end of the function",
1314 err.span_label(drop_span, "end of enclosing function is here");
1319 fn report_temporary_value_does_not_live_long_enough(
1322 borrow: &BorrowData<'tcx>,
1324 borrow_spans: UseSpans<'tcx>,
1326 explanation: BorrowExplanation,
1327 ) -> DiagnosticBuilder<'cx> {
1329 "report_temporary_value_does_not_live_long_enough(\
1330 {:?}, {:?}, {:?}, {:?}\
1332 location, borrow, drop_span, proper_span
1335 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1338 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1349 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1350 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1351 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1354 BorrowExplanation::UsedLater(..)
1355 | BorrowExplanation::UsedLaterInLoop(..)
1356 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1357 // Only give this note and suggestion if it could be relevant.
1358 err.note("consider using a `let` binding to create a longer lived value");
1362 explanation.add_explanation_to_diagnostic(
1372 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1374 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1379 fn try_report_cannot_return_reference_to_local(
1381 borrow: &BorrowData<'tcx>,
1384 category: ConstraintCategory,
1385 opt_place_desc: Option<&String>,
1386 ) -> Option<DiagnosticBuilder<'cx>> {
1387 let return_kind = match category {
1388 ConstraintCategory::Return(_) => "return",
1389 ConstraintCategory::Yield => "yield",
1393 // FIXME use a better heuristic than Spans
1394 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1400 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1401 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1402 match self.body.local_kind(local) {
1403 LocalKind::ReturnPointer | LocalKind::Temp => {
1404 bug!("temporary or return pointer with a name")
1406 LocalKind::Var => "local variable ",
1408 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1410 "variable captured by `move` "
1412 LocalKind::Arg => "function parameter ",
1418 format!("{}`{}`", local_kind, place_desc),
1419 format!("`{}` is borrowed here", place_desc),
1423 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1424 let local = root_place.local;
1425 match self.body.local_kind(local) {
1426 LocalKind::ReturnPointer | LocalKind::Temp => {
1427 ("temporary value".to_string(), "temporary value created here".to_string())
1430 "function parameter".to_string(),
1431 "function parameter borrowed here".to_string(),
1434 ("local binding".to_string(), "local binding introduced here".to_string())
1439 let mut err = self.cannot_return_reference_to_local(
1446 if return_span != borrow_span {
1447 err.span_label(borrow_span, note);
1449 let tcx = self.infcx.tcx;
1450 let ty_params = ty::List::empty();
1452 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1453 let return_ty = tcx.erase_regions(return_ty);
1456 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1459 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1460 .must_apply_modulo_regions()
1462 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1463 err.span_suggestion_hidden(
1465 "use `.collect()` to allocate the iterator",
1466 format!("{}{}", snippet, ".collect::<Vec<_>>()"),
1467 Applicability::MaybeIncorrect,
1477 fn report_escaping_closure_capture(
1479 use_span: UseSpans<'tcx>,
1481 fr_name: &RegionName,
1482 category: ConstraintCategory,
1483 constraint_span: Span,
1485 ) -> DiagnosticBuilder<'cx> {
1486 let tcx = self.infcx.tcx;
1487 let args_span = use_span.args_or_use();
1489 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1491 if string.starts_with("async ") {
1492 let pos = args_span.lo() + BytePos(6);
1493 (args_span.with_lo(pos).with_hi(pos), "move ".to_string())
1494 } else if string.starts_with("async|") {
1495 let pos = args_span.lo() + BytePos(5);
1496 (args_span.with_lo(pos).with_hi(pos), " move".to_string())
1498 (args_span.shrink_to_lo(), "move ".to_string())
1501 Err(_) => (args_span, "move |<args>| <body>".to_string()),
1503 let kind = match use_span.generator_kind() {
1504 Some(generator_kind) => match generator_kind {
1505 GeneratorKind::Async(async_kind) => match async_kind {
1506 AsyncGeneratorKind::Block => "async block",
1507 AsyncGeneratorKind::Closure => "async closure",
1508 _ => bug!("async block/closure expected, but async function found."),
1510 GeneratorKind::Gen => "generator",
1516 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1517 err.span_suggestion_verbose(
1520 "to force the {} to take ownership of {} (and any \
1521 other referenced variables), use the `move` keyword",
1525 Applicability::MachineApplicable,
1529 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1530 let msg = format!("{} is returned here", kind);
1531 err.span_note(constraint_span, &msg);
1533 ConstraintCategory::CallArgument => {
1534 fr_name.highlight_region_name(&mut err);
1535 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1537 "async blocks are not executed immediately and must either take a \
1538 reference or ownership of outside variables they use",
1541 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1542 err.span_note(constraint_span, &msg);
1546 "report_escaping_closure_capture called with unexpected constraint \
1555 fn report_escaping_data(
1558 name: &Option<String>,
1562 ) -> DiagnosticBuilder<'cx> {
1563 let tcx = self.infcx.tcx;
1565 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1568 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1572 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1575 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1577 if let Some(name) = name {
1580 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1585 format!("reference escapes the {} body here", escapes_from),
1592 fn get_moved_indexes(
1596 ) -> (Vec<MoveSite>, Vec<Location>) {
1597 fn predecessor_locations<'a>(
1598 body: &'a mir::Body<'_>,
1600 ) -> impl Iterator<Item = Location> + 'a {
1601 if location.statement_index == 0 {
1602 let predecessors = body.predecessors()[location.block].to_vec();
1603 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1605 Either::Right(std::iter::once(Location {
1606 statement_index: location.statement_index - 1,
1612 let mut mpis = vec![mpi];
1613 let move_paths = &self.move_data.move_paths;
1614 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1616 let mut stack = Vec::new();
1617 let mut back_edge_stack = Vec::new();
1619 predecessor_locations(self.body, location).for_each(|predecessor| {
1620 if location.dominates(predecessor, &self.dominators) {
1621 back_edge_stack.push(predecessor)
1623 stack.push(predecessor);
1627 let mut reached_start = false;
1629 /* Check if the mpi is initialized as an argument */
1630 let mut is_argument = false;
1631 for arg in self.body.args_iter() {
1632 let path = self.move_data.rev_lookup.find_local(arg);
1633 if mpis.contains(&path) {
1638 let mut visited = FxHashSet::default();
1639 let mut move_locations = FxHashSet::default();
1640 let mut reinits = vec![];
1641 let mut result = vec![];
1643 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1645 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1646 location, is_back_edge
1649 if !visited.insert(location) {
1655 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1656 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1657 // this analysis only tries to find moves explicitly
1658 // written by the user, so we ignore the move-outs
1659 // created by `StorageDead` and at the beginning
1662 // If we are found a use of a.b.c which was in error, then we want to look for
1663 // moves not only of a.b.c but also a.b and a.
1665 // Note that the moves data already includes "parent" paths, so we don't have to
1666 // worry about the other case: that is, if there is a move of a.b.c, it is already
1667 // marked as a move of a.b and a as well, so we will generate the correct errors
1669 for moi in &self.move_data.loc_map[location] {
1670 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1671 let path = self.move_data.moves[*moi].path;
1672 if mpis.contains(&path) {
1674 "report_use_of_moved_or_uninitialized: found {:?}",
1675 move_paths[path].place
1677 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1678 move_locations.insert(location);
1680 // Strictly speaking, we could continue our DFS here. There may be
1681 // other moves that can reach the point of error. But it is kind of
1682 // confusing to highlight them.
1690 // drop(a); // <-- current point of error
1693 // Because we stop the DFS here, we only highlight `let c = a`,
1694 // and not `let b = a`. We will of course also report an error at
1695 // `let c = a` which highlights `let b = a` as the move.
1702 let mut any_match = false;
1703 for ii in &self.move_data.init_loc_map[location] {
1704 let init = self.move_data.inits[*ii];
1706 InitKind::Deep | InitKind::NonPanicPathOnly => {
1707 if mpis.contains(&init.path) {
1711 InitKind::Shallow => {
1712 if mpi == init.path {
1719 reinits.push(location);
1725 while let Some(location) = stack.pop() {
1726 if dfs_iter(&mut result, location, false) {
1730 let mut has_predecessor = false;
1731 predecessor_locations(self.body, location).for_each(|predecessor| {
1732 if location.dominates(predecessor, &self.dominators) {
1733 back_edge_stack.push(predecessor)
1735 stack.push(predecessor);
1737 has_predecessor = true;
1740 if !has_predecessor {
1741 reached_start = true;
1744 if (is_argument || !reached_start) && result.is_empty() {
1745 /* Process back edges (moves in future loop iterations) only if
1746 the move path is definitely initialized upon loop entry,
1747 to avoid spurious "in previous iteration" errors.
1748 During DFS, if there's a path from the error back to the start
1749 of the function with no intervening init or move, then the
1750 move path may be uninitialized at loop entry.
1752 while let Some(location) = back_edge_stack.pop() {
1753 if dfs_iter(&mut result, location, true) {
1757 predecessor_locations(self.body, location)
1758 .for_each(|predecessor| back_edge_stack.push(predecessor));
1762 // Check if we can reach these reinits from a move location.
1763 let reinits_reachable = reinits
1766 let mut visited = FxHashSet::default();
1767 let mut stack = vec![*reinit];
1768 while let Some(location) = stack.pop() {
1769 if !visited.insert(location) {
1772 if move_locations.contains(&location) {
1775 stack.extend(predecessor_locations(self.body, location));
1779 .collect::<Vec<Location>>();
1780 (result, reinits_reachable)
1783 pub(crate) fn report_illegal_mutation_of_borrowed(
1786 (place, span): (Place<'tcx>, Span),
1787 loan: &BorrowData<'tcx>,
1789 let loan_spans = self.retrieve_borrow_spans(loan);
1790 let loan_span = loan_spans.args_or_use();
1792 let descr_place = self.describe_any_place(place.as_ref());
1793 if loan.kind == BorrowKind::Shallow {
1794 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1795 let mut err = self.cannot_mutate_in_immutable_section(
1802 loan_spans.var_span_label(
1804 format!("borrow occurs due to use{}", loan_spans.describe()),
1805 loan.kind.describe_mutability(),
1808 self.buffer_error(err);
1814 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
1816 loan_spans.var_span_label(
1818 format!("borrow occurs due to use{}", loan_spans.describe()),
1819 loan.kind.describe_mutability(),
1822 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
1832 self.explain_deref_coercion(loan, &mut err);
1834 self.buffer_error(err);
1837 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut DiagnosticBuilder<'_>) {
1838 let tcx = self.infcx.tcx;
1840 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
1841 Some((method_did, method_substs)),
1843 &self.body[loan.reserve_location.block].terminator,
1844 rustc_const_eval::util::find_self_call(
1847 loan.assigned_place.local,
1848 loan.reserve_location.block,
1851 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
1853 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
1854 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
1857 if let Some(Ok(instance)) = deref_target {
1858 let deref_target_ty = instance.ty(tcx, self.param_env);
1860 "borrow occurs due to deref coercion to `{}`",
1863 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
1869 /// Reports an illegal reassignment; for example, an assignment to
1870 /// (part of) a non-`mut` local that occurs potentially after that
1871 /// local has already been initialized. `place` is the path being
1872 /// assigned; `err_place` is a place providing a reason why
1873 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
1874 /// assignment to `x.f`).
1875 pub(crate) fn report_illegal_reassignment(
1877 _location: Location,
1878 (place, span): (Place<'tcx>, Span),
1879 assigned_span: Span,
1880 err_place: Place<'tcx>,
1882 let (from_arg, local_decl, local_name) = match err_place.as_local() {
1884 self.body.local_kind(local) == LocalKind::Arg,
1885 Some(&self.body.local_decls[local]),
1886 self.local_names[local],
1888 None => (false, None, None),
1891 // If root local is initialized immediately (everything apart from let
1892 // PATTERN;) then make the error refer to that local, rather than the
1893 // place being assigned later.
1894 let (place_description, assigned_span) = match local_decl {
1897 Some(box LocalInfo::User(
1898 ClearCrossCrate::Clear
1899 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
1900 opt_match_place: None,
1904 | Some(box LocalInfo::StaticRef { .. })
1908 | None => (self.describe_any_place(place.as_ref()), assigned_span),
1909 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
1912 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
1913 let msg = if from_arg {
1914 "cannot assign to immutable argument"
1916 "cannot assign twice to immutable variable"
1918 if span != assigned_span {
1920 err.span_label(assigned_span, format!("first assignment to {}", place_description));
1923 if let Some(decl) = local_decl {
1924 if let Some(name) = local_name {
1925 if decl.can_be_made_mutable() {
1926 err.span_suggestion(
1927 decl.source_info.span,
1928 "consider making this binding mutable",
1929 format!("mut {}", name),
1930 Applicability::MachineApplicable,
1935 err.span_label(span, msg);
1936 self.buffer_error(err);
1939 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
1940 let tcx = self.infcx.tcx;
1941 match place.last_projection() {
1942 None => StorageDeadOrDrop::LocalStorageDead,
1943 Some((place_base, elem)) => {
1944 // FIXME(spastorino) make this iterate
1945 let base_access = self.classify_drop_access_kind(place_base);
1947 ProjectionElem::Deref => match base_access {
1948 StorageDeadOrDrop::LocalStorageDead
1949 | StorageDeadOrDrop::BoxedStorageDead => {
1951 place_base.ty(self.body, tcx).ty.is_box(),
1952 "Drop of value behind a reference or raw pointer"
1954 StorageDeadOrDrop::BoxedStorageDead
1956 StorageDeadOrDrop::Destructor(_) => base_access,
1958 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
1959 let base_ty = place_base.ty(self.body, tcx).ty;
1960 match base_ty.kind() {
1961 ty::Adt(def, _) if def.has_dtor(tcx) => {
1962 // Report the outermost adt with a destructor
1964 StorageDeadOrDrop::Destructor(_) => base_access,
1965 StorageDeadOrDrop::LocalStorageDead
1966 | StorageDeadOrDrop::BoxedStorageDead => {
1967 StorageDeadOrDrop::Destructor(base_ty)
1974 ProjectionElem::ConstantIndex { .. }
1975 | ProjectionElem::Subslice { .. }
1976 | ProjectionElem::Index(_) => base_access,
1982 /// Describe the reason for the fake borrow that was assigned to `place`.
1983 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
1984 use rustc_middle::mir::visit::Visitor;
1985 struct FakeReadCauseFinder<'tcx> {
1987 cause: Option<FakeReadCause>,
1989 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
1990 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
1992 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
1993 if *place == self.place =>
1995 self.cause = Some(*cause);
2001 let mut visitor = FakeReadCauseFinder { place, cause: None };
2002 visitor.visit_body(&self.body);
2003 match visitor.cause {
2004 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2005 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2010 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2011 /// borrow of local value that does not live long enough.
2012 fn annotate_argument_and_return_for_borrow(
2014 borrow: &BorrowData<'tcx>,
2015 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2016 // Define a fallback for when we can't match a closure.
2018 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2022 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2024 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2025 self.mir_def_id().to_def_id(),
2026 self.infcx.tcx.fn_sig(self.mir_def_id()),
2033 // In order to determine whether we need to annotate, we need to check whether the reserve
2034 // place was an assignment into a temporary.
2036 // If it was, we check whether or not that temporary is eventually assigned into the return
2037 // place. If it was, we can add annotations about the function's return type and arguments
2038 // and it'll make sense.
2039 let location = borrow.reserve_location;
2040 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2041 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2042 &self.body[location.block].statements.get(location.statement_index)
2044 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2045 // Check that the initial assignment of the reserve location is into a temporary.
2046 let mut target = match reservation.as_local() {
2047 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2051 // Next, look through the rest of the block, checking if we are assigning the
2052 // `target` (that is, the place that contains our borrow) to anything.
2053 let mut annotated_closure = None;
2054 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2056 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2059 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2060 if let Some(assigned_to) = place.as_local() {
2062 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2066 // Check if our `target` was captured by a closure.
2067 if let Rvalue::Aggregate(
2068 box AggregateKind::Closure(def_id, substs),
2072 for operand in operands {
2073 let assigned_from = match operand {
2074 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2080 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2084 // Find the local from the operand.
2085 let assigned_from_local = match assigned_from.local_or_deref_local()
2087 Some(local) => local,
2091 if assigned_from_local != target {
2095 // If a closure captured our `target` and then assigned
2096 // into a place then we should annotate the closure in
2097 // case it ends up being assigned into the return place.
2099 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2101 "annotate_argument_and_return_for_borrow: \
2102 annotated_closure={:?} assigned_from_local={:?} \
2104 annotated_closure, assigned_from_local, assigned_to
2107 if assigned_to == mir::RETURN_PLACE {
2108 // If it was assigned directly into the return place, then
2110 return annotated_closure;
2112 // Otherwise, update the target.
2113 target = assigned_to;
2117 // If none of our closure's operands matched, then skip to the next
2122 // Otherwise, look at other types of assignment.
2123 let assigned_from = match rvalue {
2124 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2125 Rvalue::Use(operand) => match operand {
2126 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2134 "annotate_argument_and_return_for_borrow: \
2135 assigned_from={:?}",
2139 // Find the local from the rvalue.
2140 let assigned_from_local = match assigned_from.local_or_deref_local() {
2141 Some(local) => local,
2145 "annotate_argument_and_return_for_borrow: \
2146 assigned_from_local={:?}",
2147 assigned_from_local,
2150 // Check if our local matches the target - if so, we've assigned our
2151 // borrow to a new place.
2152 if assigned_from_local != target {
2156 // If we assigned our `target` into a new place, then we should
2157 // check if it was the return place.
2159 "annotate_argument_and_return_for_borrow: \
2160 assigned_from_local={:?} assigned_to={:?}",
2161 assigned_from_local, assigned_to
2163 if assigned_to == mir::RETURN_PLACE {
2164 // If it was then return the annotated closure if there was one,
2165 // else, annotate this function.
2166 return annotated_closure.or_else(fallback);
2169 // If we didn't assign into the return place, then we just update
2171 target = assigned_to;
2176 // Check the terminator if we didn't find anything in the statements.
2177 let terminator = &self.body[location.block].terminator();
2179 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2182 if let TerminatorKind::Call { destination: Some((place, _)), args, .. } =
2185 if let Some(assigned_to) = place.as_local() {
2187 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2190 for operand in args {
2191 let assigned_from = match operand {
2192 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2198 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2202 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2204 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2205 assigned_from_local,
2208 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2209 return annotated_closure.or_else(fallback);
2217 // If we haven't found an assignment into the return place, then we need not add
2219 debug!("annotate_argument_and_return_for_borrow: none found");
2223 /// Annotate the first argument and return type of a function signature if they are
2228 sig: ty::PolyFnSig<'tcx>,
2229 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2230 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2231 let is_closure = self.infcx.tcx.is_closure(did);
2232 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?);
2233 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2235 // We need to work out which arguments to highlight. We do this by looking
2236 // at the return type, where there are three cases:
2238 // 1. If there are named arguments, then we should highlight the return type and
2239 // highlight any of the arguments that are also references with that lifetime.
2240 // If there are no arguments that have the same lifetime as the return type,
2241 // then don't highlight anything.
2242 // 2. The return type is a reference with an anonymous lifetime. If this is
2243 // the case, then we can take advantage of (and teach) the lifetime elision
2246 // We know that an error is being reported. So the arguments and return type
2247 // must satisfy the elision rules. Therefore, if there is a single argument
2248 // then that means the return type and first (and only) argument have the same
2249 // lifetime and the borrow isn't meeting that, we can highlight the argument
2252 // If there are multiple arguments then the first argument must be self (else
2253 // it would not satisfy the elision rules), so we can highlight self and the
2255 // 3. The return type is not a reference. In this case, we don't highlight
2257 let return_ty = sig.output();
2258 match return_ty.skip_binder().kind() {
2259 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2260 // This is case 1 from above, return type is a named reference so we need to
2261 // search for relevant arguments.
2262 let mut arguments = Vec::new();
2263 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2264 if let ty::Ref(argument_region, _, _) = argument.kind() {
2265 if argument_region == return_region {
2266 // Need to use the `rustc_middle::ty` types to compare against the
2267 // `return_region`. Then use the `rustc_hir` type to get only
2268 // the lifetime span.
2269 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2270 // With access to the lifetime, we can get
2272 arguments.push((*argument, lifetime.span));
2274 bug!("ty type is a ref but hir type is not");
2280 // We need to have arguments. This shouldn't happen, but it's worth checking.
2281 if arguments.is_empty() {
2285 // We use a mix of the HIR and the Ty types to get information
2286 // as the HIR doesn't have full types for closure arguments.
2287 let return_ty = sig.output().skip_binder();
2288 let mut return_span = fn_decl.output.span();
2289 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2290 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2291 return_span = lifetime.span;
2295 Some(AnnotatedBorrowFnSignature::NamedFunction {
2301 ty::Ref(_, _, _) if is_closure => {
2302 // This is case 2 from above but only for closures, return type is anonymous
2303 // reference so we select
2304 // the first argument.
2305 let argument_span = fn_decl.inputs.first()?.span;
2306 let argument_ty = sig.inputs().skip_binder().first()?;
2308 // Closure arguments are wrapped in a tuple, so we need to get the first
2310 if let ty::Tuple(elems) = argument_ty.kind() {
2311 let argument_ty = elems.first()?.expect_ty();
2312 if let ty::Ref(_, _, _) = argument_ty.kind() {
2313 return Some(AnnotatedBorrowFnSignature::Closure {
2322 ty::Ref(_, _, _) => {
2323 // This is also case 2 from above but for functions, return type is still an
2324 // anonymous reference so we select the first argument.
2325 let argument_span = fn_decl.inputs.first()?.span;
2326 let argument_ty = sig.inputs().skip_binder().first()?;
2328 let return_span = fn_decl.output.span();
2329 let return_ty = sig.output().skip_binder();
2331 // We expect the first argument to be a reference.
2332 match argument_ty.kind() {
2333 ty::Ref(_, _, _) => {}
2337 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2345 // This is case 3 from above, return type is not a reference so don't highlight
2354 enum AnnotatedBorrowFnSignature<'tcx> {
2356 arguments: Vec<(Ty<'tcx>, Span)>,
2357 return_ty: Ty<'tcx>,
2361 argument_ty: Ty<'tcx>,
2362 argument_span: Span,
2363 return_ty: Ty<'tcx>,
2367 argument_ty: Ty<'tcx>,
2368 argument_span: Span,
2372 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2373 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2377 cx: &mut MirBorrowckCtxt<'_, 'tcx>,
2378 diag: &mut DiagnosticBuilder<'_>,
2381 AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2384 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2387 cx.get_region_name_for_ty(argument_ty, 0)
2389 AnnotatedBorrowFnSignature::AnonymousFunction {
2395 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2396 diag.span_label(*argument_span, format!("has type `{}`", argument_ty_name));
2398 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2399 let types_equal = return_ty_name == argument_ty_name;
2404 if types_equal { "also " } else { "" },
2410 "argument and return type have the same lifetime due to lifetime elision rules",
2413 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2414 lifetime-syntax.html#lifetime-elision>",
2417 cx.get_region_name_for_ty(return_ty, 0)
2419 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2420 // Region of return type and arguments checked to be the same earlier.
2421 let region_name = cx.get_region_name_for_ty(return_ty, 0);
2422 for (_, argument_span) in arguments {
2423 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2426 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2429 "use data from the highlighted arguments which match the `{}` lifetime of \