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::borrowck_errors;
21 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
22 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
26 explain_borrow::BorrowExplanation, FnSelfUseKind, IncludingDowncast, RegionName,
27 RegionNameSource, UseSpans,
32 /// Index of the "move out" that we found. The `MoveData` can
33 /// then tell us where the move occurred.
36 /// `true` if we traversed a back edge while walking from the point
37 /// of error to the move site.
38 traversed_back_edge: bool,
41 /// Which case a StorageDeadOrDrop is for.
42 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
43 enum StorageDeadOrDrop<'tcx> {
49 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
50 pub(crate) fn report_use_of_moved_or_uninitialized(
53 desired_action: InitializationRequiringAction,
54 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
58 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
59 moved_place={:?} used_place={:?} span={:?} mpi={:?}",
60 location, desired_action, moved_place, used_place, span, mpi
64 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
65 let span = use_spans.args_or_use();
67 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
69 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
70 move_site_vec, use_spans
72 let move_out_indices: Vec<_> =
73 move_site_vec.iter().map(|move_site| move_site.moi).collect();
75 if move_out_indices.is_empty() {
76 let root_place = PlaceRef { projection: &[], ..used_place };
78 if !self.uninitialized_error_reported.insert(root_place) {
80 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
87 match self.describe_place_with_options(used_place, IncludingDowncast(true)) {
88 Some(name) => format!("`{}`", name),
89 None => "value".to_owned(),
91 let mut err = self.cannot_act_on_uninitialized_variable(
93 desired_action.as_noun(),
95 .describe_place_with_options(moved_place, IncludingDowncast(true))
96 .unwrap_or_else(|| "_".to_owned()),
98 err.span_label(span, format!("use of possibly-uninitialized {}", item_msg));
100 use_spans.var_span_label_path_only(
102 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
105 err.buffer(&mut self.errors_buffer);
107 if let Some((reported_place, _)) = self.move_error_reported.get(&move_out_indices) {
108 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
110 "report_use_of_moved_or_uninitialized place: error suppressed \
118 let is_partial_move = move_site_vec.iter().any(|move_site| {
119 let move_out = self.move_data.moves[(*move_site).moi];
120 let moved_place = &self.move_data.move_paths[move_out.path].place;
121 // `*(_1)` where `_1` is a `Box` is actually a move out.
122 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
123 && self.body.local_decls[moved_place.local].ty.is_box();
126 && used_place != moved_place.as_ref()
127 && used_place.is_prefix_of(moved_place.as_ref())
130 let partial_str = if is_partial_move { "partial " } else { "" };
131 let partially_str = if is_partial_move { "partially " } else { "" };
133 let mut err = self.cannot_act_on_moved_value(
135 desired_action.as_noun(),
137 self.describe_place_with_options(moved_place, IncludingDowncast(true)),
140 let reinit_spans = maybe_reinitialized_locations
144 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
147 .collect::<Vec<Span>>();
148 let reinits = maybe_reinitialized_locations.len();
150 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
151 } else if reinits > 1 {
153 MultiSpan::from_spans(reinit_spans),
155 format!("these {} reinitializations might get skipped", reinits)
158 "these 3 reinitializations and {} other{} might get skipped",
160 if reinits == 4 { "" } else { "s" }
166 self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
168 let mut is_loop_move = false;
169 let mut in_pattern = false;
171 for move_site in &move_site_vec {
172 let move_out = self.move_data.moves[(*move_site).moi];
173 let moved_place = &self.move_data.move_paths[move_out.path].place;
175 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
176 let move_span = move_spans.args_or_use();
178 let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
180 let loop_message = if location == move_out.source || move_site.traversed_back_edge {
181 ", in previous iteration of loop"
186 if location == move_out.source {
190 if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
191 let place_name = self
192 .describe_place(moved_place.as_ref())
193 .map(|n| format!("`{}`", n))
194 .unwrap_or_else(|| "value".to_owned());
196 FnSelfUseKind::FnOnceCall => {
200 "{} {}moved due to this call{}",
201 place_name, partially_str, loop_message
206 "this value implements `FnOnce`, which causes it to be moved when called",
209 FnSelfUseKind::Operator { self_arg } => {
213 "{} {}moved due to usage in operator{}",
214 place_name, partially_str, loop_message
217 if self.fn_self_span_reported.insert(fn_span) {
219 // Check whether the source is accessible
225 .span_to_snippet(self_arg.span)
232 "calling this operator moves the left-hand side",
236 FnSelfUseKind::Normal {
241 if implicit_into_iter {
245 "{} {}moved due to this implicit call to `.into_iter()`{}",
246 place_name, partially_str, loop_message
249 let sess = self.infcx.tcx.sess;
250 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
251 // If we have a `&mut` ref, we need to reborrow.
252 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
253 // If we are in a loop this will be suggested later.
255 err.span_suggestion_verbose(
256 move_span.shrink_to_lo(),
258 "consider creating a fresh reborrow of {} here",
259 self.describe_place(moved_place.as_ref())
260 .map(|n| format!("`{}`", n))
262 || "the mutable reference".to_string()
265 "&mut *".to_string(),
266 Applicability::MachineApplicable,
269 } else if let Ok(snippet) =
270 sess.source_map().span_to_snippet(move_span)
274 "consider borrowing to avoid moving into the for loop",
275 format!("&{}", snippet),
276 Applicability::MaybeIncorrect,
283 "{} {}moved due to this method call{}",
284 place_name, partially_str, loop_message
288 if is_option_or_result && maybe_reinitialized_locations.is_empty() {
289 err.span_suggestion_verbose(
290 fn_call_span.shrink_to_lo(),
291 "consider calling `.as_ref()` to borrow the type's contents",
292 "as_ref().".to_string(),
293 Applicability::MachineApplicable,
296 // Avoid pointing to the same function in multiple different
298 if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span)
302 &format!("this function takes ownership of the receiver `self`, which moves {}", place_name)
306 // Deref::deref takes &self, which cannot cause a move
307 FnSelfUseKind::DerefCoercion { .. } => unreachable!(),
312 format!("value {}moved{} here{}", partially_str, move_msg, loop_message),
314 // If the move error occurs due to a loop, don't show
315 // another message for the same span
316 if loop_message.is_empty() {
317 move_spans.var_span_label(
320 "variable {}moved due to use{}",
322 move_spans.describe()
329 if let (UseSpans::PatUse(span), []) =
330 (move_spans, &maybe_reinitialized_locations[..])
332 if maybe_reinitialized_locations.is_empty() {
333 err.span_suggestion_verbose(
336 "borrow this field in the pattern to avoid moving {}",
337 self.describe_place(moved_place.as_ref())
338 .map(|n| format!("`{}`", n))
339 .unwrap_or_else(|| "the value".to_string())
342 Applicability::MachineApplicable,
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.typeck_root_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 {
980 category @ (ConstraintCategory::Return(_)
981 | ConstraintCategory::CallArgument
982 | ConstraintCategory::OpaqueType),
988 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
989 .report_escaping_closure_capture(
995 &format!("`{}`", name),
999 BorrowExplanation::MustBeValidFor {
1000 category: ConstraintCategory::Assignment,
1001 from_closure: false,
1005 RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
1011 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1012 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1020 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1030 err.buffer(&mut self.errors_buffer);
1033 fn report_local_value_does_not_live_long_enough(
1037 borrow: &BorrowData<'tcx>,
1039 borrow_spans: UseSpans<'tcx>,
1040 explanation: BorrowExplanation,
1041 ) -> DiagnosticBuilder<'cx> {
1043 "report_local_value_does_not_live_long_enough(\
1044 {:?}, {:?}, {:?}, {:?}, {:?}\
1046 location, name, borrow, drop_span, borrow_spans
1049 let borrow_span = borrow_spans.var_or_use_path_span();
1050 if let BorrowExplanation::MustBeValidFor {
1054 from_closure: false,
1058 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1063 opt_place_desc.as_ref(),
1069 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1071 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1072 let region_name = annotation.emit(self, &mut err);
1076 format!("`{}` would have to be valid for `{}`...", name, region_name),
1079 let fn_hir_id = self.mir_hir_id();
1083 "...but `{}` will be dropped here, when the {} returns",
1088 .opt_name(fn_hir_id)
1089 .map(|name| format!("function `{}`", name))
1090 .unwrap_or_else(|| {
1094 .typeck(self.mir_def_id())
1095 .node_type(fn_hir_id)
1098 ty::Closure(..) => "enclosing closure",
1099 ty::Generator(..) => "enclosing generator",
1100 kind => bug!("expected closure or generator, found {:?}", kind),
1108 "functions cannot return a borrow to data owned within the function's scope, \
1109 functions can only return borrows to data passed as arguments",
1112 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1113 references-and-borrowing.html#dangling-references>",
1116 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1118 explanation.add_explanation_to_diagnostic(
1129 err.span_label(borrow_span, "borrowed value does not live long enough");
1130 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1132 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1134 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1136 explanation.add_explanation_to_diagnostic(
1150 fn report_borrow_conflicts_with_destructor(
1153 borrow: &BorrowData<'tcx>,
1154 (place, drop_span): (Place<'tcx>, Span),
1155 kind: Option<WriteKind>,
1156 dropped_ty: Ty<'tcx>,
1159 "report_borrow_conflicts_with_destructor(\
1160 {:?}, {:?}, ({:?}, {:?}), {:?}\
1162 location, borrow, place, drop_span, kind,
1165 let borrow_spans = self.retrieve_borrow_spans(borrow);
1166 let borrow_span = borrow_spans.var_or_use();
1168 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1170 let what_was_dropped = match self.describe_place(place.as_ref()) {
1171 Some(name) => format!("`{}`", name),
1172 None => String::from("temporary value"),
1175 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1176 Some(borrowed) => format!(
1177 "here, drop of {D} needs exclusive access to `{B}`, \
1178 because the type `{T}` implements the `Drop` trait",
1179 D = what_was_dropped,
1184 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1185 D = what_was_dropped,
1189 err.span_label(drop_span, label);
1191 // Only give this note and suggestion if they could be relevant.
1193 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1195 BorrowExplanation::UsedLater { .. }
1196 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1197 err.note("consider using a `let` binding to create a longer lived value");
1202 explanation.add_explanation_to_diagnostic(
1212 err.buffer(&mut self.errors_buffer);
1215 fn report_thread_local_value_does_not_live_long_enough(
1219 ) -> DiagnosticBuilder<'cx> {
1221 "report_thread_local_value_does_not_live_long_enough(\
1224 drop_span, borrow_span
1227 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1231 "thread-local variables cannot be borrowed beyond the end of the function",
1233 err.span_label(drop_span, "end of enclosing function is here");
1238 fn report_temporary_value_does_not_live_long_enough(
1241 borrow: &BorrowData<'tcx>,
1243 borrow_spans: UseSpans<'tcx>,
1245 explanation: BorrowExplanation,
1246 ) -> DiagnosticBuilder<'cx> {
1248 "report_temporary_value_does_not_live_long_enough(\
1249 {:?}, {:?}, {:?}, {:?}\
1251 location, borrow, drop_span, proper_span
1254 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1257 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1268 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1269 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1270 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1273 BorrowExplanation::UsedLater(..)
1274 | BorrowExplanation::UsedLaterInLoop(..)
1275 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1276 // Only give this note and suggestion if it could be relevant.
1277 err.note("consider using a `let` binding to create a longer lived value");
1281 explanation.add_explanation_to_diagnostic(
1291 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1293 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1298 fn try_report_cannot_return_reference_to_local(
1300 borrow: &BorrowData<'tcx>,
1303 category: ConstraintCategory,
1304 opt_place_desc: Option<&String>,
1305 ) -> Option<DiagnosticBuilder<'cx>> {
1306 let return_kind = match category {
1307 ConstraintCategory::Return(_) => "return",
1308 ConstraintCategory::Yield => "yield",
1312 // FIXME use a better heuristic than Spans
1313 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1319 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1320 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1321 match self.body.local_kind(local) {
1322 LocalKind::ReturnPointer | LocalKind::Temp => {
1323 bug!("temporary or return pointer with a name")
1325 LocalKind::Var => "local variable ",
1327 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1329 "variable captured by `move` "
1331 LocalKind::Arg => "function parameter ",
1337 format!("{}`{}`", local_kind, place_desc),
1338 format!("`{}` is borrowed here", place_desc),
1342 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1343 let local = root_place.local;
1344 match self.body.local_kind(local) {
1345 LocalKind::ReturnPointer | LocalKind::Temp => {
1346 ("temporary value".to_string(), "temporary value created here".to_string())
1349 "function parameter".to_string(),
1350 "function parameter borrowed here".to_string(),
1353 ("local binding".to_string(), "local binding introduced here".to_string())
1358 let mut err = self.cannot_return_reference_to_local(
1365 if return_span != borrow_span {
1366 err.span_label(borrow_span, note);
1368 let tcx = self.infcx.tcx;
1369 let ty_params = ty::List::empty();
1371 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1372 let return_ty = tcx.erase_regions(return_ty);
1375 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1378 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1379 .must_apply_modulo_regions()
1381 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1382 err.span_suggestion_hidden(
1384 "use `.collect()` to allocate the iterator",
1385 format!("{}{}", snippet, ".collect::<Vec<_>>()"),
1386 Applicability::MaybeIncorrect,
1396 fn report_escaping_closure_capture(
1398 use_span: UseSpans<'tcx>,
1400 fr_name: &RegionName,
1401 category: ConstraintCategory,
1402 constraint_span: Span,
1404 ) -> DiagnosticBuilder<'cx> {
1405 let tcx = self.infcx.tcx;
1406 let args_span = use_span.args_or_use();
1408 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1410 if string.starts_with("async ") {
1411 let pos = args_span.lo() + BytePos(6);
1412 (args_span.with_lo(pos).with_hi(pos), "move ".to_string())
1413 } else if string.starts_with("async|") {
1414 let pos = args_span.lo() + BytePos(5);
1415 (args_span.with_lo(pos).with_hi(pos), " move".to_string())
1417 (args_span.shrink_to_lo(), "move ".to_string())
1420 Err(_) => (args_span, "move |<args>| <body>".to_string()),
1422 let kind = match use_span.generator_kind() {
1423 Some(generator_kind) => match generator_kind {
1424 GeneratorKind::Async(async_kind) => match async_kind {
1425 AsyncGeneratorKind::Block => "async block",
1426 AsyncGeneratorKind::Closure => "async closure",
1427 _ => bug!("async block/closure expected, but async function found."),
1429 GeneratorKind::Gen => "generator",
1435 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1436 err.span_suggestion_verbose(
1439 "to force the {} to take ownership of {} (and any \
1440 other referenced variables), use the `move` keyword",
1444 Applicability::MachineApplicable,
1448 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1449 let msg = format!("{} is returned here", kind);
1450 err.span_note(constraint_span, &msg);
1452 ConstraintCategory::CallArgument => {
1453 fr_name.highlight_region_name(&mut err);
1454 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1456 "async blocks are not executed immediately and must either take a \
1457 reference or ownership of outside variables they use",
1460 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1461 err.span_note(constraint_span, &msg);
1465 "report_escaping_closure_capture called with unexpected constraint \
1474 fn report_escaping_data(
1477 name: &Option<String>,
1481 ) -> DiagnosticBuilder<'cx> {
1482 let tcx = self.infcx.tcx;
1484 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1487 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1491 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1494 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1496 if let Some(name) = name {
1499 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1504 format!("reference escapes the {} body here", escapes_from),
1511 fn get_moved_indexes(
1515 ) -> (Vec<MoveSite>, Vec<Location>) {
1516 fn predecessor_locations(
1517 body: &'a mir::Body<'tcx>,
1519 ) -> impl Iterator<Item = Location> + 'a {
1520 if location.statement_index == 0 {
1521 let predecessors = body.predecessors()[location.block].to_vec();
1522 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1524 Either::Right(std::iter::once(Location {
1525 statement_index: location.statement_index - 1,
1531 let mut mpis = vec![mpi];
1532 let move_paths = &self.move_data.move_paths;
1533 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1535 let mut stack = Vec::new();
1536 let mut back_edge_stack = Vec::new();
1538 predecessor_locations(self.body, location).for_each(|predecessor| {
1539 if location.dominates(predecessor, &self.dominators) {
1540 back_edge_stack.push(predecessor)
1542 stack.push(predecessor);
1546 let mut reached_start = false;
1548 /* Check if the mpi is initialized as an argument */
1549 let mut is_argument = false;
1550 for arg in self.body.args_iter() {
1551 let path = self.move_data.rev_lookup.find_local(arg);
1552 if mpis.contains(&path) {
1557 let mut visited = FxHashSet::default();
1558 let mut move_locations = FxHashSet::default();
1559 let mut reinits = vec![];
1560 let mut result = vec![];
1562 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1564 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1565 location, is_back_edge
1568 if !visited.insert(location) {
1574 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1575 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1576 // this analysis only tries to find moves explicitly
1577 // written by the user, so we ignore the move-outs
1578 // created by `StorageDead` and at the beginning
1581 // If we are found a use of a.b.c which was in error, then we want to look for
1582 // moves not only of a.b.c but also a.b and a.
1584 // Note that the moves data already includes "parent" paths, so we don't have to
1585 // worry about the other case: that is, if there is a move of a.b.c, it is already
1586 // marked as a move of a.b and a as well, so we will generate the correct errors
1588 for moi in &self.move_data.loc_map[location] {
1589 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1590 let path = self.move_data.moves[*moi].path;
1591 if mpis.contains(&path) {
1593 "report_use_of_moved_or_uninitialized: found {:?}",
1594 move_paths[path].place
1596 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1597 move_locations.insert(location);
1599 // Strictly speaking, we could continue our DFS here. There may be
1600 // other moves that can reach the point of error. But it is kind of
1601 // confusing to highlight them.
1609 // drop(a); // <-- current point of error
1612 // Because we stop the DFS here, we only highlight `let c = a`,
1613 // and not `let b = a`. We will of course also report an error at
1614 // `let c = a` which highlights `let b = a` as the move.
1621 let mut any_match = false;
1622 for ii in &self.move_data.init_loc_map[location] {
1623 let init = self.move_data.inits[*ii];
1625 InitKind::Deep | InitKind::NonPanicPathOnly => {
1626 if mpis.contains(&init.path) {
1630 InitKind::Shallow => {
1631 if mpi == init.path {
1638 reinits.push(location);
1644 while let Some(location) = stack.pop() {
1645 if dfs_iter(&mut result, location, false) {
1649 let mut has_predecessor = false;
1650 predecessor_locations(self.body, location).for_each(|predecessor| {
1651 if location.dominates(predecessor, &self.dominators) {
1652 back_edge_stack.push(predecessor)
1654 stack.push(predecessor);
1656 has_predecessor = true;
1659 if !has_predecessor {
1660 reached_start = true;
1663 if (is_argument || !reached_start) && result.is_empty() {
1664 /* Process back edges (moves in future loop iterations) only if
1665 the move path is definitely initialized upon loop entry,
1666 to avoid spurious "in previous iteration" errors.
1667 During DFS, if there's a path from the error back to the start
1668 of the function with no intervening init or move, then the
1669 move path may be uninitialized at loop entry.
1671 while let Some(location) = back_edge_stack.pop() {
1672 if dfs_iter(&mut result, location, true) {
1676 predecessor_locations(self.body, location)
1677 .for_each(|predecessor| back_edge_stack.push(predecessor));
1681 // Check if we can reach these reinits from a move location.
1682 let reinits_reachable = reinits
1685 let mut visited = FxHashSet::default();
1686 let mut stack = vec![*reinit];
1687 while let Some(location) = stack.pop() {
1688 if !visited.insert(location) {
1691 if move_locations.contains(&location) {
1694 stack.extend(predecessor_locations(self.body, location));
1698 .collect::<Vec<Location>>();
1699 (result, reinits_reachable)
1702 pub(crate) fn report_illegal_mutation_of_borrowed(
1705 (place, span): (Place<'tcx>, Span),
1706 loan: &BorrowData<'tcx>,
1708 let loan_spans = self.retrieve_borrow_spans(loan);
1709 let loan_span = loan_spans.args_or_use();
1711 let descr_place = self.describe_any_place(place.as_ref());
1712 if loan.kind == BorrowKind::Shallow {
1713 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1714 let mut err = self.cannot_mutate_in_immutable_section(
1721 loan_spans.var_span_label(
1723 format!("borrow occurs due to use{}", loan_spans.describe()),
1724 loan.kind.describe_mutability(),
1727 err.buffer(&mut self.errors_buffer);
1733 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
1735 loan_spans.var_span_label(
1737 format!("borrow occurs due to use{}", loan_spans.describe()),
1738 loan.kind.describe_mutability(),
1741 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
1751 self.explain_deref_coercion(loan, &mut err);
1753 err.buffer(&mut self.errors_buffer);
1756 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut DiagnosticBuilder<'_>) {
1757 let tcx = self.infcx.tcx;
1759 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
1760 Some((method_did, method_substs)),
1762 &self.body[loan.reserve_location.block].terminator,
1763 rustc_const_eval::util::find_self_call(
1766 loan.assigned_place.local,
1767 loan.reserve_location.block,
1770 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
1772 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
1773 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
1776 if let Some(Ok(instance)) = deref_target {
1777 let deref_target_ty = instance.ty(tcx, self.param_env);
1779 "borrow occurs due to deref coercion to `{}`",
1782 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
1788 /// Reports an illegal reassignment; for example, an assignment to
1789 /// (part of) a non-`mut` local that occurs potentially after that
1790 /// local has already been initialized. `place` is the path being
1791 /// assigned; `err_place` is a place providing a reason why
1792 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
1793 /// assignment to `x.f`).
1794 pub(crate) fn report_illegal_reassignment(
1796 _location: Location,
1797 (place, span): (Place<'tcx>, Span),
1798 assigned_span: Span,
1799 err_place: Place<'tcx>,
1801 let (from_arg, local_decl, local_name) = match err_place.as_local() {
1803 self.body.local_kind(local) == LocalKind::Arg,
1804 Some(&self.body.local_decls[local]),
1805 self.local_names[local],
1807 None => (false, None, None),
1810 // If root local is initialized immediately (everything apart from let
1811 // PATTERN;) then make the error refer to that local, rather than the
1812 // place being assigned later.
1813 let (place_description, assigned_span) = match local_decl {
1816 Some(box LocalInfo::User(
1817 ClearCrossCrate::Clear
1818 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
1819 opt_match_place: None,
1823 | Some(box LocalInfo::StaticRef { .. })
1827 | None => (self.describe_any_place(place.as_ref()), assigned_span),
1828 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
1831 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
1832 let msg = if from_arg {
1833 "cannot assign to immutable argument"
1835 "cannot assign twice to immutable variable"
1837 if span != assigned_span {
1839 err.span_label(assigned_span, format!("first assignment to {}", place_description));
1842 if let Some(decl) = local_decl {
1843 if let Some(name) = local_name {
1844 if decl.can_be_made_mutable() {
1845 err.span_suggestion(
1846 decl.source_info.span,
1847 "consider making this binding mutable",
1848 format!("mut {}", name),
1849 Applicability::MachineApplicable,
1854 err.span_label(span, msg);
1855 err.buffer(&mut self.errors_buffer);
1858 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
1859 let tcx = self.infcx.tcx;
1860 match place.last_projection() {
1861 None => StorageDeadOrDrop::LocalStorageDead,
1862 Some((place_base, elem)) => {
1863 // FIXME(spastorino) make this iterate
1864 let base_access = self.classify_drop_access_kind(place_base);
1866 ProjectionElem::Deref => match base_access {
1867 StorageDeadOrDrop::LocalStorageDead
1868 | StorageDeadOrDrop::BoxedStorageDead => {
1870 place_base.ty(self.body, tcx).ty.is_box(),
1871 "Drop of value behind a reference or raw pointer"
1873 StorageDeadOrDrop::BoxedStorageDead
1875 StorageDeadOrDrop::Destructor(_) => base_access,
1877 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
1878 let base_ty = place_base.ty(self.body, tcx).ty;
1879 match base_ty.kind() {
1880 ty::Adt(def, _) if def.has_dtor(tcx) => {
1881 // Report the outermost adt with a destructor
1883 StorageDeadOrDrop::Destructor(_) => base_access,
1884 StorageDeadOrDrop::LocalStorageDead
1885 | StorageDeadOrDrop::BoxedStorageDead => {
1886 StorageDeadOrDrop::Destructor(base_ty)
1893 ProjectionElem::ConstantIndex { .. }
1894 | ProjectionElem::Subslice { .. }
1895 | ProjectionElem::Index(_) => base_access,
1901 /// Describe the reason for the fake borrow that was assigned to `place`.
1902 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
1903 use rustc_middle::mir::visit::Visitor;
1904 struct FakeReadCauseFinder<'tcx> {
1906 cause: Option<FakeReadCause>,
1908 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
1909 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
1911 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
1912 if *place == self.place =>
1914 self.cause = Some(*cause);
1920 let mut visitor = FakeReadCauseFinder { place, cause: None };
1921 visitor.visit_body(&self.body);
1922 match visitor.cause {
1923 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
1924 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
1929 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
1930 /// borrow of local value that does not live long enough.
1931 fn annotate_argument_and_return_for_borrow(
1933 borrow: &BorrowData<'tcx>,
1934 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
1935 // Define a fallback for when we can't match a closure.
1937 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
1941 let ty = self.infcx.tcx.type_of(self.mir_def_id());
1943 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
1944 self.mir_def_id().to_def_id(),
1945 self.infcx.tcx.fn_sig(self.mir_def_id()),
1952 // In order to determine whether we need to annotate, we need to check whether the reserve
1953 // place was an assignment into a temporary.
1955 // If it was, we check whether or not that temporary is eventually assigned into the return
1956 // place. If it was, we can add annotations about the function's return type and arguments
1957 // and it'll make sense.
1958 let location = borrow.reserve_location;
1959 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
1960 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
1961 &self.body[location.block].statements.get(location.statement_index)
1963 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
1964 // Check that the initial assignment of the reserve location is into a temporary.
1965 let mut target = match reservation.as_local() {
1966 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
1970 // Next, look through the rest of the block, checking if we are assigning the
1971 // `target` (that is, the place that contains our borrow) to anything.
1972 let mut annotated_closure = None;
1973 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
1975 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
1978 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
1979 if let Some(assigned_to) = place.as_local() {
1981 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
1985 // Check if our `target` was captured by a closure.
1986 if let Rvalue::Aggregate(
1987 box AggregateKind::Closure(def_id, substs),
1991 for operand in operands {
1992 let assigned_from = match operand {
1993 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
1999 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2003 // Find the local from the operand.
2004 let assigned_from_local = match assigned_from.local_or_deref_local()
2006 Some(local) => local,
2010 if assigned_from_local != target {
2014 // If a closure captured our `target` and then assigned
2015 // into a place then we should annotate the closure in
2016 // case it ends up being assigned into the return place.
2018 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2020 "annotate_argument_and_return_for_borrow: \
2021 annotated_closure={:?} assigned_from_local={:?} \
2023 annotated_closure, assigned_from_local, assigned_to
2026 if assigned_to == mir::RETURN_PLACE {
2027 // If it was assigned directly into the return place, then
2029 return annotated_closure;
2031 // Otherwise, update the target.
2032 target = assigned_to;
2036 // If none of our closure's operands matched, then skip to the next
2041 // Otherwise, look at other types of assignment.
2042 let assigned_from = match rvalue {
2043 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2044 Rvalue::Use(operand) => match operand {
2045 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2053 "annotate_argument_and_return_for_borrow: \
2054 assigned_from={:?}",
2058 // Find the local from the rvalue.
2059 let assigned_from_local = match assigned_from.local_or_deref_local() {
2060 Some(local) => local,
2064 "annotate_argument_and_return_for_borrow: \
2065 assigned_from_local={:?}",
2066 assigned_from_local,
2069 // Check if our local matches the target - if so, we've assigned our
2070 // borrow to a new place.
2071 if assigned_from_local != target {
2075 // If we assigned our `target` into a new place, then we should
2076 // check if it was the return place.
2078 "annotate_argument_and_return_for_borrow: \
2079 assigned_from_local={:?} assigned_to={:?}",
2080 assigned_from_local, assigned_to
2082 if assigned_to == mir::RETURN_PLACE {
2083 // If it was then return the annotated closure if there was one,
2084 // else, annotate this function.
2085 return annotated_closure.or_else(fallback);
2088 // If we didn't assign into the return place, then we just update
2090 target = assigned_to;
2095 // Check the terminator if we didn't find anything in the statements.
2096 let terminator = &self.body[location.block].terminator();
2098 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2101 if let TerminatorKind::Call { destination: Some((place, _)), args, .. } =
2104 if let Some(assigned_to) = place.as_local() {
2106 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2109 for operand in args {
2110 let assigned_from = match operand {
2111 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2117 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2121 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2123 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2124 assigned_from_local,
2127 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2128 return annotated_closure.or_else(fallback);
2136 // If we haven't found an assignment into the return place, then we need not add
2138 debug!("annotate_argument_and_return_for_borrow: none found");
2142 /// Annotate the first argument and return type of a function signature if they are
2147 sig: ty::PolyFnSig<'tcx>,
2148 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2149 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2150 let is_closure = self.infcx.tcx.is_closure(did);
2151 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?);
2152 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2154 // We need to work out which arguments to highlight. We do this by looking
2155 // at the return type, where there are three cases:
2157 // 1. If there are named arguments, then we should highlight the return type and
2158 // highlight any of the arguments that are also references with that lifetime.
2159 // If there are no arguments that have the same lifetime as the return type,
2160 // then don't highlight anything.
2161 // 2. The return type is a reference with an anonymous lifetime. If this is
2162 // the case, then we can take advantage of (and teach) the lifetime elision
2165 // We know that an error is being reported. So the arguments and return type
2166 // must satisfy the elision rules. Therefore, if there is a single argument
2167 // then that means the return type and first (and only) argument have the same
2168 // lifetime and the borrow isn't meeting that, we can highlight the argument
2171 // If there are multiple arguments then the first argument must be self (else
2172 // it would not satisfy the elision rules), so we can highlight self and the
2174 // 3. The return type is not a reference. In this case, we don't highlight
2176 let return_ty = sig.output();
2177 match return_ty.skip_binder().kind() {
2178 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2179 // This is case 1 from above, return type is a named reference so we need to
2180 // search for relevant arguments.
2181 let mut arguments = Vec::new();
2182 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2183 if let ty::Ref(argument_region, _, _) = argument.kind() {
2184 if argument_region == return_region {
2185 // Need to use the `rustc_middle::ty` types to compare against the
2186 // `return_region`. Then use the `rustc_hir` type to get only
2187 // the lifetime span.
2188 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2189 // With access to the lifetime, we can get
2191 arguments.push((*argument, lifetime.span));
2193 bug!("ty type is a ref but hir type is not");
2199 // We need to have arguments. This shouldn't happen, but it's worth checking.
2200 if arguments.is_empty() {
2204 // We use a mix of the HIR and the Ty types to get information
2205 // as the HIR doesn't have full types for closure arguments.
2206 let return_ty = sig.output().skip_binder();
2207 let mut return_span = fn_decl.output.span();
2208 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2209 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2210 return_span = lifetime.span;
2214 Some(AnnotatedBorrowFnSignature::NamedFunction {
2220 ty::Ref(_, _, _) if is_closure => {
2221 // This is case 2 from above but only for closures, return type is anonymous
2222 // reference so we select
2223 // the first argument.
2224 let argument_span = fn_decl.inputs.first()?.span;
2225 let argument_ty = sig.inputs().skip_binder().first()?;
2227 // Closure arguments are wrapped in a tuple, so we need to get the first
2229 if let ty::Tuple(elems) = argument_ty.kind() {
2230 let argument_ty = elems.first()?.expect_ty();
2231 if let ty::Ref(_, _, _) = argument_ty.kind() {
2232 return Some(AnnotatedBorrowFnSignature::Closure {
2241 ty::Ref(_, _, _) => {
2242 // This is also case 2 from above but for functions, return type is still an
2243 // anonymous reference so we select the first argument.
2244 let argument_span = fn_decl.inputs.first()?.span;
2245 let argument_ty = sig.inputs().skip_binder().first()?;
2247 let return_span = fn_decl.output.span();
2248 let return_ty = sig.output().skip_binder();
2250 // We expect the first argument to be a reference.
2251 match argument_ty.kind() {
2252 ty::Ref(_, _, _) => {}
2256 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2264 // This is case 3 from above, return type is not a reference so don't highlight
2273 enum AnnotatedBorrowFnSignature<'tcx> {
2275 arguments: Vec<(Ty<'tcx>, Span)>,
2276 return_ty: Ty<'tcx>,
2280 argument_ty: Ty<'tcx>,
2281 argument_span: Span,
2282 return_ty: Ty<'tcx>,
2286 argument_ty: Ty<'tcx>,
2287 argument_span: Span,
2291 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2292 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2296 cx: &mut MirBorrowckCtxt<'_, 'tcx>,
2297 diag: &mut DiagnosticBuilder<'_>,
2300 AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2303 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2306 cx.get_region_name_for_ty(argument_ty, 0)
2308 AnnotatedBorrowFnSignature::AnonymousFunction {
2314 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2315 diag.span_label(*argument_span, format!("has type `{}`", argument_ty_name));
2317 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2318 let types_equal = return_ty_name == argument_ty_name;
2323 if types_equal { "also " } else { "" },
2329 "argument and return type have the same lifetime due to lifetime elision rules",
2332 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2333 lifetime-syntax.html#lifetime-elision>",
2336 cx.get_region_name_for_ty(return_ty, 0)
2338 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2339 // Region of return type and arguments checked to be the same earlier.
2340 let region_name = cx.get_region_name_for_ty(return_ty, 0);
2341 for (_, argument_span) in arguments {
2342 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2345 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2348 "use data from the highlighted arguments which match the `{}` lifetime of \