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);
332 if !span.is_dummy() {
337 let (binding, name, desc) =
338 match self.describe_place_with_options(used_place, IncludingDowncast(true)) {
339 Some(name) => (format!("`{name}`"), format!("`{name}`"), format!("`{name}` ")),
340 None => ("value".to_string(), "the variable".to_string(), String::new()),
342 let isnt_initialized =
343 if let InitializationRequiringAction::PartialAssignment = desired_action {
344 // The same error is emitted for bindings that are *sometimes* initialized and the ones
345 // that are *partially* initialized by assigning to a field of an uninitialized
346 // binding. We differentiate between them for more accurate wording here.
347 "isn't fully initialized"
348 } else if spans.iter().filter(|i| !i.contains(span)).count() == 0 {
349 // We filter above to avoid misleading wording in cases like the following, where `x`
350 // has an `init`, but it is in the same place we're looking at:
357 "is possibly-uninitialized"
359 let used = desired_action.as_general_verb_in_past_tense();
361 struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}");
362 use_spans.var_span_label_path_only(
364 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
367 if let InitializationRequiringAction::PartialAssignment = desired_action {
369 "partial initialization isn't supported, fully initialize the binding with a \
370 default value and mutate it, or use `std::mem::MaybeUninit`",
373 err.span_label(span, format!("{binding} {used} here but it {isnt_initialized}"));
375 // We use the statements were the binding was initialized, and inspect the HIR to look
376 // for the branching codepaths that aren't covered, to point at them.
377 let hir_id = self.mir_hir_id();
378 let map = self.infcx.tcx.hir();
379 let body_id = map.body_owned_by(hir_id);
380 let body = map.body(body_id);
382 let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] };
383 visitor.visit_body(&body);
384 if visitor.errors.is_empty() {
386 if *sp < span && !sp.overlaps(span) {
387 err.span_label(*sp, "binding initialized here in some conditions");
391 for (sp, label) in visitor.errors {
392 if sp < span && !sp.overlaps(span) {
393 // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention
394 // match arms coming after the primary span because they aren't relevant:
398 // _ if { x = 2; true } => {}
403 // _ => {} // We don't want to point to this.
406 err.span_label(sp, &label);
409 err.span_label(decl_span, "binding declared here but left uninitialized");
413 fn suggest_borrow_fn_like(
415 err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
417 move_sites: &[MoveSite],
420 let tcx = self.infcx.tcx;
422 // Find out if the predicates show that the type is a Fn or FnMut
423 let find_fn_kind_from_did = |predicates: &[(ty::Predicate<'tcx>, Span)], substs| {
424 predicates.iter().find_map(|(pred, _)| {
425 let pred = if let Some(substs) = substs {
426 EarlyBinder(*pred).subst(tcx, substs).kind().skip_binder()
428 pred.kind().skip_binder()
430 if let ty::PredicateKind::Trait(pred) = pred && pred.self_ty() == ty {
431 if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
432 return Some(hir::Mutability::Not);
433 } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
434 return Some(hir::Mutability::Mut);
441 // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
442 // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
443 // These types seem reasonably opaque enough that they could be substituted with their
444 // borrowed variants in a function body when we see a move error.
445 let borrow_level = match ty.kind() {
446 ty::Param(_) => find_fn_kind_from_did(
447 tcx.explicit_predicates_of(self.mir_def_id().to_def_id()).predicates,
450 ty::Opaque(did, substs) => {
451 find_fn_kind_from_did(tcx.explicit_item_bounds(*did), Some(*substs))
453 ty::Closure(_, substs) => match substs.as_closure().kind() {
454 ty::ClosureKind::Fn => Some(hir::Mutability::Not),
455 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
461 let Some(borrow_level) = borrow_level else { return false; };
462 let sugg = move_sites
465 let move_out = self.move_data.moves[(*move_site).moi];
466 let moved_place = &self.move_data.move_paths[move_out.path].place;
467 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
468 let move_span = move_spans.args_or_use();
469 let suggestion = if borrow_level == hir::Mutability::Mut {
474 (move_span.shrink_to_lo(), suggestion)
477 err.multipart_suggestion_verbose(
479 "consider {}borrowing {value_name}",
480 if borrow_level == hir::Mutability::Mut { "mutably " } else { "" }
483 Applicability::MaybeIncorrect,
488 fn suggest_adding_copy_bounds(
490 err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
494 let tcx = self.infcx.tcx;
495 let generics = tcx.generics_of(self.mir_def_id());
497 let Some(hir_generics) = tcx
498 .typeck_root_def_id(self.mir_def_id().to_def_id())
500 .and_then(|def_id| tcx.hir().get_generics(def_id))
502 // Try to find predicates on *generic params* that would allow copying `ty`
503 let predicates: Result<Vec<_>, _> = tcx.infer_ctxt().enter(|infcx| {
504 let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
506 let copy_did = infcx.tcx.lang_items().copy_trait().unwrap();
507 let cause = ObligationCause::new(
510 rustc_infer::traits::ObligationCauseCode::MiscObligation,
512 fulfill_cx.register_bound(
515 // Erase any region vids from the type, which may not be resolved
516 infcx.tcx.erase_regions(ty),
520 // Select all, including ambiguous predicates
521 let errors = fulfill_cx.select_all_or_error(&infcx);
523 // Only emit suggestion if all required predicates are on generic
526 .map(|err| match err.obligation.predicate.kind().skip_binder() {
527 PredicateKind::Trait(predicate) => match predicate.self_ty().kind() {
528 ty::Param(param_ty) => Ok((
529 generics.type_param(param_ty, tcx),
530 predicate.trait_ref.print_only_trait_path().to_string(),
539 if let Ok(predicates) = predicates {
540 suggest_constraining_type_params(
546 .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
551 pub(crate) fn report_move_out_while_borrowed(
554 (place, span): (Place<'tcx>, Span),
555 borrow: &BorrowData<'tcx>,
558 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
559 location, place, span, borrow
561 let value_msg = self.describe_any_place(place.as_ref());
562 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
564 let borrow_spans = self.retrieve_borrow_spans(borrow);
565 let borrow_span = borrow_spans.args_or_use();
567 let move_spans = self.move_spans(place.as_ref(), location);
568 let span = move_spans.args_or_use();
571 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
572 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
573 err.span_label(span, format!("move out of {} occurs here", value_msg));
575 borrow_spans.var_span_label_path_only(
577 format!("borrow occurs due to use{}", borrow_spans.describe()),
580 move_spans.var_span_label(
582 format!("move occurs due to use{}", move_spans.describe()),
586 self.explain_why_borrow_contains_point(location, borrow, None)
587 .add_explanation_to_diagnostic(
596 self.buffer_error(err);
599 pub(crate) fn report_use_while_mutably_borrowed(
602 (place, _span): (Place<'tcx>, Span),
603 borrow: &BorrowData<'tcx>,
604 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
605 let borrow_spans = self.retrieve_borrow_spans(borrow);
606 let borrow_span = borrow_spans.args_or_use();
608 // Conflicting borrows are reported separately, so only check for move
610 let use_spans = self.move_spans(place.as_ref(), location);
611 let span = use_spans.var_or_use();
613 // 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
614 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
615 let mut err = self.cannot_use_when_mutably_borrowed(
617 &self.describe_any_place(place.as_ref()),
619 &self.describe_any_place(borrow.borrowed_place.as_ref()),
622 borrow_spans.var_span_label(
625 let place = &borrow.borrowed_place;
626 let desc_place = self.describe_any_place(place.as_ref());
627 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
632 self.explain_why_borrow_contains_point(location, borrow, None)
633 .add_explanation_to_diagnostic(
645 pub(crate) fn report_conflicting_borrow(
648 (place, span): (Place<'tcx>, Span),
649 gen_borrow_kind: BorrowKind,
650 issued_borrow: &BorrowData<'tcx>,
651 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
652 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
653 let issued_span = issued_spans.args_or_use();
655 let borrow_spans = self.borrow_spans(span, location);
656 let span = borrow_spans.args_or_use();
658 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
664 let (desc_place, msg_place, msg_borrow, union_type_name) =
665 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
667 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
668 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
670 // FIXME: supply non-"" `opt_via` when appropriate
671 let first_borrow_desc;
672 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
673 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
674 first_borrow_desc = "mutable ";
675 self.cannot_reborrow_already_borrowed(
687 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
688 first_borrow_desc = "immutable ";
689 self.cannot_reborrow_already_borrowed(
702 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
703 first_borrow_desc = "first ";
704 let mut err = self.cannot_mutably_borrow_multiply(
712 self.suggest_split_at_mut_if_applicable(
715 issued_borrow.borrowed_place,
720 (BorrowKind::Unique, BorrowKind::Unique) => {
721 first_borrow_desc = "first ";
722 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
725 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
726 if let Some(immutable_section_description) =
727 self.classify_immutable_section(issued_borrow.assigned_place)
729 let mut err = self.cannot_mutate_in_immutable_section(
733 immutable_section_description,
736 borrow_spans.var_span_label(
739 "borrow occurs due to use of {}{}",
741 borrow_spans.describe(),
748 first_borrow_desc = "immutable ";
749 self.cannot_reborrow_already_borrowed(
763 (BorrowKind::Unique, _) => {
764 first_borrow_desc = "first ";
765 self.cannot_uniquely_borrow_by_one_closure(
777 (BorrowKind::Shared, BorrowKind::Unique) => {
778 first_borrow_desc = "first ";
779 self.cannot_reborrow_already_uniquely_borrowed(
792 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
793 first_borrow_desc = "first ";
794 self.cannot_reborrow_already_uniquely_borrowed(
807 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
810 BorrowKind::Mut { .. }
813 | BorrowKind::Shallow,
817 if issued_spans == borrow_spans {
818 borrow_spans.var_span_label(
820 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
821 gen_borrow_kind.describe_mutability(),
824 let borrow_place = &issued_borrow.borrowed_place;
825 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
826 issued_spans.var_span_label(
829 "first borrow occurs due to use of {}{}",
831 issued_spans.describe(),
833 issued_borrow.kind.describe_mutability(),
836 borrow_spans.var_span_label(
839 "second borrow occurs due to use of {}{}",
841 borrow_spans.describe(),
843 gen_borrow_kind.describe_mutability(),
847 if union_type_name != "" {
849 "{} is a field of the union `{}`, so it overlaps the field {}",
850 msg_place, union_type_name, msg_borrow,
854 explanation.add_explanation_to_diagnostic(
861 Some((issued_span, span)),
864 self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation);
869 #[instrument(level = "debug", skip(self, err))]
870 fn suggest_using_local_if_applicable(
872 err: &mut Diagnostic,
874 issued_borrow: &BorrowData<'tcx>,
875 explanation: BorrowExplanation<'tcx>,
877 let used_in_call = matches!(
879 BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
882 debug!("not later used in call");
887 if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
894 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
897 issued_borrow.reserve_location
899 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
901 let inner_param_location = location;
902 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
903 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
906 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
908 "`inner_param_location` {:?} is not for an assignment: {:?}",
909 inner_param_location, inner_param_stmt
913 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
914 let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
915 let Either::Right(term) = self.body.stmt_at(loc) else {
916 debug!("{:?} is a statement, so it can't be a call", loc);
919 let TerminatorKind::Call { args, .. } = &term.kind else {
920 debug!("not a call: {:?}", term);
923 debug!("checking call args for uses of inner_param: {:?}", args);
924 if args.contains(&Operand::Move(inner_param)) {
930 debug!("no uses of inner_param found as a by-move call arg");
933 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
935 let inner_call_span = inner_call_term.source_info.span;
936 let outer_call_span = match use_span {
938 None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span,
940 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
941 // FIXME: This stops the suggestion in some cases where it should be emitted.
942 // Fix the spans for those cases so it's emitted correctly.
944 "outer span {:?} does not strictly contain inner span {:?}",
945 outer_call_span, inner_call_span
952 "try adding a local storing this{}...",
953 if use_span.is_some() { "" } else { " argument" }
959 "...and then using that local {}",
960 if use_span.is_some() { "here" } else { "as the argument to this call" }
965 fn suggest_split_at_mut_if_applicable(
967 err: &mut Diagnostic,
969 borrowed_place: Place<'tcx>,
971 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
972 (&place.projection[..], &borrowed_place.projection[..])
975 "consider using `.split_at_mut(position)` or similar method to obtain \
976 two mutable non-overlapping sub-slices",
981 /// Returns the description of the root place for a conflicting borrow and the full
982 /// descriptions of the places that caused the conflict.
984 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
985 /// attempted while a shared borrow is live, then this function will return:
990 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
991 /// a shared borrow of another field `x.y`, then this function will return:
993 /// ("x", "x.z", "x.y")
996 /// In the more complex union case, where the union is a field of a struct, then if a mutable
997 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
998 /// another field `x.u.y`, then this function will return:
1000 /// ("x.u", "x.u.z", "x.u.y")
1003 /// This is used when creating error messages like below:
1006 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
1007 /// mutable (via `a.u.s.b`) [E0502]
1009 pub(crate) fn describe_place_for_conflicting_borrow(
1011 first_borrowed_place: Place<'tcx>,
1012 second_borrowed_place: Place<'tcx>,
1013 ) -> (String, String, String, String) {
1014 // Define a small closure that we can use to check if the type of a place
1016 let union_ty = |place_base| {
1017 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
1018 // using a type annotation in the closure argument instead leads to a lifetime error.
1019 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
1020 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
1023 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
1024 // code duplication (particularly around returning an empty description in the failure
1028 // If we have a conflicting borrow of the same place, then we don't want to add
1029 // an extraneous "via x.y" to our diagnostics, so filter out this case.
1030 first_borrowed_place != second_borrowed_place
1033 // We're going to want to traverse the first borrowed place to see if we can find
1034 // field access to a union. If we find that, then we will keep the place of the
1035 // union being accessed and the field that was being accessed so we can check the
1036 // second borrowed place for the same union and an access to a different field.
1037 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
1039 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
1040 return Some((place_base, field));
1047 .and_then(|(target_base, target_field)| {
1048 // With the place of a union and a field access into it, we traverse the second
1049 // borrowed place and look for an access to a different field of the same union.
1050 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1051 if let ProjectionElem::Field(field, _) = elem {
1052 if let Some(union_ty) = union_ty(place_base) {
1053 if field != target_field && place_base == target_base {
1055 self.describe_any_place(place_base),
1056 self.describe_any_place(first_borrowed_place.as_ref()),
1057 self.describe_any_place(second_borrowed_place.as_ref()),
1058 union_ty.to_string(),
1066 .unwrap_or_else(|| {
1067 // If we didn't find a field access into a union, or both places match, then
1068 // only return the description of the first place.
1070 self.describe_any_place(first_borrowed_place.as_ref()),
1078 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1080 /// This means that some data referenced by `borrow` needs to live
1081 /// past the point where the StorageDeadOrDrop of `place` occurs.
1082 /// This is usually interpreted as meaning that `place` has too
1083 /// short a lifetime. (But sometimes it is more useful to report
1084 /// it as a more direct conflict between the execution of a
1085 /// `Drop::drop` with an aliasing borrow.)
1086 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1089 borrow: &BorrowData<'tcx>,
1090 place_span: (Place<'tcx>, Span),
1091 kind: Option<WriteKind>,
1094 "report_borrowed_value_does_not_live_long_enough(\
1095 {:?}, {:?}, {:?}, {:?}\
1097 location, borrow, place_span, kind
1100 let drop_span = place_span.1;
1102 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1104 let borrow_spans = self.retrieve_borrow_spans(borrow);
1105 let borrow_span = borrow_spans.var_or_use_path_span();
1107 assert!(root_place.projection.is_empty());
1108 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1110 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1112 if self.access_place_error_reported.contains(&(
1113 Place { local: root_place.local, projection: root_place_projection },
1117 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1123 self.access_place_error_reported.insert((
1124 Place { local: root_place.local, projection: root_place_projection },
1128 let borrowed_local = borrow.borrowed_place.local;
1129 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1131 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1132 self.buffer_error(err);
1136 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1137 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1139 // If a borrow of path `B` conflicts with drop of `D` (and
1140 // we're not in the uninteresting case where `B` is a
1141 // prefix of `D`), then report this as a more interesting
1142 // destructor conflict.
1143 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1144 self.report_borrow_conflicts_with_destructor(
1145 location, borrow, place_span, kind, dropped_ty,
1151 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1153 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1154 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1157 "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
1158 place_desc, explanation
1160 let err = match (place_desc, explanation) {
1161 // If the outlives constraint comes from inside the closure,
1166 // Box::new(|| y) as Box<Fn() -> &'static i32>
1168 // then just use the normal error. The closure isn't escaping
1169 // and `move` will not help here.
1172 BorrowExplanation::MustBeValidFor {
1174 category @ (ConstraintCategory::Return(_)
1175 | ConstraintCategory::CallArgument(_)
1176 | ConstraintCategory::OpaqueType),
1177 from_closure: false,
1182 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1183 .report_escaping_closure_capture(
1189 &format!("`{}`", name),
1193 BorrowExplanation::MustBeValidFor {
1194 category: ConstraintCategory::Assignment,
1195 from_closure: false,
1199 RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
1205 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1206 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1214 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1224 self.buffer_error(err);
1227 fn report_local_value_does_not_live_long_enough(
1231 borrow: &BorrowData<'tcx>,
1233 borrow_spans: UseSpans<'tcx>,
1234 explanation: BorrowExplanation<'tcx>,
1235 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1237 "report_local_value_does_not_live_long_enough(\
1238 {:?}, {:?}, {:?}, {:?}, {:?}\
1240 location, name, borrow, drop_span, borrow_spans
1243 let borrow_span = borrow_spans.var_or_use_path_span();
1244 if let BorrowExplanation::MustBeValidFor {
1248 from_closure: false,
1252 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1257 opt_place_desc.as_ref(),
1263 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1265 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1266 let region_name = annotation.emit(self, &mut err);
1270 format!("`{}` would have to be valid for `{}`...", name, region_name),
1273 let fn_hir_id = self.mir_hir_id();
1277 "...but `{}` will be dropped here, when the {} returns",
1282 .opt_name(fn_hir_id)
1283 .map(|name| format!("function `{}`", name))
1284 .unwrap_or_else(|| {
1288 .typeck(self.mir_def_id())
1289 .node_type(fn_hir_id)
1292 ty::Closure(..) => "enclosing closure",
1293 ty::Generator(..) => "enclosing generator",
1294 kind => bug!("expected closure or generator, found {:?}", kind),
1302 "functions cannot return a borrow to data owned within the function's scope, \
1303 functions can only return borrows to data passed as arguments",
1306 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1307 references-and-borrowing.html#dangling-references>",
1310 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1312 explanation.add_explanation_to_diagnostic(
1323 err.span_label(borrow_span, "borrowed value does not live long enough");
1324 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1326 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1328 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1330 explanation.add_explanation_to_diagnostic(
1344 fn report_borrow_conflicts_with_destructor(
1347 borrow: &BorrowData<'tcx>,
1348 (place, drop_span): (Place<'tcx>, Span),
1349 kind: Option<WriteKind>,
1350 dropped_ty: Ty<'tcx>,
1353 "report_borrow_conflicts_with_destructor(\
1354 {:?}, {:?}, ({:?}, {:?}), {:?}\
1356 location, borrow, place, drop_span, kind,
1359 let borrow_spans = self.retrieve_borrow_spans(borrow);
1360 let borrow_span = borrow_spans.var_or_use();
1362 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1364 let what_was_dropped = match self.describe_place(place.as_ref()) {
1365 Some(name) => format!("`{}`", name),
1366 None => String::from("temporary value"),
1369 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1370 Some(borrowed) => format!(
1371 "here, drop of {D} needs exclusive access to `{B}`, \
1372 because the type `{T}` implements the `Drop` trait",
1373 D = what_was_dropped,
1378 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1379 D = what_was_dropped,
1383 err.span_label(drop_span, label);
1385 // Only give this note and suggestion if they could be relevant.
1387 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1389 BorrowExplanation::UsedLater { .. }
1390 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1391 err.note("consider using a `let` binding to create a longer lived value");
1396 explanation.add_explanation_to_diagnostic(
1406 self.buffer_error(err);
1409 fn report_thread_local_value_does_not_live_long_enough(
1413 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1415 "report_thread_local_value_does_not_live_long_enough(\
1418 drop_span, borrow_span
1421 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1425 "thread-local variables cannot be borrowed beyond the end of the function",
1427 err.span_label(drop_span, "end of enclosing function is here");
1432 fn report_temporary_value_does_not_live_long_enough(
1435 borrow: &BorrowData<'tcx>,
1437 borrow_spans: UseSpans<'tcx>,
1439 explanation: BorrowExplanation<'tcx>,
1440 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1442 "report_temporary_value_does_not_live_long_enough(\
1443 {:?}, {:?}, {:?}, {:?}\
1445 location, borrow, drop_span, proper_span
1448 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1451 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1462 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1463 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1464 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1467 BorrowExplanation::UsedLater(..)
1468 | BorrowExplanation::UsedLaterInLoop(..)
1469 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1470 // Only give this note and suggestion if it could be relevant.
1471 err.note("consider using a `let` binding to create a longer lived value");
1475 explanation.add_explanation_to_diagnostic(
1485 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1487 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1492 fn try_report_cannot_return_reference_to_local(
1494 borrow: &BorrowData<'tcx>,
1497 category: ConstraintCategory<'tcx>,
1498 opt_place_desc: Option<&String>,
1499 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1500 let return_kind = match category {
1501 ConstraintCategory::Return(_) => "return",
1502 ConstraintCategory::Yield => "yield",
1506 // FIXME use a better heuristic than Spans
1507 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1513 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1514 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1515 match self.body.local_kind(local) {
1516 LocalKind::ReturnPointer | LocalKind::Temp => {
1517 bug!("temporary or return pointer with a name")
1519 LocalKind::Var => "local variable ",
1521 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1523 "variable captured by `move` "
1525 LocalKind::Arg => "function parameter ",
1531 format!("{}`{}`", local_kind, place_desc),
1532 format!("`{}` is borrowed here", place_desc),
1536 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1537 let local = root_place.local;
1538 match self.body.local_kind(local) {
1539 LocalKind::ReturnPointer | LocalKind::Temp => {
1540 ("temporary value".to_string(), "temporary value created here".to_string())
1543 "function parameter".to_string(),
1544 "function parameter borrowed here".to_string(),
1547 ("local binding".to_string(), "local binding introduced here".to_string())
1552 let mut err = self.cannot_return_reference_to_local(
1559 if return_span != borrow_span {
1560 err.span_label(borrow_span, note);
1562 let tcx = self.infcx.tcx;
1563 let ty_params = ty::List::empty();
1565 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1566 let return_ty = tcx.erase_regions(return_ty);
1569 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
1572 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1573 .must_apply_modulo_regions()
1575 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
1576 err.span_suggestion_hidden(
1578 "use `.collect()` to allocate the iterator",
1579 format!("{snippet}.collect::<Vec<_>>()"),
1580 Applicability::MaybeIncorrect,
1590 fn report_escaping_closure_capture(
1592 use_span: UseSpans<'tcx>,
1594 fr_name: &RegionName,
1595 category: ConstraintCategory<'tcx>,
1596 constraint_span: Span,
1598 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1599 let tcx = self.infcx.tcx;
1600 let args_span = use_span.args_or_use();
1602 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1604 if string.starts_with("async ") {
1605 let pos = args_span.lo() + BytePos(6);
1606 (args_span.with_lo(pos).with_hi(pos), "move ")
1607 } else if string.starts_with("async|") {
1608 let pos = args_span.lo() + BytePos(5);
1609 (args_span.with_lo(pos).with_hi(pos), " move")
1611 (args_span.shrink_to_lo(), "move ")
1614 Err(_) => (args_span, "move |<args>| <body>"),
1616 let kind = match use_span.generator_kind() {
1617 Some(generator_kind) => match generator_kind {
1618 GeneratorKind::Async(async_kind) => match async_kind {
1619 AsyncGeneratorKind::Block => "async block",
1620 AsyncGeneratorKind::Closure => "async closure",
1621 _ => bug!("async block/closure expected, but async function found."),
1623 GeneratorKind::Gen => "generator",
1629 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1630 err.span_suggestion_verbose(
1633 "to force the {} to take ownership of {} (and any \
1634 other referenced variables), use the `move` keyword",
1638 Applicability::MachineApplicable,
1642 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1643 let msg = format!("{} is returned here", kind);
1644 err.span_note(constraint_span, &msg);
1646 ConstraintCategory::CallArgument(_) => {
1647 fr_name.highlight_region_name(&mut err);
1648 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1650 "async blocks are not executed immediately and must either take a \
1651 reference or ownership of outside variables they use",
1654 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1655 err.span_note(constraint_span, &msg);
1659 "report_escaping_closure_capture called with unexpected constraint \
1668 fn report_escaping_data(
1671 name: &Option<String>,
1675 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1676 let tcx = self.infcx.tcx;
1678 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1681 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1685 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1688 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1690 if let Some(name) = name {
1693 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1698 format!("reference escapes the {} body here", escapes_from),
1705 fn get_moved_indexes(
1709 ) -> (Vec<MoveSite>, Vec<Location>) {
1710 fn predecessor_locations<'tcx, 'a>(
1711 body: &'a mir::Body<'tcx>,
1713 ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
1714 if location.statement_index == 0 {
1715 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
1716 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1718 Either::Right(std::iter::once(Location {
1719 statement_index: location.statement_index - 1,
1725 let mut mpis = vec![mpi];
1726 let move_paths = &self.move_data.move_paths;
1727 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1729 let mut stack = Vec::new();
1730 let mut back_edge_stack = Vec::new();
1732 predecessor_locations(self.body, location).for_each(|predecessor| {
1733 if location.dominates(predecessor, &self.dominators) {
1734 back_edge_stack.push(predecessor)
1736 stack.push(predecessor);
1740 let mut reached_start = false;
1742 /* Check if the mpi is initialized as an argument */
1743 let mut is_argument = false;
1744 for arg in self.body.args_iter() {
1745 let path = self.move_data.rev_lookup.find_local(arg);
1746 if mpis.contains(&path) {
1751 let mut visited = FxHashSet::default();
1752 let mut move_locations = FxHashSet::default();
1753 let mut reinits = vec![];
1754 let mut result = vec![];
1756 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1758 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1759 location, is_back_edge
1762 if !visited.insert(location) {
1768 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1769 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1770 // this analysis only tries to find moves explicitly
1771 // written by the user, so we ignore the move-outs
1772 // created by `StorageDead` and at the beginning
1775 // If we are found a use of a.b.c which was in error, then we want to look for
1776 // moves not only of a.b.c but also a.b and a.
1778 // Note that the moves data already includes "parent" paths, so we don't have to
1779 // worry about the other case: that is, if there is a move of a.b.c, it is already
1780 // marked as a move of a.b and a as well, so we will generate the correct errors
1782 for moi in &self.move_data.loc_map[location] {
1783 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1784 let path = self.move_data.moves[*moi].path;
1785 if mpis.contains(&path) {
1787 "report_use_of_moved_or_uninitialized: found {:?}",
1788 move_paths[path].place
1790 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1791 move_locations.insert(location);
1793 // Strictly speaking, we could continue our DFS here. There may be
1794 // other moves that can reach the point of error. But it is kind of
1795 // confusing to highlight them.
1803 // drop(a); // <-- current point of error
1806 // Because we stop the DFS here, we only highlight `let c = a`,
1807 // and not `let b = a`. We will of course also report an error at
1808 // `let c = a` which highlights `let b = a` as the move.
1815 let mut any_match = false;
1816 for ii in &self.move_data.init_loc_map[location] {
1817 let init = self.move_data.inits[*ii];
1819 InitKind::Deep | InitKind::NonPanicPathOnly => {
1820 if mpis.contains(&init.path) {
1824 InitKind::Shallow => {
1825 if mpi == init.path {
1832 reinits.push(location);
1838 while let Some(location) = stack.pop() {
1839 if dfs_iter(&mut result, location, false) {
1843 let mut has_predecessor = false;
1844 predecessor_locations(self.body, location).for_each(|predecessor| {
1845 if location.dominates(predecessor, &self.dominators) {
1846 back_edge_stack.push(predecessor)
1848 stack.push(predecessor);
1850 has_predecessor = true;
1853 if !has_predecessor {
1854 reached_start = true;
1857 if (is_argument || !reached_start) && result.is_empty() {
1858 /* Process back edges (moves in future loop iterations) only if
1859 the move path is definitely initialized upon loop entry,
1860 to avoid spurious "in previous iteration" errors.
1861 During DFS, if there's a path from the error back to the start
1862 of the function with no intervening init or move, then the
1863 move path may be uninitialized at loop entry.
1865 while let Some(location) = back_edge_stack.pop() {
1866 if dfs_iter(&mut result, location, true) {
1870 predecessor_locations(self.body, location)
1871 .for_each(|predecessor| back_edge_stack.push(predecessor));
1875 // Check if we can reach these reinits from a move location.
1876 let reinits_reachable = reinits
1879 let mut visited = FxHashSet::default();
1880 let mut stack = vec![*reinit];
1881 while let Some(location) = stack.pop() {
1882 if !visited.insert(location) {
1885 if move_locations.contains(&location) {
1888 stack.extend(predecessor_locations(self.body, location));
1892 .collect::<Vec<Location>>();
1893 (result, reinits_reachable)
1896 pub(crate) fn report_illegal_mutation_of_borrowed(
1899 (place, span): (Place<'tcx>, Span),
1900 loan: &BorrowData<'tcx>,
1902 let loan_spans = self.retrieve_borrow_spans(loan);
1903 let loan_span = loan_spans.args_or_use();
1905 let descr_place = self.describe_any_place(place.as_ref());
1906 if loan.kind == BorrowKind::Shallow {
1907 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1908 let mut err = self.cannot_mutate_in_immutable_section(
1915 loan_spans.var_span_label(
1917 format!("borrow occurs due to use{}", loan_spans.describe()),
1918 loan.kind.describe_mutability(),
1921 self.buffer_error(err);
1927 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
1929 loan_spans.var_span_label(
1931 format!("borrow occurs due to use{}", loan_spans.describe()),
1932 loan.kind.describe_mutability(),
1935 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
1945 self.explain_deref_coercion(loan, &mut err);
1947 self.buffer_error(err);
1950 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
1951 let tcx = self.infcx.tcx;
1953 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
1954 Some((method_did, method_substs)),
1956 &self.body[loan.reserve_location.block].terminator,
1957 rustc_const_eval::util::find_self_call(
1960 loan.assigned_place.local,
1961 loan.reserve_location.block,
1964 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
1966 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
1967 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
1970 if let Some(Ok(instance)) = deref_target {
1971 let deref_target_ty = instance.ty(tcx, self.param_env);
1973 "borrow occurs due to deref coercion to `{}`",
1976 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
1982 /// Reports an illegal reassignment; for example, an assignment to
1983 /// (part of) a non-`mut` local that occurs potentially after that
1984 /// local has already been initialized. `place` is the path being
1985 /// assigned; `err_place` is a place providing a reason why
1986 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
1987 /// assignment to `x.f`).
1988 pub(crate) fn report_illegal_reassignment(
1990 _location: Location,
1991 (place, span): (Place<'tcx>, Span),
1992 assigned_span: Span,
1993 err_place: Place<'tcx>,
1995 let (from_arg, local_decl, local_name) = match err_place.as_local() {
1997 self.body.local_kind(local) == LocalKind::Arg,
1998 Some(&self.body.local_decls[local]),
1999 self.local_names[local],
2001 None => (false, None, None),
2004 // If root local is initialized immediately (everything apart from let
2005 // PATTERN;) then make the error refer to that local, rather than the
2006 // place being assigned later.
2007 let (place_description, assigned_span) = match local_decl {
2010 Some(box LocalInfo::User(
2011 ClearCrossCrate::Clear
2012 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
2013 opt_match_place: None,
2017 | Some(box LocalInfo::StaticRef { .. })
2021 | None => (self.describe_any_place(place.as_ref()), assigned_span),
2022 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
2025 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
2026 let msg = if from_arg {
2027 "cannot assign to immutable argument"
2029 "cannot assign twice to immutable variable"
2031 if span != assigned_span && !from_arg {
2032 err.span_label(assigned_span, format!("first assignment to {}", place_description));
2034 if let Some(decl) = local_decl
2035 && let Some(name) = local_name
2036 && decl.can_be_made_mutable()
2038 err.span_suggestion(
2039 decl.source_info.span,
2040 "consider making this binding mutable",
2041 format!("mut {}", name),
2042 Applicability::MachineApplicable,
2045 err.span_label(span, msg);
2046 self.buffer_error(err);
2049 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2050 let tcx = self.infcx.tcx;
2051 let (kind, _place_ty) = place.projection.iter().fold(
2052 (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
2053 |(kind, place_ty), &elem| {
2056 ProjectionElem::Deref => match kind {
2057 StorageDeadOrDrop::LocalStorageDead
2058 | StorageDeadOrDrop::BoxedStorageDead => {
2060 place_ty.ty.is_box(),
2061 "Drop of value behind a reference or raw pointer"
2063 StorageDeadOrDrop::BoxedStorageDead
2065 StorageDeadOrDrop::Destructor(_) => kind,
2067 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
2068 match place_ty.ty.kind() {
2069 ty::Adt(def, _) if def.has_dtor(tcx) => {
2070 // Report the outermost adt with a destructor
2072 StorageDeadOrDrop::Destructor(_) => kind,
2073 StorageDeadOrDrop::LocalStorageDead
2074 | StorageDeadOrDrop::BoxedStorageDead => {
2075 StorageDeadOrDrop::Destructor(place_ty.ty)
2082 ProjectionElem::ConstantIndex { .. }
2083 | ProjectionElem::Subslice { .. }
2084 | ProjectionElem::Index(_) => kind,
2086 place_ty.projection_ty(tcx, elem),
2093 /// Describe the reason for the fake borrow that was assigned to `place`.
2094 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2095 use rustc_middle::mir::visit::Visitor;
2096 struct FakeReadCauseFinder<'tcx> {
2098 cause: Option<FakeReadCause>,
2100 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2101 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2103 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2104 if *place == self.place =>
2106 self.cause = Some(*cause);
2112 let mut visitor = FakeReadCauseFinder { place, cause: None };
2113 visitor.visit_body(&self.body);
2114 match visitor.cause {
2115 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2116 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2121 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2122 /// borrow of local value that does not live long enough.
2123 fn annotate_argument_and_return_for_borrow(
2125 borrow: &BorrowData<'tcx>,
2126 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2127 // Define a fallback for when we can't match a closure.
2129 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2133 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2135 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2136 self.mir_def_id().to_def_id(),
2137 self.infcx.tcx.fn_sig(self.mir_def_id()),
2144 // In order to determine whether we need to annotate, we need to check whether the reserve
2145 // place was an assignment into a temporary.
2147 // If it was, we check whether or not that temporary is eventually assigned into the return
2148 // place. If it was, we can add annotations about the function's return type and arguments
2149 // and it'll make sense.
2150 let location = borrow.reserve_location;
2151 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2152 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2153 &self.body[location.block].statements.get(location.statement_index)
2155 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2156 // Check that the initial assignment of the reserve location is into a temporary.
2157 let mut target = match reservation.as_local() {
2158 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2162 // Next, look through the rest of the block, checking if we are assigning the
2163 // `target` (that is, the place that contains our borrow) to anything.
2164 let mut annotated_closure = None;
2165 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2167 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2170 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2171 if let Some(assigned_to) = place.as_local() {
2173 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2177 // Check if our `target` was captured by a closure.
2178 if let Rvalue::Aggregate(
2179 box AggregateKind::Closure(def_id, substs),
2183 for operand in operands {
2184 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2188 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2192 // Find the local from the operand.
2193 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2197 if assigned_from_local != target {
2201 // If a closure captured our `target` and then assigned
2202 // into a place then we should annotate the closure in
2203 // case it ends up being assigned into the return place.
2205 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2207 "annotate_argument_and_return_for_borrow: \
2208 annotated_closure={:?} assigned_from_local={:?} \
2210 annotated_closure, assigned_from_local, assigned_to
2213 if assigned_to == mir::RETURN_PLACE {
2214 // If it was assigned directly into the return place, then
2216 return annotated_closure;
2218 // Otherwise, update the target.
2219 target = assigned_to;
2223 // If none of our closure's operands matched, then skip to the next
2228 // Otherwise, look at other types of assignment.
2229 let assigned_from = match rvalue {
2230 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2231 Rvalue::Use(operand) => match operand {
2232 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2240 "annotate_argument_and_return_for_borrow: \
2241 assigned_from={:?}",
2245 // Find the local from the rvalue.
2246 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2248 "annotate_argument_and_return_for_borrow: \
2249 assigned_from_local={:?}",
2250 assigned_from_local,
2253 // Check if our local matches the target - if so, we've assigned our
2254 // borrow to a new place.
2255 if assigned_from_local != target {
2259 // If we assigned our `target` into a new place, then we should
2260 // check if it was the return place.
2262 "annotate_argument_and_return_for_borrow: \
2263 assigned_from_local={:?} assigned_to={:?}",
2264 assigned_from_local, assigned_to
2266 if assigned_to == mir::RETURN_PLACE {
2267 // If it was then return the annotated closure if there was one,
2268 // else, annotate this function.
2269 return annotated_closure.or_else(fallback);
2272 // If we didn't assign into the return place, then we just update
2274 target = assigned_to;
2279 // Check the terminator if we didn't find anything in the statements.
2280 let terminator = &self.body[location.block].terminator();
2282 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2285 if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
2288 if let Some(assigned_to) = destination.as_local() {
2290 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2293 for operand in args {
2294 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2298 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2302 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2304 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2305 assigned_from_local,
2308 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2309 return annotated_closure.or_else(fallback);
2317 // If we haven't found an assignment into the return place, then we need not add
2319 debug!("annotate_argument_and_return_for_borrow: none found");
2323 /// Annotate the first argument and return type of a function signature if they are
2328 sig: ty::PolyFnSig<'tcx>,
2329 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2330 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2331 let is_closure = self.infcx.tcx.is_closure(did);
2332 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?);
2333 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2335 // We need to work out which arguments to highlight. We do this by looking
2336 // at the return type, where there are three cases:
2338 // 1. If there are named arguments, then we should highlight the return type and
2339 // highlight any of the arguments that are also references with that lifetime.
2340 // If there are no arguments that have the same lifetime as the return type,
2341 // then don't highlight anything.
2342 // 2. The return type is a reference with an anonymous lifetime. If this is
2343 // the case, then we can take advantage of (and teach) the lifetime elision
2346 // We know that an error is being reported. So the arguments and return type
2347 // must satisfy the elision rules. Therefore, if there is a single argument
2348 // then that means the return type and first (and only) argument have the same
2349 // lifetime and the borrow isn't meeting that, we can highlight the argument
2352 // If there are multiple arguments then the first argument must be self (else
2353 // it would not satisfy the elision rules), so we can highlight self and the
2355 // 3. The return type is not a reference. In this case, we don't highlight
2357 let return_ty = sig.output();
2358 match return_ty.skip_binder().kind() {
2359 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2360 // This is case 1 from above, return type is a named reference so we need to
2361 // search for relevant arguments.
2362 let mut arguments = Vec::new();
2363 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2364 if let ty::Ref(argument_region, _, _) = argument.kind() {
2365 if argument_region == return_region {
2366 // Need to use the `rustc_middle::ty` types to compare against the
2367 // `return_region`. Then use the `rustc_hir` type to get only
2368 // the lifetime span.
2369 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2370 // With access to the lifetime, we can get
2372 arguments.push((*argument, lifetime.span));
2374 bug!("ty type is a ref but hir type is not");
2380 // We need to have arguments. This shouldn't happen, but it's worth checking.
2381 if arguments.is_empty() {
2385 // We use a mix of the HIR and the Ty types to get information
2386 // as the HIR doesn't have full types for closure arguments.
2387 let return_ty = sig.output().skip_binder();
2388 let mut return_span = fn_decl.output.span();
2389 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2390 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2391 return_span = lifetime.span;
2395 Some(AnnotatedBorrowFnSignature::NamedFunction {
2401 ty::Ref(_, _, _) if is_closure => {
2402 // This is case 2 from above but only for closures, return type is anonymous
2403 // reference so we select
2404 // the first argument.
2405 let argument_span = fn_decl.inputs.first()?.span;
2406 let argument_ty = sig.inputs().skip_binder().first()?;
2408 // Closure arguments are wrapped in a tuple, so we need to get the first
2410 if let ty::Tuple(elems) = argument_ty.kind() {
2411 let &argument_ty = elems.first()?;
2412 if let ty::Ref(_, _, _) = argument_ty.kind() {
2413 return Some(AnnotatedBorrowFnSignature::Closure {
2422 ty::Ref(_, _, _) => {
2423 // This is also case 2 from above but for functions, return type is still an
2424 // anonymous reference so we select the first argument.
2425 let argument_span = fn_decl.inputs.first()?.span;
2426 let argument_ty = *sig.inputs().skip_binder().first()?;
2428 let return_span = fn_decl.output.span();
2429 let return_ty = sig.output().skip_binder();
2431 // We expect the first argument to be a reference.
2432 match argument_ty.kind() {
2433 ty::Ref(_, _, _) => {}
2437 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2445 // This is case 3 from above, return type is not a reference so don't highlight
2454 enum AnnotatedBorrowFnSignature<'tcx> {
2456 arguments: Vec<(Ty<'tcx>, Span)>,
2457 return_ty: Ty<'tcx>,
2461 argument_ty: Ty<'tcx>,
2462 argument_span: Span,
2463 return_ty: Ty<'tcx>,
2467 argument_ty: Ty<'tcx>,
2468 argument_span: Span,
2472 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2473 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2475 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2477 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2480 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2483 cx.get_region_name_for_ty(argument_ty, 0)
2485 &AnnotatedBorrowFnSignature::AnonymousFunction {
2491 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2492 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2494 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2495 let types_equal = return_ty_name == argument_ty_name;
2500 if types_equal { "also " } else { "" },
2506 "argument and return type have the same lifetime due to lifetime elision rules",
2509 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2510 lifetime-syntax.html#lifetime-elision>",
2513 cx.get_region_name_for_ty(return_ty, 0)
2515 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2516 // Region of return type and arguments checked to be the same earlier.
2517 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2518 for (_, argument_span) in arguments {
2519 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2522 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2525 "use data from the highlighted arguments which match the `{}` lifetime of \
2536 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr
2537 struct ReferencedStatementsVisitor<'a>(&'a [Span], bool);
2539 impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> {
2540 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
2542 hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => {
2550 /// Given a set of spans representing statements initializing the relevant binding, visit all the
2551 /// function expressions looking for branching code paths that *do not* initialize the binding.
2552 struct ConditionVisitor<'b> {
2555 errors: Vec<(Span, String)>,
2558 impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
2559 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
2561 hir::ExprKind::If(cond, body, None) => {
2562 // `if` expressions with no `else` that initialize the binding might be missing an
2564 let mut v = ReferencedStatementsVisitor(self.spans, false);
2570 "if this `if` condition is `false`, {} is not initialized",
2575 ex.span.shrink_to_hi(),
2576 format!("an `else` arm might be missing here, initializing {}", self.name),
2580 hir::ExprKind::If(cond, body, Some(other)) => {
2581 // `if` expressions where the binding is only initialized in one of the two arms
2582 // might be missing a binding initialization.
2583 let mut a = ReferencedStatementsVisitor(self.spans, false);
2585 let mut b = ReferencedStatementsVisitor(self.spans, false);
2586 b.visit_expr(other);
2588 (true, true) | (false, false) => {}
2590 if other.span.is_desugaring(DesugaringKind::WhileLoop) {
2594 "if this condition isn't met and the `while` loop runs 0 \
2595 times, {} is not initialized",
2601 body.span.shrink_to_hi().until(other.span),
2603 "if the `if` condition is `false` and this `else` arm is \
2604 executed, {} is not initialized",
2614 "if this condition is `true`, {} is not initialized",
2621 hir::ExprKind::Match(e, arms, loop_desugar) => {
2622 // If the binding is initialized in one of the match arms, then the other match
2623 // arms might be missing an initialization.
2624 let results: Vec<bool> = arms
2627 let mut v = ReferencedStatementsVisitor(self.spans, false);
2632 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
2633 for (arm, seen) in arms.iter().zip(results) {
2635 if loop_desugar == hir::MatchSource::ForLoopDesugar {
2639 "if the `for` loop runs 0 times, {} is not initialized ",
2643 } else if let Some(guard) = &arm.guard {
2645 arm.pat.span.to(guard.body().span),
2647 "if this pattern and condition are matched, {} is not \
2656 "if this pattern is matched, {} is not initialized",
2665 // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should
2666 // also be accounted for. For now it is fine, as if we don't find *any* relevant
2667 // branching code paths, we point at the places where the binding *is* initialized for
2671 walk_expr(self, ex);