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::source_map::DesugaringKind;
15 use rustc_span::symbol::sym;
16 use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP};
17 use rustc_trait_selection::infer::InferCtxtExt;
19 use crate::borrowck_errors;
22 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
23 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
27 explain_borrow::BorrowExplanation, FnSelfUseKind, IncludingDowncast, RegionName,
28 RegionNameSource, UseSpans,
33 /// Index of the "move out" that we found. The `MoveData` can
34 /// then tell us where the move occurred.
37 /// `true` if we traversed a back edge while walking from the point
38 /// of error to the move site.
39 traversed_back_edge: bool,
42 /// Which case a StorageDeadOrDrop is for.
43 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
44 enum StorageDeadOrDrop<'tcx> {
50 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
51 pub(crate) fn report_use_of_moved_or_uninitialized(
54 desired_action: InitializationRequiringAction,
55 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
59 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
60 moved_place={:?} used_place={:?} span={:?} mpi={:?}",
61 location, desired_action, moved_place, used_place, span, mpi
65 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
66 let span = use_spans.args_or_use();
68 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
70 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
71 move_site_vec, use_spans
73 let move_out_indices: Vec<_> =
74 move_site_vec.iter().map(|move_site| move_site.moi).collect();
76 if move_out_indices.is_empty() {
77 let root_place = PlaceRef { projection: &[], ..used_place };
79 if !self.uninitialized_error_reported.insert(root_place) {
81 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
88 match self.describe_place_with_options(used_place, IncludingDowncast(true)) {
89 Some(name) => format!("`{}`", name),
90 None => "value".to_owned(),
92 let mut err = self.cannot_act_on_uninitialized_variable(
94 desired_action.as_noun(),
96 .describe_place_with_options(moved_place, IncludingDowncast(true))
97 .unwrap_or_else(|| "_".to_owned()),
99 err.span_label(span, format!("use of possibly-uninitialized {}", item_msg));
101 use_spans.var_span_label_path_only(
103 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
106 err.buffer(&mut self.errors_buffer);
108 if let Some((reported_place, _)) = self.move_error_reported.get(&move_out_indices) {
109 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
111 "report_use_of_moved_or_uninitialized place: error suppressed \
119 let is_partial_move = move_site_vec.iter().any(|move_site| {
120 let move_out = self.move_data.moves[(*move_site).moi];
121 let moved_place = &self.move_data.move_paths[move_out.path].place;
122 // `*(_1)` where `_1` is a `Box` is actually a move out.
123 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
124 && self.body.local_decls[moved_place.local].ty.is_box();
127 && used_place != moved_place.as_ref()
128 && used_place.is_prefix_of(moved_place.as_ref())
131 let partial_str = if is_partial_move { "partial " } else { "" };
132 let partially_str = if is_partial_move { "partially " } else { "" };
134 let mut err = self.cannot_act_on_moved_value(
136 desired_action.as_noun(),
138 self.describe_place_with_options(moved_place, IncludingDowncast(true)),
141 let reinit_spans = maybe_reinitialized_locations
145 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
148 .collect::<Vec<Span>>();
149 let reinits = maybe_reinitialized_locations.len();
151 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
152 } else if reinits > 1 {
154 MultiSpan::from_spans(reinit_spans),
156 format!("these {} reinitializations might get skipped", reinits)
159 "these 3 reinitializations and {} other{} might get skipped",
161 if reinits == 4 { "" } else { "s" }
167 self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
169 let mut is_loop_move = false;
170 let mut in_pattern = false;
172 for move_site in &move_site_vec {
173 let move_out = self.move_data.moves[(*move_site).moi];
174 let moved_place = &self.move_data.move_paths[move_out.path].place;
176 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
177 let move_span = move_spans.args_or_use();
179 let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
181 let loop_message = if location == move_out.source || move_site.traversed_back_edge {
182 ", in previous iteration of loop"
187 if location == move_out.source {
191 if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
192 let place_name = self
193 .describe_place(moved_place.as_ref())
194 .map(|n| format!("`{}`", n))
195 .unwrap_or_else(|| "value".to_owned());
197 FnSelfUseKind::FnOnceCall => {
201 "{} {}moved due to this call{}",
202 place_name, partially_str, loop_message
207 "this value implements `FnOnce`, which causes it to be moved when called",
210 FnSelfUseKind::Operator { self_arg } => {
214 "{} {}moved due to usage in operator{}",
215 place_name, partially_str, loop_message
218 if self.fn_self_span_reported.insert(fn_span) {
220 // Check whether the source is accessible
226 .span_to_snippet(self_arg.span)
233 "calling this operator moves the left-hand side",
237 FnSelfUseKind::Normal {
242 if implicit_into_iter {
246 "{} {}moved due to this implicit call to `.into_iter()`{}",
247 place_name, partially_str, loop_message
254 "{} {}moved due to this method call{}",
255 place_name, partially_str, loop_message
259 if is_option_or_result && maybe_reinitialized_locations.is_empty() {
260 err.span_suggestion_verbose(
261 fn_call_span.shrink_to_lo(),
262 "consider calling `.as_ref()` to borrow the type's contents",
263 "as_ref().".to_string(),
264 Applicability::MachineApplicable,
267 // Avoid pointing to the same function in multiple different
269 if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span)
273 &format!("this function takes ownership of the receiver `self`, which moves {}", place_name)
277 // Deref::deref takes &self, which cannot cause a move
278 FnSelfUseKind::DerefCoercion { .. } => unreachable!(),
283 format!("value {}moved{} here{}", partially_str, move_msg, loop_message),
285 // If the move error occurs due to a loop, don't show
286 // another message for the same span
287 if loop_message.is_empty() {
288 move_spans.var_span_label(
291 "variable {}moved due to use{}",
293 move_spans.describe()
300 if let (UseSpans::PatUse(span), []) =
301 (move_spans, &maybe_reinitialized_locations[..])
303 if maybe_reinitialized_locations.is_empty() {
304 err.span_suggestion_verbose(
307 "borrow this field in the pattern to avoid moving {}",
308 self.describe_place(moved_place.as_ref())
309 .map(|n| format!("`{}`", n))
310 .unwrap_or_else(|| "the value".to_string())
313 Applicability::MachineApplicable,
319 if let Some(DesugaringKind::ForLoop(_)) = move_span.desugaring_kind() {
320 let sess = self.infcx.tcx.sess;
321 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
322 // If we have a `&mut` ref, we need to reborrow.
323 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
324 // If we are in a loop this will be suggested later.
326 err.span_suggestion_verbose(
327 move_span.shrink_to_lo(),
329 "consider creating a fresh reborrow of {} here",
330 self.describe_place(moved_place.as_ref())
331 .map(|n| format!("`{}`", n))
332 .unwrap_or_else(|| "the mutable reference".to_string()),
334 "&mut *".to_string(),
335 Applicability::MachineApplicable,
338 } else if let Ok(snippet) = sess.source_map().span_to_snippet(move_span) {
341 "consider borrowing to avoid moving into the for loop",
342 format!("&{}", snippet),
343 Applicability::MaybeIncorrect,
349 use_spans.var_span_label_path_only(
351 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
358 "value {} here after {}move",
359 desired_action.as_verb_in_past_tense(),
365 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
366 let needs_note = match ty.kind() {
367 ty::Closure(id, _) => {
368 let tables = self.infcx.tcx.typeck(id.expect_local());
369 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
371 tables.closure_kind_origins().get(hir_id).is_none()
376 let mpi = self.move_data.moves[move_out_indices[0]].path;
377 let place = &self.move_data.move_paths[mpi].place;
378 let ty = place.ty(self.body, self.infcx.tcx).ty;
380 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
381 if is_loop_move & !in_pattern {
382 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
383 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
384 err.span_suggestion_verbose(
387 "consider creating a fresh reborrow of {} here",
388 self.describe_place(moved_place)
389 .map(|n| format!("`{}`", n))
390 .unwrap_or_else(|| "the mutable reference".to_string()),
392 "&mut *".to_string(),
393 Applicability::MachineApplicable,
400 self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
401 let note_msg = match opt_name {
402 Some(ref name) => format!("`{}`", name),
403 None => "value".to_owned(),
405 if let ty::Param(param_ty) = ty.kind() {
406 let tcx = self.infcx.tcx;
407 let generics = tcx.generics_of(self.mir_def_id());
408 let param = generics.type_param(¶m_ty, tcx);
409 if let Some(generics) = tcx
411 .get_generics(tcx.closure_base_def_id(self.mir_def_id().to_def_id()))
413 suggest_constraining_type_param(
417 ¶m.name.as_str(),
423 let span = if let Some(local) = place.as_local() {
424 let decl = &self.body.local_decls[local];
425 Some(decl.source_info.span)
429 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
432 if let UseSpans::FnSelfUse {
433 kind: FnSelfUseKind::DerefCoercion { deref_target, deref_target_ty },
438 "{} occurs due to deref coercion to `{}`",
439 desired_action.as_noun(),
443 // Check first whether the source is accessible (issue #87060)
444 if self.infcx.tcx.sess.source_map().span_to_snippet(deref_target).is_ok() {
445 err.span_note(deref_target, "deref defined here");
449 if let Some((_, mut old_err)) =
450 self.move_error_reported.insert(move_out_indices, (used_place, err))
452 // Cancel the old error so it doesn't ICE.
458 pub(crate) fn report_move_out_while_borrowed(
461 (place, span): (Place<'tcx>, Span),
462 borrow: &BorrowData<'tcx>,
465 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
466 location, place, span, borrow
468 let value_msg = self.describe_any_place(place.as_ref());
469 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
471 let borrow_spans = self.retrieve_borrow_spans(borrow);
472 let borrow_span = borrow_spans.args_or_use();
474 let move_spans = self.move_spans(place.as_ref(), location);
475 let span = move_spans.args_or_use();
478 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
479 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
480 err.span_label(span, format!("move out of {} occurs here", value_msg));
482 borrow_spans.var_span_label_path_only(
484 format!("borrow occurs due to use{}", borrow_spans.describe()),
487 move_spans.var_span_label(
489 format!("move occurs due to use{}", move_spans.describe()),
493 self.explain_why_borrow_contains_point(location, borrow, None)
494 .add_explanation_to_diagnostic(
503 err.buffer(&mut self.errors_buffer);
506 pub(crate) fn report_use_while_mutably_borrowed(
509 (place, _span): (Place<'tcx>, Span),
510 borrow: &BorrowData<'tcx>,
511 ) -> DiagnosticBuilder<'cx> {
512 let borrow_spans = self.retrieve_borrow_spans(borrow);
513 let borrow_span = borrow_spans.args_or_use();
515 // Conflicting borrows are reported separately, so only check for move
517 let use_spans = self.move_spans(place.as_ref(), location);
518 let span = use_spans.var_or_use();
520 // 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
521 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
522 let mut err = self.cannot_use_when_mutably_borrowed(
524 &self.describe_any_place(place.as_ref()),
526 &self.describe_any_place(borrow.borrowed_place.as_ref()),
529 borrow_spans.var_span_label(
532 let place = &borrow.borrowed_place;
533 let desc_place = self.describe_any_place(place.as_ref());
534 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
539 self.explain_why_borrow_contains_point(location, borrow, None)
540 .add_explanation_to_diagnostic(
552 pub(crate) fn report_conflicting_borrow(
555 (place, span): (Place<'tcx>, Span),
556 gen_borrow_kind: BorrowKind,
557 issued_borrow: &BorrowData<'tcx>,
558 ) -> DiagnosticBuilder<'cx> {
559 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
560 let issued_span = issued_spans.args_or_use();
562 let borrow_spans = self.borrow_spans(span, location);
563 let span = borrow_spans.args_or_use();
565 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
571 let (desc_place, msg_place, msg_borrow, union_type_name) =
572 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
574 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
575 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
577 // FIXME: supply non-"" `opt_via` when appropriate
578 let first_borrow_desc;
579 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
580 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
581 first_borrow_desc = "mutable ";
582 self.cannot_reborrow_already_borrowed(
594 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
595 first_borrow_desc = "immutable ";
596 self.cannot_reborrow_already_borrowed(
609 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
610 first_borrow_desc = "first ";
611 let mut err = self.cannot_mutably_borrow_multiply(
619 self.suggest_split_at_mut_if_applicable(
622 issued_borrow.borrowed_place,
627 (BorrowKind::Unique, BorrowKind::Unique) => {
628 first_borrow_desc = "first ";
629 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
632 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
633 if let Some(immutable_section_description) =
634 self.classify_immutable_section(issued_borrow.assigned_place)
636 let mut err = self.cannot_mutate_in_immutable_section(
640 immutable_section_description,
643 borrow_spans.var_span_label(
646 "borrow occurs due to use of {}{}",
648 borrow_spans.describe(),
655 first_borrow_desc = "immutable ";
656 self.cannot_reborrow_already_borrowed(
670 (BorrowKind::Unique, _) => {
671 first_borrow_desc = "first ";
672 self.cannot_uniquely_borrow_by_one_closure(
684 (BorrowKind::Shared, BorrowKind::Unique) => {
685 first_borrow_desc = "first ";
686 self.cannot_reborrow_already_uniquely_borrowed(
699 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
700 first_borrow_desc = "first ";
701 self.cannot_reborrow_already_uniquely_borrowed(
714 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
717 BorrowKind::Mut { .. }
720 | BorrowKind::Shallow,
724 if issued_spans == borrow_spans {
725 borrow_spans.var_span_label(
727 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
728 gen_borrow_kind.describe_mutability(),
731 let borrow_place = &issued_borrow.borrowed_place;
732 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
733 issued_spans.var_span_label(
736 "first borrow occurs due to use of {}{}",
738 issued_spans.describe(),
740 issued_borrow.kind.describe_mutability(),
743 borrow_spans.var_span_label(
746 "second borrow occurs due to use of {}{}",
748 borrow_spans.describe(),
750 gen_borrow_kind.describe_mutability(),
754 if union_type_name != "" {
756 "{} is a field of the union `{}`, so it overlaps the field {}",
757 msg_place, union_type_name, msg_borrow,
761 explanation.add_explanation_to_diagnostic(
768 Some((issued_span, span)),
774 fn suggest_split_at_mut_if_applicable(
776 err: &mut DiagnosticBuilder<'_>,
778 borrowed_place: Place<'tcx>,
780 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
781 (&place.projection[..], &borrowed_place.projection[..])
784 "consider using `.split_at_mut(position)` or similar method to obtain \
785 two mutable non-overlapping sub-slices",
790 /// Returns the description of the root place for a conflicting borrow and the full
791 /// descriptions of the places that caused the conflict.
793 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
794 /// attempted while a shared borrow is live, then this function will return:
798 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
799 /// a shared borrow of another field `x.y`, then this function will return:
801 /// ("x", "x.z", "x.y")
803 /// In the more complex union case, where the union is a field of a struct, then if a mutable
804 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
805 /// another field `x.u.y`, then this function will return:
807 /// ("x.u", "x.u.z", "x.u.y")
809 /// This is used when creating error messages like below:
812 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
813 /// mutable (via `a.u.s.b`) [E0502]
815 pub(crate) fn describe_place_for_conflicting_borrow(
817 first_borrowed_place: Place<'tcx>,
818 second_borrowed_place: Place<'tcx>,
819 ) -> (String, String, String, String) {
820 // Define a small closure that we can use to check if the type of a place
822 let union_ty = |place_base| {
823 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
824 // using a type annotation in the closure argument instead leads to a lifetime error.
825 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
826 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
829 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
830 // code duplication (particularly around returning an empty description in the failure
834 // If we have a conflicting borrow of the same place, then we don't want to add
835 // an extraneous "via x.y" to our diagnostics, so filter out this case.
836 first_borrowed_place != second_borrowed_place
839 // We're going to want to traverse the first borrowed place to see if we can find
840 // field access to a union. If we find that, then we will keep the place of the
841 // union being accessed and the field that was being accessed so we can check the
842 // second borrowed place for the same union and an access to a different field.
843 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
845 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
846 return Some((place_base, field));
853 .and_then(|(target_base, target_field)| {
854 // With the place of a union and a field access into it, we traverse the second
855 // borrowed place and look for an access to a different field of the same union.
856 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
857 if let ProjectionElem::Field(field, _) = elem {
858 if let Some(union_ty) = union_ty(place_base) {
859 if field != target_field && place_base == target_base {
861 self.describe_any_place(place_base),
862 self.describe_any_place(first_borrowed_place.as_ref()),
863 self.describe_any_place(second_borrowed_place.as_ref()),
864 union_ty.to_string(),
873 // If we didn't find a field access into a union, or both places match, then
874 // only return the description of the first place.
876 self.describe_any_place(first_borrowed_place.as_ref()),
884 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
886 /// This means that some data referenced by `borrow` needs to live
887 /// past the point where the StorageDeadOrDrop of `place` occurs.
888 /// This is usually interpreted as meaning that `place` has too
889 /// short a lifetime. (But sometimes it is more useful to report
890 /// it as a more direct conflict between the execution of a
891 /// `Drop::drop` with an aliasing borrow.)
892 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
895 borrow: &BorrowData<'tcx>,
896 place_span: (Place<'tcx>, Span),
897 kind: Option<WriteKind>,
900 "report_borrowed_value_does_not_live_long_enough(\
901 {:?}, {:?}, {:?}, {:?}\
903 location, borrow, place_span, kind
906 let drop_span = place_span.1;
908 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
910 let borrow_spans = self.retrieve_borrow_spans(borrow);
911 let borrow_span = borrow_spans.var_or_use_path_span();
913 assert!(root_place.projection.is_empty());
914 let proper_span = self.body.local_decls[root_place.local].source_info.span;
916 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
918 if self.access_place_error_reported.contains(&(
919 Place { local: root_place.local, projection: root_place_projection },
923 "suppressing access_place error when borrow doesn't live long enough for {:?}",
929 self.access_place_error_reported.insert((
930 Place { local: root_place.local, projection: root_place_projection },
934 let borrowed_local = borrow.borrowed_place.local;
935 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
937 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
938 err.buffer(&mut self.errors_buffer);
942 if let StorageDeadOrDrop::Destructor(dropped_ty) =
943 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
945 // If a borrow of path `B` conflicts with drop of `D` (and
946 // we're not in the uninteresting case where `B` is a
947 // prefix of `D`), then report this as a more interesting
948 // destructor conflict.
949 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
950 self.report_borrow_conflicts_with_destructor(
951 location, borrow, place_span, kind, dropped_ty,
957 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
959 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
960 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
963 "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
964 place_desc, explanation
966 let err = match (place_desc, explanation) {
967 // If the outlives constraint comes from inside the closure,
972 // Box::new(|| y) as Box<Fn() -> &'static i32>
974 // then just use the normal error. The closure isn't escaping
975 // and `move` will not help here.
978 BorrowExplanation::MustBeValidFor {
982 (ConstraintCategory::Return(_)
983 | ConstraintCategory::CallArgument
984 | ConstraintCategory::OpaqueType),
990 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
991 .report_escaping_closure_capture(
997 &format!("`{}`", name),
1001 BorrowExplanation::MustBeValidFor {
1002 category: ConstraintCategory::Assignment,
1003 from_closure: false,
1007 RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
1013 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1014 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1022 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1032 err.buffer(&mut self.errors_buffer);
1035 fn report_local_value_does_not_live_long_enough(
1039 borrow: &BorrowData<'tcx>,
1041 borrow_spans: UseSpans<'tcx>,
1042 explanation: BorrowExplanation,
1043 ) -> DiagnosticBuilder<'cx> {
1045 "report_local_value_does_not_live_long_enough(\
1046 {:?}, {:?}, {:?}, {:?}, {:?}\
1048 location, name, borrow, drop_span, borrow_spans
1051 let borrow_span = borrow_spans.var_or_use_path_span();
1052 if let BorrowExplanation::MustBeValidFor {
1056 from_closure: false,
1060 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1065 opt_place_desc.as_ref(),
1071 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1073 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1074 let region_name = annotation.emit(self, &mut err);
1078 format!("`{}` would have to be valid for `{}`...", name, region_name),
1081 let fn_hir_id = self.mir_hir_id();
1085 "...but `{}` will be dropped here, when the {} returns",
1090 .opt_name(fn_hir_id)
1091 .map(|name| format!("function `{}`", name))
1092 .unwrap_or_else(|| {
1096 .typeck(self.mir_def_id())
1097 .node_type(fn_hir_id)
1100 ty::Closure(..) => "enclosing closure",
1101 ty::Generator(..) => "enclosing generator",
1102 kind => bug!("expected closure or generator, found {:?}", kind),
1110 "functions cannot return a borrow to data owned within the function's scope, \
1111 functions can only return borrows to data passed as arguments",
1114 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1115 references-and-borrowing.html#dangling-references>",
1118 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1120 explanation.add_explanation_to_diagnostic(
1131 err.span_label(borrow_span, "borrowed value does not live long enough");
1132 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1134 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1136 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1138 explanation.add_explanation_to_diagnostic(
1152 fn report_borrow_conflicts_with_destructor(
1155 borrow: &BorrowData<'tcx>,
1156 (place, drop_span): (Place<'tcx>, Span),
1157 kind: Option<WriteKind>,
1158 dropped_ty: Ty<'tcx>,
1161 "report_borrow_conflicts_with_destructor(\
1162 {:?}, {:?}, ({:?}, {:?}), {:?}\
1164 location, borrow, place, drop_span, kind,
1167 let borrow_spans = self.retrieve_borrow_spans(borrow);
1168 let borrow_span = borrow_spans.var_or_use();
1170 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1172 let what_was_dropped = match self.describe_place(place.as_ref()) {
1173 Some(name) => format!("`{}`", name),
1174 None => String::from("temporary value"),
1177 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1178 Some(borrowed) => format!(
1179 "here, drop of {D} needs exclusive access to `{B}`, \
1180 because the type `{T}` implements the `Drop` trait",
1181 D = what_was_dropped,
1186 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1187 D = what_was_dropped,
1191 err.span_label(drop_span, label);
1193 // Only give this note and suggestion if they could be relevant.
1195 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1197 BorrowExplanation::UsedLater { .. }
1198 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1199 err.note("consider using a `let` binding to create a longer lived value");
1204 explanation.add_explanation_to_diagnostic(
1214 err.buffer(&mut self.errors_buffer);
1217 fn report_thread_local_value_does_not_live_long_enough(
1221 ) -> DiagnosticBuilder<'cx> {
1223 "report_thread_local_value_does_not_live_long_enough(\
1226 drop_span, borrow_span
1229 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1233 "thread-local variables cannot be borrowed beyond the end of the function",
1235 err.span_label(drop_span, "end of enclosing function is here");
1240 fn report_temporary_value_does_not_live_long_enough(
1243 borrow: &BorrowData<'tcx>,
1245 borrow_spans: UseSpans<'tcx>,
1247 explanation: BorrowExplanation,
1248 ) -> DiagnosticBuilder<'cx> {
1250 "report_temporary_value_does_not_live_long_enough(\
1251 {:?}, {:?}, {:?}, {:?}\
1253 location, borrow, drop_span, proper_span
1256 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1259 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1270 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1271 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1272 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1275 BorrowExplanation::UsedLater(..)
1276 | BorrowExplanation::UsedLaterInLoop(..)
1277 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1278 // Only give this note and suggestion if it could be relevant.
1279 err.note("consider using a `let` binding to create a longer lived value");
1283 explanation.add_explanation_to_diagnostic(
1293 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1295 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1300 fn try_report_cannot_return_reference_to_local(
1302 borrow: &BorrowData<'tcx>,
1305 category: ConstraintCategory,
1306 opt_place_desc: Option<&String>,
1307 ) -> Option<DiagnosticBuilder<'cx>> {
1308 let return_kind = match category {
1309 ConstraintCategory::Return(_) => "return",
1310 ConstraintCategory::Yield => "yield",
1314 // FIXME use a better heuristic than Spans
1315 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1321 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1322 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1323 match self.body.local_kind(local) {
1324 LocalKind::ReturnPointer | LocalKind::Temp => {
1325 bug!("temporary or return pointer with a name")
1327 LocalKind::Var => "local variable ",
1329 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1331 "variable captured by `move` "
1333 LocalKind::Arg => "function parameter ",
1339 format!("{}`{}`", local_kind, place_desc),
1340 format!("`{}` is borrowed here", place_desc),
1344 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1345 let local = root_place.local;
1346 match self.body.local_kind(local) {
1347 LocalKind::ReturnPointer | LocalKind::Temp => {
1348 ("temporary value".to_string(), "temporary value created here".to_string())
1351 "function parameter".to_string(),
1352 "function parameter borrowed here".to_string(),
1355 ("local binding".to_string(), "local binding introduced here".to_string())
1360 let mut err = self.cannot_return_reference_to_local(
1367 if return_span != borrow_span {
1368 err.span_label(borrow_span, note);
1370 let tcx = self.infcx.tcx;
1371 let ty_params = ty::List::empty();
1373 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1374 let return_ty = tcx.erase_regions(return_ty);
1377 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1380 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1381 .must_apply_modulo_regions()
1383 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1384 err.span_suggestion_hidden(
1386 "use `.collect()` to allocate the iterator",
1387 format!("{}{}", snippet, ".collect::<Vec<_>>()"),
1388 Applicability::MaybeIncorrect,
1398 fn report_escaping_closure_capture(
1400 use_span: UseSpans<'tcx>,
1402 fr_name: &RegionName,
1403 category: ConstraintCategory,
1404 constraint_span: Span,
1406 ) -> DiagnosticBuilder<'cx> {
1407 let tcx = self.infcx.tcx;
1408 let args_span = use_span.args_or_use();
1410 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1412 if string.starts_with("async ") {
1413 let pos = args_span.lo() + BytePos(6);
1414 (args_span.with_lo(pos).with_hi(pos), "move ".to_string())
1415 } else if string.starts_with("async|") {
1416 let pos = args_span.lo() + BytePos(5);
1417 (args_span.with_lo(pos).with_hi(pos), " move".to_string())
1419 (args_span.shrink_to_lo(), "move ".to_string())
1422 Err(_) => (args_span, "move |<args>| <body>".to_string()),
1424 let kind = match use_span.generator_kind() {
1425 Some(generator_kind) => match generator_kind {
1426 GeneratorKind::Async(async_kind) => match async_kind {
1427 AsyncGeneratorKind::Block => "async block",
1428 AsyncGeneratorKind::Closure => "async closure",
1429 _ => bug!("async block/closure expected, but async function found."),
1431 GeneratorKind::Gen => "generator",
1437 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1438 err.span_suggestion_verbose(
1441 "to force the {} to take ownership of {} (and any \
1442 other referenced variables), use the `move` keyword",
1446 Applicability::MachineApplicable,
1450 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1451 let msg = format!("{} is returned here", kind);
1452 err.span_note(constraint_span, &msg);
1454 ConstraintCategory::CallArgument => {
1455 fr_name.highlight_region_name(&mut err);
1456 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1458 "async blocks are not executed immediately and must either take a \
1459 reference or ownership of outside variables they use",
1462 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1463 err.span_note(constraint_span, &msg);
1467 "report_escaping_closure_capture called with unexpected constraint \
1476 fn report_escaping_data(
1479 name: &Option<String>,
1483 ) -> DiagnosticBuilder<'cx> {
1484 let tcx = self.infcx.tcx;
1486 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1489 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1493 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1496 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1498 if let Some(name) = name {
1501 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1506 format!("reference escapes the {} body here", escapes_from),
1513 fn get_moved_indexes(
1517 ) -> (Vec<MoveSite>, Vec<Location>) {
1518 fn predecessor_locations(
1519 body: &'a mir::Body<'tcx>,
1521 ) -> impl Iterator<Item = Location> + 'a {
1522 if location.statement_index == 0 {
1523 let predecessors = body.predecessors()[location.block].to_vec();
1524 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1526 Either::Right(std::iter::once(Location {
1527 statement_index: location.statement_index - 1,
1533 let mut mpis = vec![mpi];
1534 let move_paths = &self.move_data.move_paths;
1535 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1537 let mut stack = Vec::new();
1538 let mut back_edge_stack = Vec::new();
1540 predecessor_locations(self.body, location).for_each(|predecessor| {
1541 if location.dominates(predecessor, &self.dominators) {
1542 back_edge_stack.push(predecessor)
1544 stack.push(predecessor);
1548 let mut reached_start = false;
1550 /* Check if the mpi is initialized as an argument */
1551 let mut is_argument = false;
1552 for arg in self.body.args_iter() {
1553 let path = self.move_data.rev_lookup.find_local(arg);
1554 if mpis.contains(&path) {
1559 let mut visited = FxHashSet::default();
1560 let mut move_locations = FxHashSet::default();
1561 let mut reinits = vec![];
1562 let mut result = vec![];
1564 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1566 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1567 location, is_back_edge
1570 if !visited.insert(location) {
1576 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1577 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1578 // this analysis only tries to find moves explicitly
1579 // written by the user, so we ignore the move-outs
1580 // created by `StorageDead` and at the beginning
1583 // If we are found a use of a.b.c which was in error, then we want to look for
1584 // moves not only of a.b.c but also a.b and a.
1586 // Note that the moves data already includes "parent" paths, so we don't have to
1587 // worry about the other case: that is, if there is a move of a.b.c, it is already
1588 // marked as a move of a.b and a as well, so we will generate the correct errors
1590 for moi in &self.move_data.loc_map[location] {
1591 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1592 let path = self.move_data.moves[*moi].path;
1593 if mpis.contains(&path) {
1595 "report_use_of_moved_or_uninitialized: found {:?}",
1596 move_paths[path].place
1598 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1599 move_locations.insert(location);
1601 // Strictly speaking, we could continue our DFS here. There may be
1602 // other moves that can reach the point of error. But it is kind of
1603 // confusing to highlight them.
1611 // drop(a); // <-- current point of error
1614 // Because we stop the DFS here, we only highlight `let c = a`,
1615 // and not `let b = a`. We will of course also report an error at
1616 // `let c = a` which highlights `let b = a` as the move.
1623 let mut any_match = false;
1624 for ii in &self.move_data.init_loc_map[location] {
1625 let init = self.move_data.inits[*ii];
1627 InitKind::Deep | InitKind::NonPanicPathOnly => {
1628 if mpis.contains(&init.path) {
1632 InitKind::Shallow => {
1633 if mpi == init.path {
1640 reinits.push(location);
1646 while let Some(location) = stack.pop() {
1647 if dfs_iter(&mut result, location, false) {
1651 let mut has_predecessor = false;
1652 predecessor_locations(self.body, location).for_each(|predecessor| {
1653 if location.dominates(predecessor, &self.dominators) {
1654 back_edge_stack.push(predecessor)
1656 stack.push(predecessor);
1658 has_predecessor = true;
1661 if !has_predecessor {
1662 reached_start = true;
1665 if (is_argument || !reached_start) && result.is_empty() {
1666 /* Process back edges (moves in future loop iterations) only if
1667 the move path is definitely initialized upon loop entry,
1668 to avoid spurious "in previous iteration" errors.
1669 During DFS, if there's a path from the error back to the start
1670 of the function with no intervening init or move, then the
1671 move path may be uninitialized at loop entry.
1673 while let Some(location) = back_edge_stack.pop() {
1674 if dfs_iter(&mut result, location, true) {
1678 predecessor_locations(self.body, location)
1679 .for_each(|predecessor| back_edge_stack.push(predecessor));
1683 // Check if we can reach these reinits from a move location.
1684 let reinits_reachable = reinits
1687 let mut visited = FxHashSet::default();
1688 let mut stack = vec![*reinit];
1689 while let Some(location) = stack.pop() {
1690 if !visited.insert(location) {
1693 if move_locations.contains(&location) {
1696 stack.extend(predecessor_locations(self.body, location));
1700 .collect::<Vec<Location>>();
1701 (result, reinits_reachable)
1704 pub(crate) fn report_illegal_mutation_of_borrowed(
1707 (place, span): (Place<'tcx>, Span),
1708 loan: &BorrowData<'tcx>,
1710 let loan_spans = self.retrieve_borrow_spans(loan);
1711 let loan_span = loan_spans.args_or_use();
1713 let descr_place = self.describe_any_place(place.as_ref());
1714 if loan.kind == BorrowKind::Shallow {
1715 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1716 let mut err = self.cannot_mutate_in_immutable_section(
1723 loan_spans.var_span_label(
1725 format!("borrow occurs due to use{}", loan_spans.describe()),
1726 loan.kind.describe_mutability(),
1729 err.buffer(&mut self.errors_buffer);
1735 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
1737 loan_spans.var_span_label(
1739 format!("borrow occurs due to use{}", loan_spans.describe()),
1740 loan.kind.describe_mutability(),
1743 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
1753 self.explain_deref_coercion(loan, &mut err);
1755 err.buffer(&mut self.errors_buffer);
1758 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut DiagnosticBuilder<'_>) {
1759 let tcx = self.infcx.tcx;
1761 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
1762 Some((method_did, method_substs)),
1764 &self.body[loan.reserve_location.block].terminator,
1765 rustc_const_eval::util::find_self_call(
1768 loan.assigned_place.local,
1769 loan.reserve_location.block,
1772 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
1774 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
1775 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
1778 if let Some(Ok(instance)) = deref_target {
1779 let deref_target_ty = instance.ty(tcx, self.param_env);
1781 "borrow occurs due to deref coercion to `{}`",
1784 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
1790 /// Reports an illegal reassignment; for example, an assignment to
1791 /// (part of) a non-`mut` local that occurs potentially after that
1792 /// local has already been initialized. `place` is the path being
1793 /// assigned; `err_place` is a place providing a reason why
1794 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
1795 /// assignment to `x.f`).
1796 pub(crate) fn report_illegal_reassignment(
1798 _location: Location,
1799 (place, span): (Place<'tcx>, Span),
1800 assigned_span: Span,
1801 err_place: Place<'tcx>,
1803 let (from_arg, local_decl, local_name) = match err_place.as_local() {
1805 self.body.local_kind(local) == LocalKind::Arg,
1806 Some(&self.body.local_decls[local]),
1807 self.local_names[local],
1809 None => (false, None, None),
1812 // If root local is initialized immediately (everything apart from let
1813 // PATTERN;) then make the error refer to that local, rather than the
1814 // place being assigned later.
1815 let (place_description, assigned_span) = match local_decl {
1818 Some(box LocalInfo::User(
1819 ClearCrossCrate::Clear
1820 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
1821 opt_match_place: None,
1825 | Some(box LocalInfo::StaticRef { .. })
1829 | None => (self.describe_any_place(place.as_ref()), assigned_span),
1830 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
1833 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
1834 let msg = if from_arg {
1835 "cannot assign to immutable argument"
1837 "cannot assign twice to immutable variable"
1839 if span != assigned_span {
1841 err.span_label(assigned_span, format!("first assignment to {}", place_description));
1844 if let Some(decl) = local_decl {
1845 if let Some(name) = local_name {
1846 if decl.can_be_made_mutable() {
1847 err.span_suggestion(
1848 decl.source_info.span,
1849 "consider making this binding mutable",
1850 format!("mut {}", name),
1851 Applicability::MachineApplicable,
1856 err.span_label(span, msg);
1857 err.buffer(&mut self.errors_buffer);
1860 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
1861 let tcx = self.infcx.tcx;
1862 match place.last_projection() {
1863 None => StorageDeadOrDrop::LocalStorageDead,
1864 Some((place_base, elem)) => {
1865 // FIXME(spastorino) make this iterate
1866 let base_access = self.classify_drop_access_kind(place_base);
1868 ProjectionElem::Deref => match base_access {
1869 StorageDeadOrDrop::LocalStorageDead
1870 | StorageDeadOrDrop::BoxedStorageDead => {
1872 place_base.ty(self.body, tcx).ty.is_box(),
1873 "Drop of value behind a reference or raw pointer"
1875 StorageDeadOrDrop::BoxedStorageDead
1877 StorageDeadOrDrop::Destructor(_) => base_access,
1879 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
1880 let base_ty = place_base.ty(self.body, tcx).ty;
1881 match base_ty.kind() {
1882 ty::Adt(def, _) if def.has_dtor(tcx) => {
1883 // Report the outermost adt with a destructor
1885 StorageDeadOrDrop::Destructor(_) => base_access,
1886 StorageDeadOrDrop::LocalStorageDead
1887 | StorageDeadOrDrop::BoxedStorageDead => {
1888 StorageDeadOrDrop::Destructor(base_ty)
1895 ProjectionElem::ConstantIndex { .. }
1896 | ProjectionElem::Subslice { .. }
1897 | ProjectionElem::Index(_) => base_access,
1903 /// Describe the reason for the fake borrow that was assigned to `place`.
1904 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
1905 use rustc_middle::mir::visit::Visitor;
1906 struct FakeReadCauseFinder<'tcx> {
1908 cause: Option<FakeReadCause>,
1910 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
1911 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
1913 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
1914 if *place == self.place =>
1916 self.cause = Some(*cause);
1922 let mut visitor = FakeReadCauseFinder { place, cause: None };
1923 visitor.visit_body(&self.body);
1924 match visitor.cause {
1925 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
1926 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
1931 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
1932 /// borrow of local value that does not live long enough.
1933 fn annotate_argument_and_return_for_borrow(
1935 borrow: &BorrowData<'tcx>,
1936 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
1937 // Define a fallback for when we can't match a closure.
1939 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
1943 let ty = self.infcx.tcx.type_of(self.mir_def_id());
1945 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
1946 self.mir_def_id().to_def_id(),
1947 self.infcx.tcx.fn_sig(self.mir_def_id()),
1954 // In order to determine whether we need to annotate, we need to check whether the reserve
1955 // place was an assignment into a temporary.
1957 // If it was, we check whether or not that temporary is eventually assigned into the return
1958 // place. If it was, we can add annotations about the function's return type and arguments
1959 // and it'll make sense.
1960 let location = borrow.reserve_location;
1961 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
1962 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
1963 &self.body[location.block].statements.get(location.statement_index)
1965 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
1966 // Check that the initial assignment of the reserve location is into a temporary.
1967 let mut target = match reservation.as_local() {
1968 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
1972 // Next, look through the rest of the block, checking if we are assigning the
1973 // `target` (that is, the place that contains our borrow) to anything.
1974 let mut annotated_closure = None;
1975 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
1977 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
1980 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
1981 if let Some(assigned_to) = place.as_local() {
1983 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
1987 // Check if our `target` was captured by a closure.
1988 if let Rvalue::Aggregate(
1989 box AggregateKind::Closure(def_id, substs),
1993 for operand in operands {
1994 let assigned_from = match operand {
1995 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2001 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2005 // Find the local from the operand.
2006 let assigned_from_local = match assigned_from.local_or_deref_local()
2008 Some(local) => local,
2012 if assigned_from_local != target {
2016 // If a closure captured our `target` and then assigned
2017 // into a place then we should annotate the closure in
2018 // case it ends up being assigned into the return place.
2020 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2022 "annotate_argument_and_return_for_borrow: \
2023 annotated_closure={:?} assigned_from_local={:?} \
2025 annotated_closure, assigned_from_local, assigned_to
2028 if assigned_to == mir::RETURN_PLACE {
2029 // If it was assigned directly into the return place, then
2031 return annotated_closure;
2033 // Otherwise, update the target.
2034 target = assigned_to;
2038 // If none of our closure's operands matched, then skip to the next
2043 // Otherwise, look at other types of assignment.
2044 let assigned_from = match rvalue {
2045 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2046 Rvalue::Use(operand) => match operand {
2047 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2055 "annotate_argument_and_return_for_borrow: \
2056 assigned_from={:?}",
2060 // Find the local from the rvalue.
2061 let assigned_from_local = match assigned_from.local_or_deref_local() {
2062 Some(local) => local,
2066 "annotate_argument_and_return_for_borrow: \
2067 assigned_from_local={:?}",
2068 assigned_from_local,
2071 // Check if our local matches the target - if so, we've assigned our
2072 // borrow to a new place.
2073 if assigned_from_local != target {
2077 // If we assigned our `target` into a new place, then we should
2078 // check if it was the return place.
2080 "annotate_argument_and_return_for_borrow: \
2081 assigned_from_local={:?} assigned_to={:?}",
2082 assigned_from_local, assigned_to
2084 if assigned_to == mir::RETURN_PLACE {
2085 // If it was then return the annotated closure if there was one,
2086 // else, annotate this function.
2087 return annotated_closure.or_else(fallback);
2090 // If we didn't assign into the return place, then we just update
2092 target = assigned_to;
2097 // Check the terminator if we didn't find anything in the statements.
2098 let terminator = &self.body[location.block].terminator();
2100 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2103 if let TerminatorKind::Call { destination: Some((place, _)), args, .. } =
2106 if let Some(assigned_to) = place.as_local() {
2108 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2111 for operand in args {
2112 let assigned_from = match operand {
2113 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2119 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2123 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2125 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2126 assigned_from_local,
2129 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2130 return annotated_closure.or_else(fallback);
2138 // If we haven't found an assignment into the return place, then we need not add
2140 debug!("annotate_argument_and_return_for_borrow: none found");
2144 /// Annotate the first argument and return type of a function signature if they are
2149 sig: ty::PolyFnSig<'tcx>,
2150 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2151 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2152 let is_closure = self.infcx.tcx.is_closure(did);
2153 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?);
2154 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2156 // We need to work out which arguments to highlight. We do this by looking
2157 // at the return type, where there are three cases:
2159 // 1. If there are named arguments, then we should highlight the return type and
2160 // highlight any of the arguments that are also references with that lifetime.
2161 // If there are no arguments that have the same lifetime as the return type,
2162 // then don't highlight anything.
2163 // 2. The return type is a reference with an anonymous lifetime. If this is
2164 // the case, then we can take advantage of (and teach) the lifetime elision
2167 // We know that an error is being reported. So the arguments and return type
2168 // must satisfy the elision rules. Therefore, if there is a single argument
2169 // then that means the return type and first (and only) argument have the same
2170 // lifetime and the borrow isn't meeting that, we can highlight the argument
2173 // If there are multiple arguments then the first argument must be self (else
2174 // it would not satisfy the elision rules), so we can highlight self and the
2176 // 3. The return type is not a reference. In this case, we don't highlight
2178 let return_ty = sig.output();
2179 match return_ty.skip_binder().kind() {
2180 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2181 // This is case 1 from above, return type is a named reference so we need to
2182 // search for relevant arguments.
2183 let mut arguments = Vec::new();
2184 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2185 if let ty::Ref(argument_region, _, _) = argument.kind() {
2186 if argument_region == return_region {
2187 // Need to use the `rustc_middle::ty` types to compare against the
2188 // `return_region`. Then use the `rustc_hir` type to get only
2189 // the lifetime span.
2190 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2191 // With access to the lifetime, we can get
2193 arguments.push((*argument, lifetime.span));
2195 bug!("ty type is a ref but hir type is not");
2201 // We need to have arguments. This shouldn't happen, but it's worth checking.
2202 if arguments.is_empty() {
2206 // We use a mix of the HIR and the Ty types to get information
2207 // as the HIR doesn't have full types for closure arguments.
2208 let return_ty = sig.output().skip_binder();
2209 let mut return_span = fn_decl.output.span();
2210 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2211 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2212 return_span = lifetime.span;
2216 Some(AnnotatedBorrowFnSignature::NamedFunction {
2222 ty::Ref(_, _, _) if is_closure => {
2223 // This is case 2 from above but only for closures, return type is anonymous
2224 // reference so we select
2225 // the first argument.
2226 let argument_span = fn_decl.inputs.first()?.span;
2227 let argument_ty = sig.inputs().skip_binder().first()?;
2229 // Closure arguments are wrapped in a tuple, so we need to get the first
2231 if let ty::Tuple(elems) = argument_ty.kind() {
2232 let argument_ty = elems.first()?.expect_ty();
2233 if let ty::Ref(_, _, _) = argument_ty.kind() {
2234 return Some(AnnotatedBorrowFnSignature::Closure {
2243 ty::Ref(_, _, _) => {
2244 // This is also case 2 from above but for functions, return type is still an
2245 // anonymous reference so we select the first argument.
2246 let argument_span = fn_decl.inputs.first()?.span;
2247 let argument_ty = sig.inputs().skip_binder().first()?;
2249 let return_span = fn_decl.output.span();
2250 let return_ty = sig.output().skip_binder();
2252 // We expect the first argument to be a reference.
2253 match argument_ty.kind() {
2254 ty::Ref(_, _, _) => {}
2258 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2266 // This is case 3 from above, return type is not a reference so don't highlight
2275 enum AnnotatedBorrowFnSignature<'tcx> {
2277 arguments: Vec<(Ty<'tcx>, Span)>,
2278 return_ty: Ty<'tcx>,
2282 argument_ty: Ty<'tcx>,
2283 argument_span: Span,
2284 return_ty: Ty<'tcx>,
2288 argument_ty: Ty<'tcx>,
2289 argument_span: Span,
2293 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2294 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2298 cx: &mut MirBorrowckCtxt<'_, 'tcx>,
2299 diag: &mut DiagnosticBuilder<'_>,
2302 AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2305 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2308 cx.get_region_name_for_ty(argument_ty, 0)
2310 AnnotatedBorrowFnSignature::AnonymousFunction {
2316 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2317 diag.span_label(*argument_span, format!("has type `{}`", argument_ty_name));
2319 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2320 let types_equal = return_ty_name == argument_ty_name;
2325 if types_equal { "also " } else { "" },
2331 "argument and return type have the same lifetime due to lifetime elision rules",
2334 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2335 lifetime-syntax.html#lifetime-elision>",
2338 cx.get_region_name_for_ty(return_ty, 0)
2340 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2341 // Region of return type and arguments checked to be the same earlier.
2342 let region_name = cx.get_region_name_for_ty(return_ty, 0);
2343 for (_, argument_span) in arguments {
2344 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2347 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2350 "use data from the highlighted arguments which match the `{}` lifetime of \