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(
454 // Erase any region vids from the type, which may not be resolved
455 infcx.tcx.erase_regions(ty),
459 // Select all, including ambiguous predicates
460 let errors = fulfill_cx.select_all_or_error(&infcx);
462 // Only emit suggestion if all required predicates are on generic
465 .map(|err| match err.obligation.predicate.kind().skip_binder() {
466 PredicateKind::Trait(predicate) => {
467 match predicate.self_ty().kind() {
468 ty::Param(param_ty) => Ok((
469 generics.type_param(param_ty, tcx),
472 .print_only_trait_path()
483 if let Ok(predicates) = predicates {
484 suggest_constraining_type_params(
488 predicates.iter().map(|(param, constraint)| {
489 (param.name.as_str(), &**constraint, None)
496 let span = if let Some(local) = place.as_local() {
497 let decl = &self.body.local_decls[local];
498 Some(decl.source_info.span)
502 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
505 if let UseSpans::FnSelfUse {
506 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
511 "{} occurs due to deref coercion to `{}`",
512 desired_action.as_noun(),
516 // Check first whether the source is accessible (issue #87060)
517 if self.infcx.tcx.sess.source_map().span_to_snippet(deref_target).is_ok() {
518 err.span_note(deref_target, "deref defined here");
522 self.buffer_move_error(move_out_indices, (used_place, err));
526 pub(crate) fn report_move_out_while_borrowed(
529 (place, span): (Place<'tcx>, Span),
530 borrow: &BorrowData<'tcx>,
533 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
534 location, place, span, borrow
536 let value_msg = self.describe_any_place(place.as_ref());
537 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
539 let borrow_spans = self.retrieve_borrow_spans(borrow);
540 let borrow_span = borrow_spans.args_or_use();
542 let move_spans = self.move_spans(place.as_ref(), location);
543 let span = move_spans.args_or_use();
546 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
547 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
548 err.span_label(span, format!("move out of {} occurs here", value_msg));
550 borrow_spans.var_span_label_path_only(
552 format!("borrow occurs due to use{}", borrow_spans.describe()),
555 move_spans.var_span_label(
557 format!("move occurs due to use{}", move_spans.describe()),
561 self.explain_why_borrow_contains_point(location, borrow, None)
562 .add_explanation_to_diagnostic(
571 self.buffer_error(err);
574 pub(crate) fn report_use_while_mutably_borrowed(
577 (place, _span): (Place<'tcx>, Span),
578 borrow: &BorrowData<'tcx>,
579 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
580 let borrow_spans = self.retrieve_borrow_spans(borrow);
581 let borrow_span = borrow_spans.args_or_use();
583 // Conflicting borrows are reported separately, so only check for move
585 let use_spans = self.move_spans(place.as_ref(), location);
586 let span = use_spans.var_or_use();
588 // 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
589 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
590 let mut err = self.cannot_use_when_mutably_borrowed(
592 &self.describe_any_place(place.as_ref()),
594 &self.describe_any_place(borrow.borrowed_place.as_ref()),
597 borrow_spans.var_span_label(
600 let place = &borrow.borrowed_place;
601 let desc_place = self.describe_any_place(place.as_ref());
602 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
607 self.explain_why_borrow_contains_point(location, borrow, None)
608 .add_explanation_to_diagnostic(
620 pub(crate) fn report_conflicting_borrow(
623 (place, span): (Place<'tcx>, Span),
624 gen_borrow_kind: BorrowKind,
625 issued_borrow: &BorrowData<'tcx>,
626 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
627 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
628 let issued_span = issued_spans.args_or_use();
630 let borrow_spans = self.borrow_spans(span, location);
631 let span = borrow_spans.args_or_use();
633 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
639 let (desc_place, msg_place, msg_borrow, union_type_name) =
640 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
642 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
643 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
645 // FIXME: supply non-"" `opt_via` when appropriate
646 let first_borrow_desc;
647 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
648 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
649 first_borrow_desc = "mutable ";
650 self.cannot_reborrow_already_borrowed(
662 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
663 first_borrow_desc = "immutable ";
664 self.cannot_reborrow_already_borrowed(
677 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
678 first_borrow_desc = "first ";
679 let mut err = self.cannot_mutably_borrow_multiply(
687 self.suggest_split_at_mut_if_applicable(
690 issued_borrow.borrowed_place,
695 (BorrowKind::Unique, BorrowKind::Unique) => {
696 first_borrow_desc = "first ";
697 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
700 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
701 if let Some(immutable_section_description) =
702 self.classify_immutable_section(issued_borrow.assigned_place)
704 let mut err = self.cannot_mutate_in_immutable_section(
708 immutable_section_description,
711 borrow_spans.var_span_label(
714 "borrow occurs due to use of {}{}",
716 borrow_spans.describe(),
723 first_borrow_desc = "immutable ";
724 self.cannot_reborrow_already_borrowed(
738 (BorrowKind::Unique, _) => {
739 first_borrow_desc = "first ";
740 self.cannot_uniquely_borrow_by_one_closure(
752 (BorrowKind::Shared, BorrowKind::Unique) => {
753 first_borrow_desc = "first ";
754 self.cannot_reborrow_already_uniquely_borrowed(
767 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
768 first_borrow_desc = "first ";
769 self.cannot_reborrow_already_uniquely_borrowed(
782 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
785 BorrowKind::Mut { .. }
788 | BorrowKind::Shallow,
792 if issued_spans == borrow_spans {
793 borrow_spans.var_span_label(
795 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
796 gen_borrow_kind.describe_mutability(),
799 let borrow_place = &issued_borrow.borrowed_place;
800 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
801 issued_spans.var_span_label(
804 "first borrow occurs due to use of {}{}",
806 issued_spans.describe(),
808 issued_borrow.kind.describe_mutability(),
811 borrow_spans.var_span_label(
814 "second borrow occurs due to use of {}{}",
816 borrow_spans.describe(),
818 gen_borrow_kind.describe_mutability(),
822 if union_type_name != "" {
824 "{} is a field of the union `{}`, so it overlaps the field {}",
825 msg_place, union_type_name, msg_borrow,
829 explanation.add_explanation_to_diagnostic(
836 Some((issued_span, span)),
839 self.suggest_using_local_if_applicable(
851 #[instrument(level = "debug", skip(self, err))]
852 fn suggest_using_local_if_applicable(
854 err: &mut Diagnostic,
856 (place, span): (Place<'tcx>, Span),
857 gen_borrow_kind: BorrowKind,
858 issued_borrow: &BorrowData<'tcx>,
859 explanation: BorrowExplanation,
862 matches!(explanation, BorrowExplanation::UsedLater(LaterUseKind::Call, _call_span, _));
864 debug!("not later used in call");
869 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
872 issued_borrow.reserve_location
874 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
876 let inner_param_location = location;
877 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
878 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
881 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
883 "`inner_param_location` {:?} is not for an assignment: {:?}",
884 inner_param_location, inner_param_stmt
888 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
889 let Some((inner_call_loc,inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
890 let Either::Right(term) = self.body.stmt_at(loc) else {
891 debug!("{:?} is a statement, so it can't be a call", loc);
894 let TerminatorKind::Call { args, .. } = &term.kind else {
895 debug!("not a call: {:?}", term);
898 debug!("checking call args for uses of inner_param: {:?}", args);
899 if args.contains(&Operand::Move(inner_param)) {
905 debug!("no uses of inner_param found as a by-move call arg");
908 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
910 let inner_call_span = inner_call_term.source_info.span;
911 let outer_call_span = outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span;
912 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
913 // FIXME: This stops the suggestion in some cases where it should be emitted.
914 // Fix the spans for those cases so it's emitted correctly.
916 "outer span {:?} does not strictly contain inner span {:?}",
917 outer_call_span, inner_call_span
921 err.span_help(inner_call_span, "try adding a local storing this argument...");
922 err.span_help(outer_call_span, "...and then using that local as the argument to this call");
925 fn suggest_split_at_mut_if_applicable(
927 err: &mut Diagnostic,
929 borrowed_place: Place<'tcx>,
931 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
932 (&place.projection[..], &borrowed_place.projection[..])
935 "consider using `.split_at_mut(position)` or similar method to obtain \
936 two mutable non-overlapping sub-slices",
941 /// Returns the description of the root place for a conflicting borrow and the full
942 /// descriptions of the places that caused the conflict.
944 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
945 /// attempted while a shared borrow is live, then this function will return:
949 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
950 /// a shared borrow of another field `x.y`, then this function will return:
952 /// ("x", "x.z", "x.y")
954 /// In the more complex union case, where the union is a field of a struct, then if a mutable
955 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
956 /// another field `x.u.y`, then this function will return:
958 /// ("x.u", "x.u.z", "x.u.y")
960 /// This is used when creating error messages like below:
963 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
964 /// mutable (via `a.u.s.b`) [E0502]
966 pub(crate) fn describe_place_for_conflicting_borrow(
968 first_borrowed_place: Place<'tcx>,
969 second_borrowed_place: Place<'tcx>,
970 ) -> (String, String, String, String) {
971 // Define a small closure that we can use to check if the type of a place
973 let union_ty = |place_base| {
974 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
975 // using a type annotation in the closure argument instead leads to a lifetime error.
976 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
977 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
980 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
981 // code duplication (particularly around returning an empty description in the failure
985 // If we have a conflicting borrow of the same place, then we don't want to add
986 // an extraneous "via x.y" to our diagnostics, so filter out this case.
987 first_borrowed_place != second_borrowed_place
990 // We're going to want to traverse the first borrowed place to see if we can find
991 // field access to a union. If we find that, then we will keep the place of the
992 // union being accessed and the field that was being accessed so we can check the
993 // second borrowed place for the same union and an access to a different field.
994 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
996 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
997 return Some((place_base, field));
1004 .and_then(|(target_base, target_field)| {
1005 // With the place of a union and a field access into it, we traverse the second
1006 // borrowed place and look for an access to a different field of the same union.
1007 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1008 if let ProjectionElem::Field(field, _) = elem {
1009 if let Some(union_ty) = union_ty(place_base) {
1010 if field != target_field && place_base == target_base {
1012 self.describe_any_place(place_base),
1013 self.describe_any_place(first_borrowed_place.as_ref()),
1014 self.describe_any_place(second_borrowed_place.as_ref()),
1015 union_ty.to_string(),
1023 .unwrap_or_else(|| {
1024 // If we didn't find a field access into a union, or both places match, then
1025 // only return the description of the first place.
1027 self.describe_any_place(first_borrowed_place.as_ref()),
1035 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1037 /// This means that some data referenced by `borrow` needs to live
1038 /// past the point where the StorageDeadOrDrop of `place` occurs.
1039 /// This is usually interpreted as meaning that `place` has too
1040 /// short a lifetime. (But sometimes it is more useful to report
1041 /// it as a more direct conflict between the execution of a
1042 /// `Drop::drop` with an aliasing borrow.)
1043 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1046 borrow: &BorrowData<'tcx>,
1047 place_span: (Place<'tcx>, Span),
1048 kind: Option<WriteKind>,
1051 "report_borrowed_value_does_not_live_long_enough(\
1052 {:?}, {:?}, {:?}, {:?}\
1054 location, borrow, place_span, kind
1057 let drop_span = place_span.1;
1059 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1061 let borrow_spans = self.retrieve_borrow_spans(borrow);
1062 let borrow_span = borrow_spans.var_or_use_path_span();
1064 assert!(root_place.projection.is_empty());
1065 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1067 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1069 if self.access_place_error_reported.contains(&(
1070 Place { local: root_place.local, projection: root_place_projection },
1074 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1080 self.access_place_error_reported.insert((
1081 Place { local: root_place.local, projection: root_place_projection },
1085 let borrowed_local = borrow.borrowed_place.local;
1086 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1088 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1089 self.buffer_error(err);
1093 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1094 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1096 // If a borrow of path `B` conflicts with drop of `D` (and
1097 // we're not in the uninteresting case where `B` is a
1098 // prefix of `D`), then report this as a more interesting
1099 // destructor conflict.
1100 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1101 self.report_borrow_conflicts_with_destructor(
1102 location, borrow, place_span, kind, dropped_ty,
1108 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1110 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1111 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1114 "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
1115 place_desc, explanation
1117 let err = match (place_desc, explanation) {
1118 // If the outlives constraint comes from inside the closure,
1123 // Box::new(|| y) as Box<Fn() -> &'static i32>
1125 // then just use the normal error. The closure isn't escaping
1126 // and `move` will not help here.
1129 BorrowExplanation::MustBeValidFor {
1131 category @ (ConstraintCategory::Return(_)
1132 | ConstraintCategory::CallArgument
1133 | ConstraintCategory::OpaqueType),
1134 from_closure: false,
1139 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1140 .report_escaping_closure_capture(
1146 &format!("`{}`", name),
1150 BorrowExplanation::MustBeValidFor {
1151 category: ConstraintCategory::Assignment,
1152 from_closure: false,
1156 RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
1162 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1163 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1171 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1181 self.buffer_error(err);
1184 fn report_local_value_does_not_live_long_enough(
1188 borrow: &BorrowData<'tcx>,
1190 borrow_spans: UseSpans<'tcx>,
1191 explanation: BorrowExplanation,
1192 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1194 "report_local_value_does_not_live_long_enough(\
1195 {:?}, {:?}, {:?}, {:?}, {:?}\
1197 location, name, borrow, drop_span, borrow_spans
1200 let borrow_span = borrow_spans.var_or_use_path_span();
1201 if let BorrowExplanation::MustBeValidFor {
1205 from_closure: false,
1209 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1214 opt_place_desc.as_ref(),
1220 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1222 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1223 let region_name = annotation.emit(self, &mut err);
1227 format!("`{}` would have to be valid for `{}`...", name, region_name),
1230 let fn_hir_id = self.mir_hir_id();
1234 "...but `{}` will be dropped here, when the {} returns",
1239 .opt_name(fn_hir_id)
1240 .map(|name| format!("function `{}`", name))
1241 .unwrap_or_else(|| {
1245 .typeck(self.mir_def_id())
1246 .node_type(fn_hir_id)
1249 ty::Closure(..) => "enclosing closure",
1250 ty::Generator(..) => "enclosing generator",
1251 kind => bug!("expected closure or generator, found {:?}", kind),
1259 "functions cannot return a borrow to data owned within the function's scope, \
1260 functions can only return borrows to data passed as arguments",
1263 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1264 references-and-borrowing.html#dangling-references>",
1267 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1269 explanation.add_explanation_to_diagnostic(
1280 err.span_label(borrow_span, "borrowed value does not live long enough");
1281 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1283 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1285 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1287 explanation.add_explanation_to_diagnostic(
1301 fn report_borrow_conflicts_with_destructor(
1304 borrow: &BorrowData<'tcx>,
1305 (place, drop_span): (Place<'tcx>, Span),
1306 kind: Option<WriteKind>,
1307 dropped_ty: Ty<'tcx>,
1310 "report_borrow_conflicts_with_destructor(\
1311 {:?}, {:?}, ({:?}, {:?}), {:?}\
1313 location, borrow, place, drop_span, kind,
1316 let borrow_spans = self.retrieve_borrow_spans(borrow);
1317 let borrow_span = borrow_spans.var_or_use();
1319 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1321 let what_was_dropped = match self.describe_place(place.as_ref()) {
1322 Some(name) => format!("`{}`", name),
1323 None => String::from("temporary value"),
1326 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1327 Some(borrowed) => format!(
1328 "here, drop of {D} needs exclusive access to `{B}`, \
1329 because the type `{T}` implements the `Drop` trait",
1330 D = what_was_dropped,
1335 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1336 D = what_was_dropped,
1340 err.span_label(drop_span, label);
1342 // Only give this note and suggestion if they could be relevant.
1344 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1346 BorrowExplanation::UsedLater { .. }
1347 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1348 err.note("consider using a `let` binding to create a longer lived value");
1353 explanation.add_explanation_to_diagnostic(
1363 self.buffer_error(err);
1366 fn report_thread_local_value_does_not_live_long_enough(
1370 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1372 "report_thread_local_value_does_not_live_long_enough(\
1375 drop_span, borrow_span
1378 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1382 "thread-local variables cannot be borrowed beyond the end of the function",
1384 err.span_label(drop_span, "end of enclosing function is here");
1389 fn report_temporary_value_does_not_live_long_enough(
1392 borrow: &BorrowData<'tcx>,
1394 borrow_spans: UseSpans<'tcx>,
1396 explanation: BorrowExplanation,
1397 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1399 "report_temporary_value_does_not_live_long_enough(\
1400 {:?}, {:?}, {:?}, {:?}\
1402 location, borrow, drop_span, proper_span
1405 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1408 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1419 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1420 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1421 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1424 BorrowExplanation::UsedLater(..)
1425 | BorrowExplanation::UsedLaterInLoop(..)
1426 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1427 // Only give this note and suggestion if it could be relevant.
1428 err.note("consider using a `let` binding to create a longer lived value");
1432 explanation.add_explanation_to_diagnostic(
1442 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1444 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1449 fn try_report_cannot_return_reference_to_local(
1451 borrow: &BorrowData<'tcx>,
1454 category: ConstraintCategory,
1455 opt_place_desc: Option<&String>,
1456 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1457 let return_kind = match category {
1458 ConstraintCategory::Return(_) => "return",
1459 ConstraintCategory::Yield => "yield",
1463 // FIXME use a better heuristic than Spans
1464 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1470 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1471 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1472 match self.body.local_kind(local) {
1473 LocalKind::ReturnPointer | LocalKind::Temp => {
1474 bug!("temporary or return pointer with a name")
1476 LocalKind::Var => "local variable ",
1478 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1480 "variable captured by `move` "
1482 LocalKind::Arg => "function parameter ",
1488 format!("{}`{}`", local_kind, place_desc),
1489 format!("`{}` is borrowed here", place_desc),
1493 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1494 let local = root_place.local;
1495 match self.body.local_kind(local) {
1496 LocalKind::ReturnPointer | LocalKind::Temp => {
1497 ("temporary value".to_string(), "temporary value created here".to_string())
1500 "function parameter".to_string(),
1501 "function parameter borrowed here".to_string(),
1504 ("local binding".to_string(), "local binding introduced here".to_string())
1509 let mut err = self.cannot_return_reference_to_local(
1516 if return_span != borrow_span {
1517 err.span_label(borrow_span, note);
1519 let tcx = self.infcx.tcx;
1520 let ty_params = ty::List::empty();
1522 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1523 let return_ty = tcx.erase_regions(return_ty);
1526 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1529 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1530 .must_apply_modulo_regions()
1532 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1533 err.span_suggestion_hidden(
1535 "use `.collect()` to allocate the iterator",
1536 format!("{}{}", snippet, ".collect::<Vec<_>>()"),
1537 Applicability::MaybeIncorrect,
1547 fn report_escaping_closure_capture(
1549 use_span: UseSpans<'tcx>,
1551 fr_name: &RegionName,
1552 category: ConstraintCategory,
1553 constraint_span: Span,
1555 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1556 let tcx = self.infcx.tcx;
1557 let args_span = use_span.args_or_use();
1559 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1561 if string.starts_with("async ") {
1562 let pos = args_span.lo() + BytePos(6);
1563 (args_span.with_lo(pos).with_hi(pos), "move ".to_string())
1564 } else if string.starts_with("async|") {
1565 let pos = args_span.lo() + BytePos(5);
1566 (args_span.with_lo(pos).with_hi(pos), " move".to_string())
1568 (args_span.shrink_to_lo(), "move ".to_string())
1571 Err(_) => (args_span, "move |<args>| <body>".to_string()),
1573 let kind = match use_span.generator_kind() {
1574 Some(generator_kind) => match generator_kind {
1575 GeneratorKind::Async(async_kind) => match async_kind {
1576 AsyncGeneratorKind::Block => "async block",
1577 AsyncGeneratorKind::Closure => "async closure",
1578 _ => bug!("async block/closure expected, but async function found."),
1580 GeneratorKind::Gen => "generator",
1586 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1587 err.span_suggestion_verbose(
1590 "to force the {} to take ownership of {} (and any \
1591 other referenced variables), use the `move` keyword",
1595 Applicability::MachineApplicable,
1599 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1600 let msg = format!("{} is returned here", kind);
1601 err.span_note(constraint_span, &msg);
1603 ConstraintCategory::CallArgument => {
1604 fr_name.highlight_region_name(&mut err);
1605 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1607 "async blocks are not executed immediately and must either take a \
1608 reference or ownership of outside variables they use",
1611 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1612 err.span_note(constraint_span, &msg);
1616 "report_escaping_closure_capture called with unexpected constraint \
1625 fn report_escaping_data(
1628 name: &Option<String>,
1632 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1633 let tcx = self.infcx.tcx;
1635 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1638 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1642 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1645 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1647 if let Some(name) = name {
1650 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1655 format!("reference escapes the {} body here", escapes_from),
1662 fn get_moved_indexes(
1666 ) -> (Vec<MoveSite>, Vec<Location>) {
1667 fn predecessor_locations<'a>(
1668 body: &'a mir::Body<'_>,
1670 ) -> impl Iterator<Item = Location> + 'a {
1671 if location.statement_index == 0 {
1672 let predecessors = body.predecessors()[location.block].to_vec();
1673 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1675 Either::Right(std::iter::once(Location {
1676 statement_index: location.statement_index - 1,
1682 let mut mpis = vec![mpi];
1683 let move_paths = &self.move_data.move_paths;
1684 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1686 let mut stack = Vec::new();
1687 let mut back_edge_stack = Vec::new();
1689 predecessor_locations(self.body, location).for_each(|predecessor| {
1690 if location.dominates(predecessor, &self.dominators) {
1691 back_edge_stack.push(predecessor)
1693 stack.push(predecessor);
1697 let mut reached_start = false;
1699 /* Check if the mpi is initialized as an argument */
1700 let mut is_argument = false;
1701 for arg in self.body.args_iter() {
1702 let path = self.move_data.rev_lookup.find_local(arg);
1703 if mpis.contains(&path) {
1708 let mut visited = FxHashSet::default();
1709 let mut move_locations = FxHashSet::default();
1710 let mut reinits = vec![];
1711 let mut result = vec![];
1713 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1715 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1716 location, is_back_edge
1719 if !visited.insert(location) {
1725 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1726 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1727 // this analysis only tries to find moves explicitly
1728 // written by the user, so we ignore the move-outs
1729 // created by `StorageDead` and at the beginning
1732 // If we are found a use of a.b.c which was in error, then we want to look for
1733 // moves not only of a.b.c but also a.b and a.
1735 // Note that the moves data already includes "parent" paths, so we don't have to
1736 // worry about the other case: that is, if there is a move of a.b.c, it is already
1737 // marked as a move of a.b and a as well, so we will generate the correct errors
1739 for moi in &self.move_data.loc_map[location] {
1740 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1741 let path = self.move_data.moves[*moi].path;
1742 if mpis.contains(&path) {
1744 "report_use_of_moved_or_uninitialized: found {:?}",
1745 move_paths[path].place
1747 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1748 move_locations.insert(location);
1750 // Strictly speaking, we could continue our DFS here. There may be
1751 // other moves that can reach the point of error. But it is kind of
1752 // confusing to highlight them.
1760 // drop(a); // <-- current point of error
1763 // Because we stop the DFS here, we only highlight `let c = a`,
1764 // and not `let b = a`. We will of course also report an error at
1765 // `let c = a` which highlights `let b = a` as the move.
1772 let mut any_match = false;
1773 for ii in &self.move_data.init_loc_map[location] {
1774 let init = self.move_data.inits[*ii];
1776 InitKind::Deep | InitKind::NonPanicPathOnly => {
1777 if mpis.contains(&init.path) {
1781 InitKind::Shallow => {
1782 if mpi == init.path {
1789 reinits.push(location);
1795 while let Some(location) = stack.pop() {
1796 if dfs_iter(&mut result, location, false) {
1800 let mut has_predecessor = false;
1801 predecessor_locations(self.body, location).for_each(|predecessor| {
1802 if location.dominates(predecessor, &self.dominators) {
1803 back_edge_stack.push(predecessor)
1805 stack.push(predecessor);
1807 has_predecessor = true;
1810 if !has_predecessor {
1811 reached_start = true;
1814 if (is_argument || !reached_start) && result.is_empty() {
1815 /* Process back edges (moves in future loop iterations) only if
1816 the move path is definitely initialized upon loop entry,
1817 to avoid spurious "in previous iteration" errors.
1818 During DFS, if there's a path from the error back to the start
1819 of the function with no intervening init or move, then the
1820 move path may be uninitialized at loop entry.
1822 while let Some(location) = back_edge_stack.pop() {
1823 if dfs_iter(&mut result, location, true) {
1827 predecessor_locations(self.body, location)
1828 .for_each(|predecessor| back_edge_stack.push(predecessor));
1832 // Check if we can reach these reinits from a move location.
1833 let reinits_reachable = reinits
1836 let mut visited = FxHashSet::default();
1837 let mut stack = vec![*reinit];
1838 while let Some(location) = stack.pop() {
1839 if !visited.insert(location) {
1842 if move_locations.contains(&location) {
1845 stack.extend(predecessor_locations(self.body, location));
1849 .collect::<Vec<Location>>();
1850 (result, reinits_reachable)
1853 pub(crate) fn report_illegal_mutation_of_borrowed(
1856 (place, span): (Place<'tcx>, Span),
1857 loan: &BorrowData<'tcx>,
1859 let loan_spans = self.retrieve_borrow_spans(loan);
1860 let loan_span = loan_spans.args_or_use();
1862 let descr_place = self.describe_any_place(place.as_ref());
1863 if loan.kind == BorrowKind::Shallow {
1864 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1865 let mut err = self.cannot_mutate_in_immutable_section(
1872 loan_spans.var_span_label(
1874 format!("borrow occurs due to use{}", loan_spans.describe()),
1875 loan.kind.describe_mutability(),
1878 self.buffer_error(err);
1884 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
1886 loan_spans.var_span_label(
1888 format!("borrow occurs due to use{}", loan_spans.describe()),
1889 loan.kind.describe_mutability(),
1892 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
1902 self.explain_deref_coercion(loan, &mut err);
1904 self.buffer_error(err);
1907 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
1908 let tcx = self.infcx.tcx;
1910 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
1911 Some((method_did, method_substs)),
1913 &self.body[loan.reserve_location.block].terminator,
1914 rustc_const_eval::util::find_self_call(
1917 loan.assigned_place.local,
1918 loan.reserve_location.block,
1921 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
1923 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
1924 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
1927 if let Some(Ok(instance)) = deref_target {
1928 let deref_target_ty = instance.ty(tcx, self.param_env);
1930 "borrow occurs due to deref coercion to `{}`",
1933 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
1939 /// Reports an illegal reassignment; for example, an assignment to
1940 /// (part of) a non-`mut` local that occurs potentially after that
1941 /// local has already been initialized. `place` is the path being
1942 /// assigned; `err_place` is a place providing a reason why
1943 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
1944 /// assignment to `x.f`).
1945 pub(crate) fn report_illegal_reassignment(
1947 _location: Location,
1948 (place, span): (Place<'tcx>, Span),
1949 assigned_span: Span,
1950 err_place: Place<'tcx>,
1952 let (from_arg, local_decl, local_name) = match err_place.as_local() {
1954 self.body.local_kind(local) == LocalKind::Arg,
1955 Some(&self.body.local_decls[local]),
1956 self.local_names[local],
1958 None => (false, None, None),
1961 // If root local is initialized immediately (everything apart from let
1962 // PATTERN;) then make the error refer to that local, rather than the
1963 // place being assigned later.
1964 let (place_description, assigned_span) = match local_decl {
1967 Some(box LocalInfo::User(
1968 ClearCrossCrate::Clear
1969 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
1970 opt_match_place: None,
1974 | Some(box LocalInfo::StaticRef { .. })
1978 | None => (self.describe_any_place(place.as_ref()), assigned_span),
1979 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
1982 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
1983 let msg = if from_arg {
1984 "cannot assign to immutable argument"
1986 "cannot assign twice to immutable variable"
1988 if span != assigned_span {
1990 err.span_label(assigned_span, format!("first assignment to {}", place_description));
1993 if let Some(decl) = local_decl
1994 && let Some(name) = local_name
1995 && decl.can_be_made_mutable()
1997 err.span_suggestion(
1998 decl.source_info.span,
1999 "consider making this binding mutable",
2000 format!("mut {}", name),
2001 Applicability::MachineApplicable,
2004 err.span_label(span, msg);
2005 self.buffer_error(err);
2008 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2009 let tcx = self.infcx.tcx;
2010 match place.last_projection() {
2011 None => StorageDeadOrDrop::LocalStorageDead,
2012 Some((place_base, elem)) => {
2013 // FIXME(spastorino) make this iterate
2014 let base_access = self.classify_drop_access_kind(place_base);
2016 ProjectionElem::Deref => match base_access {
2017 StorageDeadOrDrop::LocalStorageDead
2018 | StorageDeadOrDrop::BoxedStorageDead => {
2020 place_base.ty(self.body, tcx).ty.is_box(),
2021 "Drop of value behind a reference or raw pointer"
2023 StorageDeadOrDrop::BoxedStorageDead
2025 StorageDeadOrDrop::Destructor(_) => base_access,
2027 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
2028 let base_ty = place_base.ty(self.body, tcx).ty;
2029 match base_ty.kind() {
2030 ty::Adt(def, _) if def.has_dtor(tcx) => {
2031 // Report the outermost adt with a destructor
2033 StorageDeadOrDrop::Destructor(_) => base_access,
2034 StorageDeadOrDrop::LocalStorageDead
2035 | StorageDeadOrDrop::BoxedStorageDead => {
2036 StorageDeadOrDrop::Destructor(base_ty)
2043 ProjectionElem::ConstantIndex { .. }
2044 | ProjectionElem::Subslice { .. }
2045 | ProjectionElem::Index(_) => base_access,
2051 /// Describe the reason for the fake borrow that was assigned to `place`.
2052 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2053 use rustc_middle::mir::visit::Visitor;
2054 struct FakeReadCauseFinder<'tcx> {
2056 cause: Option<FakeReadCause>,
2058 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2059 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2061 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2062 if *place == self.place =>
2064 self.cause = Some(*cause);
2070 let mut visitor = FakeReadCauseFinder { place, cause: None };
2071 visitor.visit_body(&self.body);
2072 match visitor.cause {
2073 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2074 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2079 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2080 /// borrow of local value that does not live long enough.
2081 fn annotate_argument_and_return_for_borrow(
2083 borrow: &BorrowData<'tcx>,
2084 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2085 // Define a fallback for when we can't match a closure.
2087 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2091 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2093 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2094 self.mir_def_id().to_def_id(),
2095 self.infcx.tcx.fn_sig(self.mir_def_id()),
2102 // In order to determine whether we need to annotate, we need to check whether the reserve
2103 // place was an assignment into a temporary.
2105 // If it was, we check whether or not that temporary is eventually assigned into the return
2106 // place. If it was, we can add annotations about the function's return type and arguments
2107 // and it'll make sense.
2108 let location = borrow.reserve_location;
2109 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2110 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2111 &self.body[location.block].statements.get(location.statement_index)
2113 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2114 // Check that the initial assignment of the reserve location is into a temporary.
2115 let mut target = match reservation.as_local() {
2116 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2120 // Next, look through the rest of the block, checking if we are assigning the
2121 // `target` (that is, the place that contains our borrow) to anything.
2122 let mut annotated_closure = None;
2123 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2125 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2128 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2129 if let Some(assigned_to) = place.as_local() {
2131 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2135 // Check if our `target` was captured by a closure.
2136 if let Rvalue::Aggregate(
2137 box AggregateKind::Closure(def_id, substs),
2141 for operand in operands {
2142 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2146 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2150 // Find the local from the operand.
2151 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2155 if assigned_from_local != target {
2159 // If a closure captured our `target` and then assigned
2160 // into a place then we should annotate the closure in
2161 // case it ends up being assigned into the return place.
2163 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2165 "annotate_argument_and_return_for_borrow: \
2166 annotated_closure={:?} assigned_from_local={:?} \
2168 annotated_closure, assigned_from_local, assigned_to
2171 if assigned_to == mir::RETURN_PLACE {
2172 // If it was assigned directly into the return place, then
2174 return annotated_closure;
2176 // Otherwise, update the target.
2177 target = assigned_to;
2181 // If none of our closure's operands matched, then skip to the next
2186 // Otherwise, look at other types of assignment.
2187 let assigned_from = match rvalue {
2188 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2189 Rvalue::Use(operand) => match operand {
2190 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2198 "annotate_argument_and_return_for_borrow: \
2199 assigned_from={:?}",
2203 // Find the local from the rvalue.
2204 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2206 "annotate_argument_and_return_for_borrow: \
2207 assigned_from_local={:?}",
2208 assigned_from_local,
2211 // Check if our local matches the target - if so, we've assigned our
2212 // borrow to a new place.
2213 if assigned_from_local != target {
2217 // If we assigned our `target` into a new place, then we should
2218 // check if it was the return place.
2220 "annotate_argument_and_return_for_borrow: \
2221 assigned_from_local={:?} assigned_to={:?}",
2222 assigned_from_local, assigned_to
2224 if assigned_to == mir::RETURN_PLACE {
2225 // If it was then return the annotated closure if there was one,
2226 // else, annotate this function.
2227 return annotated_closure.or_else(fallback);
2230 // If we didn't assign into the return place, then we just update
2232 target = assigned_to;
2237 // Check the terminator if we didn't find anything in the statements.
2238 let terminator = &self.body[location.block].terminator();
2240 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2243 if let TerminatorKind::Call { destination: Some((place, _)), args, .. } =
2246 if let Some(assigned_to) = place.as_local() {
2248 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2251 for operand in args {
2252 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2256 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2260 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2262 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2263 assigned_from_local,
2266 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2267 return annotated_closure.or_else(fallback);
2275 // If we haven't found an assignment into the return place, then we need not add
2277 debug!("annotate_argument_and_return_for_borrow: none found");
2281 /// Annotate the first argument and return type of a function signature if they are
2286 sig: ty::PolyFnSig<'tcx>,
2287 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2288 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2289 let is_closure = self.infcx.tcx.is_closure(did);
2290 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?);
2291 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2293 // We need to work out which arguments to highlight. We do this by looking
2294 // at the return type, where there are three cases:
2296 // 1. If there are named arguments, then we should highlight the return type and
2297 // highlight any of the arguments that are also references with that lifetime.
2298 // If there are no arguments that have the same lifetime as the return type,
2299 // then don't highlight anything.
2300 // 2. The return type is a reference with an anonymous lifetime. If this is
2301 // the case, then we can take advantage of (and teach) the lifetime elision
2304 // We know that an error is being reported. So the arguments and return type
2305 // must satisfy the elision rules. Therefore, if there is a single argument
2306 // then that means the return type and first (and only) argument have the same
2307 // lifetime and the borrow isn't meeting that, we can highlight the argument
2310 // If there are multiple arguments then the first argument must be self (else
2311 // it would not satisfy the elision rules), so we can highlight self and the
2313 // 3. The return type is not a reference. In this case, we don't highlight
2315 let return_ty = sig.output();
2316 match return_ty.skip_binder().kind() {
2317 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2318 // This is case 1 from above, return type is a named reference so we need to
2319 // search for relevant arguments.
2320 let mut arguments = Vec::new();
2321 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2322 if let ty::Ref(argument_region, _, _) = argument.kind() {
2323 if argument_region == return_region {
2324 // Need to use the `rustc_middle::ty` types to compare against the
2325 // `return_region`. Then use the `rustc_hir` type to get only
2326 // the lifetime span.
2327 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2328 // With access to the lifetime, we can get
2330 arguments.push((*argument, lifetime.span));
2332 bug!("ty type is a ref but hir type is not");
2338 // We need to have arguments. This shouldn't happen, but it's worth checking.
2339 if arguments.is_empty() {
2343 // We use a mix of the HIR and the Ty types to get information
2344 // as the HIR doesn't have full types for closure arguments.
2345 let return_ty = sig.output().skip_binder();
2346 let mut return_span = fn_decl.output.span();
2347 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2348 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2349 return_span = lifetime.span;
2353 Some(AnnotatedBorrowFnSignature::NamedFunction {
2359 ty::Ref(_, _, _) if is_closure => {
2360 // This is case 2 from above but only for closures, return type is anonymous
2361 // reference so we select
2362 // the first argument.
2363 let argument_span = fn_decl.inputs.first()?.span;
2364 let argument_ty = sig.inputs().skip_binder().first()?;
2366 // Closure arguments are wrapped in a tuple, so we need to get the first
2368 if let ty::Tuple(elems) = argument_ty.kind() {
2369 let &argument_ty = elems.first()?;
2370 if let ty::Ref(_, _, _) = argument_ty.kind() {
2371 return Some(AnnotatedBorrowFnSignature::Closure {
2380 ty::Ref(_, _, _) => {
2381 // This is also case 2 from above but for functions, return type is still an
2382 // anonymous reference so we select the first argument.
2383 let argument_span = fn_decl.inputs.first()?.span;
2384 let argument_ty = *sig.inputs().skip_binder().first()?;
2386 let return_span = fn_decl.output.span();
2387 let return_ty = sig.output().skip_binder();
2389 // We expect the first argument to be a reference.
2390 match argument_ty.kind() {
2391 ty::Ref(_, _, _) => {}
2395 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2403 // This is case 3 from above, return type is not a reference so don't highlight
2412 enum AnnotatedBorrowFnSignature<'tcx> {
2414 arguments: Vec<(Ty<'tcx>, Span)>,
2415 return_ty: Ty<'tcx>,
2419 argument_ty: Ty<'tcx>,
2420 argument_span: Span,
2421 return_ty: Ty<'tcx>,
2425 argument_ty: Ty<'tcx>,
2426 argument_span: Span,
2430 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2431 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2433 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2435 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2438 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2441 cx.get_region_name_for_ty(argument_ty, 0)
2443 &AnnotatedBorrowFnSignature::AnonymousFunction {
2449 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2450 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2452 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2453 let types_equal = return_ty_name == argument_ty_name;
2458 if types_equal { "also " } else { "" },
2464 "argument and return type have the same lifetime due to lifetime elision rules",
2467 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2468 lifetime-syntax.html#lifetime-elision>",
2471 cx.get_region_name_for_ty(return_ty, 0)
2473 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2474 // Region of return type and arguments checked to be the same earlier.
2475 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2476 for (_, argument_span) in arguments {
2477 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2480 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2483 "use data from the highlighted arguments which match the `{}` lifetime of \