2 use rustc_const_eval::util::CallKind;
3 use rustc_data_structures::fx::FxHashSet;
4 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
6 use rustc_hir::def_id::DefId;
7 use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
8 use rustc_infer::infer::TyCtxtInferExt;
9 use rustc_infer::traits::ObligationCause;
10 use rustc_middle::mir::{
11 self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
12 FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
13 ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
15 use rustc_middle::ty::{
16 self, suggest_constraining_type_param, suggest_constraining_type_params, PredicateKind, Ty,
18 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
19 use rustc_span::symbol::sym;
20 use rustc_span::{BytePos, MultiSpan, Span};
21 use rustc_trait_selection::infer::InferCtxtExt;
22 use rustc_trait_selection::traits::TraitEngineExt as _;
24 use crate::borrow_set::TwoPhaseActivation;
25 use crate::borrowck_errors;
27 use crate::diagnostics::find_all_local_uses;
29 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
30 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
34 explain_borrow::{BorrowExplanation, LaterUseKind},
35 IncludingDowncast, RegionName, RegionNameSource, UseSpans,
40 /// Index of the "move out" that we found. The `MoveData` can
41 /// then tell us where the move occurred.
44 /// `true` if we traversed a back edge while walking from the point
45 /// of error to the move site.
46 traversed_back_edge: bool,
49 /// Which case a StorageDeadOrDrop is for.
50 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
51 enum StorageDeadOrDrop<'tcx> {
57 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
58 pub(crate) fn report_use_of_moved_or_uninitialized(
61 desired_action: InitializationRequiringAction,
62 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
66 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
67 moved_place={:?} used_place={:?} span={:?} mpi={:?}",
68 location, desired_action, moved_place, used_place, span, mpi
72 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
73 let span = use_spans.args_or_use();
75 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
77 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
78 move_site_vec, use_spans
80 let move_out_indices: Vec<_> =
81 move_site_vec.iter().map(|move_site| move_site.moi).collect();
83 if move_out_indices.is_empty() {
84 let root_place = PlaceRef { projection: &[], ..used_place };
86 if !self.uninitialized_error_reported.insert(root_place) {
88 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
95 match self.describe_place_with_options(used_place, IncludingDowncast(true)) {
96 Some(name) => format!("`{}`", name),
97 None => "value".to_owned(),
99 let mut err = self.cannot_act_on_uninitialized_variable(
101 desired_action.as_noun(),
103 .describe_place_with_options(moved_place, IncludingDowncast(true))
104 .unwrap_or_else(|| "_".to_owned()),
106 err.span_label(span, format!("use of possibly-uninitialized {}", item_msg));
108 use_spans.var_span_label_path_only(
110 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
113 self.buffer_error(err);
115 if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
116 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
118 "report_use_of_moved_or_uninitialized place: error suppressed \
126 let is_partial_move = move_site_vec.iter().any(|move_site| {
127 let move_out = self.move_data.moves[(*move_site).moi];
128 let moved_place = &self.move_data.move_paths[move_out.path].place;
129 // `*(_1)` where `_1` is a `Box` is actually a move out.
130 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
131 && self.body.local_decls[moved_place.local].ty.is_box();
134 && used_place != moved_place.as_ref()
135 && used_place.is_prefix_of(moved_place.as_ref())
138 let partial_str = if is_partial_move { "partial " } else { "" };
139 let partially_str = if is_partial_move { "partially " } else { "" };
141 let mut err = self.cannot_act_on_moved_value(
143 desired_action.as_noun(),
145 self.describe_place_with_options(moved_place, IncludingDowncast(true)),
148 let reinit_spans = maybe_reinitialized_locations
152 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
155 .collect::<Vec<Span>>();
156 let reinits = maybe_reinitialized_locations.len();
158 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
159 } else if reinits > 1 {
161 MultiSpan::from_spans(reinit_spans),
163 format!("these {} reinitializations might get skipped", reinits)
166 "these 3 reinitializations and {} other{} might get skipped",
168 if reinits == 4 { "" } else { "s" }
174 self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
176 let mut is_loop_move = false;
177 let mut in_pattern = false;
179 for move_site in &move_site_vec {
180 let move_out = self.move_data.moves[(*move_site).moi];
181 let moved_place = &self.move_data.move_paths[move_out.path].place;
183 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
184 let move_span = move_spans.args_or_use();
186 let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
188 let loop_message = if location == move_out.source || move_site.traversed_back_edge {
189 ", in previous iteration of loop"
194 if location == move_out.source {
198 self.explain_captures(
209 maybe_reinitialized_locations.is_empty(),
212 if let (UseSpans::PatUse(span), []) =
213 (move_spans, &maybe_reinitialized_locations[..])
215 if maybe_reinitialized_locations.is_empty() {
216 err.span_suggestion_verbose(
219 "borrow this field in the pattern to avoid moving {}",
220 self.describe_place(moved_place.as_ref())
221 .map(|n| format!("`{}`", n))
222 .unwrap_or_else(|| "the value".to_string())
225 Applicability::MachineApplicable,
232 use_spans.var_span_label_path_only(
234 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
241 "value {} here after {}move",
242 desired_action.as_verb_in_past_tense(),
248 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
249 let needs_note = match ty.kind() {
250 ty::Closure(id, _) => {
251 let tables = self.infcx.tcx.typeck(id.expect_local());
252 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
254 tables.closure_kind_origins().get(hir_id).is_none()
259 let mpi = self.move_data.moves[move_out_indices[0]].path;
260 let place = &self.move_data.move_paths[mpi].place;
261 let ty = place.ty(self.body, self.infcx.tcx).ty;
263 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
264 if is_loop_move & !in_pattern {
265 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
266 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
267 err.span_suggestion_verbose(
270 "consider creating a fresh reborrow of {} here",
271 self.describe_place(moved_place)
272 .map(|n| format!("`{}`", n))
273 .unwrap_or_else(|| "the mutable reference".to_string()),
275 "&mut *".to_string(),
276 Applicability::MachineApplicable,
283 self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
284 let note_msg = match opt_name {
285 Some(ref name) => format!("`{}`", name),
286 None => "value".to_owned(),
288 if let ty::Param(param_ty) = ty.kind() {
289 let tcx = self.infcx.tcx;
290 let generics = tcx.generics_of(self.mir_def_id());
291 let param = generics.type_param(¶m_ty, tcx);
292 if let Some(generics) = tcx
293 .typeck_root_def_id(self.mir_def_id().to_def_id())
295 .and_then(|def_id| tcx.hir().get_generics(def_id))
297 suggest_constraining_type_param(
307 // Try to find predicates on *generic params* that would allow copying `ty`
309 let tcx = self.infcx.tcx;
310 let generics = tcx.generics_of(self.mir_def_id());
311 if let Some(hir_generics) = tcx
312 .typeck_root_def_id(self.mir_def_id().to_def_id())
314 .and_then(|def_id| tcx.hir().get_generics(def_id))
316 let predicates: Result<Vec<_>, _> = tcx.infer_ctxt().enter(|infcx| {
318 <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
320 let copy_did = infcx.tcx.lang_items().copy_trait().unwrap();
321 let cause = ObligationCause::new(
324 rustc_infer::traits::ObligationCauseCode::MiscObligation,
326 fulfill_cx.register_bound(
329 // Erase any region vids from the type, which may not be resolved
330 infcx.tcx.erase_regions(ty),
334 // Select all, including ambiguous predicates
335 let errors = fulfill_cx.select_all_or_error(&infcx);
337 // Only emit suggestion if all required predicates are on generic
340 .map(|err| match err.obligation.predicate.kind().skip_binder() {
341 PredicateKind::Trait(predicate) => {
342 match predicate.self_ty().kind() {
343 ty::Param(param_ty) => Ok((
344 generics.type_param(param_ty, tcx),
347 .print_only_trait_path()
358 if let Ok(predicates) = predicates {
359 suggest_constraining_type_params(
363 predicates.iter().map(|(param, constraint)| {
364 (param.name.as_str(), &**constraint, None)
371 let span = if let Some(local) = place.as_local() {
372 let decl = &self.body.local_decls[local];
373 Some(decl.source_info.span)
377 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
380 if let UseSpans::FnSelfUse {
381 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
386 "{} occurs due to deref coercion to `{}`",
387 desired_action.as_noun(),
391 // Check first whether the source is accessible (issue #87060)
392 if self.infcx.tcx.sess.source_map().span_to_snippet(deref_target).is_ok() {
393 err.span_note(deref_target, "deref defined here");
397 self.buffer_move_error(move_out_indices, (used_place, err));
401 pub(crate) fn report_move_out_while_borrowed(
404 (place, span): (Place<'tcx>, Span),
405 borrow: &BorrowData<'tcx>,
408 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
409 location, place, span, borrow
411 let value_msg = self.describe_any_place(place.as_ref());
412 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
414 let borrow_spans = self.retrieve_borrow_spans(borrow);
415 let borrow_span = borrow_spans.args_or_use();
417 let move_spans = self.move_spans(place.as_ref(), location);
418 let span = move_spans.args_or_use();
421 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
422 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
423 err.span_label(span, format!("move out of {} occurs here", value_msg));
425 borrow_spans.var_span_label_path_only(
427 format!("borrow occurs due to use{}", borrow_spans.describe()),
430 move_spans.var_span_label(
432 format!("move occurs due to use{}", move_spans.describe()),
436 self.explain_why_borrow_contains_point(location, borrow, None)
437 .add_explanation_to_diagnostic(
446 self.buffer_error(err);
449 pub(crate) fn report_use_while_mutably_borrowed(
452 (place, _span): (Place<'tcx>, Span),
453 borrow: &BorrowData<'tcx>,
454 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
455 let borrow_spans = self.retrieve_borrow_spans(borrow);
456 let borrow_span = borrow_spans.args_or_use();
458 // Conflicting borrows are reported separately, so only check for move
460 let use_spans = self.move_spans(place.as_ref(), location);
461 let span = use_spans.var_or_use();
463 // 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
464 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
465 let mut err = self.cannot_use_when_mutably_borrowed(
467 &self.describe_any_place(place.as_ref()),
469 &self.describe_any_place(borrow.borrowed_place.as_ref()),
472 borrow_spans.var_span_label(
475 let place = &borrow.borrowed_place;
476 let desc_place = self.describe_any_place(place.as_ref());
477 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
482 self.explain_why_borrow_contains_point(location, borrow, None)
483 .add_explanation_to_diagnostic(
495 pub(crate) fn report_conflicting_borrow(
498 (place, span): (Place<'tcx>, Span),
499 gen_borrow_kind: BorrowKind,
500 issued_borrow: &BorrowData<'tcx>,
501 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
502 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
503 let issued_span = issued_spans.args_or_use();
505 let borrow_spans = self.borrow_spans(span, location);
506 let span = borrow_spans.args_or_use();
508 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
514 let (desc_place, msg_place, msg_borrow, union_type_name) =
515 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
517 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
518 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
520 // FIXME: supply non-"" `opt_via` when appropriate
521 let first_borrow_desc;
522 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
523 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
524 first_borrow_desc = "mutable ";
525 self.cannot_reborrow_already_borrowed(
537 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
538 first_borrow_desc = "immutable ";
539 self.cannot_reborrow_already_borrowed(
552 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
553 first_borrow_desc = "first ";
554 let mut err = self.cannot_mutably_borrow_multiply(
562 self.suggest_split_at_mut_if_applicable(
565 issued_borrow.borrowed_place,
570 (BorrowKind::Unique, BorrowKind::Unique) => {
571 first_borrow_desc = "first ";
572 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
575 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
576 if let Some(immutable_section_description) =
577 self.classify_immutable_section(issued_borrow.assigned_place)
579 let mut err = self.cannot_mutate_in_immutable_section(
583 immutable_section_description,
586 borrow_spans.var_span_label(
589 "borrow occurs due to use of {}{}",
591 borrow_spans.describe(),
598 first_borrow_desc = "immutable ";
599 self.cannot_reborrow_already_borrowed(
613 (BorrowKind::Unique, _) => {
614 first_borrow_desc = "first ";
615 self.cannot_uniquely_borrow_by_one_closure(
627 (BorrowKind::Shared, BorrowKind::Unique) => {
628 first_borrow_desc = "first ";
629 self.cannot_reborrow_already_uniquely_borrowed(
642 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
643 first_borrow_desc = "first ";
644 self.cannot_reborrow_already_uniquely_borrowed(
657 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
660 BorrowKind::Mut { .. }
663 | BorrowKind::Shallow,
667 if issued_spans == borrow_spans {
668 borrow_spans.var_span_label(
670 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
671 gen_borrow_kind.describe_mutability(),
674 let borrow_place = &issued_borrow.borrowed_place;
675 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
676 issued_spans.var_span_label(
679 "first borrow occurs due to use of {}{}",
681 issued_spans.describe(),
683 issued_borrow.kind.describe_mutability(),
686 borrow_spans.var_span_label(
689 "second borrow occurs due to use of {}{}",
691 borrow_spans.describe(),
693 gen_borrow_kind.describe_mutability(),
697 if union_type_name != "" {
699 "{} is a field of the union `{}`, so it overlaps the field {}",
700 msg_place, union_type_name, msg_borrow,
704 explanation.add_explanation_to_diagnostic(
711 Some((issued_span, span)),
714 self.suggest_using_local_if_applicable(
726 #[instrument(level = "debug", skip(self, err))]
727 fn suggest_using_local_if_applicable(
729 err: &mut Diagnostic,
731 (place, span): (Place<'tcx>, Span),
732 gen_borrow_kind: BorrowKind,
733 issued_borrow: &BorrowData<'tcx>,
734 explanation: BorrowExplanation,
737 matches!(explanation, BorrowExplanation::UsedLater(LaterUseKind::Call, _call_span, _));
739 debug!("not later used in call");
744 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
747 issued_borrow.reserve_location
749 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
751 let inner_param_location = location;
752 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
753 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
756 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
758 "`inner_param_location` {:?} is not for an assignment: {:?}",
759 inner_param_location, inner_param_stmt
763 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
764 let Some((inner_call_loc,inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
765 let Either::Right(term) = self.body.stmt_at(loc) else {
766 debug!("{:?} is a statement, so it can't be a call", loc);
769 let TerminatorKind::Call { args, .. } = &term.kind else {
770 debug!("not a call: {:?}", term);
773 debug!("checking call args for uses of inner_param: {:?}", args);
774 if args.contains(&Operand::Move(inner_param)) {
780 debug!("no uses of inner_param found as a by-move call arg");
783 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
785 let inner_call_span = inner_call_term.source_info.span;
786 let outer_call_span = outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span;
787 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
788 // FIXME: This stops the suggestion in some cases where it should be emitted.
789 // Fix the spans for those cases so it's emitted correctly.
791 "outer span {:?} does not strictly contain inner span {:?}",
792 outer_call_span, inner_call_span
796 err.span_help(inner_call_span, "try adding a local storing this argument...");
797 err.span_help(outer_call_span, "...and then using that local as the argument to this call");
800 fn suggest_split_at_mut_if_applicable(
802 err: &mut Diagnostic,
804 borrowed_place: Place<'tcx>,
806 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
807 (&place.projection[..], &borrowed_place.projection[..])
810 "consider using `.split_at_mut(position)` or similar method to obtain \
811 two mutable non-overlapping sub-slices",
816 /// Returns the description of the root place for a conflicting borrow and the full
817 /// descriptions of the places that caused the conflict.
819 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
820 /// attempted while a shared borrow is live, then this function will return:
824 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
825 /// a shared borrow of another field `x.y`, then this function will return:
827 /// ("x", "x.z", "x.y")
829 /// In the more complex union case, where the union is a field of a struct, then if a mutable
830 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
831 /// another field `x.u.y`, then this function will return:
833 /// ("x.u", "x.u.z", "x.u.y")
835 /// This is used when creating error messages like below:
838 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
839 /// mutable (via `a.u.s.b`) [E0502]
841 pub(crate) fn describe_place_for_conflicting_borrow(
843 first_borrowed_place: Place<'tcx>,
844 second_borrowed_place: Place<'tcx>,
845 ) -> (String, String, String, String) {
846 // Define a small closure that we can use to check if the type of a place
848 let union_ty = |place_base| {
849 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
850 // using a type annotation in the closure argument instead leads to a lifetime error.
851 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
852 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
855 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
856 // code duplication (particularly around returning an empty description in the failure
860 // If we have a conflicting borrow of the same place, then we don't want to add
861 // an extraneous "via x.y" to our diagnostics, so filter out this case.
862 first_borrowed_place != second_borrowed_place
865 // We're going to want to traverse the first borrowed place to see if we can find
866 // field access to a union. If we find that, then we will keep the place of the
867 // union being accessed and the field that was being accessed so we can check the
868 // second borrowed place for the same union and an access to a different field.
869 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
871 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
872 return Some((place_base, field));
879 .and_then(|(target_base, target_field)| {
880 // With the place of a union and a field access into it, we traverse the second
881 // borrowed place and look for an access to a different field of the same union.
882 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
883 if let ProjectionElem::Field(field, _) = elem {
884 if let Some(union_ty) = union_ty(place_base) {
885 if field != target_field && place_base == target_base {
887 self.describe_any_place(place_base),
888 self.describe_any_place(first_borrowed_place.as_ref()),
889 self.describe_any_place(second_borrowed_place.as_ref()),
890 union_ty.to_string(),
899 // If we didn't find a field access into a union, or both places match, then
900 // only return the description of the first place.
902 self.describe_any_place(first_borrowed_place.as_ref()),
910 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
912 /// This means that some data referenced by `borrow` needs to live
913 /// past the point where the StorageDeadOrDrop of `place` occurs.
914 /// This is usually interpreted as meaning that `place` has too
915 /// short a lifetime. (But sometimes it is more useful to report
916 /// it as a more direct conflict between the execution of a
917 /// `Drop::drop` with an aliasing borrow.)
918 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
921 borrow: &BorrowData<'tcx>,
922 place_span: (Place<'tcx>, Span),
923 kind: Option<WriteKind>,
926 "report_borrowed_value_does_not_live_long_enough(\
927 {:?}, {:?}, {:?}, {:?}\
929 location, borrow, place_span, kind
932 let drop_span = place_span.1;
934 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
936 let borrow_spans = self.retrieve_borrow_spans(borrow);
937 let borrow_span = borrow_spans.var_or_use_path_span();
939 assert!(root_place.projection.is_empty());
940 let proper_span = self.body.local_decls[root_place.local].source_info.span;
942 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
944 if self.access_place_error_reported.contains(&(
945 Place { local: root_place.local, projection: root_place_projection },
949 "suppressing access_place error when borrow doesn't live long enough for {:?}",
955 self.access_place_error_reported.insert((
956 Place { local: root_place.local, projection: root_place_projection },
960 let borrowed_local = borrow.borrowed_place.local;
961 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
963 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
964 self.buffer_error(err);
968 if let StorageDeadOrDrop::Destructor(dropped_ty) =
969 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
971 // If a borrow of path `B` conflicts with drop of `D` (and
972 // we're not in the uninteresting case where `B` is a
973 // prefix of `D`), then report this as a more interesting
974 // destructor conflict.
975 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
976 self.report_borrow_conflicts_with_destructor(
977 location, borrow, place_span, kind, dropped_ty,
983 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
985 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
986 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
989 "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
990 place_desc, explanation
992 let err = match (place_desc, explanation) {
993 // If the outlives constraint comes from inside the closure,
998 // Box::new(|| y) as Box<Fn() -> &'static i32>
1000 // then just use the normal error. The closure isn't escaping
1001 // and `move` will not help here.
1004 BorrowExplanation::MustBeValidFor {
1006 category @ (ConstraintCategory::Return(_)
1007 | ConstraintCategory::CallArgument
1008 | ConstraintCategory::OpaqueType),
1009 from_closure: false,
1014 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1015 .report_escaping_closure_capture(
1021 &format!("`{}`", name),
1025 BorrowExplanation::MustBeValidFor {
1026 category: ConstraintCategory::Assignment,
1027 from_closure: false,
1031 RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
1037 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1038 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1046 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1056 self.buffer_error(err);
1059 fn report_local_value_does_not_live_long_enough(
1063 borrow: &BorrowData<'tcx>,
1065 borrow_spans: UseSpans<'tcx>,
1066 explanation: BorrowExplanation,
1067 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1069 "report_local_value_does_not_live_long_enough(\
1070 {:?}, {:?}, {:?}, {:?}, {:?}\
1072 location, name, borrow, drop_span, borrow_spans
1075 let borrow_span = borrow_spans.var_or_use_path_span();
1076 if let BorrowExplanation::MustBeValidFor {
1080 from_closure: false,
1084 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1089 opt_place_desc.as_ref(),
1095 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1097 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1098 let region_name = annotation.emit(self, &mut err);
1102 format!("`{}` would have to be valid for `{}`...", name, region_name),
1105 let fn_hir_id = self.mir_hir_id();
1109 "...but `{}` will be dropped here, when the {} returns",
1114 .opt_name(fn_hir_id)
1115 .map(|name| format!("function `{}`", name))
1116 .unwrap_or_else(|| {
1120 .typeck(self.mir_def_id())
1121 .node_type(fn_hir_id)
1124 ty::Closure(..) => "enclosing closure",
1125 ty::Generator(..) => "enclosing generator",
1126 kind => bug!("expected closure or generator, found {:?}", kind),
1134 "functions cannot return a borrow to data owned within the function's scope, \
1135 functions can only return borrows to data passed as arguments",
1138 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1139 references-and-borrowing.html#dangling-references>",
1142 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1144 explanation.add_explanation_to_diagnostic(
1155 err.span_label(borrow_span, "borrowed value does not live long enough");
1156 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1158 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1160 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1162 explanation.add_explanation_to_diagnostic(
1176 fn report_borrow_conflicts_with_destructor(
1179 borrow: &BorrowData<'tcx>,
1180 (place, drop_span): (Place<'tcx>, Span),
1181 kind: Option<WriteKind>,
1182 dropped_ty: Ty<'tcx>,
1185 "report_borrow_conflicts_with_destructor(\
1186 {:?}, {:?}, ({:?}, {:?}), {:?}\
1188 location, borrow, place, drop_span, kind,
1191 let borrow_spans = self.retrieve_borrow_spans(borrow);
1192 let borrow_span = borrow_spans.var_or_use();
1194 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1196 let what_was_dropped = match self.describe_place(place.as_ref()) {
1197 Some(name) => format!("`{}`", name),
1198 None => String::from("temporary value"),
1201 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1202 Some(borrowed) => format!(
1203 "here, drop of {D} needs exclusive access to `{B}`, \
1204 because the type `{T}` implements the `Drop` trait",
1205 D = what_was_dropped,
1210 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1211 D = what_was_dropped,
1215 err.span_label(drop_span, label);
1217 // Only give this note and suggestion if they could be relevant.
1219 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1221 BorrowExplanation::UsedLater { .. }
1222 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1223 err.note("consider using a `let` binding to create a longer lived value");
1228 explanation.add_explanation_to_diagnostic(
1238 self.buffer_error(err);
1241 fn report_thread_local_value_does_not_live_long_enough(
1245 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1247 "report_thread_local_value_does_not_live_long_enough(\
1250 drop_span, borrow_span
1253 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1257 "thread-local variables cannot be borrowed beyond the end of the function",
1259 err.span_label(drop_span, "end of enclosing function is here");
1264 fn report_temporary_value_does_not_live_long_enough(
1267 borrow: &BorrowData<'tcx>,
1269 borrow_spans: UseSpans<'tcx>,
1271 explanation: BorrowExplanation,
1272 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1274 "report_temporary_value_does_not_live_long_enough(\
1275 {:?}, {:?}, {:?}, {:?}\
1277 location, borrow, drop_span, proper_span
1280 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1283 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1294 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1295 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1296 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1299 BorrowExplanation::UsedLater(..)
1300 | BorrowExplanation::UsedLaterInLoop(..)
1301 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1302 // Only give this note and suggestion if it could be relevant.
1303 err.note("consider using a `let` binding to create a longer lived value");
1307 explanation.add_explanation_to_diagnostic(
1317 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1319 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1324 fn try_report_cannot_return_reference_to_local(
1326 borrow: &BorrowData<'tcx>,
1329 category: ConstraintCategory,
1330 opt_place_desc: Option<&String>,
1331 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1332 let return_kind = match category {
1333 ConstraintCategory::Return(_) => "return",
1334 ConstraintCategory::Yield => "yield",
1338 // FIXME use a better heuristic than Spans
1339 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1345 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1346 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1347 match self.body.local_kind(local) {
1348 LocalKind::ReturnPointer | LocalKind::Temp => {
1349 bug!("temporary or return pointer with a name")
1351 LocalKind::Var => "local variable ",
1353 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1355 "variable captured by `move` "
1357 LocalKind::Arg => "function parameter ",
1363 format!("{}`{}`", local_kind, place_desc),
1364 format!("`{}` is borrowed here", place_desc),
1368 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1369 let local = root_place.local;
1370 match self.body.local_kind(local) {
1371 LocalKind::ReturnPointer | LocalKind::Temp => {
1372 ("temporary value".to_string(), "temporary value created here".to_string())
1375 "function parameter".to_string(),
1376 "function parameter borrowed here".to_string(),
1379 ("local binding".to_string(), "local binding introduced here".to_string())
1384 let mut err = self.cannot_return_reference_to_local(
1391 if return_span != borrow_span {
1392 err.span_label(borrow_span, note);
1394 let tcx = self.infcx.tcx;
1395 let ty_params = ty::List::empty();
1397 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1398 let return_ty = tcx.erase_regions(return_ty);
1401 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1404 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1405 .must_apply_modulo_regions()
1407 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1408 err.span_suggestion_hidden(
1410 "use `.collect()` to allocate the iterator",
1411 format!("{}{}", snippet, ".collect::<Vec<_>>()"),
1412 Applicability::MaybeIncorrect,
1422 fn report_escaping_closure_capture(
1424 use_span: UseSpans<'tcx>,
1426 fr_name: &RegionName,
1427 category: ConstraintCategory,
1428 constraint_span: Span,
1430 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1431 let tcx = self.infcx.tcx;
1432 let args_span = use_span.args_or_use();
1434 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1436 if string.starts_with("async ") {
1437 let pos = args_span.lo() + BytePos(6);
1438 (args_span.with_lo(pos).with_hi(pos), "move ".to_string())
1439 } else if string.starts_with("async|") {
1440 let pos = args_span.lo() + BytePos(5);
1441 (args_span.with_lo(pos).with_hi(pos), " move".to_string())
1443 (args_span.shrink_to_lo(), "move ".to_string())
1446 Err(_) => (args_span, "move |<args>| <body>".to_string()),
1448 let kind = match use_span.generator_kind() {
1449 Some(generator_kind) => match generator_kind {
1450 GeneratorKind::Async(async_kind) => match async_kind {
1451 AsyncGeneratorKind::Block => "async block",
1452 AsyncGeneratorKind::Closure => "async closure",
1453 _ => bug!("async block/closure expected, but async function found."),
1455 GeneratorKind::Gen => "generator",
1461 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1462 err.span_suggestion_verbose(
1465 "to force the {} to take ownership of {} (and any \
1466 other referenced variables), use the `move` keyword",
1470 Applicability::MachineApplicable,
1474 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1475 let msg = format!("{} is returned here", kind);
1476 err.span_note(constraint_span, &msg);
1478 ConstraintCategory::CallArgument => {
1479 fr_name.highlight_region_name(&mut err);
1480 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1482 "async blocks are not executed immediately and must either take a \
1483 reference or ownership of outside variables they use",
1486 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1487 err.span_note(constraint_span, &msg);
1491 "report_escaping_closure_capture called with unexpected constraint \
1500 fn report_escaping_data(
1503 name: &Option<String>,
1507 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1508 let tcx = self.infcx.tcx;
1510 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1513 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1517 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1520 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1522 if let Some(name) = name {
1525 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1530 format!("reference escapes the {} body here", escapes_from),
1537 fn get_moved_indexes(
1541 ) -> (Vec<MoveSite>, Vec<Location>) {
1542 fn predecessor_locations<'a>(
1543 body: &'a mir::Body<'_>,
1545 ) -> impl Iterator<Item = Location> + 'a {
1546 if location.statement_index == 0 {
1547 let predecessors = body.predecessors()[location.block].to_vec();
1548 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1550 Either::Right(std::iter::once(Location {
1551 statement_index: location.statement_index - 1,
1557 let mut mpis = vec![mpi];
1558 let move_paths = &self.move_data.move_paths;
1559 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1561 let mut stack = Vec::new();
1562 let mut back_edge_stack = Vec::new();
1564 predecessor_locations(self.body, location).for_each(|predecessor| {
1565 if location.dominates(predecessor, &self.dominators) {
1566 back_edge_stack.push(predecessor)
1568 stack.push(predecessor);
1572 let mut reached_start = false;
1574 /* Check if the mpi is initialized as an argument */
1575 let mut is_argument = false;
1576 for arg in self.body.args_iter() {
1577 let path = self.move_data.rev_lookup.find_local(arg);
1578 if mpis.contains(&path) {
1583 let mut visited = FxHashSet::default();
1584 let mut move_locations = FxHashSet::default();
1585 let mut reinits = vec![];
1586 let mut result = vec![];
1588 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1590 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1591 location, is_back_edge
1594 if !visited.insert(location) {
1600 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1601 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1602 // this analysis only tries to find moves explicitly
1603 // written by the user, so we ignore the move-outs
1604 // created by `StorageDead` and at the beginning
1607 // If we are found a use of a.b.c which was in error, then we want to look for
1608 // moves not only of a.b.c but also a.b and a.
1610 // Note that the moves data already includes "parent" paths, so we don't have to
1611 // worry about the other case: that is, if there is a move of a.b.c, it is already
1612 // marked as a move of a.b and a as well, so we will generate the correct errors
1614 for moi in &self.move_data.loc_map[location] {
1615 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1616 let path = self.move_data.moves[*moi].path;
1617 if mpis.contains(&path) {
1619 "report_use_of_moved_or_uninitialized: found {:?}",
1620 move_paths[path].place
1622 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1623 move_locations.insert(location);
1625 // Strictly speaking, we could continue our DFS here. There may be
1626 // other moves that can reach the point of error. But it is kind of
1627 // confusing to highlight them.
1635 // drop(a); // <-- current point of error
1638 // Because we stop the DFS here, we only highlight `let c = a`,
1639 // and not `let b = a`. We will of course also report an error at
1640 // `let c = a` which highlights `let b = a` as the move.
1647 let mut any_match = false;
1648 for ii in &self.move_data.init_loc_map[location] {
1649 let init = self.move_data.inits[*ii];
1651 InitKind::Deep | InitKind::NonPanicPathOnly => {
1652 if mpis.contains(&init.path) {
1656 InitKind::Shallow => {
1657 if mpi == init.path {
1664 reinits.push(location);
1670 while let Some(location) = stack.pop() {
1671 if dfs_iter(&mut result, location, false) {
1675 let mut has_predecessor = false;
1676 predecessor_locations(self.body, location).for_each(|predecessor| {
1677 if location.dominates(predecessor, &self.dominators) {
1678 back_edge_stack.push(predecessor)
1680 stack.push(predecessor);
1682 has_predecessor = true;
1685 if !has_predecessor {
1686 reached_start = true;
1689 if (is_argument || !reached_start) && result.is_empty() {
1690 /* Process back edges (moves in future loop iterations) only if
1691 the move path is definitely initialized upon loop entry,
1692 to avoid spurious "in previous iteration" errors.
1693 During DFS, if there's a path from the error back to the start
1694 of the function with no intervening init or move, then the
1695 move path may be uninitialized at loop entry.
1697 while let Some(location) = back_edge_stack.pop() {
1698 if dfs_iter(&mut result, location, true) {
1702 predecessor_locations(self.body, location)
1703 .for_each(|predecessor| back_edge_stack.push(predecessor));
1707 // Check if we can reach these reinits from a move location.
1708 let reinits_reachable = reinits
1711 let mut visited = FxHashSet::default();
1712 let mut stack = vec![*reinit];
1713 while let Some(location) = stack.pop() {
1714 if !visited.insert(location) {
1717 if move_locations.contains(&location) {
1720 stack.extend(predecessor_locations(self.body, location));
1724 .collect::<Vec<Location>>();
1725 (result, reinits_reachable)
1728 pub(crate) fn report_illegal_mutation_of_borrowed(
1731 (place, span): (Place<'tcx>, Span),
1732 loan: &BorrowData<'tcx>,
1734 let loan_spans = self.retrieve_borrow_spans(loan);
1735 let loan_span = loan_spans.args_or_use();
1737 let descr_place = self.describe_any_place(place.as_ref());
1738 if loan.kind == BorrowKind::Shallow {
1739 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1740 let mut err = self.cannot_mutate_in_immutable_section(
1747 loan_spans.var_span_label(
1749 format!("borrow occurs due to use{}", loan_spans.describe()),
1750 loan.kind.describe_mutability(),
1753 self.buffer_error(err);
1759 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
1761 loan_spans.var_span_label(
1763 format!("borrow occurs due to use{}", loan_spans.describe()),
1764 loan.kind.describe_mutability(),
1767 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
1777 self.explain_deref_coercion(loan, &mut err);
1779 self.buffer_error(err);
1782 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
1783 let tcx = self.infcx.tcx;
1785 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
1786 Some((method_did, method_substs)),
1788 &self.body[loan.reserve_location.block].terminator,
1789 rustc_const_eval::util::find_self_call(
1792 loan.assigned_place.local,
1793 loan.reserve_location.block,
1796 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
1798 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
1799 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
1802 if let Some(Ok(instance)) = deref_target {
1803 let deref_target_ty = instance.ty(tcx, self.param_env);
1805 "borrow occurs due to deref coercion to `{}`",
1808 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
1814 /// Reports an illegal reassignment; for example, an assignment to
1815 /// (part of) a non-`mut` local that occurs potentially after that
1816 /// local has already been initialized. `place` is the path being
1817 /// assigned; `err_place` is a place providing a reason why
1818 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
1819 /// assignment to `x.f`).
1820 pub(crate) fn report_illegal_reassignment(
1822 _location: Location,
1823 (place, span): (Place<'tcx>, Span),
1824 assigned_span: Span,
1825 err_place: Place<'tcx>,
1827 let (from_arg, local_decl, local_name) = match err_place.as_local() {
1829 self.body.local_kind(local) == LocalKind::Arg,
1830 Some(&self.body.local_decls[local]),
1831 self.local_names[local],
1833 None => (false, None, None),
1836 // If root local is initialized immediately (everything apart from let
1837 // PATTERN;) then make the error refer to that local, rather than the
1838 // place being assigned later.
1839 let (place_description, assigned_span) = match local_decl {
1842 Some(box LocalInfo::User(
1843 ClearCrossCrate::Clear
1844 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
1845 opt_match_place: None,
1849 | Some(box LocalInfo::StaticRef { .. })
1853 | None => (self.describe_any_place(place.as_ref()), assigned_span),
1854 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
1857 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
1858 let msg = if from_arg {
1859 "cannot assign to immutable argument"
1861 "cannot assign twice to immutable variable"
1863 if span != assigned_span {
1865 err.span_label(assigned_span, format!("first assignment to {}", place_description));
1868 if let Some(decl) = local_decl
1869 && let Some(name) = local_name
1870 && decl.can_be_made_mutable()
1872 err.span_suggestion(
1873 decl.source_info.span,
1874 "consider making this binding mutable",
1875 format!("mut {}", name),
1876 Applicability::MachineApplicable,
1879 err.span_label(span, msg);
1880 self.buffer_error(err);
1883 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
1884 let tcx = self.infcx.tcx;
1885 match place.last_projection() {
1886 None => StorageDeadOrDrop::LocalStorageDead,
1887 Some((place_base, elem)) => {
1888 // FIXME(spastorino) make this iterate
1889 let base_access = self.classify_drop_access_kind(place_base);
1891 ProjectionElem::Deref => match base_access {
1892 StorageDeadOrDrop::LocalStorageDead
1893 | StorageDeadOrDrop::BoxedStorageDead => {
1895 place_base.ty(self.body, tcx).ty.is_box(),
1896 "Drop of value behind a reference or raw pointer"
1898 StorageDeadOrDrop::BoxedStorageDead
1900 StorageDeadOrDrop::Destructor(_) => base_access,
1902 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
1903 let base_ty = place_base.ty(self.body, tcx).ty;
1904 match base_ty.kind() {
1905 ty::Adt(def, _) if def.has_dtor(tcx) => {
1906 // Report the outermost adt with a destructor
1908 StorageDeadOrDrop::Destructor(_) => base_access,
1909 StorageDeadOrDrop::LocalStorageDead
1910 | StorageDeadOrDrop::BoxedStorageDead => {
1911 StorageDeadOrDrop::Destructor(base_ty)
1918 ProjectionElem::ConstantIndex { .. }
1919 | ProjectionElem::Subslice { .. }
1920 | ProjectionElem::Index(_) => base_access,
1926 /// Describe the reason for the fake borrow that was assigned to `place`.
1927 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
1928 use rustc_middle::mir::visit::Visitor;
1929 struct FakeReadCauseFinder<'tcx> {
1931 cause: Option<FakeReadCause>,
1933 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
1934 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
1936 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
1937 if *place == self.place =>
1939 self.cause = Some(*cause);
1945 let mut visitor = FakeReadCauseFinder { place, cause: None };
1946 visitor.visit_body(&self.body);
1947 match visitor.cause {
1948 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
1949 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
1954 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
1955 /// borrow of local value that does not live long enough.
1956 fn annotate_argument_and_return_for_borrow(
1958 borrow: &BorrowData<'tcx>,
1959 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
1960 // Define a fallback for when we can't match a closure.
1962 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
1966 let ty = self.infcx.tcx.type_of(self.mir_def_id());
1968 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
1969 self.mir_def_id().to_def_id(),
1970 self.infcx.tcx.fn_sig(self.mir_def_id()),
1977 // In order to determine whether we need to annotate, we need to check whether the reserve
1978 // place was an assignment into a temporary.
1980 // If it was, we check whether or not that temporary is eventually assigned into the return
1981 // place. If it was, we can add annotations about the function's return type and arguments
1982 // and it'll make sense.
1983 let location = borrow.reserve_location;
1984 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
1985 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
1986 &self.body[location.block].statements.get(location.statement_index)
1988 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
1989 // Check that the initial assignment of the reserve location is into a temporary.
1990 let mut target = match reservation.as_local() {
1991 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
1995 // Next, look through the rest of the block, checking if we are assigning the
1996 // `target` (that is, the place that contains our borrow) to anything.
1997 let mut annotated_closure = None;
1998 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2000 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2003 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2004 if let Some(assigned_to) = place.as_local() {
2006 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2010 // Check if our `target` was captured by a closure.
2011 if let Rvalue::Aggregate(
2012 box AggregateKind::Closure(def_id, substs),
2016 for operand in operands {
2017 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2021 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2025 // Find the local from the operand.
2026 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2030 if assigned_from_local != target {
2034 // If a closure captured our `target` and then assigned
2035 // into a place then we should annotate the closure in
2036 // case it ends up being assigned into the return place.
2038 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2040 "annotate_argument_and_return_for_borrow: \
2041 annotated_closure={:?} assigned_from_local={:?} \
2043 annotated_closure, assigned_from_local, assigned_to
2046 if assigned_to == mir::RETURN_PLACE {
2047 // If it was assigned directly into the return place, then
2049 return annotated_closure;
2051 // Otherwise, update the target.
2052 target = assigned_to;
2056 // If none of our closure's operands matched, then skip to the next
2061 // Otherwise, look at other types of assignment.
2062 let assigned_from = match rvalue {
2063 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2064 Rvalue::Use(operand) => match operand {
2065 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2073 "annotate_argument_and_return_for_borrow: \
2074 assigned_from={:?}",
2078 // Find the local from the rvalue.
2079 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2081 "annotate_argument_and_return_for_borrow: \
2082 assigned_from_local={:?}",
2083 assigned_from_local,
2086 // Check if our local matches the target - if so, we've assigned our
2087 // borrow to a new place.
2088 if assigned_from_local != target {
2092 // If we assigned our `target` into a new place, then we should
2093 // check if it was the return place.
2095 "annotate_argument_and_return_for_borrow: \
2096 assigned_from_local={:?} assigned_to={:?}",
2097 assigned_from_local, assigned_to
2099 if assigned_to == mir::RETURN_PLACE {
2100 // If it was then return the annotated closure if there was one,
2101 // else, annotate this function.
2102 return annotated_closure.or_else(fallback);
2105 // If we didn't assign into the return place, then we just update
2107 target = assigned_to;
2112 // Check the terminator if we didn't find anything in the statements.
2113 let terminator = &self.body[location.block].terminator();
2115 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2118 if let TerminatorKind::Call { destination: Some((place, _)), args, .. } =
2121 if let Some(assigned_to) = place.as_local() {
2123 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2126 for operand in args {
2127 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2131 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2135 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2137 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2138 assigned_from_local,
2141 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2142 return annotated_closure.or_else(fallback);
2150 // If we haven't found an assignment into the return place, then we need not add
2152 debug!("annotate_argument_and_return_for_borrow: none found");
2156 /// Annotate the first argument and return type of a function signature if they are
2161 sig: ty::PolyFnSig<'tcx>,
2162 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2163 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2164 let is_closure = self.infcx.tcx.is_closure(did);
2165 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?);
2166 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2168 // We need to work out which arguments to highlight. We do this by looking
2169 // at the return type, where there are three cases:
2171 // 1. If there are named arguments, then we should highlight the return type and
2172 // highlight any of the arguments that are also references with that lifetime.
2173 // If there are no arguments that have the same lifetime as the return type,
2174 // then don't highlight anything.
2175 // 2. The return type is a reference with an anonymous lifetime. If this is
2176 // the case, then we can take advantage of (and teach) the lifetime elision
2179 // We know that an error is being reported. So the arguments and return type
2180 // must satisfy the elision rules. Therefore, if there is a single argument
2181 // then that means the return type and first (and only) argument have the same
2182 // lifetime and the borrow isn't meeting that, we can highlight the argument
2185 // If there are multiple arguments then the first argument must be self (else
2186 // it would not satisfy the elision rules), so we can highlight self and the
2188 // 3. The return type is not a reference. In this case, we don't highlight
2190 let return_ty = sig.output();
2191 match return_ty.skip_binder().kind() {
2192 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2193 // This is case 1 from above, return type is a named reference so we need to
2194 // search for relevant arguments.
2195 let mut arguments = Vec::new();
2196 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2197 if let ty::Ref(argument_region, _, _) = argument.kind() {
2198 if argument_region == return_region {
2199 // Need to use the `rustc_middle::ty` types to compare against the
2200 // `return_region`. Then use the `rustc_hir` type to get only
2201 // the lifetime span.
2202 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2203 // With access to the lifetime, we can get
2205 arguments.push((*argument, lifetime.span));
2207 bug!("ty type is a ref but hir type is not");
2213 // We need to have arguments. This shouldn't happen, but it's worth checking.
2214 if arguments.is_empty() {
2218 // We use a mix of the HIR and the Ty types to get information
2219 // as the HIR doesn't have full types for closure arguments.
2220 let return_ty = sig.output().skip_binder();
2221 let mut return_span = fn_decl.output.span();
2222 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2223 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2224 return_span = lifetime.span;
2228 Some(AnnotatedBorrowFnSignature::NamedFunction {
2234 ty::Ref(_, _, _) if is_closure => {
2235 // This is case 2 from above but only for closures, return type is anonymous
2236 // reference so we select
2237 // the first argument.
2238 let argument_span = fn_decl.inputs.first()?.span;
2239 let argument_ty = sig.inputs().skip_binder().first()?;
2241 // Closure arguments are wrapped in a tuple, so we need to get the first
2243 if let ty::Tuple(elems) = argument_ty.kind() {
2244 let &argument_ty = elems.first()?;
2245 if let ty::Ref(_, _, _) = argument_ty.kind() {
2246 return Some(AnnotatedBorrowFnSignature::Closure {
2255 ty::Ref(_, _, _) => {
2256 // This is also case 2 from above but for functions, return type is still an
2257 // anonymous reference so we select the first argument.
2258 let argument_span = fn_decl.inputs.first()?.span;
2259 let argument_ty = *sig.inputs().skip_binder().first()?;
2261 let return_span = fn_decl.output.span();
2262 let return_ty = sig.output().skip_binder();
2264 // We expect the first argument to be a reference.
2265 match argument_ty.kind() {
2266 ty::Ref(_, _, _) => {}
2270 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2278 // This is case 3 from above, return type is not a reference so don't highlight
2287 enum AnnotatedBorrowFnSignature<'tcx> {
2289 arguments: Vec<(Ty<'tcx>, Span)>,
2290 return_ty: Ty<'tcx>,
2294 argument_ty: Ty<'tcx>,
2295 argument_span: Span,
2296 return_ty: Ty<'tcx>,
2300 argument_ty: Ty<'tcx>,
2301 argument_span: Span,
2305 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2306 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2308 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2310 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2313 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2316 cx.get_region_name_for_ty(argument_ty, 0)
2318 &AnnotatedBorrowFnSignature::AnonymousFunction {
2324 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2325 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2327 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2328 let types_equal = return_ty_name == argument_ty_name;
2333 if types_equal { "also " } else { "" },
2339 "argument and return type have the same lifetime due to lifetime elision rules",
2342 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2343 lifetime-syntax.html#lifetime-elision>",
2346 cx.get_region_name_for_ty(return_ty, 0)
2348 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2349 // Region of return type and arguments checked to be the same earlier.
2350 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2351 for (_, argument_span) in arguments {
2352 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2355 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2358 "use data from the highlighted arguments which match the `{}` lifetime of \