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
413 .get_generics(tcx.typeck_root_def_id(self.mir_def_id().to_def_id()))
415 suggest_constraining_type_param(
419 ¶m.name.as_str(),
425 let span = if let Some(local) = place.as_local() {
426 let decl = &self.body.local_decls[local];
427 Some(decl.source_info.span)
431 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
434 if let UseSpans::FnSelfUse {
435 kind: FnSelfUseKind::DerefCoercion { deref_target, deref_target_ty },
440 "{} occurs due to deref coercion to `{}`",
441 desired_action.as_noun(),
445 // Check first whether the source is accessible (issue #87060)
446 if self.infcx.tcx.sess.source_map().span_to_snippet(deref_target).is_ok() {
447 err.span_note(deref_target, "deref defined here");
451 if let Some((_, mut old_err)) =
452 self.move_error_reported.insert(move_out_indices, (used_place, err))
454 // Cancel the old error so it doesn't ICE.
460 pub(crate) fn report_move_out_while_borrowed(
463 (place, span): (Place<'tcx>, Span),
464 borrow: &BorrowData<'tcx>,
467 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
468 location, place, span, borrow
470 let value_msg = self.describe_any_place(place.as_ref());
471 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
473 let borrow_spans = self.retrieve_borrow_spans(borrow);
474 let borrow_span = borrow_spans.args_or_use();
476 let move_spans = self.move_spans(place.as_ref(), location);
477 let span = move_spans.args_or_use();
480 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
481 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
482 err.span_label(span, format!("move out of {} occurs here", value_msg));
484 borrow_spans.var_span_label_path_only(
486 format!("borrow occurs due to use{}", borrow_spans.describe()),
489 move_spans.var_span_label(
491 format!("move occurs due to use{}", move_spans.describe()),
495 self.explain_why_borrow_contains_point(location, borrow, None)
496 .add_explanation_to_diagnostic(
505 err.buffer(&mut self.errors_buffer);
508 pub(crate) fn report_use_while_mutably_borrowed(
511 (place, _span): (Place<'tcx>, Span),
512 borrow: &BorrowData<'tcx>,
513 ) -> DiagnosticBuilder<'cx> {
514 let borrow_spans = self.retrieve_borrow_spans(borrow);
515 let borrow_span = borrow_spans.args_or_use();
517 // Conflicting borrows are reported separately, so only check for move
519 let use_spans = self.move_spans(place.as_ref(), location);
520 let span = use_spans.var_or_use();
522 // 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
523 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
524 let mut err = self.cannot_use_when_mutably_borrowed(
526 &self.describe_any_place(place.as_ref()),
528 &self.describe_any_place(borrow.borrowed_place.as_ref()),
531 borrow_spans.var_span_label(
534 let place = &borrow.borrowed_place;
535 let desc_place = self.describe_any_place(place.as_ref());
536 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
541 self.explain_why_borrow_contains_point(location, borrow, None)
542 .add_explanation_to_diagnostic(
554 pub(crate) fn report_conflicting_borrow(
557 (place, span): (Place<'tcx>, Span),
558 gen_borrow_kind: BorrowKind,
559 issued_borrow: &BorrowData<'tcx>,
560 ) -> DiagnosticBuilder<'cx> {
561 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
562 let issued_span = issued_spans.args_or_use();
564 let borrow_spans = self.borrow_spans(span, location);
565 let span = borrow_spans.args_or_use();
567 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
573 let (desc_place, msg_place, msg_borrow, union_type_name) =
574 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
576 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
577 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
579 // FIXME: supply non-"" `opt_via` when appropriate
580 let first_borrow_desc;
581 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
582 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
583 first_borrow_desc = "mutable ";
584 self.cannot_reborrow_already_borrowed(
596 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
597 first_borrow_desc = "immutable ";
598 self.cannot_reborrow_already_borrowed(
611 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
612 first_borrow_desc = "first ";
613 let mut err = self.cannot_mutably_borrow_multiply(
621 self.suggest_split_at_mut_if_applicable(
624 issued_borrow.borrowed_place,
629 (BorrowKind::Unique, BorrowKind::Unique) => {
630 first_borrow_desc = "first ";
631 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
634 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
635 if let Some(immutable_section_description) =
636 self.classify_immutable_section(issued_borrow.assigned_place)
638 let mut err = self.cannot_mutate_in_immutable_section(
642 immutable_section_description,
645 borrow_spans.var_span_label(
648 "borrow occurs due to use of {}{}",
650 borrow_spans.describe(),
657 first_borrow_desc = "immutable ";
658 self.cannot_reborrow_already_borrowed(
672 (BorrowKind::Unique, _) => {
673 first_borrow_desc = "first ";
674 self.cannot_uniquely_borrow_by_one_closure(
686 (BorrowKind::Shared, BorrowKind::Unique) => {
687 first_borrow_desc = "first ";
688 self.cannot_reborrow_already_uniquely_borrowed(
701 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
702 first_borrow_desc = "first ";
703 self.cannot_reborrow_already_uniquely_borrowed(
716 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
719 BorrowKind::Mut { .. }
722 | BorrowKind::Shallow,
726 if issued_spans == borrow_spans {
727 borrow_spans.var_span_label(
729 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
730 gen_borrow_kind.describe_mutability(),
733 let borrow_place = &issued_borrow.borrowed_place;
734 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
735 issued_spans.var_span_label(
738 "first borrow occurs due to use of {}{}",
740 issued_spans.describe(),
742 issued_borrow.kind.describe_mutability(),
745 borrow_spans.var_span_label(
748 "second borrow occurs due to use of {}{}",
750 borrow_spans.describe(),
752 gen_borrow_kind.describe_mutability(),
756 if union_type_name != "" {
758 "{} is a field of the union `{}`, so it overlaps the field {}",
759 msg_place, union_type_name, msg_borrow,
763 explanation.add_explanation_to_diagnostic(
770 Some((issued_span, span)),
773 self.suggest_using_local_if_applicable(
785 #[instrument(level = "debug", skip(self, err))]
786 fn suggest_using_local_if_applicable(
788 err: &mut DiagnosticBuilder<'_>,
790 (place, span): (Place<'tcx>, Span),
791 gen_borrow_kind: BorrowKind,
792 issued_borrow: &BorrowData<'tcx>,
793 explanation: BorrowExplanation,
796 matches!(explanation, BorrowExplanation::UsedLater(LaterUseKind::Call, _call_span, _));
798 debug!("not later used in call");
803 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
806 issued_borrow.reserve_location
808 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
810 let inner_param_location = location;
811 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
812 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
815 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
817 "`inner_param_location` {:?} is not for an assignment: {:?}",
818 inner_param_location, inner_param_stmt
822 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
823 let Some((inner_call_loc,inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
824 let Either::Right(term) = self.body.stmt_at(loc) else {
825 debug!("{:?} is a statement, so it can't be a call", loc);
828 let TerminatorKind::Call { args, .. } = &term.kind else {
829 debug!("not a call: {:?}", term);
832 debug!("checking call args for uses of inner_param: {:?}", args);
833 if args.contains(&Operand::Move(inner_param)) {
839 debug!("no uses of inner_param found as a by-move call arg");
842 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
844 let inner_call_span = inner_call_term.source_info.span;
845 let outer_call_span = outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span;
846 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
847 // FIXME: This stops the suggestion in some cases where it should be emitted.
848 // Fix the spans for those cases so it's emitted correctly.
850 "outer span {:?} does not strictly contain inner span {:?}",
851 outer_call_span, inner_call_span
855 err.span_help(inner_call_span, "try adding a local storing this argument...");
856 err.span_help(outer_call_span, "...and then using that local as the argument to this call");
859 fn suggest_split_at_mut_if_applicable(
861 err: &mut DiagnosticBuilder<'_>,
863 borrowed_place: Place<'tcx>,
865 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
866 (&place.projection[..], &borrowed_place.projection[..])
869 "consider using `.split_at_mut(position)` or similar method to obtain \
870 two mutable non-overlapping sub-slices",
875 /// Returns the description of the root place for a conflicting borrow and the full
876 /// descriptions of the places that caused the conflict.
878 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
879 /// attempted while a shared borrow is live, then this function will return:
883 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
884 /// a shared borrow of another field `x.y`, then this function will return:
886 /// ("x", "x.z", "x.y")
888 /// In the more complex union case, where the union is a field of a struct, then if a mutable
889 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
890 /// another field `x.u.y`, then this function will return:
892 /// ("x.u", "x.u.z", "x.u.y")
894 /// This is used when creating error messages like below:
897 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
898 /// mutable (via `a.u.s.b`) [E0502]
900 pub(crate) fn describe_place_for_conflicting_borrow(
902 first_borrowed_place: Place<'tcx>,
903 second_borrowed_place: Place<'tcx>,
904 ) -> (String, String, String, String) {
905 // Define a small closure that we can use to check if the type of a place
907 let union_ty = |place_base| {
908 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
909 // using a type annotation in the closure argument instead leads to a lifetime error.
910 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
911 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
914 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
915 // code duplication (particularly around returning an empty description in the failure
919 // If we have a conflicting borrow of the same place, then we don't want to add
920 // an extraneous "via x.y" to our diagnostics, so filter out this case.
921 first_borrowed_place != second_borrowed_place
924 // We're going to want to traverse the first borrowed place to see if we can find
925 // field access to a union. If we find that, then we will keep the place of the
926 // union being accessed and the field that was being accessed so we can check the
927 // second borrowed place for the same union and an access to a different field.
928 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
930 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
931 return Some((place_base, field));
938 .and_then(|(target_base, target_field)| {
939 // With the place of a union and a field access into it, we traverse the second
940 // borrowed place and look for an access to a different field of the same union.
941 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
942 if let ProjectionElem::Field(field, _) = elem {
943 if let Some(union_ty) = union_ty(place_base) {
944 if field != target_field && place_base == target_base {
946 self.describe_any_place(place_base),
947 self.describe_any_place(first_borrowed_place.as_ref()),
948 self.describe_any_place(second_borrowed_place.as_ref()),
949 union_ty.to_string(),
958 // If we didn't find a field access into a union, or both places match, then
959 // only return the description of the first place.
961 self.describe_any_place(first_borrowed_place.as_ref()),
969 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
971 /// This means that some data referenced by `borrow` needs to live
972 /// past the point where the StorageDeadOrDrop of `place` occurs.
973 /// This is usually interpreted as meaning that `place` has too
974 /// short a lifetime. (But sometimes it is more useful to report
975 /// it as a more direct conflict between the execution of a
976 /// `Drop::drop` with an aliasing borrow.)
977 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
980 borrow: &BorrowData<'tcx>,
981 place_span: (Place<'tcx>, Span),
982 kind: Option<WriteKind>,
985 "report_borrowed_value_does_not_live_long_enough(\
986 {:?}, {:?}, {:?}, {:?}\
988 location, borrow, place_span, kind
991 let drop_span = place_span.1;
993 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
995 let borrow_spans = self.retrieve_borrow_spans(borrow);
996 let borrow_span = borrow_spans.var_or_use_path_span();
998 assert!(root_place.projection.is_empty());
999 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1001 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1003 if self.access_place_error_reported.contains(&(
1004 Place { local: root_place.local, projection: root_place_projection },
1008 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1014 self.access_place_error_reported.insert((
1015 Place { local: root_place.local, projection: root_place_projection },
1019 let borrowed_local = borrow.borrowed_place.local;
1020 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1022 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1023 err.buffer(&mut self.errors_buffer);
1027 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1028 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1030 // If a borrow of path `B` conflicts with drop of `D` (and
1031 // we're not in the uninteresting case where `B` is a
1032 // prefix of `D`), then report this as a more interesting
1033 // destructor conflict.
1034 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1035 self.report_borrow_conflicts_with_destructor(
1036 location, borrow, place_span, kind, dropped_ty,
1042 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1044 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1045 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1048 "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
1049 place_desc, explanation
1051 let err = match (place_desc, explanation) {
1052 // If the outlives constraint comes from inside the closure,
1057 // Box::new(|| y) as Box<Fn() -> &'static i32>
1059 // then just use the normal error. The closure isn't escaping
1060 // and `move` will not help here.
1063 BorrowExplanation::MustBeValidFor {
1065 category @ (ConstraintCategory::Return(_)
1066 | ConstraintCategory::CallArgument
1067 | ConstraintCategory::OpaqueType),
1068 from_closure: false,
1073 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1074 .report_escaping_closure_capture(
1080 &format!("`{}`", name),
1084 BorrowExplanation::MustBeValidFor {
1085 category: ConstraintCategory::Assignment,
1086 from_closure: false,
1090 RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
1096 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1097 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1105 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1115 err.buffer(&mut self.errors_buffer);
1118 fn report_local_value_does_not_live_long_enough(
1122 borrow: &BorrowData<'tcx>,
1124 borrow_spans: UseSpans<'tcx>,
1125 explanation: BorrowExplanation,
1126 ) -> DiagnosticBuilder<'cx> {
1128 "report_local_value_does_not_live_long_enough(\
1129 {:?}, {:?}, {:?}, {:?}, {:?}\
1131 location, name, borrow, drop_span, borrow_spans
1134 let borrow_span = borrow_spans.var_or_use_path_span();
1135 if let BorrowExplanation::MustBeValidFor {
1139 from_closure: false,
1143 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1148 opt_place_desc.as_ref(),
1154 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1156 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1157 let region_name = annotation.emit(self, &mut err);
1161 format!("`{}` would have to be valid for `{}`...", name, region_name),
1164 let fn_hir_id = self.mir_hir_id();
1168 "...but `{}` will be dropped here, when the {} returns",
1173 .opt_name(fn_hir_id)
1174 .map(|name| format!("function `{}`", name))
1175 .unwrap_or_else(|| {
1179 .typeck(self.mir_def_id())
1180 .node_type(fn_hir_id)
1183 ty::Closure(..) => "enclosing closure",
1184 ty::Generator(..) => "enclosing generator",
1185 kind => bug!("expected closure or generator, found {:?}", kind),
1193 "functions cannot return a borrow to data owned within the function's scope, \
1194 functions can only return borrows to data passed as arguments",
1197 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1198 references-and-borrowing.html#dangling-references>",
1201 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1203 explanation.add_explanation_to_diagnostic(
1214 err.span_label(borrow_span, "borrowed value does not live long enough");
1215 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1217 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1219 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1221 explanation.add_explanation_to_diagnostic(
1235 fn report_borrow_conflicts_with_destructor(
1238 borrow: &BorrowData<'tcx>,
1239 (place, drop_span): (Place<'tcx>, Span),
1240 kind: Option<WriteKind>,
1241 dropped_ty: Ty<'tcx>,
1244 "report_borrow_conflicts_with_destructor(\
1245 {:?}, {:?}, ({:?}, {:?}), {:?}\
1247 location, borrow, place, drop_span, kind,
1250 let borrow_spans = self.retrieve_borrow_spans(borrow);
1251 let borrow_span = borrow_spans.var_or_use();
1253 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1255 let what_was_dropped = match self.describe_place(place.as_ref()) {
1256 Some(name) => format!("`{}`", name),
1257 None => String::from("temporary value"),
1260 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1261 Some(borrowed) => format!(
1262 "here, drop of {D} needs exclusive access to `{B}`, \
1263 because the type `{T}` implements the `Drop` trait",
1264 D = what_was_dropped,
1269 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1270 D = what_was_dropped,
1274 err.span_label(drop_span, label);
1276 // Only give this note and suggestion if they could be relevant.
1278 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1280 BorrowExplanation::UsedLater { .. }
1281 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1282 err.note("consider using a `let` binding to create a longer lived value");
1287 explanation.add_explanation_to_diagnostic(
1297 err.buffer(&mut self.errors_buffer);
1300 fn report_thread_local_value_does_not_live_long_enough(
1304 ) -> DiagnosticBuilder<'cx> {
1306 "report_thread_local_value_does_not_live_long_enough(\
1309 drop_span, borrow_span
1312 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1316 "thread-local variables cannot be borrowed beyond the end of the function",
1318 err.span_label(drop_span, "end of enclosing function is here");
1323 fn report_temporary_value_does_not_live_long_enough(
1326 borrow: &BorrowData<'tcx>,
1328 borrow_spans: UseSpans<'tcx>,
1330 explanation: BorrowExplanation,
1331 ) -> DiagnosticBuilder<'cx> {
1333 "report_temporary_value_does_not_live_long_enough(\
1334 {:?}, {:?}, {:?}, {:?}\
1336 location, borrow, drop_span, proper_span
1339 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1342 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1353 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1354 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1355 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1358 BorrowExplanation::UsedLater(..)
1359 | BorrowExplanation::UsedLaterInLoop(..)
1360 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1361 // Only give this note and suggestion if it could be relevant.
1362 err.note("consider using a `let` binding to create a longer lived value");
1366 explanation.add_explanation_to_diagnostic(
1376 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1378 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1383 fn try_report_cannot_return_reference_to_local(
1385 borrow: &BorrowData<'tcx>,
1388 category: ConstraintCategory,
1389 opt_place_desc: Option<&String>,
1390 ) -> Option<DiagnosticBuilder<'cx>> {
1391 let return_kind = match category {
1392 ConstraintCategory::Return(_) => "return",
1393 ConstraintCategory::Yield => "yield",
1397 // FIXME use a better heuristic than Spans
1398 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1404 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1405 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1406 match self.body.local_kind(local) {
1407 LocalKind::ReturnPointer | LocalKind::Temp => {
1408 bug!("temporary or return pointer with a name")
1410 LocalKind::Var => "local variable ",
1412 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1414 "variable captured by `move` "
1416 LocalKind::Arg => "function parameter ",
1422 format!("{}`{}`", local_kind, place_desc),
1423 format!("`{}` is borrowed here", place_desc),
1427 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1428 let local = root_place.local;
1429 match self.body.local_kind(local) {
1430 LocalKind::ReturnPointer | LocalKind::Temp => {
1431 ("temporary value".to_string(), "temporary value created here".to_string())
1434 "function parameter".to_string(),
1435 "function parameter borrowed here".to_string(),
1438 ("local binding".to_string(), "local binding introduced here".to_string())
1443 let mut err = self.cannot_return_reference_to_local(
1450 if return_span != borrow_span {
1451 err.span_label(borrow_span, note);
1453 let tcx = self.infcx.tcx;
1454 let ty_params = ty::List::empty();
1456 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1457 let return_ty = tcx.erase_regions(return_ty);
1460 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1463 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1464 .must_apply_modulo_regions()
1466 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1467 err.span_suggestion_hidden(
1469 "use `.collect()` to allocate the iterator",
1470 format!("{}{}", snippet, ".collect::<Vec<_>>()"),
1471 Applicability::MaybeIncorrect,
1481 fn report_escaping_closure_capture(
1483 use_span: UseSpans<'tcx>,
1485 fr_name: &RegionName,
1486 category: ConstraintCategory,
1487 constraint_span: Span,
1489 ) -> DiagnosticBuilder<'cx> {
1490 let tcx = self.infcx.tcx;
1491 let args_span = use_span.args_or_use();
1493 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1495 if string.starts_with("async ") {
1496 let pos = args_span.lo() + BytePos(6);
1497 (args_span.with_lo(pos).with_hi(pos), "move ".to_string())
1498 } else if string.starts_with("async|") {
1499 let pos = args_span.lo() + BytePos(5);
1500 (args_span.with_lo(pos).with_hi(pos), " move".to_string())
1502 (args_span.shrink_to_lo(), "move ".to_string())
1505 Err(_) => (args_span, "move |<args>| <body>".to_string()),
1507 let kind = match use_span.generator_kind() {
1508 Some(generator_kind) => match generator_kind {
1509 GeneratorKind::Async(async_kind) => match async_kind {
1510 AsyncGeneratorKind::Block => "async block",
1511 AsyncGeneratorKind::Closure => "async closure",
1512 _ => bug!("async block/closure expected, but async function found."),
1514 GeneratorKind::Gen => "generator",
1520 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1521 err.span_suggestion_verbose(
1524 "to force the {} to take ownership of {} (and any \
1525 other referenced variables), use the `move` keyword",
1529 Applicability::MachineApplicable,
1533 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1534 let msg = format!("{} is returned here", kind);
1535 err.span_note(constraint_span, &msg);
1537 ConstraintCategory::CallArgument => {
1538 fr_name.highlight_region_name(&mut err);
1539 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1541 "async blocks are not executed immediately and must either take a \
1542 reference or ownership of outside variables they use",
1545 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1546 err.span_note(constraint_span, &msg);
1550 "report_escaping_closure_capture called with unexpected constraint \
1559 fn report_escaping_data(
1562 name: &Option<String>,
1566 ) -> DiagnosticBuilder<'cx> {
1567 let tcx = self.infcx.tcx;
1569 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1572 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1576 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1579 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1581 if let Some(name) = name {
1584 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1589 format!("reference escapes the {} body here", escapes_from),
1596 fn get_moved_indexes(
1600 ) -> (Vec<MoveSite>, Vec<Location>) {
1601 fn predecessor_locations(
1602 body: &'a mir::Body<'tcx>,
1604 ) -> impl Iterator<Item = Location> + 'a {
1605 if location.statement_index == 0 {
1606 let predecessors = body.predecessors()[location.block].to_vec();
1607 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1609 Either::Right(std::iter::once(Location {
1610 statement_index: location.statement_index - 1,
1616 let mut mpis = vec![mpi];
1617 let move_paths = &self.move_data.move_paths;
1618 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1620 let mut stack = Vec::new();
1621 let mut back_edge_stack = Vec::new();
1623 predecessor_locations(self.body, location).for_each(|predecessor| {
1624 if location.dominates(predecessor, &self.dominators) {
1625 back_edge_stack.push(predecessor)
1627 stack.push(predecessor);
1631 let mut reached_start = false;
1633 /* Check if the mpi is initialized as an argument */
1634 let mut is_argument = false;
1635 for arg in self.body.args_iter() {
1636 let path = self.move_data.rev_lookup.find_local(arg);
1637 if mpis.contains(&path) {
1642 let mut visited = FxHashSet::default();
1643 let mut move_locations = FxHashSet::default();
1644 let mut reinits = vec![];
1645 let mut result = vec![];
1647 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1649 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1650 location, is_back_edge
1653 if !visited.insert(location) {
1659 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1660 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1661 // this analysis only tries to find moves explicitly
1662 // written by the user, so we ignore the move-outs
1663 // created by `StorageDead` and at the beginning
1666 // If we are found a use of a.b.c which was in error, then we want to look for
1667 // moves not only of a.b.c but also a.b and a.
1669 // Note that the moves data already includes "parent" paths, so we don't have to
1670 // worry about the other case: that is, if there is a move of a.b.c, it is already
1671 // marked as a move of a.b and a as well, so we will generate the correct errors
1673 for moi in &self.move_data.loc_map[location] {
1674 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1675 let path = self.move_data.moves[*moi].path;
1676 if mpis.contains(&path) {
1678 "report_use_of_moved_or_uninitialized: found {:?}",
1679 move_paths[path].place
1681 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1682 move_locations.insert(location);
1684 // Strictly speaking, we could continue our DFS here. There may be
1685 // other moves that can reach the point of error. But it is kind of
1686 // confusing to highlight them.
1694 // drop(a); // <-- current point of error
1697 // Because we stop the DFS here, we only highlight `let c = a`,
1698 // and not `let b = a`. We will of course also report an error at
1699 // `let c = a` which highlights `let b = a` as the move.
1706 let mut any_match = false;
1707 for ii in &self.move_data.init_loc_map[location] {
1708 let init = self.move_data.inits[*ii];
1710 InitKind::Deep | InitKind::NonPanicPathOnly => {
1711 if mpis.contains(&init.path) {
1715 InitKind::Shallow => {
1716 if mpi == init.path {
1723 reinits.push(location);
1729 while let Some(location) = stack.pop() {
1730 if dfs_iter(&mut result, location, false) {
1734 let mut has_predecessor = false;
1735 predecessor_locations(self.body, location).for_each(|predecessor| {
1736 if location.dominates(predecessor, &self.dominators) {
1737 back_edge_stack.push(predecessor)
1739 stack.push(predecessor);
1741 has_predecessor = true;
1744 if !has_predecessor {
1745 reached_start = true;
1748 if (is_argument || !reached_start) && result.is_empty() {
1749 /* Process back edges (moves in future loop iterations) only if
1750 the move path is definitely initialized upon loop entry,
1751 to avoid spurious "in previous iteration" errors.
1752 During DFS, if there's a path from the error back to the start
1753 of the function with no intervening init or move, then the
1754 move path may be uninitialized at loop entry.
1756 while let Some(location) = back_edge_stack.pop() {
1757 if dfs_iter(&mut result, location, true) {
1761 predecessor_locations(self.body, location)
1762 .for_each(|predecessor| back_edge_stack.push(predecessor));
1766 // Check if we can reach these reinits from a move location.
1767 let reinits_reachable = reinits
1770 let mut visited = FxHashSet::default();
1771 let mut stack = vec![*reinit];
1772 while let Some(location) = stack.pop() {
1773 if !visited.insert(location) {
1776 if move_locations.contains(&location) {
1779 stack.extend(predecessor_locations(self.body, location));
1783 .collect::<Vec<Location>>();
1784 (result, reinits_reachable)
1787 pub(crate) fn report_illegal_mutation_of_borrowed(
1790 (place, span): (Place<'tcx>, Span),
1791 loan: &BorrowData<'tcx>,
1793 let loan_spans = self.retrieve_borrow_spans(loan);
1794 let loan_span = loan_spans.args_or_use();
1796 let descr_place = self.describe_any_place(place.as_ref());
1797 if loan.kind == BorrowKind::Shallow {
1798 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1799 let mut err = self.cannot_mutate_in_immutable_section(
1806 loan_spans.var_span_label(
1808 format!("borrow occurs due to use{}", loan_spans.describe()),
1809 loan.kind.describe_mutability(),
1812 err.buffer(&mut self.errors_buffer);
1818 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
1820 loan_spans.var_span_label(
1822 format!("borrow occurs due to use{}", loan_spans.describe()),
1823 loan.kind.describe_mutability(),
1826 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
1836 self.explain_deref_coercion(loan, &mut err);
1838 err.buffer(&mut self.errors_buffer);
1841 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut DiagnosticBuilder<'_>) {
1842 let tcx = self.infcx.tcx;
1844 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
1845 Some((method_did, method_substs)),
1847 &self.body[loan.reserve_location.block].terminator,
1848 rustc_const_eval::util::find_self_call(
1851 loan.assigned_place.local,
1852 loan.reserve_location.block,
1855 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
1857 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
1858 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
1861 if let Some(Ok(instance)) = deref_target {
1862 let deref_target_ty = instance.ty(tcx, self.param_env);
1864 "borrow occurs due to deref coercion to `{}`",
1867 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
1873 /// Reports an illegal reassignment; for example, an assignment to
1874 /// (part of) a non-`mut` local that occurs potentially after that
1875 /// local has already been initialized. `place` is the path being
1876 /// assigned; `err_place` is a place providing a reason why
1877 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
1878 /// assignment to `x.f`).
1879 pub(crate) fn report_illegal_reassignment(
1881 _location: Location,
1882 (place, span): (Place<'tcx>, Span),
1883 assigned_span: Span,
1884 err_place: Place<'tcx>,
1886 let (from_arg, local_decl, local_name) = match err_place.as_local() {
1888 self.body.local_kind(local) == LocalKind::Arg,
1889 Some(&self.body.local_decls[local]),
1890 self.local_names[local],
1892 None => (false, None, None),
1895 // If root local is initialized immediately (everything apart from let
1896 // PATTERN;) then make the error refer to that local, rather than the
1897 // place being assigned later.
1898 let (place_description, assigned_span) = match local_decl {
1901 Some(box LocalInfo::User(
1902 ClearCrossCrate::Clear
1903 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
1904 opt_match_place: None,
1908 | Some(box LocalInfo::StaticRef { .. })
1912 | None => (self.describe_any_place(place.as_ref()), assigned_span),
1913 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
1916 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
1917 let msg = if from_arg {
1918 "cannot assign to immutable argument"
1920 "cannot assign twice to immutable variable"
1922 if span != assigned_span {
1924 err.span_label(assigned_span, format!("first assignment to {}", place_description));
1927 if let Some(decl) = local_decl {
1928 if let Some(name) = local_name {
1929 if decl.can_be_made_mutable() {
1930 err.span_suggestion(
1931 decl.source_info.span,
1932 "consider making this binding mutable",
1933 format!("mut {}", name),
1934 Applicability::MachineApplicable,
1939 err.span_label(span, msg);
1940 err.buffer(&mut self.errors_buffer);
1943 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
1944 let tcx = self.infcx.tcx;
1945 match place.last_projection() {
1946 None => StorageDeadOrDrop::LocalStorageDead,
1947 Some((place_base, elem)) => {
1948 // FIXME(spastorino) make this iterate
1949 let base_access = self.classify_drop_access_kind(place_base);
1951 ProjectionElem::Deref => match base_access {
1952 StorageDeadOrDrop::LocalStorageDead
1953 | StorageDeadOrDrop::BoxedStorageDead => {
1955 place_base.ty(self.body, tcx).ty.is_box(),
1956 "Drop of value behind a reference or raw pointer"
1958 StorageDeadOrDrop::BoxedStorageDead
1960 StorageDeadOrDrop::Destructor(_) => base_access,
1962 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
1963 let base_ty = place_base.ty(self.body, tcx).ty;
1964 match base_ty.kind() {
1965 ty::Adt(def, _) if def.has_dtor(tcx) => {
1966 // Report the outermost adt with a destructor
1968 StorageDeadOrDrop::Destructor(_) => base_access,
1969 StorageDeadOrDrop::LocalStorageDead
1970 | StorageDeadOrDrop::BoxedStorageDead => {
1971 StorageDeadOrDrop::Destructor(base_ty)
1978 ProjectionElem::ConstantIndex { .. }
1979 | ProjectionElem::Subslice { .. }
1980 | ProjectionElem::Index(_) => base_access,
1986 /// Describe the reason for the fake borrow that was assigned to `place`.
1987 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
1988 use rustc_middle::mir::visit::Visitor;
1989 struct FakeReadCauseFinder<'tcx> {
1991 cause: Option<FakeReadCause>,
1993 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
1994 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
1996 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
1997 if *place == self.place =>
1999 self.cause = Some(*cause);
2005 let mut visitor = FakeReadCauseFinder { place, cause: None };
2006 visitor.visit_body(&self.body);
2007 match visitor.cause {
2008 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2009 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2014 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2015 /// borrow of local value that does not live long enough.
2016 fn annotate_argument_and_return_for_borrow(
2018 borrow: &BorrowData<'tcx>,
2019 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2020 // Define a fallback for when we can't match a closure.
2022 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2026 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2028 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2029 self.mir_def_id().to_def_id(),
2030 self.infcx.tcx.fn_sig(self.mir_def_id()),
2037 // In order to determine whether we need to annotate, we need to check whether the reserve
2038 // place was an assignment into a temporary.
2040 // If it was, we check whether or not that temporary is eventually assigned into the return
2041 // place. If it was, we can add annotations about the function's return type and arguments
2042 // and it'll make sense.
2043 let location = borrow.reserve_location;
2044 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2045 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2046 &self.body[location.block].statements.get(location.statement_index)
2048 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2049 // Check that the initial assignment of the reserve location is into a temporary.
2050 let mut target = match reservation.as_local() {
2051 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2055 // Next, look through the rest of the block, checking if we are assigning the
2056 // `target` (that is, the place that contains our borrow) to anything.
2057 let mut annotated_closure = None;
2058 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2060 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2063 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2064 if let Some(assigned_to) = place.as_local() {
2066 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2070 // Check if our `target` was captured by a closure.
2071 if let Rvalue::Aggregate(
2072 box AggregateKind::Closure(def_id, substs),
2076 for operand in operands {
2077 let assigned_from = match operand {
2078 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2084 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2088 // Find the local from the operand.
2089 let assigned_from_local = match assigned_from.local_or_deref_local()
2091 Some(local) => local,
2095 if assigned_from_local != target {
2099 // If a closure captured our `target` and then assigned
2100 // into a place then we should annotate the closure in
2101 // case it ends up being assigned into the return place.
2103 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2105 "annotate_argument_and_return_for_borrow: \
2106 annotated_closure={:?} assigned_from_local={:?} \
2108 annotated_closure, assigned_from_local, assigned_to
2111 if assigned_to == mir::RETURN_PLACE {
2112 // If it was assigned directly into the return place, then
2114 return annotated_closure;
2116 // Otherwise, update the target.
2117 target = assigned_to;
2121 // If none of our closure's operands matched, then skip to the next
2126 // Otherwise, look at other types of assignment.
2127 let assigned_from = match rvalue {
2128 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2129 Rvalue::Use(operand) => match operand {
2130 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2138 "annotate_argument_and_return_for_borrow: \
2139 assigned_from={:?}",
2143 // Find the local from the rvalue.
2144 let assigned_from_local = match assigned_from.local_or_deref_local() {
2145 Some(local) => local,
2149 "annotate_argument_and_return_for_borrow: \
2150 assigned_from_local={:?}",
2151 assigned_from_local,
2154 // Check if our local matches the target - if so, we've assigned our
2155 // borrow to a new place.
2156 if assigned_from_local != target {
2160 // If we assigned our `target` into a new place, then we should
2161 // check if it was the return place.
2163 "annotate_argument_and_return_for_borrow: \
2164 assigned_from_local={:?} assigned_to={:?}",
2165 assigned_from_local, assigned_to
2167 if assigned_to == mir::RETURN_PLACE {
2168 // If it was then return the annotated closure if there was one,
2169 // else, annotate this function.
2170 return annotated_closure.or_else(fallback);
2173 // If we didn't assign into the return place, then we just update
2175 target = assigned_to;
2180 // Check the terminator if we didn't find anything in the statements.
2181 let terminator = &self.body[location.block].terminator();
2183 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2186 if let TerminatorKind::Call { destination: Some((place, _)), args, .. } =
2189 if let Some(assigned_to) = place.as_local() {
2191 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2194 for operand in args {
2195 let assigned_from = match operand {
2196 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2202 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2206 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2208 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2209 assigned_from_local,
2212 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2213 return annotated_closure.or_else(fallback);
2221 // If we haven't found an assignment into the return place, then we need not add
2223 debug!("annotate_argument_and_return_for_borrow: none found");
2227 /// Annotate the first argument and return type of a function signature if they are
2232 sig: ty::PolyFnSig<'tcx>,
2233 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2234 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2235 let is_closure = self.infcx.tcx.is_closure(did);
2236 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?);
2237 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2239 // We need to work out which arguments to highlight. We do this by looking
2240 // at the return type, where there are three cases:
2242 // 1. If there are named arguments, then we should highlight the return type and
2243 // highlight any of the arguments that are also references with that lifetime.
2244 // If there are no arguments that have the same lifetime as the return type,
2245 // then don't highlight anything.
2246 // 2. The return type is a reference with an anonymous lifetime. If this is
2247 // the case, then we can take advantage of (and teach) the lifetime elision
2250 // We know that an error is being reported. So the arguments and return type
2251 // must satisfy the elision rules. Therefore, if there is a single argument
2252 // then that means the return type and first (and only) argument have the same
2253 // lifetime and the borrow isn't meeting that, we can highlight the argument
2256 // If there are multiple arguments then the first argument must be self (else
2257 // it would not satisfy the elision rules), so we can highlight self and the
2259 // 3. The return type is not a reference. In this case, we don't highlight
2261 let return_ty = sig.output();
2262 match return_ty.skip_binder().kind() {
2263 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2264 // This is case 1 from above, return type is a named reference so we need to
2265 // search for relevant arguments.
2266 let mut arguments = Vec::new();
2267 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2268 if let ty::Ref(argument_region, _, _) = argument.kind() {
2269 if argument_region == return_region {
2270 // Need to use the `rustc_middle::ty` types to compare against the
2271 // `return_region`. Then use the `rustc_hir` type to get only
2272 // the lifetime span.
2273 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2274 // With access to the lifetime, we can get
2276 arguments.push((*argument, lifetime.span));
2278 bug!("ty type is a ref but hir type is not");
2284 // We need to have arguments. This shouldn't happen, but it's worth checking.
2285 if arguments.is_empty() {
2289 // We use a mix of the HIR and the Ty types to get information
2290 // as the HIR doesn't have full types for closure arguments.
2291 let return_ty = sig.output().skip_binder();
2292 let mut return_span = fn_decl.output.span();
2293 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2294 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2295 return_span = lifetime.span;
2299 Some(AnnotatedBorrowFnSignature::NamedFunction {
2305 ty::Ref(_, _, _) if is_closure => {
2306 // This is case 2 from above but only for closures, return type is anonymous
2307 // reference so we select
2308 // the first argument.
2309 let argument_span = fn_decl.inputs.first()?.span;
2310 let argument_ty = sig.inputs().skip_binder().first()?;
2312 // Closure arguments are wrapped in a tuple, so we need to get the first
2314 if let ty::Tuple(elems) = argument_ty.kind() {
2315 let argument_ty = elems.first()?.expect_ty();
2316 if let ty::Ref(_, _, _) = argument_ty.kind() {
2317 return Some(AnnotatedBorrowFnSignature::Closure {
2326 ty::Ref(_, _, _) => {
2327 // This is also case 2 from above but for functions, return type is still an
2328 // anonymous reference so we select the first argument.
2329 let argument_span = fn_decl.inputs.first()?.span;
2330 let argument_ty = sig.inputs().skip_binder().first()?;
2332 let return_span = fn_decl.output.span();
2333 let return_ty = sig.output().skip_binder();
2335 // We expect the first argument to be a reference.
2336 match argument_ty.kind() {
2337 ty::Ref(_, _, _) => {}
2341 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2349 // This is case 3 from above, return type is not a reference so don't highlight
2358 enum AnnotatedBorrowFnSignature<'tcx> {
2360 arguments: Vec<(Ty<'tcx>, Span)>,
2361 return_ty: Ty<'tcx>,
2365 argument_ty: Ty<'tcx>,
2366 argument_span: Span,
2367 return_ty: Ty<'tcx>,
2371 argument_ty: Ty<'tcx>,
2372 argument_span: Span,
2376 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2377 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2381 cx: &mut MirBorrowckCtxt<'_, 'tcx>,
2382 diag: &mut DiagnosticBuilder<'_>,
2385 AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2388 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2391 cx.get_region_name_for_ty(argument_ty, 0)
2393 AnnotatedBorrowFnSignature::AnonymousFunction {
2399 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2400 diag.span_label(*argument_span, format!("has type `{}`", argument_ty_name));
2402 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2403 let types_equal = return_ty_name == argument_ty_name;
2408 if types_equal { "also " } else { "" },
2414 "argument and return type have the same lifetime due to lifetime elision rules",
2417 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2418 lifetime-syntax.html#lifetime-elision>",
2421 cx.get_region_name_for_ty(return_ty, 0)
2423 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2424 // Region of return type and arguments checked to be the same earlier.
2425 let region_name = cx.get_region_name_for_ty(return_ty, 0);
2426 for (_, argument_span) in arguments {
2427 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2430 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2433 "use data from the highlighted arguments which match the `{}` lifetime of \