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 err.buffer(&mut self.errors_buffer);
109 if let Some((reported_place, _)) = self.move_error_reported.get(&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 if let Some((_, mut old_err)) =
453 self.move_error_reported.insert(move_out_indices, (used_place, err))
455 // Cancel the old error so it doesn't ICE.
461 pub(crate) fn report_move_out_while_borrowed(
464 (place, span): (Place<'tcx>, Span),
465 borrow: &BorrowData<'tcx>,
468 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
469 location, place, span, borrow
471 let value_msg = self.describe_any_place(place.as_ref());
472 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
474 let borrow_spans = self.retrieve_borrow_spans(borrow);
475 let borrow_span = borrow_spans.args_or_use();
477 let move_spans = self.move_spans(place.as_ref(), location);
478 let span = move_spans.args_or_use();
481 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
482 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
483 err.span_label(span, format!("move out of {} occurs here", value_msg));
485 borrow_spans.var_span_label_path_only(
487 format!("borrow occurs due to use{}", borrow_spans.describe()),
490 move_spans.var_span_label(
492 format!("move occurs due to use{}", move_spans.describe()),
496 self.explain_why_borrow_contains_point(location, borrow, None)
497 .add_explanation_to_diagnostic(
506 err.buffer(&mut self.errors_buffer);
509 pub(crate) fn report_use_while_mutably_borrowed(
512 (place, _span): (Place<'tcx>, Span),
513 borrow: &BorrowData<'tcx>,
514 ) -> DiagnosticBuilder<'cx> {
515 let borrow_spans = self.retrieve_borrow_spans(borrow);
516 let borrow_span = borrow_spans.args_or_use();
518 // Conflicting borrows are reported separately, so only check for move
520 let use_spans = self.move_spans(place.as_ref(), location);
521 let span = use_spans.var_or_use();
523 // 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
524 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
525 let mut err = self.cannot_use_when_mutably_borrowed(
527 &self.describe_any_place(place.as_ref()),
529 &self.describe_any_place(borrow.borrowed_place.as_ref()),
532 borrow_spans.var_span_label(
535 let place = &borrow.borrowed_place;
536 let desc_place = self.describe_any_place(place.as_ref());
537 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
542 self.explain_why_borrow_contains_point(location, borrow, None)
543 .add_explanation_to_diagnostic(
555 pub(crate) fn report_conflicting_borrow(
558 (place, span): (Place<'tcx>, Span),
559 gen_borrow_kind: BorrowKind,
560 issued_borrow: &BorrowData<'tcx>,
561 ) -> DiagnosticBuilder<'cx> {
562 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
563 let issued_span = issued_spans.args_or_use();
565 let borrow_spans = self.borrow_spans(span, location);
566 let span = borrow_spans.args_or_use();
568 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
574 let (desc_place, msg_place, msg_borrow, union_type_name) =
575 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
577 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
578 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
580 // FIXME: supply non-"" `opt_via` when appropriate
581 let first_borrow_desc;
582 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
583 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
584 first_borrow_desc = "mutable ";
585 self.cannot_reborrow_already_borrowed(
597 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
598 first_borrow_desc = "immutable ";
599 self.cannot_reborrow_already_borrowed(
612 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
613 first_borrow_desc = "first ";
614 let mut err = self.cannot_mutably_borrow_multiply(
622 self.suggest_split_at_mut_if_applicable(
625 issued_borrow.borrowed_place,
630 (BorrowKind::Unique, BorrowKind::Unique) => {
631 first_borrow_desc = "first ";
632 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
635 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
636 if let Some(immutable_section_description) =
637 self.classify_immutable_section(issued_borrow.assigned_place)
639 let mut err = self.cannot_mutate_in_immutable_section(
643 immutable_section_description,
646 borrow_spans.var_span_label(
649 "borrow occurs due to use of {}{}",
651 borrow_spans.describe(),
658 first_borrow_desc = "immutable ";
659 self.cannot_reborrow_already_borrowed(
673 (BorrowKind::Unique, _) => {
674 first_borrow_desc = "first ";
675 self.cannot_uniquely_borrow_by_one_closure(
687 (BorrowKind::Shared, BorrowKind::Unique) => {
688 first_borrow_desc = "first ";
689 self.cannot_reborrow_already_uniquely_borrowed(
702 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
703 first_borrow_desc = "first ";
704 self.cannot_reborrow_already_uniquely_borrowed(
717 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
720 BorrowKind::Mut { .. }
723 | BorrowKind::Shallow,
727 if issued_spans == borrow_spans {
728 borrow_spans.var_span_label(
730 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
731 gen_borrow_kind.describe_mutability(),
734 let borrow_place = &issued_borrow.borrowed_place;
735 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
736 issued_spans.var_span_label(
739 "first borrow occurs due to use of {}{}",
741 issued_spans.describe(),
743 issued_borrow.kind.describe_mutability(),
746 borrow_spans.var_span_label(
749 "second borrow occurs due to use of {}{}",
751 borrow_spans.describe(),
753 gen_borrow_kind.describe_mutability(),
757 if union_type_name != "" {
759 "{} is a field of the union `{}`, so it overlaps the field {}",
760 msg_place, union_type_name, msg_borrow,
764 explanation.add_explanation_to_diagnostic(
771 Some((issued_span, span)),
774 self.suggest_using_local_if_applicable(
786 #[instrument(level = "debug", skip(self, err))]
787 fn suggest_using_local_if_applicable(
789 err: &mut DiagnosticBuilder<'_>,
791 (place, span): (Place<'tcx>, Span),
792 gen_borrow_kind: BorrowKind,
793 issued_borrow: &BorrowData<'tcx>,
794 explanation: BorrowExplanation,
797 matches!(explanation, BorrowExplanation::UsedLater(LaterUseKind::Call, _call_span, _));
799 debug!("not later used in call");
804 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
807 issued_borrow.reserve_location
809 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
811 let inner_param_location = location;
812 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
813 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
816 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
818 "`inner_param_location` {:?} is not for an assignment: {:?}",
819 inner_param_location, inner_param_stmt
823 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
824 let Some((inner_call_loc,inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
825 let Either::Right(term) = self.body.stmt_at(loc) else {
826 debug!("{:?} is a statement, so it can't be a call", loc);
829 let TerminatorKind::Call { args, .. } = &term.kind else {
830 debug!("not a call: {:?}", term);
833 debug!("checking call args for uses of inner_param: {:?}", args);
834 if args.contains(&Operand::Move(inner_param)) {
840 debug!("no uses of inner_param found as a by-move call arg");
843 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
845 let inner_call_span = inner_call_term.source_info.span;
846 let outer_call_span = outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span;
847 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
848 // FIXME: This stops the suggestion in some cases where it should be emitted.
849 // Fix the spans for those cases so it's emitted correctly.
851 "outer span {:?} does not strictly contain inner span {:?}",
852 outer_call_span, inner_call_span
856 err.span_help(inner_call_span, "try adding a local storing this argument...");
857 err.span_help(outer_call_span, "...and then using that local as the argument to this call");
860 fn suggest_split_at_mut_if_applicable(
862 err: &mut DiagnosticBuilder<'_>,
864 borrowed_place: Place<'tcx>,
866 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
867 (&place.projection[..], &borrowed_place.projection[..])
870 "consider using `.split_at_mut(position)` or similar method to obtain \
871 two mutable non-overlapping sub-slices",
876 /// Returns the description of the root place for a conflicting borrow and the full
877 /// descriptions of the places that caused the conflict.
879 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
880 /// attempted while a shared borrow is live, then this function will return:
884 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
885 /// a shared borrow of another field `x.y`, then this function will return:
887 /// ("x", "x.z", "x.y")
889 /// In the more complex union case, where the union is a field of a struct, then if a mutable
890 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
891 /// another field `x.u.y`, then this function will return:
893 /// ("x.u", "x.u.z", "x.u.y")
895 /// This is used when creating error messages like below:
898 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
899 /// mutable (via `a.u.s.b`) [E0502]
901 pub(crate) fn describe_place_for_conflicting_borrow(
903 first_borrowed_place: Place<'tcx>,
904 second_borrowed_place: Place<'tcx>,
905 ) -> (String, String, String, String) {
906 // Define a small closure that we can use to check if the type of a place
908 let union_ty = |place_base| {
909 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
910 // using a type annotation in the closure argument instead leads to a lifetime error.
911 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
912 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
915 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
916 // code duplication (particularly around returning an empty description in the failure
920 // If we have a conflicting borrow of the same place, then we don't want to add
921 // an extraneous "via x.y" to our diagnostics, so filter out this case.
922 first_borrowed_place != second_borrowed_place
925 // We're going to want to traverse the first borrowed place to see if we can find
926 // field access to a union. If we find that, then we will keep the place of the
927 // union being accessed and the field that was being accessed so we can check the
928 // second borrowed place for the same union and an access to a different field.
929 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
931 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
932 return Some((place_base, field));
939 .and_then(|(target_base, target_field)| {
940 // With the place of a union and a field access into it, we traverse the second
941 // borrowed place and look for an access to a different field of the same union.
942 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
943 if let ProjectionElem::Field(field, _) = elem {
944 if let Some(union_ty) = union_ty(place_base) {
945 if field != target_field && place_base == target_base {
947 self.describe_any_place(place_base),
948 self.describe_any_place(first_borrowed_place.as_ref()),
949 self.describe_any_place(second_borrowed_place.as_ref()),
950 union_ty.to_string(),
959 // If we didn't find a field access into a union, or both places match, then
960 // only return the description of the first place.
962 self.describe_any_place(first_borrowed_place.as_ref()),
970 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
972 /// This means that some data referenced by `borrow` needs to live
973 /// past the point where the StorageDeadOrDrop of `place` occurs.
974 /// This is usually interpreted as meaning that `place` has too
975 /// short a lifetime. (But sometimes it is more useful to report
976 /// it as a more direct conflict between the execution of a
977 /// `Drop::drop` with an aliasing borrow.)
978 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
981 borrow: &BorrowData<'tcx>,
982 place_span: (Place<'tcx>, Span),
983 kind: Option<WriteKind>,
986 "report_borrowed_value_does_not_live_long_enough(\
987 {:?}, {:?}, {:?}, {:?}\
989 location, borrow, place_span, kind
992 let drop_span = place_span.1;
994 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
996 let borrow_spans = self.retrieve_borrow_spans(borrow);
997 let borrow_span = borrow_spans.var_or_use_path_span();
999 assert!(root_place.projection.is_empty());
1000 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1002 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1004 if self.access_place_error_reported.contains(&(
1005 Place { local: root_place.local, projection: root_place_projection },
1009 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1015 self.access_place_error_reported.insert((
1016 Place { local: root_place.local, projection: root_place_projection },
1020 let borrowed_local = borrow.borrowed_place.local;
1021 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1023 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1024 err.buffer(&mut self.errors_buffer);
1028 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1029 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1031 // If a borrow of path `B` conflicts with drop of `D` (and
1032 // we're not in the uninteresting case where `B` is a
1033 // prefix of `D`), then report this as a more interesting
1034 // destructor conflict.
1035 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1036 self.report_borrow_conflicts_with_destructor(
1037 location, borrow, place_span, kind, dropped_ty,
1043 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1045 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1046 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1049 "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
1050 place_desc, explanation
1052 let err = match (place_desc, explanation) {
1053 // If the outlives constraint comes from inside the closure,
1058 // Box::new(|| y) as Box<Fn() -> &'static i32>
1060 // then just use the normal error. The closure isn't escaping
1061 // and `move` will not help here.
1064 BorrowExplanation::MustBeValidFor {
1066 category @ (ConstraintCategory::Return(_)
1067 | ConstraintCategory::CallArgument
1068 | ConstraintCategory::OpaqueType),
1069 from_closure: false,
1074 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1075 .report_escaping_closure_capture(
1081 &format!("`{}`", name),
1085 BorrowExplanation::MustBeValidFor {
1086 category: ConstraintCategory::Assignment,
1087 from_closure: false,
1091 RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
1097 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1098 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1106 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1116 err.buffer(&mut self.errors_buffer);
1119 fn report_local_value_does_not_live_long_enough(
1123 borrow: &BorrowData<'tcx>,
1125 borrow_spans: UseSpans<'tcx>,
1126 explanation: BorrowExplanation,
1127 ) -> DiagnosticBuilder<'cx> {
1129 "report_local_value_does_not_live_long_enough(\
1130 {:?}, {:?}, {:?}, {:?}, {:?}\
1132 location, name, borrow, drop_span, borrow_spans
1135 let borrow_span = borrow_spans.var_or_use_path_span();
1136 if let BorrowExplanation::MustBeValidFor {
1140 from_closure: false,
1144 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1149 opt_place_desc.as_ref(),
1155 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1157 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1158 let region_name = annotation.emit(self, &mut err);
1162 format!("`{}` would have to be valid for `{}`...", name, region_name),
1165 let fn_hir_id = self.mir_hir_id();
1169 "...but `{}` will be dropped here, when the {} returns",
1174 .opt_name(fn_hir_id)
1175 .map(|name| format!("function `{}`", name))
1176 .unwrap_or_else(|| {
1180 .typeck(self.mir_def_id())
1181 .node_type(fn_hir_id)
1184 ty::Closure(..) => "enclosing closure",
1185 ty::Generator(..) => "enclosing generator",
1186 kind => bug!("expected closure or generator, found {:?}", kind),
1194 "functions cannot return a borrow to data owned within the function's scope, \
1195 functions can only return borrows to data passed as arguments",
1198 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1199 references-and-borrowing.html#dangling-references>",
1202 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1204 explanation.add_explanation_to_diagnostic(
1215 err.span_label(borrow_span, "borrowed value does not live long enough");
1216 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1218 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1220 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1222 explanation.add_explanation_to_diagnostic(
1236 fn report_borrow_conflicts_with_destructor(
1239 borrow: &BorrowData<'tcx>,
1240 (place, drop_span): (Place<'tcx>, Span),
1241 kind: Option<WriteKind>,
1242 dropped_ty: Ty<'tcx>,
1245 "report_borrow_conflicts_with_destructor(\
1246 {:?}, {:?}, ({:?}, {:?}), {:?}\
1248 location, borrow, place, drop_span, kind,
1251 let borrow_spans = self.retrieve_borrow_spans(borrow);
1252 let borrow_span = borrow_spans.var_or_use();
1254 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1256 let what_was_dropped = match self.describe_place(place.as_ref()) {
1257 Some(name) => format!("`{}`", name),
1258 None => String::from("temporary value"),
1261 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1262 Some(borrowed) => format!(
1263 "here, drop of {D} needs exclusive access to `{B}`, \
1264 because the type `{T}` implements the `Drop` trait",
1265 D = what_was_dropped,
1270 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1271 D = what_was_dropped,
1275 err.span_label(drop_span, label);
1277 // Only give this note and suggestion if they could be relevant.
1279 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1281 BorrowExplanation::UsedLater { .. }
1282 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1283 err.note("consider using a `let` binding to create a longer lived value");
1288 explanation.add_explanation_to_diagnostic(
1298 err.buffer(&mut self.errors_buffer);
1301 fn report_thread_local_value_does_not_live_long_enough(
1305 ) -> DiagnosticBuilder<'cx> {
1307 "report_thread_local_value_does_not_live_long_enough(\
1310 drop_span, borrow_span
1313 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1317 "thread-local variables cannot be borrowed beyond the end of the function",
1319 err.span_label(drop_span, "end of enclosing function is here");
1324 fn report_temporary_value_does_not_live_long_enough(
1327 borrow: &BorrowData<'tcx>,
1329 borrow_spans: UseSpans<'tcx>,
1331 explanation: BorrowExplanation,
1332 ) -> DiagnosticBuilder<'cx> {
1334 "report_temporary_value_does_not_live_long_enough(\
1335 {:?}, {:?}, {:?}, {:?}\
1337 location, borrow, drop_span, proper_span
1340 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1343 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1354 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1355 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1356 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1359 BorrowExplanation::UsedLater(..)
1360 | BorrowExplanation::UsedLaterInLoop(..)
1361 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1362 // Only give this note and suggestion if it could be relevant.
1363 err.note("consider using a `let` binding to create a longer lived value");
1367 explanation.add_explanation_to_diagnostic(
1377 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1379 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1384 fn try_report_cannot_return_reference_to_local(
1386 borrow: &BorrowData<'tcx>,
1389 category: ConstraintCategory,
1390 opt_place_desc: Option<&String>,
1391 ) -> Option<DiagnosticBuilder<'cx>> {
1392 let return_kind = match category {
1393 ConstraintCategory::Return(_) => "return",
1394 ConstraintCategory::Yield => "yield",
1398 // FIXME use a better heuristic than Spans
1399 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1405 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1406 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1407 match self.body.local_kind(local) {
1408 LocalKind::ReturnPointer | LocalKind::Temp => {
1409 bug!("temporary or return pointer with a name")
1411 LocalKind::Var => "local variable ",
1413 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1415 "variable captured by `move` "
1417 LocalKind::Arg => "function parameter ",
1423 format!("{}`{}`", local_kind, place_desc),
1424 format!("`{}` is borrowed here", place_desc),
1428 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1429 let local = root_place.local;
1430 match self.body.local_kind(local) {
1431 LocalKind::ReturnPointer | LocalKind::Temp => {
1432 ("temporary value".to_string(), "temporary value created here".to_string())
1435 "function parameter".to_string(),
1436 "function parameter borrowed here".to_string(),
1439 ("local binding".to_string(), "local binding introduced here".to_string())
1444 let mut err = self.cannot_return_reference_to_local(
1451 if return_span != borrow_span {
1452 err.span_label(borrow_span, note);
1454 let tcx = self.infcx.tcx;
1455 let ty_params = ty::List::empty();
1457 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1458 let return_ty = tcx.erase_regions(return_ty);
1461 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1464 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1465 .must_apply_modulo_regions()
1467 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1468 err.span_suggestion_hidden(
1470 "use `.collect()` to allocate the iterator",
1471 format!("{}{}", snippet, ".collect::<Vec<_>>()"),
1472 Applicability::MaybeIncorrect,
1482 fn report_escaping_closure_capture(
1484 use_span: UseSpans<'tcx>,
1486 fr_name: &RegionName,
1487 category: ConstraintCategory,
1488 constraint_span: Span,
1490 ) -> DiagnosticBuilder<'cx> {
1491 let tcx = self.infcx.tcx;
1492 let args_span = use_span.args_or_use();
1494 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1496 if string.starts_with("async ") {
1497 let pos = args_span.lo() + BytePos(6);
1498 (args_span.with_lo(pos).with_hi(pos), "move ".to_string())
1499 } else if string.starts_with("async|") {
1500 let pos = args_span.lo() + BytePos(5);
1501 (args_span.with_lo(pos).with_hi(pos), " move".to_string())
1503 (args_span.shrink_to_lo(), "move ".to_string())
1506 Err(_) => (args_span, "move |<args>| <body>".to_string()),
1508 let kind = match use_span.generator_kind() {
1509 Some(generator_kind) => match generator_kind {
1510 GeneratorKind::Async(async_kind) => match async_kind {
1511 AsyncGeneratorKind::Block => "async block",
1512 AsyncGeneratorKind::Closure => "async closure",
1513 _ => bug!("async block/closure expected, but async function found."),
1515 GeneratorKind::Gen => "generator",
1521 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1522 err.span_suggestion_verbose(
1525 "to force the {} to take ownership of {} (and any \
1526 other referenced variables), use the `move` keyword",
1530 Applicability::MachineApplicable,
1534 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1535 let msg = format!("{} is returned here", kind);
1536 err.span_note(constraint_span, &msg);
1538 ConstraintCategory::CallArgument => {
1539 fr_name.highlight_region_name(&mut err);
1540 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1542 "async blocks are not executed immediately and must either take a \
1543 reference or ownership of outside variables they use",
1546 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1547 err.span_note(constraint_span, &msg);
1551 "report_escaping_closure_capture called with unexpected constraint \
1560 fn report_escaping_data(
1563 name: &Option<String>,
1567 ) -> DiagnosticBuilder<'cx> {
1568 let tcx = self.infcx.tcx;
1570 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1573 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1577 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1580 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1582 if let Some(name) = name {
1585 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1590 format!("reference escapes the {} body here", escapes_from),
1597 fn get_moved_indexes(
1601 ) -> (Vec<MoveSite>, Vec<Location>) {
1602 fn predecessor_locations<'a>(
1603 body: &'a mir::Body<'_>,
1605 ) -> impl Iterator<Item = Location> + 'a {
1606 if location.statement_index == 0 {
1607 let predecessors = body.predecessors()[location.block].to_vec();
1608 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1610 Either::Right(std::iter::once(Location {
1611 statement_index: location.statement_index - 1,
1617 let mut mpis = vec![mpi];
1618 let move_paths = &self.move_data.move_paths;
1619 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1621 let mut stack = Vec::new();
1622 let mut back_edge_stack = Vec::new();
1624 predecessor_locations(self.body, location).for_each(|predecessor| {
1625 if location.dominates(predecessor, &self.dominators) {
1626 back_edge_stack.push(predecessor)
1628 stack.push(predecessor);
1632 let mut reached_start = false;
1634 /* Check if the mpi is initialized as an argument */
1635 let mut is_argument = false;
1636 for arg in self.body.args_iter() {
1637 let path = self.move_data.rev_lookup.find_local(arg);
1638 if mpis.contains(&path) {
1643 let mut visited = FxHashSet::default();
1644 let mut move_locations = FxHashSet::default();
1645 let mut reinits = vec![];
1646 let mut result = vec![];
1648 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1650 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1651 location, is_back_edge
1654 if !visited.insert(location) {
1660 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1661 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1662 // this analysis only tries to find moves explicitly
1663 // written by the user, so we ignore the move-outs
1664 // created by `StorageDead` and at the beginning
1667 // If we are found a use of a.b.c which was in error, then we want to look for
1668 // moves not only of a.b.c but also a.b and a.
1670 // Note that the moves data already includes "parent" paths, so we don't have to
1671 // worry about the other case: that is, if there is a move of a.b.c, it is already
1672 // marked as a move of a.b and a as well, so we will generate the correct errors
1674 for moi in &self.move_data.loc_map[location] {
1675 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1676 let path = self.move_data.moves[*moi].path;
1677 if mpis.contains(&path) {
1679 "report_use_of_moved_or_uninitialized: found {:?}",
1680 move_paths[path].place
1682 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1683 move_locations.insert(location);
1685 // Strictly speaking, we could continue our DFS here. There may be
1686 // other moves that can reach the point of error. But it is kind of
1687 // confusing to highlight them.
1695 // drop(a); // <-- current point of error
1698 // Because we stop the DFS here, we only highlight `let c = a`,
1699 // and not `let b = a`. We will of course also report an error at
1700 // `let c = a` which highlights `let b = a` as the move.
1707 let mut any_match = false;
1708 for ii in &self.move_data.init_loc_map[location] {
1709 let init = self.move_data.inits[*ii];
1711 InitKind::Deep | InitKind::NonPanicPathOnly => {
1712 if mpis.contains(&init.path) {
1716 InitKind::Shallow => {
1717 if mpi == init.path {
1724 reinits.push(location);
1730 while let Some(location) = stack.pop() {
1731 if dfs_iter(&mut result, location, false) {
1735 let mut has_predecessor = false;
1736 predecessor_locations(self.body, location).for_each(|predecessor| {
1737 if location.dominates(predecessor, &self.dominators) {
1738 back_edge_stack.push(predecessor)
1740 stack.push(predecessor);
1742 has_predecessor = true;
1745 if !has_predecessor {
1746 reached_start = true;
1749 if (is_argument || !reached_start) && result.is_empty() {
1750 /* Process back edges (moves in future loop iterations) only if
1751 the move path is definitely initialized upon loop entry,
1752 to avoid spurious "in previous iteration" errors.
1753 During DFS, if there's a path from the error back to the start
1754 of the function with no intervening init or move, then the
1755 move path may be uninitialized at loop entry.
1757 while let Some(location) = back_edge_stack.pop() {
1758 if dfs_iter(&mut result, location, true) {
1762 predecessor_locations(self.body, location)
1763 .for_each(|predecessor| back_edge_stack.push(predecessor));
1767 // Check if we can reach these reinits from a move location.
1768 let reinits_reachable = reinits
1771 let mut visited = FxHashSet::default();
1772 let mut stack = vec![*reinit];
1773 while let Some(location) = stack.pop() {
1774 if !visited.insert(location) {
1777 if move_locations.contains(&location) {
1780 stack.extend(predecessor_locations(self.body, location));
1784 .collect::<Vec<Location>>();
1785 (result, reinits_reachable)
1788 pub(crate) fn report_illegal_mutation_of_borrowed(
1791 (place, span): (Place<'tcx>, Span),
1792 loan: &BorrowData<'tcx>,
1794 let loan_spans = self.retrieve_borrow_spans(loan);
1795 let loan_span = loan_spans.args_or_use();
1797 let descr_place = self.describe_any_place(place.as_ref());
1798 if loan.kind == BorrowKind::Shallow {
1799 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1800 let mut err = self.cannot_mutate_in_immutable_section(
1807 loan_spans.var_span_label(
1809 format!("borrow occurs due to use{}", loan_spans.describe()),
1810 loan.kind.describe_mutability(),
1813 err.buffer(&mut self.errors_buffer);
1819 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
1821 loan_spans.var_span_label(
1823 format!("borrow occurs due to use{}", loan_spans.describe()),
1824 loan.kind.describe_mutability(),
1827 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
1837 self.explain_deref_coercion(loan, &mut err);
1839 err.buffer(&mut self.errors_buffer);
1842 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut DiagnosticBuilder<'_>) {
1843 let tcx = self.infcx.tcx;
1845 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
1846 Some((method_did, method_substs)),
1848 &self.body[loan.reserve_location.block].terminator,
1849 rustc_const_eval::util::find_self_call(
1852 loan.assigned_place.local,
1853 loan.reserve_location.block,
1856 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
1858 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
1859 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
1862 if let Some(Ok(instance)) = deref_target {
1863 let deref_target_ty = instance.ty(tcx, self.param_env);
1865 "borrow occurs due to deref coercion to `{}`",
1868 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
1874 /// Reports an illegal reassignment; for example, an assignment to
1875 /// (part of) a non-`mut` local that occurs potentially after that
1876 /// local has already been initialized. `place` is the path being
1877 /// assigned; `err_place` is a place providing a reason why
1878 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
1879 /// assignment to `x.f`).
1880 pub(crate) fn report_illegal_reassignment(
1882 _location: Location,
1883 (place, span): (Place<'tcx>, Span),
1884 assigned_span: Span,
1885 err_place: Place<'tcx>,
1887 let (from_arg, local_decl, local_name) = match err_place.as_local() {
1889 self.body.local_kind(local) == LocalKind::Arg,
1890 Some(&self.body.local_decls[local]),
1891 self.local_names[local],
1893 None => (false, None, None),
1896 // If root local is initialized immediately (everything apart from let
1897 // PATTERN;) then make the error refer to that local, rather than the
1898 // place being assigned later.
1899 let (place_description, assigned_span) = match local_decl {
1902 Some(box LocalInfo::User(
1903 ClearCrossCrate::Clear
1904 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
1905 opt_match_place: None,
1909 | Some(box LocalInfo::StaticRef { .. })
1913 | None => (self.describe_any_place(place.as_ref()), assigned_span),
1914 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
1917 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
1918 let msg = if from_arg {
1919 "cannot assign to immutable argument"
1921 "cannot assign twice to immutable variable"
1923 if span != assigned_span {
1925 err.span_label(assigned_span, format!("first assignment to {}", place_description));
1928 if let Some(decl) = local_decl {
1929 if let Some(name) = local_name {
1930 if decl.can_be_made_mutable() {
1931 err.span_suggestion(
1932 decl.source_info.span,
1933 "consider making this binding mutable",
1934 format!("mut {}", name),
1935 Applicability::MachineApplicable,
1940 err.span_label(span, msg);
1941 err.buffer(&mut self.errors_buffer);
1944 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
1945 let tcx = self.infcx.tcx;
1946 match place.last_projection() {
1947 None => StorageDeadOrDrop::LocalStorageDead,
1948 Some((place_base, elem)) => {
1949 // FIXME(spastorino) make this iterate
1950 let base_access = self.classify_drop_access_kind(place_base);
1952 ProjectionElem::Deref => match base_access {
1953 StorageDeadOrDrop::LocalStorageDead
1954 | StorageDeadOrDrop::BoxedStorageDead => {
1956 place_base.ty(self.body, tcx).ty.is_box(),
1957 "Drop of value behind a reference or raw pointer"
1959 StorageDeadOrDrop::BoxedStorageDead
1961 StorageDeadOrDrop::Destructor(_) => base_access,
1963 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
1964 let base_ty = place_base.ty(self.body, tcx).ty;
1965 match base_ty.kind() {
1966 ty::Adt(def, _) if def.has_dtor(tcx) => {
1967 // Report the outermost adt with a destructor
1969 StorageDeadOrDrop::Destructor(_) => base_access,
1970 StorageDeadOrDrop::LocalStorageDead
1971 | StorageDeadOrDrop::BoxedStorageDead => {
1972 StorageDeadOrDrop::Destructor(base_ty)
1979 ProjectionElem::ConstantIndex { .. }
1980 | ProjectionElem::Subslice { .. }
1981 | ProjectionElem::Index(_) => base_access,
1987 /// Describe the reason for the fake borrow that was assigned to `place`.
1988 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
1989 use rustc_middle::mir::visit::Visitor;
1990 struct FakeReadCauseFinder<'tcx> {
1992 cause: Option<FakeReadCause>,
1994 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
1995 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
1997 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
1998 if *place == self.place =>
2000 self.cause = Some(*cause);
2006 let mut visitor = FakeReadCauseFinder { place, cause: None };
2007 visitor.visit_body(&self.body);
2008 match visitor.cause {
2009 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2010 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2015 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2016 /// borrow of local value that does not live long enough.
2017 fn annotate_argument_and_return_for_borrow(
2019 borrow: &BorrowData<'tcx>,
2020 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2021 // Define a fallback for when we can't match a closure.
2023 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2027 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2029 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2030 self.mir_def_id().to_def_id(),
2031 self.infcx.tcx.fn_sig(self.mir_def_id()),
2038 // In order to determine whether we need to annotate, we need to check whether the reserve
2039 // place was an assignment into a temporary.
2041 // If it was, we check whether or not that temporary is eventually assigned into the return
2042 // place. If it was, we can add annotations about the function's return type and arguments
2043 // and it'll make sense.
2044 let location = borrow.reserve_location;
2045 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2046 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2047 &self.body[location.block].statements.get(location.statement_index)
2049 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2050 // Check that the initial assignment of the reserve location is into a temporary.
2051 let mut target = match reservation.as_local() {
2052 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2056 // Next, look through the rest of the block, checking if we are assigning the
2057 // `target` (that is, the place that contains our borrow) to anything.
2058 let mut annotated_closure = None;
2059 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2061 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2064 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2065 if let Some(assigned_to) = place.as_local() {
2067 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2071 // Check if our `target` was captured by a closure.
2072 if let Rvalue::Aggregate(
2073 box AggregateKind::Closure(def_id, substs),
2077 for operand in operands {
2078 let assigned_from = match operand {
2079 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2085 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2089 // Find the local from the operand.
2090 let assigned_from_local = match assigned_from.local_or_deref_local()
2092 Some(local) => local,
2096 if assigned_from_local != target {
2100 // If a closure captured our `target` and then assigned
2101 // into a place then we should annotate the closure in
2102 // case it ends up being assigned into the return place.
2104 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2106 "annotate_argument_and_return_for_borrow: \
2107 annotated_closure={:?} assigned_from_local={:?} \
2109 annotated_closure, assigned_from_local, assigned_to
2112 if assigned_to == mir::RETURN_PLACE {
2113 // If it was assigned directly into the return place, then
2115 return annotated_closure;
2117 // Otherwise, update the target.
2118 target = assigned_to;
2122 // If none of our closure's operands matched, then skip to the next
2127 // Otherwise, look at other types of assignment.
2128 let assigned_from = match rvalue {
2129 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2130 Rvalue::Use(operand) => match operand {
2131 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2139 "annotate_argument_and_return_for_borrow: \
2140 assigned_from={:?}",
2144 // Find the local from the rvalue.
2145 let assigned_from_local = match assigned_from.local_or_deref_local() {
2146 Some(local) => local,
2150 "annotate_argument_and_return_for_borrow: \
2151 assigned_from_local={:?}",
2152 assigned_from_local,
2155 // Check if our local matches the target - if so, we've assigned our
2156 // borrow to a new place.
2157 if assigned_from_local != target {
2161 // If we assigned our `target` into a new place, then we should
2162 // check if it was the return place.
2164 "annotate_argument_and_return_for_borrow: \
2165 assigned_from_local={:?} assigned_to={:?}",
2166 assigned_from_local, assigned_to
2168 if assigned_to == mir::RETURN_PLACE {
2169 // If it was then return the annotated closure if there was one,
2170 // else, annotate this function.
2171 return annotated_closure.or_else(fallback);
2174 // If we didn't assign into the return place, then we just update
2176 target = assigned_to;
2181 // Check the terminator if we didn't find anything in the statements.
2182 let terminator = &self.body[location.block].terminator();
2184 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2187 if let TerminatorKind::Call { destination: Some((place, _)), args, .. } =
2190 if let Some(assigned_to) = place.as_local() {
2192 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2195 for operand in args {
2196 let assigned_from = match operand {
2197 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2203 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2207 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2209 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2210 assigned_from_local,
2213 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2214 return annotated_closure.or_else(fallback);
2222 // If we haven't found an assignment into the return place, then we need not add
2224 debug!("annotate_argument_and_return_for_borrow: none found");
2228 /// Annotate the first argument and return type of a function signature if they are
2233 sig: ty::PolyFnSig<'tcx>,
2234 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2235 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2236 let is_closure = self.infcx.tcx.is_closure(did);
2237 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?);
2238 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2240 // We need to work out which arguments to highlight. We do this by looking
2241 // at the return type, where there are three cases:
2243 // 1. If there are named arguments, then we should highlight the return type and
2244 // highlight any of the arguments that are also references with that lifetime.
2245 // If there are no arguments that have the same lifetime as the return type,
2246 // then don't highlight anything.
2247 // 2. The return type is a reference with an anonymous lifetime. If this is
2248 // the case, then we can take advantage of (and teach) the lifetime elision
2251 // We know that an error is being reported. So the arguments and return type
2252 // must satisfy the elision rules. Therefore, if there is a single argument
2253 // then that means the return type and first (and only) argument have the same
2254 // lifetime and the borrow isn't meeting that, we can highlight the argument
2257 // If there are multiple arguments then the first argument must be self (else
2258 // it would not satisfy the elision rules), so we can highlight self and the
2260 // 3. The return type is not a reference. In this case, we don't highlight
2262 let return_ty = sig.output();
2263 match return_ty.skip_binder().kind() {
2264 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2265 // This is case 1 from above, return type is a named reference so we need to
2266 // search for relevant arguments.
2267 let mut arguments = Vec::new();
2268 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2269 if let ty::Ref(argument_region, _, _) = argument.kind() {
2270 if argument_region == return_region {
2271 // Need to use the `rustc_middle::ty` types to compare against the
2272 // `return_region`. Then use the `rustc_hir` type to get only
2273 // the lifetime span.
2274 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2275 // With access to the lifetime, we can get
2277 arguments.push((*argument, lifetime.span));
2279 bug!("ty type is a ref but hir type is not");
2285 // We need to have arguments. This shouldn't happen, but it's worth checking.
2286 if arguments.is_empty() {
2290 // We use a mix of the HIR and the Ty types to get information
2291 // as the HIR doesn't have full types for closure arguments.
2292 let return_ty = sig.output().skip_binder();
2293 let mut return_span = fn_decl.output.span();
2294 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2295 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2296 return_span = lifetime.span;
2300 Some(AnnotatedBorrowFnSignature::NamedFunction {
2306 ty::Ref(_, _, _) if is_closure => {
2307 // This is case 2 from above but only for closures, return type is anonymous
2308 // reference so we select
2309 // the first argument.
2310 let argument_span = fn_decl.inputs.first()?.span;
2311 let argument_ty = sig.inputs().skip_binder().first()?;
2313 // Closure arguments are wrapped in a tuple, so we need to get the first
2315 if let ty::Tuple(elems) = argument_ty.kind() {
2316 let argument_ty = elems.first()?.expect_ty();
2317 if let ty::Ref(_, _, _) = argument_ty.kind() {
2318 return Some(AnnotatedBorrowFnSignature::Closure {
2327 ty::Ref(_, _, _) => {
2328 // This is also case 2 from above but for functions, return type is still an
2329 // anonymous reference so we select the first argument.
2330 let argument_span = fn_decl.inputs.first()?.span;
2331 let argument_ty = sig.inputs().skip_binder().first()?;
2333 let return_span = fn_decl.output.span();
2334 let return_ty = sig.output().skip_binder();
2336 // We expect the first argument to be a reference.
2337 match argument_ty.kind() {
2338 ty::Ref(_, _, _) => {}
2342 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2350 // This is case 3 from above, return type is not a reference so don't highlight
2359 enum AnnotatedBorrowFnSignature<'tcx> {
2361 arguments: Vec<(Ty<'tcx>, Span)>,
2362 return_ty: Ty<'tcx>,
2366 argument_ty: Ty<'tcx>,
2367 argument_span: Span,
2368 return_ty: Ty<'tcx>,
2372 argument_ty: Ty<'tcx>,
2373 argument_span: Span,
2377 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2378 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2382 cx: &mut MirBorrowckCtxt<'_, 'tcx>,
2383 diag: &mut DiagnosticBuilder<'_>,
2386 AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2389 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2392 cx.get_region_name_for_ty(argument_ty, 0)
2394 AnnotatedBorrowFnSignature::AnonymousFunction {
2400 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2401 diag.span_label(*argument_span, format!("has type `{}`", argument_ty_name));
2403 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2404 let types_equal = return_ty_name == argument_ty_name;
2409 if types_equal { "also " } else { "" },
2415 "argument and return type have the same lifetime due to lifetime elision rules",
2418 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2419 lifetime-syntax.html#lifetime-elision>",
2422 cx.get_region_name_for_ty(return_ty, 0)
2424 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2425 // Region of return type and arguments checked to be the same earlier.
2426 let region_name = cx.get_region_name_for_ty(return_ty, 0);
2427 for (_, argument_span) in arguments {
2428 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2431 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2434 "use data from the highlighted arguments which match the `{}` lifetime of \