2 use rustc_const_eval::util::{CallDesugaringKind, 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, DUMMY_SP};
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 if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
199 let place_name = self
200 .describe_place(moved_place.as_ref())
201 .map(|n| format!("`{}`", n))
202 .unwrap_or_else(|| "value".to_owned());
204 CallKind::FnCall { fn_trait_id, .. }
205 if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() =>
210 "{} {}moved due to this call{}",
211 place_name, partially_str, loop_message
216 "this value implements `FnOnce`, which causes it to be moved when called",
219 CallKind::Operator { self_arg, .. } => {
220 let self_arg = self_arg.unwrap();
224 "{} {}moved due to usage in operator{}",
225 place_name, partially_str, loop_message
228 if self.fn_self_span_reported.insert(fn_span) {
230 // Check whether the source is accessible
236 .span_to_snippet(self_arg.span)
243 "calling this operator moves the left-hand side",
247 CallKind::Normal { self_arg, desugaring, is_option_or_result } => {
248 let self_arg = self_arg.unwrap();
249 if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
253 "{} {}moved due to this implicit call to `.into_iter()`{}",
254 place_name, partially_str, loop_message
257 let sess = self.infcx.tcx.sess;
258 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
259 // If we have a `&mut` ref, we need to reborrow.
260 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
261 // If we are in a loop this will be suggested later.
263 err.span_suggestion_verbose(
264 move_span.shrink_to_lo(),
266 "consider creating a fresh reborrow of {} here",
267 self.describe_place(moved_place.as_ref())
268 .map(|n| format!("`{}`", n))
270 || "the mutable reference".to_string()
273 "&mut *".to_string(),
274 Applicability::MachineApplicable,
277 } else if let Ok(snippet) =
278 sess.source_map().span_to_snippet(move_span)
282 "consider borrowing to avoid moving into the for loop",
283 format!("&{}", snippet),
284 Applicability::MaybeIncorrect,
291 "{} {}moved due to this method call{}",
292 place_name, partially_str, loop_message
296 if is_option_or_result && maybe_reinitialized_locations.is_empty() {
297 err.span_suggestion_verbose(
298 fn_call_span.shrink_to_lo(),
299 "consider calling `.as_ref()` to borrow the type's contents",
300 "as_ref().".to_string(),
301 Applicability::MachineApplicable,
304 // Avoid pointing to the same function in multiple different
306 if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span)
310 &format!("this function takes ownership of the receiver `self`, which moves {}", place_name)
314 // Other desugarings takes &self, which cannot cause a move
320 format!("value {}moved{} here{}", partially_str, move_msg, loop_message),
322 // If the move error occurs due to a loop, don't show
323 // another message for the same span
324 if loop_message.is_empty() {
325 move_spans.var_span_label(
328 "variable {}moved due to use{}",
330 move_spans.describe()
337 if let (UseSpans::PatUse(span), []) =
338 (move_spans, &maybe_reinitialized_locations[..])
340 if maybe_reinitialized_locations.is_empty() {
341 err.span_suggestion_verbose(
344 "borrow this field in the pattern to avoid moving {}",
345 self.describe_place(moved_place.as_ref())
346 .map(|n| format!("`{}`", n))
347 .unwrap_or_else(|| "the value".to_string())
350 Applicability::MachineApplicable,
357 use_spans.var_span_label_path_only(
359 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
366 "value {} here after {}move",
367 desired_action.as_verb_in_past_tense(),
373 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
374 let needs_note = match ty.kind() {
375 ty::Closure(id, _) => {
376 let tables = self.infcx.tcx.typeck(id.expect_local());
377 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
379 tables.closure_kind_origins().get(hir_id).is_none()
384 let mpi = self.move_data.moves[move_out_indices[0]].path;
385 let place = &self.move_data.move_paths[mpi].place;
386 let ty = place.ty(self.body, self.infcx.tcx).ty;
388 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
389 if is_loop_move & !in_pattern {
390 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
391 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
392 err.span_suggestion_verbose(
395 "consider creating a fresh reborrow of {} here",
396 self.describe_place(moved_place)
397 .map(|n| format!("`{}`", n))
398 .unwrap_or_else(|| "the mutable reference".to_string()),
400 "&mut *".to_string(),
401 Applicability::MachineApplicable,
408 self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
409 let note_msg = match opt_name {
410 Some(ref name) => format!("`{}`", name),
411 None => "value".to_owned(),
413 if let ty::Param(param_ty) = ty.kind() {
414 let tcx = self.infcx.tcx;
415 let generics = tcx.generics_of(self.mir_def_id());
416 let param = generics.type_param(¶m_ty, tcx);
417 if let Some(generics) = tcx
418 .typeck_root_def_id(self.mir_def_id().to_def_id())
420 .and_then(|def_id| tcx.hir().get_generics(def_id))
422 suggest_constraining_type_param(
432 // Try to find predicates on *generic params* that would allow copying `ty`
434 let tcx = self.infcx.tcx;
435 let generics = tcx.generics_of(self.mir_def_id());
436 if let Some(hir_generics) = tcx
437 .typeck_root_def_id(self.mir_def_id().to_def_id())
439 .and_then(|def_id| tcx.hir().get_generics(def_id))
441 let predicates: Result<Vec<_>, _> = tcx.infer_ctxt().enter(|infcx| {
443 <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
445 let copy_did = infcx.tcx.lang_items().copy_trait().unwrap();
446 let cause = ObligationCause::new(
449 rustc_infer::traits::ObligationCauseCode::MiscObligation,
451 fulfill_cx.register_bound(&infcx, self.param_env, ty, copy_did, cause);
452 let errors = fulfill_cx.select_where_possible(&infcx);
454 // Only emit suggestion if all required predicates are on generic
457 .map(|err| match err.obligation.predicate.kind().skip_binder() {
458 PredicateKind::Trait(predicate) => {
459 match predicate.self_ty().kind() {
460 ty::Param(param_ty) => Ok((
461 generics.type_param(param_ty, tcx),
464 .print_only_trait_path()
475 if let Ok(predicates) = predicates {
476 suggest_constraining_type_params(
480 predicates.iter().map(|(param, constraint)| {
481 (param.name.as_str(), &**constraint, None)
488 let span = if let Some(local) = place.as_local() {
489 let decl = &self.body.local_decls[local];
490 Some(decl.source_info.span)
494 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
497 if let UseSpans::FnSelfUse {
498 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
503 "{} occurs due to deref coercion to `{}`",
504 desired_action.as_noun(),
508 // Check first whether the source is accessible (issue #87060)
509 if self.infcx.tcx.sess.source_map().span_to_snippet(deref_target).is_ok() {
510 err.span_note(deref_target, "deref defined here");
514 self.buffer_move_error(move_out_indices, (used_place, err));
518 pub(crate) fn report_move_out_while_borrowed(
521 (place, span): (Place<'tcx>, Span),
522 borrow: &BorrowData<'tcx>,
525 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
526 location, place, span, borrow
528 let value_msg = self.describe_any_place(place.as_ref());
529 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
531 let borrow_spans = self.retrieve_borrow_spans(borrow);
532 let borrow_span = borrow_spans.args_or_use();
534 let move_spans = self.move_spans(place.as_ref(), location);
535 let span = move_spans.args_or_use();
538 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
539 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
540 err.span_label(span, format!("move out of {} occurs here", value_msg));
542 borrow_spans.var_span_label_path_only(
544 format!("borrow occurs due to use{}", borrow_spans.describe()),
547 move_spans.var_span_label(
549 format!("move occurs due to use{}", move_spans.describe()),
553 self.explain_why_borrow_contains_point(location, borrow, None)
554 .add_explanation_to_diagnostic(
563 self.buffer_error(err);
566 pub(crate) fn report_use_while_mutably_borrowed(
569 (place, _span): (Place<'tcx>, Span),
570 borrow: &BorrowData<'tcx>,
571 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
572 let borrow_spans = self.retrieve_borrow_spans(borrow);
573 let borrow_span = borrow_spans.args_or_use();
575 // Conflicting borrows are reported separately, so only check for move
577 let use_spans = self.move_spans(place.as_ref(), location);
578 let span = use_spans.var_or_use();
580 // 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
581 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
582 let mut err = self.cannot_use_when_mutably_borrowed(
584 &self.describe_any_place(place.as_ref()),
586 &self.describe_any_place(borrow.borrowed_place.as_ref()),
589 borrow_spans.var_span_label(
592 let place = &borrow.borrowed_place;
593 let desc_place = self.describe_any_place(place.as_ref());
594 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
599 self.explain_why_borrow_contains_point(location, borrow, None)
600 .add_explanation_to_diagnostic(
612 pub(crate) fn report_conflicting_borrow(
615 (place, span): (Place<'tcx>, Span),
616 gen_borrow_kind: BorrowKind,
617 issued_borrow: &BorrowData<'tcx>,
618 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
619 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
620 let issued_span = issued_spans.args_or_use();
622 let borrow_spans = self.borrow_spans(span, location);
623 let span = borrow_spans.args_or_use();
625 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
631 let (desc_place, msg_place, msg_borrow, union_type_name) =
632 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
634 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
635 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
637 // FIXME: supply non-"" `opt_via` when appropriate
638 let first_borrow_desc;
639 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
640 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
641 first_borrow_desc = "mutable ";
642 self.cannot_reborrow_already_borrowed(
654 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
655 first_borrow_desc = "immutable ";
656 self.cannot_reborrow_already_borrowed(
669 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
670 first_borrow_desc = "first ";
671 let mut err = self.cannot_mutably_borrow_multiply(
679 self.suggest_split_at_mut_if_applicable(
682 issued_borrow.borrowed_place,
687 (BorrowKind::Unique, BorrowKind::Unique) => {
688 first_borrow_desc = "first ";
689 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
692 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
693 if let Some(immutable_section_description) =
694 self.classify_immutable_section(issued_borrow.assigned_place)
696 let mut err = self.cannot_mutate_in_immutable_section(
700 immutable_section_description,
703 borrow_spans.var_span_label(
706 "borrow occurs due to use of {}{}",
708 borrow_spans.describe(),
715 first_borrow_desc = "immutable ";
716 self.cannot_reborrow_already_borrowed(
730 (BorrowKind::Unique, _) => {
731 first_borrow_desc = "first ";
732 self.cannot_uniquely_borrow_by_one_closure(
744 (BorrowKind::Shared, BorrowKind::Unique) => {
745 first_borrow_desc = "first ";
746 self.cannot_reborrow_already_uniquely_borrowed(
759 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
760 first_borrow_desc = "first ";
761 self.cannot_reborrow_already_uniquely_borrowed(
774 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
777 BorrowKind::Mut { .. }
780 | BorrowKind::Shallow,
784 if issued_spans == borrow_spans {
785 borrow_spans.var_span_label(
787 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
788 gen_borrow_kind.describe_mutability(),
791 let borrow_place = &issued_borrow.borrowed_place;
792 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
793 issued_spans.var_span_label(
796 "first borrow occurs due to use of {}{}",
798 issued_spans.describe(),
800 issued_borrow.kind.describe_mutability(),
803 borrow_spans.var_span_label(
806 "second borrow occurs due to use of {}{}",
808 borrow_spans.describe(),
810 gen_borrow_kind.describe_mutability(),
814 if union_type_name != "" {
816 "{} is a field of the union `{}`, so it overlaps the field {}",
817 msg_place, union_type_name, msg_borrow,
821 explanation.add_explanation_to_diagnostic(
828 Some((issued_span, span)),
831 self.suggest_using_local_if_applicable(
843 #[instrument(level = "debug", skip(self, err))]
844 fn suggest_using_local_if_applicable(
846 err: &mut Diagnostic,
848 (place, span): (Place<'tcx>, Span),
849 gen_borrow_kind: BorrowKind,
850 issued_borrow: &BorrowData<'tcx>,
851 explanation: BorrowExplanation,
854 matches!(explanation, BorrowExplanation::UsedLater(LaterUseKind::Call, _call_span, _));
856 debug!("not later used in call");
861 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
864 issued_borrow.reserve_location
866 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
868 let inner_param_location = location;
869 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
870 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
873 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
875 "`inner_param_location` {:?} is not for an assignment: {:?}",
876 inner_param_location, inner_param_stmt
880 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
881 let Some((inner_call_loc,inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
882 let Either::Right(term) = self.body.stmt_at(loc) else {
883 debug!("{:?} is a statement, so it can't be a call", loc);
886 let TerminatorKind::Call { args, .. } = &term.kind else {
887 debug!("not a call: {:?}", term);
890 debug!("checking call args for uses of inner_param: {:?}", args);
891 if args.contains(&Operand::Move(inner_param)) {
897 debug!("no uses of inner_param found as a by-move call arg");
900 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
902 let inner_call_span = inner_call_term.source_info.span;
903 let outer_call_span = outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span;
904 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
905 // FIXME: This stops the suggestion in some cases where it should be emitted.
906 // Fix the spans for those cases so it's emitted correctly.
908 "outer span {:?} does not strictly contain inner span {:?}",
909 outer_call_span, inner_call_span
913 err.span_help(inner_call_span, "try adding a local storing this argument...");
914 err.span_help(outer_call_span, "...and then using that local as the argument to this call");
917 fn suggest_split_at_mut_if_applicable(
919 err: &mut Diagnostic,
921 borrowed_place: Place<'tcx>,
923 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
924 (&place.projection[..], &borrowed_place.projection[..])
927 "consider using `.split_at_mut(position)` or similar method to obtain \
928 two mutable non-overlapping sub-slices",
933 /// Returns the description of the root place for a conflicting borrow and the full
934 /// descriptions of the places that caused the conflict.
936 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
937 /// attempted while a shared borrow is live, then this function will return:
941 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
942 /// a shared borrow of another field `x.y`, then this function will return:
944 /// ("x", "x.z", "x.y")
946 /// In the more complex union case, where the union is a field of a struct, then if a mutable
947 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
948 /// another field `x.u.y`, then this function will return:
950 /// ("x.u", "x.u.z", "x.u.y")
952 /// This is used when creating error messages like below:
955 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
956 /// mutable (via `a.u.s.b`) [E0502]
958 pub(crate) fn describe_place_for_conflicting_borrow(
960 first_borrowed_place: Place<'tcx>,
961 second_borrowed_place: Place<'tcx>,
962 ) -> (String, String, String, String) {
963 // Define a small closure that we can use to check if the type of a place
965 let union_ty = |place_base| {
966 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
967 // using a type annotation in the closure argument instead leads to a lifetime error.
968 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
969 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
972 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
973 // code duplication (particularly around returning an empty description in the failure
977 // If we have a conflicting borrow of the same place, then we don't want to add
978 // an extraneous "via x.y" to our diagnostics, so filter out this case.
979 first_borrowed_place != second_borrowed_place
982 // We're going to want to traverse the first borrowed place to see if we can find
983 // field access to a union. If we find that, then we will keep the place of the
984 // union being accessed and the field that was being accessed so we can check the
985 // second borrowed place for the same union and an access to a different field.
986 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
988 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
989 return Some((place_base, field));
996 .and_then(|(target_base, target_field)| {
997 // With the place of a union and a field access into it, we traverse the second
998 // borrowed place and look for an access to a different field of the same union.
999 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1000 if let ProjectionElem::Field(field, _) = elem {
1001 if let Some(union_ty) = union_ty(place_base) {
1002 if field != target_field && place_base == target_base {
1004 self.describe_any_place(place_base),
1005 self.describe_any_place(first_borrowed_place.as_ref()),
1006 self.describe_any_place(second_borrowed_place.as_ref()),
1007 union_ty.to_string(),
1015 .unwrap_or_else(|| {
1016 // If we didn't find a field access into a union, or both places match, then
1017 // only return the description of the first place.
1019 self.describe_any_place(first_borrowed_place.as_ref()),
1027 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1029 /// This means that some data referenced by `borrow` needs to live
1030 /// past the point where the StorageDeadOrDrop of `place` occurs.
1031 /// This is usually interpreted as meaning that `place` has too
1032 /// short a lifetime. (But sometimes it is more useful to report
1033 /// it as a more direct conflict between the execution of a
1034 /// `Drop::drop` with an aliasing borrow.)
1035 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1038 borrow: &BorrowData<'tcx>,
1039 place_span: (Place<'tcx>, Span),
1040 kind: Option<WriteKind>,
1043 "report_borrowed_value_does_not_live_long_enough(\
1044 {:?}, {:?}, {:?}, {:?}\
1046 location, borrow, place_span, kind
1049 let drop_span = place_span.1;
1051 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1053 let borrow_spans = self.retrieve_borrow_spans(borrow);
1054 let borrow_span = borrow_spans.var_or_use_path_span();
1056 assert!(root_place.projection.is_empty());
1057 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1059 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1061 if self.access_place_error_reported.contains(&(
1062 Place { local: root_place.local, projection: root_place_projection },
1066 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1072 self.access_place_error_reported.insert((
1073 Place { local: root_place.local, projection: root_place_projection },
1077 let borrowed_local = borrow.borrowed_place.local;
1078 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1080 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1081 self.buffer_error(err);
1085 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1086 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1088 // If a borrow of path `B` conflicts with drop of `D` (and
1089 // we're not in the uninteresting case where `B` is a
1090 // prefix of `D`), then report this as a more interesting
1091 // destructor conflict.
1092 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1093 self.report_borrow_conflicts_with_destructor(
1094 location, borrow, place_span, kind, dropped_ty,
1100 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1102 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1103 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1106 "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
1107 place_desc, explanation
1109 let err = match (place_desc, explanation) {
1110 // If the outlives constraint comes from inside the closure,
1115 // Box::new(|| y) as Box<Fn() -> &'static i32>
1117 // then just use the normal error. The closure isn't escaping
1118 // and `move` will not help here.
1121 BorrowExplanation::MustBeValidFor {
1123 category @ (ConstraintCategory::Return(_)
1124 | ConstraintCategory::CallArgument
1125 | ConstraintCategory::OpaqueType),
1126 from_closure: false,
1131 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1132 .report_escaping_closure_capture(
1138 &format!("`{}`", name),
1142 BorrowExplanation::MustBeValidFor {
1143 category: ConstraintCategory::Assignment,
1144 from_closure: false,
1148 RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
1154 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1155 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1163 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1173 self.buffer_error(err);
1176 fn report_local_value_does_not_live_long_enough(
1180 borrow: &BorrowData<'tcx>,
1182 borrow_spans: UseSpans<'tcx>,
1183 explanation: BorrowExplanation,
1184 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1186 "report_local_value_does_not_live_long_enough(\
1187 {:?}, {:?}, {:?}, {:?}, {:?}\
1189 location, name, borrow, drop_span, borrow_spans
1192 let borrow_span = borrow_spans.var_or_use_path_span();
1193 if let BorrowExplanation::MustBeValidFor {
1197 from_closure: false,
1201 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1206 opt_place_desc.as_ref(),
1212 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1214 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1215 let region_name = annotation.emit(self, &mut err);
1219 format!("`{}` would have to be valid for `{}`...", name, region_name),
1222 let fn_hir_id = self.mir_hir_id();
1226 "...but `{}` will be dropped here, when the {} returns",
1231 .opt_name(fn_hir_id)
1232 .map(|name| format!("function `{}`", name))
1233 .unwrap_or_else(|| {
1237 .typeck(self.mir_def_id())
1238 .node_type(fn_hir_id)
1241 ty::Closure(..) => "enclosing closure",
1242 ty::Generator(..) => "enclosing generator",
1243 kind => bug!("expected closure or generator, found {:?}", kind),
1251 "functions cannot return a borrow to data owned within the function's scope, \
1252 functions can only return borrows to data passed as arguments",
1255 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1256 references-and-borrowing.html#dangling-references>",
1259 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1261 explanation.add_explanation_to_diagnostic(
1272 err.span_label(borrow_span, "borrowed value does not live long enough");
1273 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1275 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1277 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1279 explanation.add_explanation_to_diagnostic(
1293 fn report_borrow_conflicts_with_destructor(
1296 borrow: &BorrowData<'tcx>,
1297 (place, drop_span): (Place<'tcx>, Span),
1298 kind: Option<WriteKind>,
1299 dropped_ty: Ty<'tcx>,
1302 "report_borrow_conflicts_with_destructor(\
1303 {:?}, {:?}, ({:?}, {:?}), {:?}\
1305 location, borrow, place, drop_span, kind,
1308 let borrow_spans = self.retrieve_borrow_spans(borrow);
1309 let borrow_span = borrow_spans.var_or_use();
1311 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1313 let what_was_dropped = match self.describe_place(place.as_ref()) {
1314 Some(name) => format!("`{}`", name),
1315 None => String::from("temporary value"),
1318 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1319 Some(borrowed) => format!(
1320 "here, drop of {D} needs exclusive access to `{B}`, \
1321 because the type `{T}` implements the `Drop` trait",
1322 D = what_was_dropped,
1327 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1328 D = what_was_dropped,
1332 err.span_label(drop_span, label);
1334 // Only give this note and suggestion if they could be relevant.
1336 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1338 BorrowExplanation::UsedLater { .. }
1339 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1340 err.note("consider using a `let` binding to create a longer lived value");
1345 explanation.add_explanation_to_diagnostic(
1355 self.buffer_error(err);
1358 fn report_thread_local_value_does_not_live_long_enough(
1362 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1364 "report_thread_local_value_does_not_live_long_enough(\
1367 drop_span, borrow_span
1370 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1374 "thread-local variables cannot be borrowed beyond the end of the function",
1376 err.span_label(drop_span, "end of enclosing function is here");
1381 fn report_temporary_value_does_not_live_long_enough(
1384 borrow: &BorrowData<'tcx>,
1386 borrow_spans: UseSpans<'tcx>,
1388 explanation: BorrowExplanation,
1389 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1391 "report_temporary_value_does_not_live_long_enough(\
1392 {:?}, {:?}, {:?}, {:?}\
1394 location, borrow, drop_span, proper_span
1397 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1400 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1411 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1412 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1413 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1416 BorrowExplanation::UsedLater(..)
1417 | BorrowExplanation::UsedLaterInLoop(..)
1418 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1419 // Only give this note and suggestion if it could be relevant.
1420 err.note("consider using a `let` binding to create a longer lived value");
1424 explanation.add_explanation_to_diagnostic(
1434 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1436 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1441 fn try_report_cannot_return_reference_to_local(
1443 borrow: &BorrowData<'tcx>,
1446 category: ConstraintCategory,
1447 opt_place_desc: Option<&String>,
1448 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1449 let return_kind = match category {
1450 ConstraintCategory::Return(_) => "return",
1451 ConstraintCategory::Yield => "yield",
1455 // FIXME use a better heuristic than Spans
1456 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1462 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1463 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1464 match self.body.local_kind(local) {
1465 LocalKind::ReturnPointer | LocalKind::Temp => {
1466 bug!("temporary or return pointer with a name")
1468 LocalKind::Var => "local variable ",
1470 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1472 "variable captured by `move` "
1474 LocalKind::Arg => "function parameter ",
1480 format!("{}`{}`", local_kind, place_desc),
1481 format!("`{}` is borrowed here", place_desc),
1485 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1486 let local = root_place.local;
1487 match self.body.local_kind(local) {
1488 LocalKind::ReturnPointer | LocalKind::Temp => {
1489 ("temporary value".to_string(), "temporary value created here".to_string())
1492 "function parameter".to_string(),
1493 "function parameter borrowed here".to_string(),
1496 ("local binding".to_string(), "local binding introduced here".to_string())
1501 let mut err = self.cannot_return_reference_to_local(
1508 if return_span != borrow_span {
1509 err.span_label(borrow_span, note);
1511 let tcx = self.infcx.tcx;
1512 let ty_params = ty::List::empty();
1514 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1515 let return_ty = tcx.erase_regions(return_ty);
1518 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1521 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1522 .must_apply_modulo_regions()
1524 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1525 err.span_suggestion_hidden(
1527 "use `.collect()` to allocate the iterator",
1528 format!("{}{}", snippet, ".collect::<Vec<_>>()"),
1529 Applicability::MaybeIncorrect,
1539 fn report_escaping_closure_capture(
1541 use_span: UseSpans<'tcx>,
1543 fr_name: &RegionName,
1544 category: ConstraintCategory,
1545 constraint_span: Span,
1547 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1548 let tcx = self.infcx.tcx;
1549 let args_span = use_span.args_or_use();
1551 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1553 if string.starts_with("async ") {
1554 let pos = args_span.lo() + BytePos(6);
1555 (args_span.with_lo(pos).with_hi(pos), "move ".to_string())
1556 } else if string.starts_with("async|") {
1557 let pos = args_span.lo() + BytePos(5);
1558 (args_span.with_lo(pos).with_hi(pos), " move".to_string())
1560 (args_span.shrink_to_lo(), "move ".to_string())
1563 Err(_) => (args_span, "move |<args>| <body>".to_string()),
1565 let kind = match use_span.generator_kind() {
1566 Some(generator_kind) => match generator_kind {
1567 GeneratorKind::Async(async_kind) => match async_kind {
1568 AsyncGeneratorKind::Block => "async block",
1569 AsyncGeneratorKind::Closure => "async closure",
1570 _ => bug!("async block/closure expected, but async function found."),
1572 GeneratorKind::Gen => "generator",
1578 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1579 err.span_suggestion_verbose(
1582 "to force the {} to take ownership of {} (and any \
1583 other referenced variables), use the `move` keyword",
1587 Applicability::MachineApplicable,
1591 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1592 let msg = format!("{} is returned here", kind);
1593 err.span_note(constraint_span, &msg);
1595 ConstraintCategory::CallArgument => {
1596 fr_name.highlight_region_name(&mut err);
1597 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1599 "async blocks are not executed immediately and must either take a \
1600 reference or ownership of outside variables they use",
1603 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1604 err.span_note(constraint_span, &msg);
1608 "report_escaping_closure_capture called with unexpected constraint \
1617 fn report_escaping_data(
1620 name: &Option<String>,
1624 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1625 let tcx = self.infcx.tcx;
1627 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1630 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1634 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1637 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1639 if let Some(name) = name {
1642 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1647 format!("reference escapes the {} body here", escapes_from),
1654 fn get_moved_indexes(
1658 ) -> (Vec<MoveSite>, Vec<Location>) {
1659 fn predecessor_locations<'a>(
1660 body: &'a mir::Body<'_>,
1662 ) -> impl Iterator<Item = Location> + 'a {
1663 if location.statement_index == 0 {
1664 let predecessors = body.predecessors()[location.block].to_vec();
1665 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1667 Either::Right(std::iter::once(Location {
1668 statement_index: location.statement_index - 1,
1674 let mut mpis = vec![mpi];
1675 let move_paths = &self.move_data.move_paths;
1676 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1678 let mut stack = Vec::new();
1679 let mut back_edge_stack = Vec::new();
1681 predecessor_locations(self.body, location).for_each(|predecessor| {
1682 if location.dominates(predecessor, &self.dominators) {
1683 back_edge_stack.push(predecessor)
1685 stack.push(predecessor);
1689 let mut reached_start = false;
1691 /* Check if the mpi is initialized as an argument */
1692 let mut is_argument = false;
1693 for arg in self.body.args_iter() {
1694 let path = self.move_data.rev_lookup.find_local(arg);
1695 if mpis.contains(&path) {
1700 let mut visited = FxHashSet::default();
1701 let mut move_locations = FxHashSet::default();
1702 let mut reinits = vec![];
1703 let mut result = vec![];
1705 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1707 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1708 location, is_back_edge
1711 if !visited.insert(location) {
1717 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1718 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1719 // this analysis only tries to find moves explicitly
1720 // written by the user, so we ignore the move-outs
1721 // created by `StorageDead` and at the beginning
1724 // If we are found a use of a.b.c which was in error, then we want to look for
1725 // moves not only of a.b.c but also a.b and a.
1727 // Note that the moves data already includes "parent" paths, so we don't have to
1728 // worry about the other case: that is, if there is a move of a.b.c, it is already
1729 // marked as a move of a.b and a as well, so we will generate the correct errors
1731 for moi in &self.move_data.loc_map[location] {
1732 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1733 let path = self.move_data.moves[*moi].path;
1734 if mpis.contains(&path) {
1736 "report_use_of_moved_or_uninitialized: found {:?}",
1737 move_paths[path].place
1739 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1740 move_locations.insert(location);
1742 // Strictly speaking, we could continue our DFS here. There may be
1743 // other moves that can reach the point of error. But it is kind of
1744 // confusing to highlight them.
1752 // drop(a); // <-- current point of error
1755 // Because we stop the DFS here, we only highlight `let c = a`,
1756 // and not `let b = a`. We will of course also report an error at
1757 // `let c = a` which highlights `let b = a` as the move.
1764 let mut any_match = false;
1765 for ii in &self.move_data.init_loc_map[location] {
1766 let init = self.move_data.inits[*ii];
1768 InitKind::Deep | InitKind::NonPanicPathOnly => {
1769 if mpis.contains(&init.path) {
1773 InitKind::Shallow => {
1774 if mpi == init.path {
1781 reinits.push(location);
1787 while let Some(location) = stack.pop() {
1788 if dfs_iter(&mut result, location, false) {
1792 let mut has_predecessor = false;
1793 predecessor_locations(self.body, location).for_each(|predecessor| {
1794 if location.dominates(predecessor, &self.dominators) {
1795 back_edge_stack.push(predecessor)
1797 stack.push(predecessor);
1799 has_predecessor = true;
1802 if !has_predecessor {
1803 reached_start = true;
1806 if (is_argument || !reached_start) && result.is_empty() {
1807 /* Process back edges (moves in future loop iterations) only if
1808 the move path is definitely initialized upon loop entry,
1809 to avoid spurious "in previous iteration" errors.
1810 During DFS, if there's a path from the error back to the start
1811 of the function with no intervening init or move, then the
1812 move path may be uninitialized at loop entry.
1814 while let Some(location) = back_edge_stack.pop() {
1815 if dfs_iter(&mut result, location, true) {
1819 predecessor_locations(self.body, location)
1820 .for_each(|predecessor| back_edge_stack.push(predecessor));
1824 // Check if we can reach these reinits from a move location.
1825 let reinits_reachable = reinits
1828 let mut visited = FxHashSet::default();
1829 let mut stack = vec![*reinit];
1830 while let Some(location) = stack.pop() {
1831 if !visited.insert(location) {
1834 if move_locations.contains(&location) {
1837 stack.extend(predecessor_locations(self.body, location));
1841 .collect::<Vec<Location>>();
1842 (result, reinits_reachable)
1845 pub(crate) fn report_illegal_mutation_of_borrowed(
1848 (place, span): (Place<'tcx>, Span),
1849 loan: &BorrowData<'tcx>,
1851 let loan_spans = self.retrieve_borrow_spans(loan);
1852 let loan_span = loan_spans.args_or_use();
1854 let descr_place = self.describe_any_place(place.as_ref());
1855 if loan.kind == BorrowKind::Shallow {
1856 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1857 let mut err = self.cannot_mutate_in_immutable_section(
1864 loan_spans.var_span_label(
1866 format!("borrow occurs due to use{}", loan_spans.describe()),
1867 loan.kind.describe_mutability(),
1870 self.buffer_error(err);
1876 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
1878 loan_spans.var_span_label(
1880 format!("borrow occurs due to use{}", loan_spans.describe()),
1881 loan.kind.describe_mutability(),
1884 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
1894 self.explain_deref_coercion(loan, &mut err);
1896 self.buffer_error(err);
1899 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
1900 let tcx = self.infcx.tcx;
1902 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
1903 Some((method_did, method_substs)),
1905 &self.body[loan.reserve_location.block].terminator,
1906 rustc_const_eval::util::find_self_call(
1909 loan.assigned_place.local,
1910 loan.reserve_location.block,
1913 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
1915 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
1916 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
1919 if let Some(Ok(instance)) = deref_target {
1920 let deref_target_ty = instance.ty(tcx, self.param_env);
1922 "borrow occurs due to deref coercion to `{}`",
1925 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
1931 /// Reports an illegal reassignment; for example, an assignment to
1932 /// (part of) a non-`mut` local that occurs potentially after that
1933 /// local has already been initialized. `place` is the path being
1934 /// assigned; `err_place` is a place providing a reason why
1935 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
1936 /// assignment to `x.f`).
1937 pub(crate) fn report_illegal_reassignment(
1939 _location: Location,
1940 (place, span): (Place<'tcx>, Span),
1941 assigned_span: Span,
1942 err_place: Place<'tcx>,
1944 let (from_arg, local_decl, local_name) = match err_place.as_local() {
1946 self.body.local_kind(local) == LocalKind::Arg,
1947 Some(&self.body.local_decls[local]),
1948 self.local_names[local],
1950 None => (false, None, None),
1953 // If root local is initialized immediately (everything apart from let
1954 // PATTERN;) then make the error refer to that local, rather than the
1955 // place being assigned later.
1956 let (place_description, assigned_span) = match local_decl {
1959 Some(box LocalInfo::User(
1960 ClearCrossCrate::Clear
1961 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
1962 opt_match_place: None,
1966 | Some(box LocalInfo::StaticRef { .. })
1970 | None => (self.describe_any_place(place.as_ref()), assigned_span),
1971 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
1974 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
1975 let msg = if from_arg {
1976 "cannot assign to immutable argument"
1978 "cannot assign twice to immutable variable"
1980 if span != assigned_span {
1982 err.span_label(assigned_span, format!("first assignment to {}", place_description));
1985 if let Some(decl) = local_decl
1986 && let Some(name) = local_name
1987 && decl.can_be_made_mutable()
1989 err.span_suggestion(
1990 decl.source_info.span,
1991 "consider making this binding mutable",
1992 format!("mut {}", name),
1993 Applicability::MachineApplicable,
1996 err.span_label(span, msg);
1997 self.buffer_error(err);
2000 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2001 let tcx = self.infcx.tcx;
2002 match place.last_projection() {
2003 None => StorageDeadOrDrop::LocalStorageDead,
2004 Some((place_base, elem)) => {
2005 // FIXME(spastorino) make this iterate
2006 let base_access = self.classify_drop_access_kind(place_base);
2008 ProjectionElem::Deref => match base_access {
2009 StorageDeadOrDrop::LocalStorageDead
2010 | StorageDeadOrDrop::BoxedStorageDead => {
2012 place_base.ty(self.body, tcx).ty.is_box(),
2013 "Drop of value behind a reference or raw pointer"
2015 StorageDeadOrDrop::BoxedStorageDead
2017 StorageDeadOrDrop::Destructor(_) => base_access,
2019 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
2020 let base_ty = place_base.ty(self.body, tcx).ty;
2021 match base_ty.kind() {
2022 ty::Adt(def, _) if def.has_dtor(tcx) => {
2023 // Report the outermost adt with a destructor
2025 StorageDeadOrDrop::Destructor(_) => base_access,
2026 StorageDeadOrDrop::LocalStorageDead
2027 | StorageDeadOrDrop::BoxedStorageDead => {
2028 StorageDeadOrDrop::Destructor(base_ty)
2035 ProjectionElem::ConstantIndex { .. }
2036 | ProjectionElem::Subslice { .. }
2037 | ProjectionElem::Index(_) => base_access,
2043 /// Describe the reason for the fake borrow that was assigned to `place`.
2044 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2045 use rustc_middle::mir::visit::Visitor;
2046 struct FakeReadCauseFinder<'tcx> {
2048 cause: Option<FakeReadCause>,
2050 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2051 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2053 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2054 if *place == self.place =>
2056 self.cause = Some(*cause);
2062 let mut visitor = FakeReadCauseFinder { place, cause: None };
2063 visitor.visit_body(&self.body);
2064 match visitor.cause {
2065 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2066 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2071 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2072 /// borrow of local value that does not live long enough.
2073 fn annotate_argument_and_return_for_borrow(
2075 borrow: &BorrowData<'tcx>,
2076 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2077 // Define a fallback for when we can't match a closure.
2079 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2083 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2085 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2086 self.mir_def_id().to_def_id(),
2087 self.infcx.tcx.fn_sig(self.mir_def_id()),
2094 // In order to determine whether we need to annotate, we need to check whether the reserve
2095 // place was an assignment into a temporary.
2097 // If it was, we check whether or not that temporary is eventually assigned into the return
2098 // place. If it was, we can add annotations about the function's return type and arguments
2099 // and it'll make sense.
2100 let location = borrow.reserve_location;
2101 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2102 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2103 &self.body[location.block].statements.get(location.statement_index)
2105 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2106 // Check that the initial assignment of the reserve location is into a temporary.
2107 let mut target = match reservation.as_local() {
2108 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2112 // Next, look through the rest of the block, checking if we are assigning the
2113 // `target` (that is, the place that contains our borrow) to anything.
2114 let mut annotated_closure = None;
2115 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2117 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2120 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2121 if let Some(assigned_to) = place.as_local() {
2123 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2127 // Check if our `target` was captured by a closure.
2128 if let Rvalue::Aggregate(
2129 box AggregateKind::Closure(def_id, substs),
2133 for operand in operands {
2134 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2138 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2142 // Find the local from the operand.
2143 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2147 if assigned_from_local != target {
2151 // If a closure captured our `target` and then assigned
2152 // into a place then we should annotate the closure in
2153 // case it ends up being assigned into the return place.
2155 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2157 "annotate_argument_and_return_for_borrow: \
2158 annotated_closure={:?} assigned_from_local={:?} \
2160 annotated_closure, assigned_from_local, assigned_to
2163 if assigned_to == mir::RETURN_PLACE {
2164 // If it was assigned directly into the return place, then
2166 return annotated_closure;
2168 // Otherwise, update the target.
2169 target = assigned_to;
2173 // If none of our closure's operands matched, then skip to the next
2178 // Otherwise, look at other types of assignment.
2179 let assigned_from = match rvalue {
2180 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2181 Rvalue::Use(operand) => match operand {
2182 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2190 "annotate_argument_and_return_for_borrow: \
2191 assigned_from={:?}",
2195 // Find the local from the rvalue.
2196 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2198 "annotate_argument_and_return_for_borrow: \
2199 assigned_from_local={:?}",
2200 assigned_from_local,
2203 // Check if our local matches the target - if so, we've assigned our
2204 // borrow to a new place.
2205 if assigned_from_local != target {
2209 // If we assigned our `target` into a new place, then we should
2210 // check if it was the return place.
2212 "annotate_argument_and_return_for_borrow: \
2213 assigned_from_local={:?} assigned_to={:?}",
2214 assigned_from_local, assigned_to
2216 if assigned_to == mir::RETURN_PLACE {
2217 // If it was then return the annotated closure if there was one,
2218 // else, annotate this function.
2219 return annotated_closure.or_else(fallback);
2222 // If we didn't assign into the return place, then we just update
2224 target = assigned_to;
2229 // Check the terminator if we didn't find anything in the statements.
2230 let terminator = &self.body[location.block].terminator();
2232 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2235 if let TerminatorKind::Call { destination: Some((place, _)), args, .. } =
2238 if let Some(assigned_to) = place.as_local() {
2240 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2243 for operand in args {
2244 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2248 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2252 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2254 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2255 assigned_from_local,
2258 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2259 return annotated_closure.or_else(fallback);
2267 // If we haven't found an assignment into the return place, then we need not add
2269 debug!("annotate_argument_and_return_for_borrow: none found");
2273 /// Annotate the first argument and return type of a function signature if they are
2278 sig: ty::PolyFnSig<'tcx>,
2279 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2280 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2281 let is_closure = self.infcx.tcx.is_closure(did);
2282 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?);
2283 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2285 // We need to work out which arguments to highlight. We do this by looking
2286 // at the return type, where there are three cases:
2288 // 1. If there are named arguments, then we should highlight the return type and
2289 // highlight any of the arguments that are also references with that lifetime.
2290 // If there are no arguments that have the same lifetime as the return type,
2291 // then don't highlight anything.
2292 // 2. The return type is a reference with an anonymous lifetime. If this is
2293 // the case, then we can take advantage of (and teach) the lifetime elision
2296 // We know that an error is being reported. So the arguments and return type
2297 // must satisfy the elision rules. Therefore, if there is a single argument
2298 // then that means the return type and first (and only) argument have the same
2299 // lifetime and the borrow isn't meeting that, we can highlight the argument
2302 // If there are multiple arguments then the first argument must be self (else
2303 // it would not satisfy the elision rules), so we can highlight self and the
2305 // 3. The return type is not a reference. In this case, we don't highlight
2307 let return_ty = sig.output();
2308 match return_ty.skip_binder().kind() {
2309 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2310 // This is case 1 from above, return type is a named reference so we need to
2311 // search for relevant arguments.
2312 let mut arguments = Vec::new();
2313 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2314 if let ty::Ref(argument_region, _, _) = argument.kind() {
2315 if argument_region == return_region {
2316 // Need to use the `rustc_middle::ty` types to compare against the
2317 // `return_region`. Then use the `rustc_hir` type to get only
2318 // the lifetime span.
2319 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2320 // With access to the lifetime, we can get
2322 arguments.push((*argument, lifetime.span));
2324 bug!("ty type is a ref but hir type is not");
2330 // We need to have arguments. This shouldn't happen, but it's worth checking.
2331 if arguments.is_empty() {
2335 // We use a mix of the HIR and the Ty types to get information
2336 // as the HIR doesn't have full types for closure arguments.
2337 let return_ty = sig.output().skip_binder();
2338 let mut return_span = fn_decl.output.span();
2339 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2340 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2341 return_span = lifetime.span;
2345 Some(AnnotatedBorrowFnSignature::NamedFunction {
2351 ty::Ref(_, _, _) if is_closure => {
2352 // This is case 2 from above but only for closures, return type is anonymous
2353 // reference so we select
2354 // the first argument.
2355 let argument_span = fn_decl.inputs.first()?.span;
2356 let argument_ty = sig.inputs().skip_binder().first()?;
2358 // Closure arguments are wrapped in a tuple, so we need to get the first
2360 if let ty::Tuple(elems) = argument_ty.kind() {
2361 let &argument_ty = elems.first()?;
2362 if let ty::Ref(_, _, _) = argument_ty.kind() {
2363 return Some(AnnotatedBorrowFnSignature::Closure {
2372 ty::Ref(_, _, _) => {
2373 // This is also case 2 from above but for functions, return type is still an
2374 // anonymous reference so we select the first argument.
2375 let argument_span = fn_decl.inputs.first()?.span;
2376 let argument_ty = *sig.inputs().skip_binder().first()?;
2378 let return_span = fn_decl.output.span();
2379 let return_ty = sig.output().skip_binder();
2381 // We expect the first argument to be a reference.
2382 match argument_ty.kind() {
2383 ty::Ref(_, _, _) => {}
2387 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2395 // This is case 3 from above, return type is not a reference so don't highlight
2404 enum AnnotatedBorrowFnSignature<'tcx> {
2406 arguments: Vec<(Ty<'tcx>, Span)>,
2407 return_ty: Ty<'tcx>,
2411 argument_ty: Ty<'tcx>,
2412 argument_span: Span,
2413 return_ty: Ty<'tcx>,
2417 argument_ty: Ty<'tcx>,
2418 argument_span: Span,
2422 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2423 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2425 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2427 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2430 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2433 cx.get_region_name_for_ty(argument_ty, 0)
2435 &AnnotatedBorrowFnSignature::AnonymousFunction {
2441 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2442 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2444 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2445 let types_equal = return_ty_name == argument_ty_name;
2450 if types_equal { "also " } else { "" },
2456 "argument and return type have the same lifetime due to lifetime elision rules",
2459 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2460 lifetime-syntax.html#lifetime-elision>",
2463 cx.get_region_name_for_ty(return_ty, 0)
2465 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2466 // Region of return type and arguments checked to be the same earlier.
2467 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2468 for (_, argument_span) in arguments {
2469 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2472 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2475 "use data from the highlighted arguments which match the `{}` lifetime of \