2 use rustc_const_eval::util::CallKind;
3 use rustc_data_structures::captures::Captures;
4 use rustc_data_structures::fx::FxHashSet;
6 struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
9 use rustc_hir::def_id::DefId;
10 use rustc_hir::intravisit::{walk_expr, Visitor};
11 use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
12 use rustc_infer::infer::TyCtxtInferExt;
13 use rustc_infer::traits::ObligationCause;
14 use rustc_middle::mir::tcx::PlaceTy;
15 use rustc_middle::mir::{
16 self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
17 FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
18 ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
20 use rustc_middle::ty::{
21 self, subst::Subst, suggest_constraining_type_params, EarlyBinder, PredicateKind, Ty,
23 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
24 use rustc_span::hygiene::DesugaringKind;
25 use rustc_span::symbol::sym;
26 use rustc_span::{BytePos, Span};
27 use rustc_trait_selection::infer::InferCtxtExt;
28 use rustc_trait_selection::traits::TraitEngineExt as _;
30 use crate::borrow_set::TwoPhaseActivation;
31 use crate::borrowck_errors;
33 use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
34 use crate::diagnostics::find_all_local_uses;
36 borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
37 InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
41 explain_borrow::{BorrowExplanation, LaterUseKind},
42 IncludingDowncast, RegionName, RegionNameSource, UseSpans,
47 /// Index of the "move out" that we found. The `MoveData` can
48 /// then tell us where the move occurred.
51 /// `true` if we traversed a back edge while walking from the point
52 /// of error to the move site.
53 traversed_back_edge: bool,
56 /// Which case a StorageDeadOrDrop is for.
57 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
58 enum StorageDeadOrDrop<'tcx> {
64 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
65 pub(crate) fn report_use_of_moved_or_uninitialized(
68 desired_action: InitializationRequiringAction,
69 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),
73 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
74 moved_place={:?} used_place={:?} span={:?} mpi={:?}",
75 location, desired_action, moved_place, used_place, span, mpi
79 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
80 let span = use_spans.args_or_use();
82 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
84 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
85 move_site_vec, use_spans
87 let move_out_indices: Vec<_> =
88 move_site_vec.iter().map(|move_site| move_site.moi).collect();
90 if move_out_indices.is_empty() {
91 let root_place = PlaceRef { projection: &[], ..used_place };
93 if !self.uninitialized_error_reported.insert(root_place) {
95 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
102 self.report_use_of_uninitialized(mpi, used_place, desired_action, span, use_spans);
103 self.buffer_error(err);
105 if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
106 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
108 "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}",
115 let is_partial_move = move_site_vec.iter().any(|move_site| {
116 let move_out = self.move_data.moves[(*move_site).moi];
117 let moved_place = &self.move_data.move_paths[move_out.path].place;
118 // `*(_1)` where `_1` is a `Box` is actually a move out.
119 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
120 && self.body.local_decls[moved_place.local].ty.is_box();
123 && used_place != moved_place.as_ref()
124 && used_place.is_prefix_of(moved_place.as_ref())
127 let partial_str = if is_partial_move { "partial " } else { "" };
128 let partially_str = if is_partial_move { "partially " } else { "" };
130 let mut err = self.cannot_act_on_moved_value(
132 desired_action.as_noun(),
134 self.describe_place_with_options(moved_place, IncludingDowncast(true)),
137 let reinit_spans = maybe_reinitialized_locations
141 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
144 .collect::<Vec<Span>>();
146 let reinits = maybe_reinitialized_locations.len();
148 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
149 } else if reinits > 1 {
151 MultiSpan::from_spans(reinit_spans),
153 format!("these {} reinitializations might get skipped", reinits)
156 "these 3 reinitializations and {} other{} might get skipped",
158 if reinits == 4 { "" } else { "s" }
164 self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
166 let mut is_loop_move = false;
167 let mut in_pattern = false;
169 for move_site in &move_site_vec {
170 let move_out = self.move_data.moves[(*move_site).moi];
171 let moved_place = &self.move_data.move_paths[move_out.path].place;
173 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
174 let move_span = move_spans.args_or_use();
176 let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
178 let loop_message = if location == move_out.source || move_site.traversed_back_edge {
179 ", in previous iteration of loop"
184 if location == move_out.source {
188 self.explain_captures(
199 maybe_reinitialized_locations.is_empty(),
202 if let (UseSpans::PatUse(span), []) =
203 (move_spans, &maybe_reinitialized_locations[..])
205 if maybe_reinitialized_locations.is_empty() {
206 err.span_suggestion_verbose(
209 "borrow this field in the pattern to avoid moving {}",
210 self.describe_place(moved_place.as_ref())
211 .map(|n| format!("`{}`", n))
212 .unwrap_or_else(|| "the value".to_string())
215 Applicability::MachineApplicable,
222 use_spans.var_span_label_path_only(
224 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
231 "value {} here after {}move",
232 desired_action.as_verb_in_past_tense(),
238 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
239 let needs_note = match ty.kind() {
240 ty::Closure(id, _) => {
241 let tables = self.infcx.tcx.typeck(id.expect_local());
242 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
244 tables.closure_kind_origins().get(hir_id).is_none()
249 let mpi = self.move_data.moves[move_out_indices[0]].path;
250 let place = &self.move_data.move_paths[mpi].place;
251 let ty = place.ty(self.body, self.infcx.tcx).ty;
253 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
254 if is_loop_move & !in_pattern {
255 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
256 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
257 err.span_suggestion_verbose(
260 "consider creating a fresh reborrow of {} here",
261 self.describe_place(moved_place)
262 .map(|n| format!("`{}`", n))
263 .unwrap_or_else(|| "the mutable reference".to_string()),
266 Applicability::MachineApplicable,
272 self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
273 let note_msg = match opt_name {
274 Some(ref name) => format!("`{}`", name),
275 None => "value".to_owned(),
277 if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) {
278 // Suppress the next suggestion since we don't want to put more bounds onto
279 // something that already has `Fn`-like bounds (or is a closure), so we can't
282 self.suggest_adding_copy_bounds(&mut err, ty, span);
286 let span = if let Some(local) = place.as_local() {
287 Some(self.body.local_decls[local].source_info.span)
291 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
294 if let UseSpans::FnSelfUse {
295 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
300 "{} occurs due to deref coercion to `{}`",
301 desired_action.as_noun(),
305 // Check first whether the source is accessible (issue #87060)
306 if self.infcx.tcx.sess.source_map().span_to_snippet(deref_target).is_ok() {
307 err.span_note(deref_target, "deref defined here");
311 self.buffer_move_error(move_out_indices, (used_place, err));
315 fn report_use_of_uninitialized(
318 used_place: PlaceRef<'tcx>,
319 desired_action: InitializationRequiringAction,
321 use_spans: UseSpans<'tcx>,
322 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
323 // We need all statements in the body where the binding was assigned to to later find all
324 // the branching code paths where the binding *wasn't* assigned to.
325 let inits = &self.move_data.init_path_map[mpi];
326 let move_path = &self.move_data.move_paths[mpi];
327 let decl_span = self.body.local_decls[move_path.place.local].source_info.span;
328 let mut spans = vec![];
329 for init_idx in inits {
330 let init = &self.move_data.inits[*init_idx];
331 let span = init.span(&self.body);
335 let (binding, name, desc) =
336 match self.describe_place_with_options(used_place, IncludingDowncast(true)) {
337 Some(name) => (format!("`{name}`"), format!("`{name}`"), format!("`{name}` ")),
338 None => ("value".to_string(), "the variable".to_string(), String::new()),
340 let initialized = if let InitializationRequiringAction::PartialAssignment = desired_action {
341 // The same error is emitted for bindings that are *sometimes* initialized and the ones
342 // that are *partially* initialized by assigning to a field of an uninitialized
343 // binding. We differentiate between them for more accurate wording here.
345 } else if spans.iter().filter(|i| !i.contains(span)).count() == 0 {
346 // We filter above to avoid misleading wording in cases like:
353 "initialized in all conditions"
355 let used = desired_action.as_general_verb_in_past_tense();
357 struct_span_err!(self, span, E0381, "{used} binding {desc}isn't {initialized}");
358 use_spans.var_span_label_path_only(
360 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
363 if let InitializationRequiringAction::PartialAssignment = desired_action {
365 "partial initialization isn't supported, fully initialize the binding with a \
366 default value and mutate it, or use `std::mem::MaybeUninit`",
369 err.span_label(span, format!("{binding} {used} here but it isn't {initialized}"));
371 // We use the statements were the binding was initialized, and inspect the HIR to look
372 // for the branching codepaths that aren't covered, to point at them.
373 let hir_id = self.mir_hir_id();
374 let map = self.infcx.tcx.hir();
375 let body_id = map.body_owned_by(hir_id);
376 let body = map.body(body_id);
378 let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] };
379 visitor.visit_body(&body);
380 if visitor.errors.is_empty() {
382 if *sp < span && !sp.overlaps(span) {
383 err.span_label(*sp, "binding initialized here in some conditions");
387 for (sp, label) in visitor.errors {
388 if sp < span && !sp.overlaps(span) {
389 // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention
390 // match arms coming after the primary span because they aren't relevant:
394 // _ if { x = 2; true } => {}
399 // _ => {} // We don't want to point to this.
402 err.span_label(sp, &label);
405 err.span_label(decl_span, "binding declared here but left uninitialized");
409 fn suggest_borrow_fn_like(
411 err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
413 move_sites: &[MoveSite],
416 let tcx = self.infcx.tcx;
418 // Find out if the predicates show that the type is a Fn or FnMut
419 let find_fn_kind_from_did = |predicates: &[(ty::Predicate<'tcx>, Span)], substs| {
420 predicates.iter().find_map(|(pred, _)| {
421 let pred = if let Some(substs) = substs {
422 EarlyBinder(*pred).subst(tcx, substs).kind().skip_binder()
424 pred.kind().skip_binder()
426 if let ty::PredicateKind::Trait(pred) = pred && pred.self_ty() == ty {
427 if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
428 return Some(hir::Mutability::Not);
429 } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
430 return Some(hir::Mutability::Mut);
437 // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
438 // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
439 // These types seem reasonably opaque enough that they could be substituted with their
440 // borrowed variants in a function body when we see a move error.
441 let borrow_level = match ty.kind() {
442 ty::Param(_) => find_fn_kind_from_did(
443 tcx.explicit_predicates_of(self.mir_def_id().to_def_id()).predicates,
446 ty::Opaque(did, substs) => {
447 find_fn_kind_from_did(tcx.explicit_item_bounds(*did), Some(*substs))
449 ty::Closure(_, substs) => match substs.as_closure().kind() {
450 ty::ClosureKind::Fn => Some(hir::Mutability::Not),
451 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
457 let Some(borrow_level) = borrow_level else { return false; };
458 let sugg = move_sites
461 let move_out = self.move_data.moves[(*move_site).moi];
462 let moved_place = &self.move_data.move_paths[move_out.path].place;
463 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
464 let move_span = move_spans.args_or_use();
465 let suggestion = if borrow_level == hir::Mutability::Mut {
470 (move_span.shrink_to_lo(), suggestion)
473 err.multipart_suggestion_verbose(
475 "consider {}borrowing {value_name}",
476 if borrow_level == hir::Mutability::Mut { "mutably " } else { "" }
479 Applicability::MaybeIncorrect,
484 fn suggest_adding_copy_bounds(
486 err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
490 let tcx = self.infcx.tcx;
491 let generics = tcx.generics_of(self.mir_def_id());
493 let Some(hir_generics) = tcx
494 .typeck_root_def_id(self.mir_def_id().to_def_id())
496 .and_then(|def_id| tcx.hir().get_generics(def_id))
498 // Try to find predicates on *generic params* that would allow copying `ty`
499 let predicates: Result<Vec<_>, _> = tcx.infer_ctxt().enter(|infcx| {
500 let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
502 let copy_did = infcx.tcx.lang_items().copy_trait().unwrap();
503 let cause = ObligationCause::new(
506 rustc_infer::traits::ObligationCauseCode::MiscObligation,
508 fulfill_cx.register_bound(
511 // Erase any region vids from the type, which may not be resolved
512 infcx.tcx.erase_regions(ty),
516 // Select all, including ambiguous predicates
517 let errors = fulfill_cx.select_all_or_error(&infcx);
519 // Only emit suggestion if all required predicates are on generic
522 .map(|err| match err.obligation.predicate.kind().skip_binder() {
523 PredicateKind::Trait(predicate) => match predicate.self_ty().kind() {
524 ty::Param(param_ty) => Ok((
525 generics.type_param(param_ty, tcx),
526 predicate.trait_ref.print_only_trait_path().to_string(),
535 if let Ok(predicates) = predicates {
536 suggest_constraining_type_params(
542 .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
547 pub(crate) fn report_move_out_while_borrowed(
550 (place, span): (Place<'tcx>, Span),
551 borrow: &BorrowData<'tcx>,
554 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
555 location, place, span, borrow
557 let value_msg = self.describe_any_place(place.as_ref());
558 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
560 let borrow_spans = self.retrieve_borrow_spans(borrow);
561 let borrow_span = borrow_spans.args_or_use();
563 let move_spans = self.move_spans(place.as_ref(), location);
564 let span = move_spans.args_or_use();
567 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
568 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
569 err.span_label(span, format!("move out of {} occurs here", value_msg));
571 borrow_spans.var_span_label_path_only(
573 format!("borrow occurs due to use{}", borrow_spans.describe()),
576 move_spans.var_span_label(
578 format!("move occurs due to use{}", move_spans.describe()),
582 self.explain_why_borrow_contains_point(location, borrow, None)
583 .add_explanation_to_diagnostic(
592 self.buffer_error(err);
595 pub(crate) fn report_use_while_mutably_borrowed(
598 (place, _span): (Place<'tcx>, Span),
599 borrow: &BorrowData<'tcx>,
600 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
601 let borrow_spans = self.retrieve_borrow_spans(borrow);
602 let borrow_span = borrow_spans.args_or_use();
604 // Conflicting borrows are reported separately, so only check for move
606 let use_spans = self.move_spans(place.as_ref(), location);
607 let span = use_spans.var_or_use();
609 // 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
610 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
611 let mut err = self.cannot_use_when_mutably_borrowed(
613 &self.describe_any_place(place.as_ref()),
615 &self.describe_any_place(borrow.borrowed_place.as_ref()),
618 borrow_spans.var_span_label(
621 let place = &borrow.borrowed_place;
622 let desc_place = self.describe_any_place(place.as_ref());
623 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
628 self.explain_why_borrow_contains_point(location, borrow, None)
629 .add_explanation_to_diagnostic(
641 pub(crate) fn report_conflicting_borrow(
644 (place, span): (Place<'tcx>, Span),
645 gen_borrow_kind: BorrowKind,
646 issued_borrow: &BorrowData<'tcx>,
647 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
648 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
649 let issued_span = issued_spans.args_or_use();
651 let borrow_spans = self.borrow_spans(span, location);
652 let span = borrow_spans.args_or_use();
654 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
660 let (desc_place, msg_place, msg_borrow, union_type_name) =
661 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
663 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
664 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
666 // FIXME: supply non-"" `opt_via` when appropriate
667 let first_borrow_desc;
668 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
669 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
670 first_borrow_desc = "mutable ";
671 self.cannot_reborrow_already_borrowed(
683 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
684 first_borrow_desc = "immutable ";
685 self.cannot_reborrow_already_borrowed(
698 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
699 first_borrow_desc = "first ";
700 let mut err = self.cannot_mutably_borrow_multiply(
708 self.suggest_split_at_mut_if_applicable(
711 issued_borrow.borrowed_place,
716 (BorrowKind::Unique, BorrowKind::Unique) => {
717 first_borrow_desc = "first ";
718 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
721 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
722 if let Some(immutable_section_description) =
723 self.classify_immutable_section(issued_borrow.assigned_place)
725 let mut err = self.cannot_mutate_in_immutable_section(
729 immutable_section_description,
732 borrow_spans.var_span_label(
735 "borrow occurs due to use of {}{}",
737 borrow_spans.describe(),
744 first_borrow_desc = "immutable ";
745 self.cannot_reborrow_already_borrowed(
759 (BorrowKind::Unique, _) => {
760 first_borrow_desc = "first ";
761 self.cannot_uniquely_borrow_by_one_closure(
773 (BorrowKind::Shared, BorrowKind::Unique) => {
774 first_borrow_desc = "first ";
775 self.cannot_reborrow_already_uniquely_borrowed(
788 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
789 first_borrow_desc = "first ";
790 self.cannot_reborrow_already_uniquely_borrowed(
803 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
806 BorrowKind::Mut { .. }
809 | BorrowKind::Shallow,
813 if issued_spans == borrow_spans {
814 borrow_spans.var_span_label(
816 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
817 gen_borrow_kind.describe_mutability(),
820 let borrow_place = &issued_borrow.borrowed_place;
821 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
822 issued_spans.var_span_label(
825 "first borrow occurs due to use of {}{}",
827 issued_spans.describe(),
829 issued_borrow.kind.describe_mutability(),
832 borrow_spans.var_span_label(
835 "second borrow occurs due to use of {}{}",
837 borrow_spans.describe(),
839 gen_borrow_kind.describe_mutability(),
843 if union_type_name != "" {
845 "{} is a field of the union `{}`, so it overlaps the field {}",
846 msg_place, union_type_name, msg_borrow,
850 explanation.add_explanation_to_diagnostic(
857 Some((issued_span, span)),
860 self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation);
865 #[instrument(level = "debug", skip(self, err))]
866 fn suggest_using_local_if_applicable(
868 err: &mut Diagnostic,
870 issued_borrow: &BorrowData<'tcx>,
871 explanation: BorrowExplanation<'tcx>,
873 let used_in_call = matches!(
875 BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
878 debug!("not later used in call");
883 if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
890 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
893 issued_borrow.reserve_location
895 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
897 let inner_param_location = location;
898 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
899 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
902 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
904 "`inner_param_location` {:?} is not for an assignment: {:?}",
905 inner_param_location, inner_param_stmt
909 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
910 let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
911 let Either::Right(term) = self.body.stmt_at(loc) else {
912 debug!("{:?} is a statement, so it can't be a call", loc);
915 let TerminatorKind::Call { args, .. } = &term.kind else {
916 debug!("not a call: {:?}", term);
919 debug!("checking call args for uses of inner_param: {:?}", args);
920 if args.contains(&Operand::Move(inner_param)) {
926 debug!("no uses of inner_param found as a by-move call arg");
929 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
931 let inner_call_span = inner_call_term.source_info.span;
932 let outer_call_span = match use_span {
934 None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span,
936 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
937 // FIXME: This stops the suggestion in some cases where it should be emitted.
938 // Fix the spans for those cases so it's emitted correctly.
940 "outer span {:?} does not strictly contain inner span {:?}",
941 outer_call_span, inner_call_span
948 "try adding a local storing this{}...",
949 if use_span.is_some() { "" } else { " argument" }
955 "...and then using that local {}",
956 if use_span.is_some() { "here" } else { "as the argument to this call" }
961 fn suggest_split_at_mut_if_applicable(
963 err: &mut Diagnostic,
965 borrowed_place: Place<'tcx>,
967 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
968 (&place.projection[..], &borrowed_place.projection[..])
971 "consider using `.split_at_mut(position)` or similar method to obtain \
972 two mutable non-overlapping sub-slices",
977 /// Returns the description of the root place for a conflicting borrow and the full
978 /// descriptions of the places that caused the conflict.
980 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
981 /// attempted while a shared borrow is live, then this function will return:
986 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
987 /// a shared borrow of another field `x.y`, then this function will return:
989 /// ("x", "x.z", "x.y")
992 /// In the more complex union case, where the union is a field of a struct, then if a mutable
993 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
994 /// another field `x.u.y`, then this function will return:
996 /// ("x.u", "x.u.z", "x.u.y")
999 /// This is used when creating error messages like below:
1002 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
1003 /// mutable (via `a.u.s.b`) [E0502]
1005 pub(crate) fn describe_place_for_conflicting_borrow(
1007 first_borrowed_place: Place<'tcx>,
1008 second_borrowed_place: Place<'tcx>,
1009 ) -> (String, String, String, String) {
1010 // Define a small closure that we can use to check if the type of a place
1012 let union_ty = |place_base| {
1013 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
1014 // using a type annotation in the closure argument instead leads to a lifetime error.
1015 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
1016 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
1019 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
1020 // code duplication (particularly around returning an empty description in the failure
1024 // If we have a conflicting borrow of the same place, then we don't want to add
1025 // an extraneous "via x.y" to our diagnostics, so filter out this case.
1026 first_borrowed_place != second_borrowed_place
1029 // We're going to want to traverse the first borrowed place to see if we can find
1030 // field access to a union. If we find that, then we will keep the place of the
1031 // union being accessed and the field that was being accessed so we can check the
1032 // second borrowed place for the same union and an access to a different field.
1033 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
1035 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
1036 return Some((place_base, field));
1043 .and_then(|(target_base, target_field)| {
1044 // With the place of a union and a field access into it, we traverse the second
1045 // borrowed place and look for an access to a different field of the same union.
1046 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1047 if let ProjectionElem::Field(field, _) = elem {
1048 if let Some(union_ty) = union_ty(place_base) {
1049 if field != target_field && place_base == target_base {
1051 self.describe_any_place(place_base),
1052 self.describe_any_place(first_borrowed_place.as_ref()),
1053 self.describe_any_place(second_borrowed_place.as_ref()),
1054 union_ty.to_string(),
1062 .unwrap_or_else(|| {
1063 // If we didn't find a field access into a union, or both places match, then
1064 // only return the description of the first place.
1066 self.describe_any_place(first_borrowed_place.as_ref()),
1074 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1076 /// This means that some data referenced by `borrow` needs to live
1077 /// past the point where the StorageDeadOrDrop of `place` occurs.
1078 /// This is usually interpreted as meaning that `place` has too
1079 /// short a lifetime. (But sometimes it is more useful to report
1080 /// it as a more direct conflict between the execution of a
1081 /// `Drop::drop` with an aliasing borrow.)
1082 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1085 borrow: &BorrowData<'tcx>,
1086 place_span: (Place<'tcx>, Span),
1087 kind: Option<WriteKind>,
1090 "report_borrowed_value_does_not_live_long_enough(\
1091 {:?}, {:?}, {:?}, {:?}\
1093 location, borrow, place_span, kind
1096 let drop_span = place_span.1;
1098 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1100 let borrow_spans = self.retrieve_borrow_spans(borrow);
1101 let borrow_span = borrow_spans.var_or_use_path_span();
1103 assert!(root_place.projection.is_empty());
1104 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1106 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1108 if self.access_place_error_reported.contains(&(
1109 Place { local: root_place.local, projection: root_place_projection },
1113 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1119 self.access_place_error_reported.insert((
1120 Place { local: root_place.local, projection: root_place_projection },
1124 let borrowed_local = borrow.borrowed_place.local;
1125 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1127 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1128 self.buffer_error(err);
1132 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1133 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1135 // If a borrow of path `B` conflicts with drop of `D` (and
1136 // we're not in the uninteresting case where `B` is a
1137 // prefix of `D`), then report this as a more interesting
1138 // destructor conflict.
1139 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1140 self.report_borrow_conflicts_with_destructor(
1141 location, borrow, place_span, kind, dropped_ty,
1147 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1149 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1150 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1153 "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
1154 place_desc, explanation
1156 let err = match (place_desc, explanation) {
1157 // If the outlives constraint comes from inside the closure,
1162 // Box::new(|| y) as Box<Fn() -> &'static i32>
1164 // then just use the normal error. The closure isn't escaping
1165 // and `move` will not help here.
1168 BorrowExplanation::MustBeValidFor {
1170 category @ (ConstraintCategory::Return(_)
1171 | ConstraintCategory::CallArgument(_)
1172 | ConstraintCategory::OpaqueType),
1173 from_closure: false,
1178 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1179 .report_escaping_closure_capture(
1185 &format!("`{}`", name),
1189 BorrowExplanation::MustBeValidFor {
1190 category: ConstraintCategory::Assignment,
1191 from_closure: false,
1195 RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
1201 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1202 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1210 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1220 self.buffer_error(err);
1223 fn report_local_value_does_not_live_long_enough(
1227 borrow: &BorrowData<'tcx>,
1229 borrow_spans: UseSpans<'tcx>,
1230 explanation: BorrowExplanation<'tcx>,
1231 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1233 "report_local_value_does_not_live_long_enough(\
1234 {:?}, {:?}, {:?}, {:?}, {:?}\
1236 location, name, borrow, drop_span, borrow_spans
1239 let borrow_span = borrow_spans.var_or_use_path_span();
1240 if let BorrowExplanation::MustBeValidFor {
1244 from_closure: false,
1248 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1253 opt_place_desc.as_ref(),
1259 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1261 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1262 let region_name = annotation.emit(self, &mut err);
1266 format!("`{}` would have to be valid for `{}`...", name, region_name),
1269 let fn_hir_id = self.mir_hir_id();
1273 "...but `{}` will be dropped here, when the {} returns",
1278 .opt_name(fn_hir_id)
1279 .map(|name| format!("function `{}`", name))
1280 .unwrap_or_else(|| {
1284 .typeck(self.mir_def_id())
1285 .node_type(fn_hir_id)
1288 ty::Closure(..) => "enclosing closure",
1289 ty::Generator(..) => "enclosing generator",
1290 kind => bug!("expected closure or generator, found {:?}", kind),
1298 "functions cannot return a borrow to data owned within the function's scope, \
1299 functions can only return borrows to data passed as arguments",
1302 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1303 references-and-borrowing.html#dangling-references>",
1306 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1308 explanation.add_explanation_to_diagnostic(
1319 err.span_label(borrow_span, "borrowed value does not live long enough");
1320 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1322 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1324 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1326 explanation.add_explanation_to_diagnostic(
1340 fn report_borrow_conflicts_with_destructor(
1343 borrow: &BorrowData<'tcx>,
1344 (place, drop_span): (Place<'tcx>, Span),
1345 kind: Option<WriteKind>,
1346 dropped_ty: Ty<'tcx>,
1349 "report_borrow_conflicts_with_destructor(\
1350 {:?}, {:?}, ({:?}, {:?}), {:?}\
1352 location, borrow, place, drop_span, kind,
1355 let borrow_spans = self.retrieve_borrow_spans(borrow);
1356 let borrow_span = borrow_spans.var_or_use();
1358 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1360 let what_was_dropped = match self.describe_place(place.as_ref()) {
1361 Some(name) => format!("`{}`", name),
1362 None => String::from("temporary value"),
1365 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1366 Some(borrowed) => format!(
1367 "here, drop of {D} needs exclusive access to `{B}`, \
1368 because the type `{T}` implements the `Drop` trait",
1369 D = what_was_dropped,
1374 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1375 D = what_was_dropped,
1379 err.span_label(drop_span, label);
1381 // Only give this note and suggestion if they could be relevant.
1383 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1385 BorrowExplanation::UsedLater { .. }
1386 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1387 err.note("consider using a `let` binding to create a longer lived value");
1392 explanation.add_explanation_to_diagnostic(
1402 self.buffer_error(err);
1405 fn report_thread_local_value_does_not_live_long_enough(
1409 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1411 "report_thread_local_value_does_not_live_long_enough(\
1414 drop_span, borrow_span
1417 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1421 "thread-local variables cannot be borrowed beyond the end of the function",
1423 err.span_label(drop_span, "end of enclosing function is here");
1428 fn report_temporary_value_does_not_live_long_enough(
1431 borrow: &BorrowData<'tcx>,
1433 borrow_spans: UseSpans<'tcx>,
1435 explanation: BorrowExplanation<'tcx>,
1436 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1438 "report_temporary_value_does_not_live_long_enough(\
1439 {:?}, {:?}, {:?}, {:?}\
1441 location, borrow, drop_span, proper_span
1444 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1447 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1458 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1459 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1460 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1463 BorrowExplanation::UsedLater(..)
1464 | BorrowExplanation::UsedLaterInLoop(..)
1465 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1466 // Only give this note and suggestion if it could be relevant.
1467 err.note("consider using a `let` binding to create a longer lived value");
1471 explanation.add_explanation_to_diagnostic(
1481 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1483 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1488 fn try_report_cannot_return_reference_to_local(
1490 borrow: &BorrowData<'tcx>,
1493 category: ConstraintCategory<'tcx>,
1494 opt_place_desc: Option<&String>,
1495 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1496 let return_kind = match category {
1497 ConstraintCategory::Return(_) => "return",
1498 ConstraintCategory::Yield => "yield",
1502 // FIXME use a better heuristic than Spans
1503 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1509 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1510 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1511 match self.body.local_kind(local) {
1512 LocalKind::ReturnPointer | LocalKind::Temp => {
1513 bug!("temporary or return pointer with a name")
1515 LocalKind::Var => "local variable ",
1517 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1519 "variable captured by `move` "
1521 LocalKind::Arg => "function parameter ",
1527 format!("{}`{}`", local_kind, place_desc),
1528 format!("`{}` is borrowed here", place_desc),
1532 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1533 let local = root_place.local;
1534 match self.body.local_kind(local) {
1535 LocalKind::ReturnPointer | LocalKind::Temp => {
1536 ("temporary value".to_string(), "temporary value created here".to_string())
1539 "function parameter".to_string(),
1540 "function parameter borrowed here".to_string(),
1543 ("local binding".to_string(), "local binding introduced here".to_string())
1548 let mut err = self.cannot_return_reference_to_local(
1555 if return_span != borrow_span {
1556 err.span_label(borrow_span, note);
1558 let tcx = self.infcx.tcx;
1559 let ty_params = ty::List::empty();
1561 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1562 let return_ty = tcx.erase_regions(return_ty);
1565 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1568 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1569 .must_apply_modulo_regions()
1571 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1572 err.span_suggestion_hidden(
1574 "use `.collect()` to allocate the iterator",
1575 format!("{snippet}.collect::<Vec<_>>()"),
1576 Applicability::MaybeIncorrect,
1586 fn report_escaping_closure_capture(
1588 use_span: UseSpans<'tcx>,
1590 fr_name: &RegionName,
1591 category: ConstraintCategory<'tcx>,
1592 constraint_span: Span,
1594 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1595 let tcx = self.infcx.tcx;
1596 let args_span = use_span.args_or_use();
1598 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1600 if string.starts_with("async ") {
1601 let pos = args_span.lo() + BytePos(6);
1602 (args_span.with_lo(pos).with_hi(pos), "move ")
1603 } else if string.starts_with("async|") {
1604 let pos = args_span.lo() + BytePos(5);
1605 (args_span.with_lo(pos).with_hi(pos), " move")
1607 (args_span.shrink_to_lo(), "move ")
1610 Err(_) => (args_span, "move |<args>| <body>"),
1612 let kind = match use_span.generator_kind() {
1613 Some(generator_kind) => match generator_kind {
1614 GeneratorKind::Async(async_kind) => match async_kind {
1615 AsyncGeneratorKind::Block => "async block",
1616 AsyncGeneratorKind::Closure => "async closure",
1617 _ => bug!("async block/closure expected, but async function found."),
1619 GeneratorKind::Gen => "generator",
1625 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1626 err.span_suggestion_verbose(
1629 "to force the {} to take ownership of {} (and any \
1630 other referenced variables), use the `move` keyword",
1634 Applicability::MachineApplicable,
1638 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1639 let msg = format!("{} is returned here", kind);
1640 err.span_note(constraint_span, &msg);
1642 ConstraintCategory::CallArgument(_) => {
1643 fr_name.highlight_region_name(&mut err);
1644 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1646 "async blocks are not executed immediately and must either take a \
1647 reference or ownership of outside variables they use",
1650 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1651 err.span_note(constraint_span, &msg);
1655 "report_escaping_closure_capture called with unexpected constraint \
1664 fn report_escaping_data(
1667 name: &Option<String>,
1671 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1672 let tcx = self.infcx.tcx;
1674 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1677 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1681 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1684 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1686 if let Some(name) = name {
1689 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1694 format!("reference escapes the {} body here", escapes_from),
1701 fn get_moved_indexes(
1705 ) -> (Vec<MoveSite>, Vec<Location>) {
1706 fn predecessor_locations<'tcx, 'a>(
1707 body: &'a mir::Body<'tcx>,
1709 ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
1710 if location.statement_index == 0 {
1711 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
1712 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1714 Either::Right(std::iter::once(Location {
1715 statement_index: location.statement_index - 1,
1721 let mut mpis = vec![mpi];
1722 let move_paths = &self.move_data.move_paths;
1723 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1725 let mut stack = Vec::new();
1726 let mut back_edge_stack = Vec::new();
1728 predecessor_locations(self.body, location).for_each(|predecessor| {
1729 if location.dominates(predecessor, &self.dominators) {
1730 back_edge_stack.push(predecessor)
1732 stack.push(predecessor);
1736 let mut reached_start = false;
1738 /* Check if the mpi is initialized as an argument */
1739 let mut is_argument = false;
1740 for arg in self.body.args_iter() {
1741 let path = self.move_data.rev_lookup.find_local(arg);
1742 if mpis.contains(&path) {
1747 let mut visited = FxHashSet::default();
1748 let mut move_locations = FxHashSet::default();
1749 let mut reinits = vec![];
1750 let mut result = vec![];
1752 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1754 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1755 location, is_back_edge
1758 if !visited.insert(location) {
1764 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1765 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1766 // this analysis only tries to find moves explicitly
1767 // written by the user, so we ignore the move-outs
1768 // created by `StorageDead` and at the beginning
1771 // If we are found a use of a.b.c which was in error, then we want to look for
1772 // moves not only of a.b.c but also a.b and a.
1774 // Note that the moves data already includes "parent" paths, so we don't have to
1775 // worry about the other case: that is, if there is a move of a.b.c, it is already
1776 // marked as a move of a.b and a as well, so we will generate the correct errors
1778 for moi in &self.move_data.loc_map[location] {
1779 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1780 let path = self.move_data.moves[*moi].path;
1781 if mpis.contains(&path) {
1783 "report_use_of_moved_or_uninitialized: found {:?}",
1784 move_paths[path].place
1786 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1787 move_locations.insert(location);
1789 // Strictly speaking, we could continue our DFS here. There may be
1790 // other moves that can reach the point of error. But it is kind of
1791 // confusing to highlight them.
1799 // drop(a); // <-- current point of error
1802 // Because we stop the DFS here, we only highlight `let c = a`,
1803 // and not `let b = a`. We will of course also report an error at
1804 // `let c = a` which highlights `let b = a` as the move.
1811 let mut any_match = false;
1812 for ii in &self.move_data.init_loc_map[location] {
1813 let init = self.move_data.inits[*ii];
1815 InitKind::Deep | InitKind::NonPanicPathOnly => {
1816 if mpis.contains(&init.path) {
1820 InitKind::Shallow => {
1821 if mpi == init.path {
1828 reinits.push(location);
1834 while let Some(location) = stack.pop() {
1835 if dfs_iter(&mut result, location, false) {
1839 let mut has_predecessor = false;
1840 predecessor_locations(self.body, location).for_each(|predecessor| {
1841 if location.dominates(predecessor, &self.dominators) {
1842 back_edge_stack.push(predecessor)
1844 stack.push(predecessor);
1846 has_predecessor = true;
1849 if !has_predecessor {
1850 reached_start = true;
1853 if (is_argument || !reached_start) && result.is_empty() {
1854 /* Process back edges (moves in future loop iterations) only if
1855 the move path is definitely initialized upon loop entry,
1856 to avoid spurious "in previous iteration" errors.
1857 During DFS, if there's a path from the error back to the start
1858 of the function with no intervening init or move, then the
1859 move path may be uninitialized at loop entry.
1861 while let Some(location) = back_edge_stack.pop() {
1862 if dfs_iter(&mut result, location, true) {
1866 predecessor_locations(self.body, location)
1867 .for_each(|predecessor| back_edge_stack.push(predecessor));
1871 // Check if we can reach these reinits from a move location.
1872 let reinits_reachable = reinits
1875 let mut visited = FxHashSet::default();
1876 let mut stack = vec![*reinit];
1877 while let Some(location) = stack.pop() {
1878 if !visited.insert(location) {
1881 if move_locations.contains(&location) {
1884 stack.extend(predecessor_locations(self.body, location));
1888 .collect::<Vec<Location>>();
1889 (result, reinits_reachable)
1892 pub(crate) fn report_illegal_mutation_of_borrowed(
1895 (place, span): (Place<'tcx>, Span),
1896 loan: &BorrowData<'tcx>,
1898 let loan_spans = self.retrieve_borrow_spans(loan);
1899 let loan_span = loan_spans.args_or_use();
1901 let descr_place = self.describe_any_place(place.as_ref());
1902 if loan.kind == BorrowKind::Shallow {
1903 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1904 let mut err = self.cannot_mutate_in_immutable_section(
1911 loan_spans.var_span_label(
1913 format!("borrow occurs due to use{}", loan_spans.describe()),
1914 loan.kind.describe_mutability(),
1917 self.buffer_error(err);
1923 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
1925 loan_spans.var_span_label(
1927 format!("borrow occurs due to use{}", loan_spans.describe()),
1928 loan.kind.describe_mutability(),
1931 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
1941 self.explain_deref_coercion(loan, &mut err);
1943 self.buffer_error(err);
1946 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
1947 let tcx = self.infcx.tcx;
1949 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
1950 Some((method_did, method_substs)),
1952 &self.body[loan.reserve_location.block].terminator,
1953 rustc_const_eval::util::find_self_call(
1956 loan.assigned_place.local,
1957 loan.reserve_location.block,
1960 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
1962 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
1963 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
1966 if let Some(Ok(instance)) = deref_target {
1967 let deref_target_ty = instance.ty(tcx, self.param_env);
1969 "borrow occurs due to deref coercion to `{}`",
1972 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
1978 /// Reports an illegal reassignment; for example, an assignment to
1979 /// (part of) a non-`mut` local that occurs potentially after that
1980 /// local has already been initialized. `place` is the path being
1981 /// assigned; `err_place` is a place providing a reason why
1982 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
1983 /// assignment to `x.f`).
1984 pub(crate) fn report_illegal_reassignment(
1986 _location: Location,
1987 (place, span): (Place<'tcx>, Span),
1988 assigned_span: Span,
1989 err_place: Place<'tcx>,
1991 let (from_arg, local_decl, local_name) = match err_place.as_local() {
1993 self.body.local_kind(local) == LocalKind::Arg,
1994 Some(&self.body.local_decls[local]),
1995 self.local_names[local],
1997 None => (false, None, None),
2000 // If root local is initialized immediately (everything apart from let
2001 // PATTERN;) then make the error refer to that local, rather than the
2002 // place being assigned later.
2003 let (place_description, assigned_span) = match local_decl {
2006 Some(box LocalInfo::User(
2007 ClearCrossCrate::Clear
2008 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
2009 opt_match_place: None,
2013 | Some(box LocalInfo::StaticRef { .. })
2017 | None => (self.describe_any_place(place.as_ref()), assigned_span),
2018 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
2021 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
2022 let msg = if from_arg {
2023 "cannot assign to immutable argument"
2025 "cannot assign twice to immutable variable"
2027 if span != assigned_span && !from_arg {
2028 err.span_label(assigned_span, format!("first assignment to {}", place_description));
2030 if let Some(decl) = local_decl
2031 && let Some(name) = local_name
2032 && decl.can_be_made_mutable()
2034 err.span_suggestion(
2035 decl.source_info.span,
2036 "consider making this binding mutable",
2037 format!("mut {}", name),
2038 Applicability::MachineApplicable,
2041 err.span_label(span, msg);
2042 self.buffer_error(err);
2045 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2046 let tcx = self.infcx.tcx;
2047 let (kind, _place_ty) = place.projection.iter().fold(
2048 (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
2049 |(kind, place_ty), &elem| {
2052 ProjectionElem::Deref => match kind {
2053 StorageDeadOrDrop::LocalStorageDead
2054 | StorageDeadOrDrop::BoxedStorageDead => {
2056 place_ty.ty.is_box(),
2057 "Drop of value behind a reference or raw pointer"
2059 StorageDeadOrDrop::BoxedStorageDead
2061 StorageDeadOrDrop::Destructor(_) => kind,
2063 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
2064 match place_ty.ty.kind() {
2065 ty::Adt(def, _) if def.has_dtor(tcx) => {
2066 // Report the outermost adt with a destructor
2068 StorageDeadOrDrop::Destructor(_) => kind,
2069 StorageDeadOrDrop::LocalStorageDead
2070 | StorageDeadOrDrop::BoxedStorageDead => {
2071 StorageDeadOrDrop::Destructor(place_ty.ty)
2078 ProjectionElem::ConstantIndex { .. }
2079 | ProjectionElem::Subslice { .. }
2080 | ProjectionElem::Index(_) => kind,
2082 place_ty.projection_ty(tcx, elem),
2089 /// Describe the reason for the fake borrow that was assigned to `place`.
2090 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2091 use rustc_middle::mir::visit::Visitor;
2092 struct FakeReadCauseFinder<'tcx> {
2094 cause: Option<FakeReadCause>,
2096 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2097 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2099 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2100 if *place == self.place =>
2102 self.cause = Some(*cause);
2108 let mut visitor = FakeReadCauseFinder { place, cause: None };
2109 visitor.visit_body(&self.body);
2110 match visitor.cause {
2111 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2112 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2117 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2118 /// borrow of local value that does not live long enough.
2119 fn annotate_argument_and_return_for_borrow(
2121 borrow: &BorrowData<'tcx>,
2122 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2123 // Define a fallback for when we can't match a closure.
2125 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2129 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2131 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2132 self.mir_def_id().to_def_id(),
2133 self.infcx.tcx.fn_sig(self.mir_def_id()),
2140 // In order to determine whether we need to annotate, we need to check whether the reserve
2141 // place was an assignment into a temporary.
2143 // If it was, we check whether or not that temporary is eventually assigned into the return
2144 // place. If it was, we can add annotations about the function's return type and arguments
2145 // and it'll make sense.
2146 let location = borrow.reserve_location;
2147 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2148 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2149 &self.body[location.block].statements.get(location.statement_index)
2151 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2152 // Check that the initial assignment of the reserve location is into a temporary.
2153 let mut target = match reservation.as_local() {
2154 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2158 // Next, look through the rest of the block, checking if we are assigning the
2159 // `target` (that is, the place that contains our borrow) to anything.
2160 let mut annotated_closure = None;
2161 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2163 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2166 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2167 if let Some(assigned_to) = place.as_local() {
2169 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2173 // Check if our `target` was captured by a closure.
2174 if let Rvalue::Aggregate(
2175 box AggregateKind::Closure(def_id, substs),
2179 for operand in operands {
2180 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2184 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2188 // Find the local from the operand.
2189 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2193 if assigned_from_local != target {
2197 // If a closure captured our `target` and then assigned
2198 // into a place then we should annotate the closure in
2199 // case it ends up being assigned into the return place.
2201 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2203 "annotate_argument_and_return_for_borrow: \
2204 annotated_closure={:?} assigned_from_local={:?} \
2206 annotated_closure, assigned_from_local, assigned_to
2209 if assigned_to == mir::RETURN_PLACE {
2210 // If it was assigned directly into the return place, then
2212 return annotated_closure;
2214 // Otherwise, update the target.
2215 target = assigned_to;
2219 // If none of our closure's operands matched, then skip to the next
2224 // Otherwise, look at other types of assignment.
2225 let assigned_from = match rvalue {
2226 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2227 Rvalue::Use(operand) => match operand {
2228 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2236 "annotate_argument_and_return_for_borrow: \
2237 assigned_from={:?}",
2241 // Find the local from the rvalue.
2242 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2244 "annotate_argument_and_return_for_borrow: \
2245 assigned_from_local={:?}",
2246 assigned_from_local,
2249 // Check if our local matches the target - if so, we've assigned our
2250 // borrow to a new place.
2251 if assigned_from_local != target {
2255 // If we assigned our `target` into a new place, then we should
2256 // check if it was the return place.
2258 "annotate_argument_and_return_for_borrow: \
2259 assigned_from_local={:?} assigned_to={:?}",
2260 assigned_from_local, assigned_to
2262 if assigned_to == mir::RETURN_PLACE {
2263 // If it was then return the annotated closure if there was one,
2264 // else, annotate this function.
2265 return annotated_closure.or_else(fallback);
2268 // If we didn't assign into the return place, then we just update
2270 target = assigned_to;
2275 // Check the terminator if we didn't find anything in the statements.
2276 let terminator = &self.body[location.block].terminator();
2278 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2281 if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
2284 if let Some(assigned_to) = destination.as_local() {
2286 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2289 for operand in args {
2290 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2294 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2298 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2300 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2301 assigned_from_local,
2304 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2305 return annotated_closure.or_else(fallback);
2313 // If we haven't found an assignment into the return place, then we need not add
2315 debug!("annotate_argument_and_return_for_borrow: none found");
2319 /// Annotate the first argument and return type of a function signature if they are
2324 sig: ty::PolyFnSig<'tcx>,
2325 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2326 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2327 let is_closure = self.infcx.tcx.is_closure(did);
2328 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?);
2329 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2331 // We need to work out which arguments to highlight. We do this by looking
2332 // at the return type, where there are three cases:
2334 // 1. If there are named arguments, then we should highlight the return type and
2335 // highlight any of the arguments that are also references with that lifetime.
2336 // If there are no arguments that have the same lifetime as the return type,
2337 // then don't highlight anything.
2338 // 2. The return type is a reference with an anonymous lifetime. If this is
2339 // the case, then we can take advantage of (and teach) the lifetime elision
2342 // We know that an error is being reported. So the arguments and return type
2343 // must satisfy the elision rules. Therefore, if there is a single argument
2344 // then that means the return type and first (and only) argument have the same
2345 // lifetime and the borrow isn't meeting that, we can highlight the argument
2348 // If there are multiple arguments then the first argument must be self (else
2349 // it would not satisfy the elision rules), so we can highlight self and the
2351 // 3. The return type is not a reference. In this case, we don't highlight
2353 let return_ty = sig.output();
2354 match return_ty.skip_binder().kind() {
2355 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2356 // This is case 1 from above, return type is a named reference so we need to
2357 // search for relevant arguments.
2358 let mut arguments = Vec::new();
2359 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2360 if let ty::Ref(argument_region, _, _) = argument.kind() {
2361 if argument_region == return_region {
2362 // Need to use the `rustc_middle::ty` types to compare against the
2363 // `return_region`. Then use the `rustc_hir` type to get only
2364 // the lifetime span.
2365 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2366 // With access to the lifetime, we can get
2368 arguments.push((*argument, lifetime.span));
2370 bug!("ty type is a ref but hir type is not");
2376 // We need to have arguments. This shouldn't happen, but it's worth checking.
2377 if arguments.is_empty() {
2381 // We use a mix of the HIR and the Ty types to get information
2382 // as the HIR doesn't have full types for closure arguments.
2383 let return_ty = sig.output().skip_binder();
2384 let mut return_span = fn_decl.output.span();
2385 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2386 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2387 return_span = lifetime.span;
2391 Some(AnnotatedBorrowFnSignature::NamedFunction {
2397 ty::Ref(_, _, _) if is_closure => {
2398 // This is case 2 from above but only for closures, return type is anonymous
2399 // reference so we select
2400 // the first argument.
2401 let argument_span = fn_decl.inputs.first()?.span;
2402 let argument_ty = sig.inputs().skip_binder().first()?;
2404 // Closure arguments are wrapped in a tuple, so we need to get the first
2406 if let ty::Tuple(elems) = argument_ty.kind() {
2407 let &argument_ty = elems.first()?;
2408 if let ty::Ref(_, _, _) = argument_ty.kind() {
2409 return Some(AnnotatedBorrowFnSignature::Closure {
2418 ty::Ref(_, _, _) => {
2419 // This is also case 2 from above but for functions, return type is still an
2420 // anonymous reference so we select the first argument.
2421 let argument_span = fn_decl.inputs.first()?.span;
2422 let argument_ty = *sig.inputs().skip_binder().first()?;
2424 let return_span = fn_decl.output.span();
2425 let return_ty = sig.output().skip_binder();
2427 // We expect the first argument to be a reference.
2428 match argument_ty.kind() {
2429 ty::Ref(_, _, _) => {}
2433 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2441 // This is case 3 from above, return type is not a reference so don't highlight
2450 enum AnnotatedBorrowFnSignature<'tcx> {
2452 arguments: Vec<(Ty<'tcx>, Span)>,
2453 return_ty: Ty<'tcx>,
2457 argument_ty: Ty<'tcx>,
2458 argument_span: Span,
2459 return_ty: Ty<'tcx>,
2463 argument_ty: Ty<'tcx>,
2464 argument_span: Span,
2468 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2469 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2471 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2473 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2476 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2479 cx.get_region_name_for_ty(argument_ty, 0)
2481 &AnnotatedBorrowFnSignature::AnonymousFunction {
2487 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2488 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2490 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2491 let types_equal = return_ty_name == argument_ty_name;
2496 if types_equal { "also " } else { "" },
2502 "argument and return type have the same lifetime due to lifetime elision rules",
2505 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2506 lifetime-syntax.html#lifetime-elision>",
2509 cx.get_region_name_for_ty(return_ty, 0)
2511 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2512 // Region of return type and arguments checked to be the same earlier.
2513 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2514 for (_, argument_span) in arguments {
2515 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2518 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2521 "use data from the highlighted arguments which match the `{}` lifetime of \
2532 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr
2533 struct ReferencedStatementsVisitor<'a>(&'a [Span], bool);
2535 impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> {
2536 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
2538 hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => {
2546 /// Given a set of spans representing statements initializing the relevant binding, visit all the
2547 /// function expressions looking for branching code paths that *do not* initialize the binding.
2548 struct ConditionVisitor<'b> {
2551 errors: Vec<(Span, String)>,
2554 impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
2555 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
2557 hir::ExprKind::If(cond, body, None) => {
2558 // `if` expressions with no `else` that initialize the binding might be missing an
2560 let mut v = ReferencedStatementsVisitor(self.spans, false);
2564 ex.span.to(cond.span),
2566 "this `if` expression might be missing an `else` arm that initializes \
2573 hir::ExprKind::If(cond, body, Some(other)) => {
2574 // `if` expressions where the binding is only initialized in one of the two arms
2575 // might be missing a binding initialization.
2576 let mut a = ReferencedStatementsVisitor(self.spans, false);
2578 let mut b = ReferencedStatementsVisitor(self.spans, false);
2579 b.visit_expr(other);
2581 (true, true) | (false, false) => {}
2583 if other.span.is_desugaring(DesugaringKind::WhileLoop) {
2587 "{} is uninitialized if this condition isn't met and the \
2588 `while` loop runs 0 times",
2594 body.span.shrink_to_hi().until(other.span),
2596 "{} is uninitialized if this `else` arm is executed",
2605 format!("{} is uninitialized if this condition is met", self.name),
2610 hir::ExprKind::Match(e, arms, loop_desugar) => {
2611 // If the binding is initialized in one of the match arms, then the other match
2612 // arms might be missing an initialization.
2613 let results: Vec<bool> = arms
2616 let mut v = ReferencedStatementsVisitor(self.spans, false);
2621 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
2622 for (arm, seen) in arms.iter().zip(results) {
2624 if loop_desugar == hir::MatchSource::ForLoopDesugar {
2628 "{} is uninitialized if the `for` loop runs 0 times",
2632 } else if let Some(guard) = &arm.guard {
2634 arm.pat.span.to(guard.body().span),
2636 "{} is uninitialized if this pattern and condition are \
2645 "{} is uninitialized if this pattern is matched",
2654 // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should
2655 // also be accounted for. For now it is fine, as if we don't find *any* relevant
2656 // branching code paths, we point at the places where the binding *is* initialized for
2660 walk_expr(self, ex);