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",
101 let err = self.report_use_of_uninitialized(
109 self.buffer_error(err);
111 if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
112 if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
114 "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}",
121 let is_partial_move = move_site_vec.iter().any(|move_site| {
122 let move_out = self.move_data.moves[(*move_site).moi];
123 let moved_place = &self.move_data.move_paths[move_out.path].place;
124 // `*(_1)` where `_1` is a `Box` is actually a move out.
125 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]
126 && self.body.local_decls[moved_place.local].ty.is_box();
129 && used_place != moved_place.as_ref()
130 && used_place.is_prefix_of(moved_place.as_ref())
133 let partial_str = if is_partial_move { "partial " } else { "" };
134 let partially_str = if is_partial_move { "partially " } else { "" };
136 let mut err = self.cannot_act_on_moved_value(
138 desired_action.as_noun(),
140 self.describe_place_with_options(moved_place, IncludingDowncast(true)),
143 let reinit_spans = maybe_reinitialized_locations
147 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
150 .collect::<Vec<Span>>();
152 let reinits = maybe_reinitialized_locations.len();
154 err.span_label(reinit_spans[0], "this reinitialization might get skipped");
155 } else if reinits > 1 {
157 MultiSpan::from_spans(reinit_spans),
159 format!("these {} reinitializations might get skipped", reinits)
162 "these 3 reinitializations and {} other{} might get skipped",
164 if reinits == 4 { "" } else { "s" }
170 self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
172 let mut is_loop_move = false;
173 let mut in_pattern = false;
175 for move_site in &move_site_vec {
176 let move_out = self.move_data.moves[(*move_site).moi];
177 let moved_place = &self.move_data.move_paths[move_out.path].place;
179 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
180 let move_span = move_spans.args_or_use();
182 let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
184 let loop_message = if location == move_out.source || move_site.traversed_back_edge {
185 ", in previous iteration of loop"
190 if location == move_out.source {
194 self.explain_captures(
205 maybe_reinitialized_locations.is_empty(),
208 if let (UseSpans::PatUse(span), []) =
209 (move_spans, &maybe_reinitialized_locations[..])
211 if maybe_reinitialized_locations.is_empty() {
212 err.span_suggestion_verbose(
215 "borrow this field in the pattern to avoid moving {}",
216 self.describe_place(moved_place.as_ref())
217 .map(|n| format!("`{}`", n))
218 .unwrap_or_else(|| "the value".to_string())
221 Applicability::MachineApplicable,
228 use_spans.var_span_label_path_only(
230 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
237 "value {} here after {}move",
238 desired_action.as_verb_in_past_tense(),
244 let ty = used_place.ty(self.body, self.infcx.tcx).ty;
245 let needs_note = match ty.kind() {
246 ty::Closure(id, _) => {
247 let tables = self.infcx.tcx.typeck(id.expect_local());
248 let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local());
250 tables.closure_kind_origins().get(hir_id).is_none()
255 let mpi = self.move_data.moves[move_out_indices[0]].path;
256 let place = &self.move_data.move_paths[mpi].place;
257 let ty = place.ty(self.body, self.infcx.tcx).ty;
259 // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
260 if is_loop_move & !in_pattern {
261 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
262 // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
263 err.span_suggestion_verbose(
266 "consider creating a fresh reborrow of {} here",
267 self.describe_place(moved_place)
268 .map(|n| format!("`{}`", n))
269 .unwrap_or_else(|| "the mutable reference".to_string()),
272 Applicability::MachineApplicable,
278 self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
279 let note_msg = match opt_name {
280 Some(ref name) => format!("`{}`", name),
281 None => "value".to_owned(),
283 if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) {
284 // Suppress the next suggestion since we don't want to put more bounds onto
285 // something that already has `Fn`-like bounds (or is a closure), so we can't
288 self.suggest_adding_copy_bounds(&mut err, ty, span);
292 let span = if let Some(local) = place.as_local() {
293 Some(self.body.local_decls[local].source_info.span)
297 self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str);
300 if let UseSpans::FnSelfUse {
301 kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
306 "{} occurs due to deref coercion to `{}`",
307 desired_action.as_noun(),
311 // Check first whether the source is accessible (issue #87060)
312 if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) {
313 err.span_note(deref_target, "deref defined here");
317 self.buffer_move_error(move_out_indices, (used_place, err));
321 fn report_use_of_uninitialized(
324 used_place: PlaceRef<'tcx>,
325 moved_place: PlaceRef<'tcx>,
326 desired_action: InitializationRequiringAction,
328 use_spans: UseSpans<'tcx>,
329 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
330 // We need all statements in the body where the binding was assigned to to later find all
331 // the branching code paths where the binding *wasn't* assigned to.
332 let inits = &self.move_data.init_path_map[mpi];
333 let move_path = &self.move_data.move_paths[mpi];
334 let decl_span = self.body.local_decls[move_path.place.local].source_info.span;
335 let mut spans = vec![];
336 for init_idx in inits {
337 let init = &self.move_data.inits[*init_idx];
338 let span = init.span(&self.body);
339 if !span.is_dummy() {
345 match self.describe_place_with_options(moved_place, IncludingDowncast(true)) {
346 Some(name) => (format!("`{name}`"), format!("`{name}` ")),
347 None => ("the variable".to_string(), String::new()),
349 let path = match self.describe_place_with_options(used_place, IncludingDowncast(true)) {
350 Some(name) => format!("`{name}`"),
351 None => "value".to_string(),
354 // We use the statements were the binding was initialized, and inspect the HIR to look
355 // for the branching codepaths that aren't covered, to point at them.
356 let hir_id = self.mir_hir_id();
357 let map = self.infcx.tcx.hir();
358 let body_id = map.body_owned_by(hir_id);
359 let body = map.body(body_id);
361 let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] };
362 visitor.visit_body(&body);
364 let isnt_initialized = if let InitializationRequiringAction::PartialAssignment
365 | InitializationRequiringAction::Assignment = desired_action
367 // The same error is emitted for bindings that are *sometimes* initialized and the ones
368 // that are *partially* initialized by assigning to a field of an uninitialized
369 // binding. We differentiate between them for more accurate wording here.
370 "isn't fully initialized"
374 // We filter these to avoid misleading wording in cases like the following,
375 // where `x` has an `init`, but it is in the same place we're looking at:
381 // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
386 .any(|sp| span < sp && !sp.contains(span))
393 "is possibly-uninitialized"
396 let used = desired_action.as_general_verb_in_past_tense();
398 struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}");
399 use_spans.var_span_label_path_only(
401 format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
404 if let InitializationRequiringAction::PartialAssignment
405 | InitializationRequiringAction::Assignment = desired_action
408 "partial initialization isn't supported, fully initialize the binding with a \
409 default value and mutate it, or use `std::mem::MaybeUninit`",
412 err.span_label(span, format!("{path} {used} here but it {isnt_initialized}"));
414 let mut shown = false;
415 for (sp, label) in visitor.errors {
416 if sp < span && !sp.overlaps(span) {
417 // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention
418 // match arms coming after the primary span because they aren't relevant:
422 // _ if { x = 2; true } => {}
427 // _ => {} // We don't want to point to this.
430 err.span_label(sp, &label);
436 if *sp < span && !sp.overlaps(span) {
437 err.span_label(*sp, "binding initialized here in some conditions");
441 err.span_label(decl_span, "binding declared here but left uninitialized");
445 fn suggest_borrow_fn_like(
447 err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
449 move_sites: &[MoveSite],
452 let tcx = self.infcx.tcx;
454 // Find out if the predicates show that the type is a Fn or FnMut
455 let find_fn_kind_from_did = |predicates: &[(ty::Predicate<'tcx>, Span)], substs| {
456 predicates.iter().find_map(|(pred, _)| {
457 let pred = if let Some(substs) = substs {
458 EarlyBinder(*pred).subst(tcx, substs).kind().skip_binder()
460 pred.kind().skip_binder()
462 if let ty::PredicateKind::Trait(pred) = pred && pred.self_ty() == ty {
463 if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
464 return Some(hir::Mutability::Not);
465 } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
466 return Some(hir::Mutability::Mut);
473 // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
474 // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
475 // These types seem reasonably opaque enough that they could be substituted with their
476 // borrowed variants in a function body when we see a move error.
477 let borrow_level = match ty.kind() {
478 ty::Param(_) => find_fn_kind_from_did(
479 tcx.explicit_predicates_of(self.mir_def_id().to_def_id()).predicates,
482 ty::Opaque(did, substs) => {
483 find_fn_kind_from_did(tcx.explicit_item_bounds(*did), Some(*substs))
485 ty::Closure(_, substs) => match substs.as_closure().kind() {
486 ty::ClosureKind::Fn => Some(hir::Mutability::Not),
487 ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
493 let Some(borrow_level) = borrow_level else { return false; };
494 let sugg = move_sites
497 let move_out = self.move_data.moves[(*move_site).moi];
498 let moved_place = &self.move_data.move_paths[move_out.path].place;
499 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);
500 let move_span = move_spans.args_or_use();
501 let suggestion = if borrow_level == hir::Mutability::Mut {
506 (move_span.shrink_to_lo(), suggestion)
509 err.multipart_suggestion_verbose(
511 "consider {}borrowing {value_name}",
512 if borrow_level == hir::Mutability::Mut { "mutably " } else { "" }
515 Applicability::MaybeIncorrect,
520 fn suggest_adding_copy_bounds(
522 err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
526 let tcx = self.infcx.tcx;
527 let generics = tcx.generics_of(self.mir_def_id());
529 let Some(hir_generics) = tcx
530 .typeck_root_def_id(self.mir_def_id().to_def_id())
532 .and_then(|def_id| tcx.hir().get_generics(def_id))
534 // Try to find predicates on *generic params* that would allow copying `ty`
535 let predicates: Result<Vec<_>, _> = tcx.infer_ctxt().enter(|infcx| {
536 let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
538 let copy_did = infcx.tcx.lang_items().copy_trait().unwrap();
539 let cause = ObligationCause::new(
542 rustc_infer::traits::ObligationCauseCode::MiscObligation,
544 fulfill_cx.register_bound(
547 // Erase any region vids from the type, which may not be resolved
548 infcx.tcx.erase_regions(ty),
552 // Select all, including ambiguous predicates
553 let errors = fulfill_cx.select_all_or_error(&infcx);
555 // Only emit suggestion if all required predicates are on generic
558 .map(|err| match err.obligation.predicate.kind().skip_binder() {
559 PredicateKind::Trait(predicate) => match predicate.self_ty().kind() {
560 ty::Param(param_ty) => Ok((
561 generics.type_param(param_ty, tcx),
562 predicate.trait_ref.print_only_trait_path().to_string(),
571 if let Ok(predicates) = predicates {
572 suggest_constraining_type_params(
578 .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
583 pub(crate) fn report_move_out_while_borrowed(
586 (place, span): (Place<'tcx>, Span),
587 borrow: &BorrowData<'tcx>,
590 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
591 location, place, span, borrow
593 let value_msg = self.describe_any_place(place.as_ref());
594 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
596 let borrow_spans = self.retrieve_borrow_spans(borrow);
597 let borrow_span = borrow_spans.args_or_use();
599 let move_spans = self.move_spans(place.as_ref(), location);
600 let span = move_spans.args_or_use();
603 self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
604 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
605 err.span_label(span, format!("move out of {} occurs here", value_msg));
607 borrow_spans.var_span_label_path_only(
609 format!("borrow occurs due to use{}", borrow_spans.describe()),
612 move_spans.var_span_label(
614 format!("move occurs due to use{}", move_spans.describe()),
618 self.explain_why_borrow_contains_point(location, borrow, None)
619 .add_explanation_to_diagnostic(
628 self.buffer_error(err);
631 pub(crate) fn report_use_while_mutably_borrowed(
634 (place, _span): (Place<'tcx>, Span),
635 borrow: &BorrowData<'tcx>,
636 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
637 let borrow_spans = self.retrieve_borrow_spans(borrow);
638 let borrow_span = borrow_spans.args_or_use();
640 // Conflicting borrows are reported separately, so only check for move
642 let use_spans = self.move_spans(place.as_ref(), location);
643 let span = use_spans.var_or_use();
645 // 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
646 // we call `var_span_label` on `borrow_spans` to annotate if the existing borrow was in a closure
647 let mut err = self.cannot_use_when_mutably_borrowed(
649 &self.describe_any_place(place.as_ref()),
651 &self.describe_any_place(borrow.borrowed_place.as_ref()),
654 borrow_spans.var_span_label(
657 let place = &borrow.borrowed_place;
658 let desc_place = self.describe_any_place(place.as_ref());
659 format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
664 self.explain_why_borrow_contains_point(location, borrow, None)
665 .add_explanation_to_diagnostic(
677 pub(crate) fn report_conflicting_borrow(
680 (place, span): (Place<'tcx>, Span),
681 gen_borrow_kind: BorrowKind,
682 issued_borrow: &BorrowData<'tcx>,
683 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
684 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
685 let issued_span = issued_spans.args_or_use();
687 let borrow_spans = self.borrow_spans(span, location);
688 let span = borrow_spans.args_or_use();
690 let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
696 let (desc_place, msg_place, msg_borrow, union_type_name) =
697 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);
699 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);
700 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };
702 // FIXME: supply non-"" `opt_via` when appropriate
703 let first_borrow_desc;
704 let mut err = match (gen_borrow_kind, issued_borrow.kind) {
705 (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
706 first_borrow_desc = "mutable ";
707 self.cannot_reborrow_already_borrowed(
719 (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
720 first_borrow_desc = "immutable ";
721 self.cannot_reborrow_already_borrowed(
734 (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
735 first_borrow_desc = "first ";
736 let mut err = self.cannot_mutably_borrow_multiply(
744 self.suggest_split_at_mut_if_applicable(
747 issued_borrow.borrowed_place,
752 (BorrowKind::Unique, BorrowKind::Unique) => {
753 first_borrow_desc = "first ";
754 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
757 (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
758 if let Some(immutable_section_description) =
759 self.classify_immutable_section(issued_borrow.assigned_place)
761 let mut err = self.cannot_mutate_in_immutable_section(
765 immutable_section_description,
768 borrow_spans.var_span_label(
771 "borrow occurs due to use of {}{}",
773 borrow_spans.describe(),
780 first_borrow_desc = "immutable ";
781 self.cannot_reborrow_already_borrowed(
795 (BorrowKind::Unique, _) => {
796 first_borrow_desc = "first ";
797 self.cannot_uniquely_borrow_by_one_closure(
809 (BorrowKind::Shared, BorrowKind::Unique) => {
810 first_borrow_desc = "first ";
811 self.cannot_reborrow_already_uniquely_borrowed(
824 (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
825 first_borrow_desc = "first ";
826 self.cannot_reborrow_already_uniquely_borrowed(
839 (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
842 BorrowKind::Mut { .. }
845 | BorrowKind::Shallow,
849 if issued_spans == borrow_spans {
850 borrow_spans.var_span_label(
852 format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe(),),
853 gen_borrow_kind.describe_mutability(),
856 let borrow_place = &issued_borrow.borrowed_place;
857 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
858 issued_spans.var_span_label(
861 "first borrow occurs due to use of {}{}",
863 issued_spans.describe(),
865 issued_borrow.kind.describe_mutability(),
868 borrow_spans.var_span_label(
871 "second borrow occurs due to use of {}{}",
873 borrow_spans.describe(),
875 gen_borrow_kind.describe_mutability(),
879 if union_type_name != "" {
881 "{} is a field of the union `{}`, so it overlaps the field {}",
882 msg_place, union_type_name, msg_borrow,
886 explanation.add_explanation_to_diagnostic(
893 Some((issued_span, span)),
896 self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation);
901 #[instrument(level = "debug", skip(self, err))]
902 fn suggest_using_local_if_applicable(
904 err: &mut Diagnostic,
906 issued_borrow: &BorrowData<'tcx>,
907 explanation: BorrowExplanation<'tcx>,
909 let used_in_call = matches!(
911 BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
914 debug!("not later used in call");
919 if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
926 if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
929 issued_borrow.reserve_location
931 let outer_call_stmt = self.body.stmt_at(outer_call_loc);
933 let inner_param_location = location;
934 let Some(inner_param_stmt) = self.body.stmt_at(inner_param_location).left() else {
935 debug!("`inner_param_location` {:?} is not for a statement", inner_param_location);
938 let Some(&inner_param) = inner_param_stmt.kind.as_assign().map(|(p, _)| p) else {
940 "`inner_param_location` {:?} is not for an assignment: {:?}",
941 inner_param_location, inner_param_stmt
945 let inner_param_uses = find_all_local_uses::find(self.body, inner_param.local);
946 let Some((inner_call_loc, inner_call_term)) = inner_param_uses.into_iter().find_map(|loc| {
947 let Either::Right(term) = self.body.stmt_at(loc) else {
948 debug!("{:?} is a statement, so it can't be a call", loc);
951 let TerminatorKind::Call { args, .. } = &term.kind else {
952 debug!("not a call: {:?}", term);
955 debug!("checking call args for uses of inner_param: {:?}", args);
956 if args.contains(&Operand::Move(inner_param)) {
962 debug!("no uses of inner_param found as a by-move call arg");
965 debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc);
967 let inner_call_span = inner_call_term.source_info.span;
968 let outer_call_span = match use_span {
970 None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span,
972 if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) {
973 // FIXME: This stops the suggestion in some cases where it should be emitted.
974 // Fix the spans for those cases so it's emitted correctly.
976 "outer span {:?} does not strictly contain inner span {:?}",
977 outer_call_span, inner_call_span
984 "try adding a local storing this{}...",
985 if use_span.is_some() { "" } else { " argument" }
991 "...and then using that local {}",
992 if use_span.is_some() { "here" } else { "as the argument to this call" }
997 fn suggest_split_at_mut_if_applicable(
999 err: &mut Diagnostic,
1001 borrowed_place: Place<'tcx>,
1003 if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
1004 (&place.projection[..], &borrowed_place.projection[..])
1007 "consider using `.split_at_mut(position)` or similar method to obtain \
1008 two mutable non-overlapping sub-slices",
1013 /// Returns the description of the root place for a conflicting borrow and the full
1014 /// descriptions of the places that caused the conflict.
1016 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
1017 /// attempted while a shared borrow is live, then this function will return:
1022 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
1023 /// a shared borrow of another field `x.y`, then this function will return:
1025 /// ("x", "x.z", "x.y")
1028 /// In the more complex union case, where the union is a field of a struct, then if a mutable
1029 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
1030 /// another field `x.u.y`, then this function will return:
1032 /// ("x.u", "x.u.z", "x.u.y")
1035 /// This is used when creating error messages like below:
1038 /// cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
1039 /// mutable (via `a.u.s.b`) [E0502]
1041 pub(crate) fn describe_place_for_conflicting_borrow(
1043 first_borrowed_place: Place<'tcx>,
1044 second_borrowed_place: Place<'tcx>,
1045 ) -> (String, String, String, String) {
1046 // Define a small closure that we can use to check if the type of a place
1048 let union_ty = |place_base| {
1049 // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`;
1050 // using a type annotation in the closure argument instead leads to a lifetime error.
1051 let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty;
1052 ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
1055 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
1056 // code duplication (particularly around returning an empty description in the failure
1060 // If we have a conflicting borrow of the same place, then we don't want to add
1061 // an extraneous "via x.y" to our diagnostics, so filter out this case.
1062 first_borrowed_place != second_borrowed_place
1065 // We're going to want to traverse the first borrowed place to see if we can find
1066 // field access to a union. If we find that, then we will keep the place of the
1067 // union being accessed and the field that was being accessed so we can check the
1068 // second borrowed place for the same union and an access to a different field.
1069 for (place_base, elem) in first_borrowed_place.iter_projections().rev() {
1071 ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => {
1072 return Some((place_base, field));
1079 .and_then(|(target_base, target_field)| {
1080 // With the place of a union and a field access into it, we traverse the second
1081 // borrowed place and look for an access to a different field of the same union.
1082 for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
1083 if let ProjectionElem::Field(field, _) = elem {
1084 if let Some(union_ty) = union_ty(place_base) {
1085 if field != target_field && place_base == target_base {
1087 self.describe_any_place(place_base),
1088 self.describe_any_place(first_borrowed_place.as_ref()),
1089 self.describe_any_place(second_borrowed_place.as_ref()),
1090 union_ty.to_string(),
1098 .unwrap_or_else(|| {
1099 // If we didn't find a field access into a union, or both places match, then
1100 // only return the description of the first place.
1102 self.describe_any_place(first_borrowed_place.as_ref()),
1110 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
1112 /// This means that some data referenced by `borrow` needs to live
1113 /// past the point where the StorageDeadOrDrop of `place` occurs.
1114 /// This is usually interpreted as meaning that `place` has too
1115 /// short a lifetime. (But sometimes it is more useful to report
1116 /// it as a more direct conflict between the execution of a
1117 /// `Drop::drop` with an aliasing borrow.)
1118 pub(crate) fn report_borrowed_value_does_not_live_long_enough(
1121 borrow: &BorrowData<'tcx>,
1122 place_span: (Place<'tcx>, Span),
1123 kind: Option<WriteKind>,
1126 "report_borrowed_value_does_not_live_long_enough(\
1127 {:?}, {:?}, {:?}, {:?}\
1129 location, borrow, place_span, kind
1132 let drop_span = place_span.1;
1134 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1136 let borrow_spans = self.retrieve_borrow_spans(borrow);
1137 let borrow_span = borrow_spans.var_or_use_path_span();
1139 assert!(root_place.projection.is_empty());
1140 let proper_span = self.body.local_decls[root_place.local].source_info.span;
1142 let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
1144 if self.access_place_error_reported.contains(&(
1145 Place { local: root_place.local, projection: root_place_projection },
1149 "suppressing access_place error when borrow doesn't live long enough for {:?}",
1155 self.access_place_error_reported.insert((
1156 Place { local: root_place.local, projection: root_place_projection },
1160 let borrowed_local = borrow.borrowed_place.local;
1161 if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
1163 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
1164 self.buffer_error(err);
1168 if let StorageDeadOrDrop::Destructor(dropped_ty) =
1169 self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
1171 // If a borrow of path `B` conflicts with drop of `D` (and
1172 // we're not in the uninteresting case where `B` is a
1173 // prefix of `D`), then report this as a more interesting
1174 // destructor conflict.
1175 if !borrow.borrowed_place.as_ref().is_prefix_of(place_span.0.as_ref()) {
1176 self.report_borrow_conflicts_with_destructor(
1177 location, borrow, place_span, kind, dropped_ty,
1183 let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
1185 let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
1186 let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
1189 "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
1190 place_desc, explanation
1192 let err = match (place_desc, explanation) {
1193 // If the outlives constraint comes from inside the closure,
1198 // Box::new(|| y) as Box<Fn() -> &'static i32>
1200 // then just use the normal error. The closure isn't escaping
1201 // and `move` will not help here.
1204 BorrowExplanation::MustBeValidFor {
1206 category @ (ConstraintCategory::Return(_)
1207 | ConstraintCategory::CallArgument(_)
1208 | ConstraintCategory::OpaqueType),
1209 from_closure: false,
1214 ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
1215 .report_escaping_closure_capture(
1221 &format!("`{}`", name),
1225 BorrowExplanation::MustBeValidFor {
1226 category: ConstraintCategory::Assignment,
1227 from_closure: false,
1231 RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
1237 ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
1238 (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
1246 (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
1256 self.buffer_error(err);
1259 fn report_local_value_does_not_live_long_enough(
1263 borrow: &BorrowData<'tcx>,
1265 borrow_spans: UseSpans<'tcx>,
1266 explanation: BorrowExplanation<'tcx>,
1267 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1269 "report_local_value_does_not_live_long_enough(\
1270 {:?}, {:?}, {:?}, {:?}, {:?}\
1272 location, name, borrow, drop_span, borrow_spans
1275 let borrow_span = borrow_spans.var_or_use_path_span();
1276 if let BorrowExplanation::MustBeValidFor {
1280 from_closure: false,
1284 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1289 opt_place_desc.as_ref(),
1295 let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name));
1297 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
1298 let region_name = annotation.emit(self, &mut err);
1302 format!("`{}` would have to be valid for `{}`...", name, region_name),
1305 let fn_hir_id = self.mir_hir_id();
1309 "...but `{}` will be dropped here, when the {} returns",
1314 .opt_name(fn_hir_id)
1315 .map(|name| format!("function `{}`", name))
1316 .unwrap_or_else(|| {
1320 .typeck(self.mir_def_id())
1321 .node_type(fn_hir_id)
1324 ty::Closure(..) => "enclosing closure",
1325 ty::Generator(..) => "enclosing generator",
1326 kind => bug!("expected closure or generator, found {:?}", kind),
1334 "functions cannot return a borrow to data owned within the function's scope, \
1335 functions can only return borrows to data passed as arguments",
1338 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
1339 references-and-borrowing.html#dangling-references>",
1342 if let BorrowExplanation::MustBeValidFor { .. } = explanation {
1344 explanation.add_explanation_to_diagnostic(
1355 err.span_label(borrow_span, "borrowed value does not live long enough");
1356 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
1358 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1360 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1362 explanation.add_explanation_to_diagnostic(
1376 fn report_borrow_conflicts_with_destructor(
1379 borrow: &BorrowData<'tcx>,
1380 (place, drop_span): (Place<'tcx>, Span),
1381 kind: Option<WriteKind>,
1382 dropped_ty: Ty<'tcx>,
1385 "report_borrow_conflicts_with_destructor(\
1386 {:?}, {:?}, ({:?}, {:?}), {:?}\
1388 location, borrow, place, drop_span, kind,
1391 let borrow_spans = self.retrieve_borrow_spans(borrow);
1392 let borrow_span = borrow_spans.var_or_use();
1394 let mut err = self.cannot_borrow_across_destructor(borrow_span);
1396 let what_was_dropped = match self.describe_place(place.as_ref()) {
1397 Some(name) => format!("`{}`", name),
1398 None => String::from("temporary value"),
1401 let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
1402 Some(borrowed) => format!(
1403 "here, drop of {D} needs exclusive access to `{B}`, \
1404 because the type `{T}` implements the `Drop` trait",
1405 D = what_was_dropped,
1410 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
1411 D = what_was_dropped,
1415 err.span_label(drop_span, label);
1417 // Only give this note and suggestion if they could be relevant.
1419 self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
1421 BorrowExplanation::UsedLater { .. }
1422 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1423 err.note("consider using a `let` binding to create a longer lived value");
1428 explanation.add_explanation_to_diagnostic(
1438 self.buffer_error(err);
1441 fn report_thread_local_value_does_not_live_long_enough(
1445 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1447 "report_thread_local_value_does_not_live_long_enough(\
1450 drop_span, borrow_span
1453 let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
1457 "thread-local variables cannot be borrowed beyond the end of the function",
1459 err.span_label(drop_span, "end of enclosing function is here");
1464 fn report_temporary_value_does_not_live_long_enough(
1467 borrow: &BorrowData<'tcx>,
1469 borrow_spans: UseSpans<'tcx>,
1471 explanation: BorrowExplanation<'tcx>,
1472 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1474 "report_temporary_value_does_not_live_long_enough(\
1475 {:?}, {:?}, {:?}, {:?}\
1477 location, borrow, drop_span, proper_span
1480 if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
1483 if let Some(diag) = self.try_report_cannot_return_reference_to_local(
1494 let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
1495 err.span_label(proper_span, "creates a temporary which is freed while still in use");
1496 err.span_label(drop_span, "temporary value is freed at the end of this statement");
1499 BorrowExplanation::UsedLater(..)
1500 | BorrowExplanation::UsedLaterInLoop(..)
1501 | BorrowExplanation::UsedLaterWhenDropped { .. } => {
1502 // Only give this note and suggestion if it could be relevant.
1503 err.note("consider using a `let` binding to create a longer lived value");
1507 explanation.add_explanation_to_diagnostic(
1517 let within = if borrow_spans.for_generator() { " by generator" } else { "" };
1519 borrow_spans.args_span_label(&mut err, format!("value captured here{}", within));
1524 fn try_report_cannot_return_reference_to_local(
1526 borrow: &BorrowData<'tcx>,
1529 category: ConstraintCategory<'tcx>,
1530 opt_place_desc: Option<&String>,
1531 ) -> Option<DiagnosticBuilder<'cx, ErrorGuaranteed>> {
1532 let return_kind = match category {
1533 ConstraintCategory::Return(_) => "return",
1534 ConstraintCategory::Yield => "yield",
1538 // FIXME use a better heuristic than Spans
1539 let reference_desc = if return_span == self.body.source_info(borrow.reserve_location).span {
1545 let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
1546 let local_kind = if let Some(local) = borrow.borrowed_place.as_local() {
1547 match self.body.local_kind(local) {
1548 LocalKind::ReturnPointer | LocalKind::Temp => {
1549 bug!("temporary or return pointer with a name")
1551 LocalKind::Var => "local variable ",
1553 if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL =>
1555 "variable captured by `move` "
1557 LocalKind::Arg => "function parameter ",
1563 format!("{}`{}`", local_kind, place_desc),
1564 format!("`{}` is borrowed here", place_desc),
1568 self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
1569 let local = root_place.local;
1570 match self.body.local_kind(local) {
1571 LocalKind::ReturnPointer | LocalKind::Temp => {
1572 ("temporary value".to_string(), "temporary value created here".to_string())
1575 "function parameter".to_string(),
1576 "function parameter borrowed here".to_string(),
1579 ("local binding".to_string(), "local binding introduced here".to_string())
1584 let mut err = self.cannot_return_reference_to_local(
1591 if return_span != borrow_span {
1592 err.span_label(borrow_span, note);
1594 let tcx = self.infcx.tcx;
1595 let ty_params = ty::List::empty();
1597 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
1598 let return_ty = tcx.erase_regions(return_ty);
1601 if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator)
1604 .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
1605 .must_apply_modulo_regions()
1607 err.span_suggestion_hidden(
1608 return_span.shrink_to_hi(),
1609 "use `.collect()` to allocate the iterator",
1610 ".collect::<Vec<_>>()",
1611 Applicability::MaybeIncorrect,
1619 fn report_escaping_closure_capture(
1621 use_span: UseSpans<'tcx>,
1623 fr_name: &RegionName,
1624 category: ConstraintCategory<'tcx>,
1625 constraint_span: Span,
1627 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1628 let tcx = self.infcx.tcx;
1629 let args_span = use_span.args_or_use();
1631 let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
1633 if string.starts_with("async ") {
1634 let pos = args_span.lo() + BytePos(6);
1635 (args_span.with_lo(pos).with_hi(pos), "move ")
1636 } else if string.starts_with("async|") {
1637 let pos = args_span.lo() + BytePos(5);
1638 (args_span.with_lo(pos).with_hi(pos), " move")
1640 (args_span.shrink_to_lo(), "move ")
1643 Err(_) => (args_span, "move |<args>| <body>"),
1645 let kind = match use_span.generator_kind() {
1646 Some(generator_kind) => match generator_kind {
1647 GeneratorKind::Async(async_kind) => match async_kind {
1648 AsyncGeneratorKind::Block => "async block",
1649 AsyncGeneratorKind::Closure => "async closure",
1650 _ => bug!("async block/closure expected, but async function found."),
1652 GeneratorKind::Gen => "generator",
1658 self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1659 err.span_suggestion_verbose(
1662 "to force the {} to take ownership of {} (and any \
1663 other referenced variables), use the `move` keyword",
1667 Applicability::MachineApplicable,
1671 ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => {
1672 let msg = format!("{} is returned here", kind);
1673 err.span_note(constraint_span, &msg);
1675 ConstraintCategory::CallArgument(_) => {
1676 fr_name.highlight_region_name(&mut err);
1677 if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
1679 "async blocks are not executed immediately and must either take a \
1680 reference or ownership of outside variables they use",
1683 let msg = format!("function requires argument type to outlive `{}`", fr_name);
1684 err.span_note(constraint_span, &msg);
1688 "report_escaping_closure_capture called with unexpected constraint \
1697 fn report_escaping_data(
1700 name: &Option<String>,
1704 ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
1705 let tcx = self.infcx.tcx;
1707 let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
1710 borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from);
1714 format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from),
1717 err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from));
1719 if let Some(name) = name {
1722 format!("reference to `{}` escapes the {} body here", name, escapes_from),
1727 format!("reference escapes the {} body here", escapes_from),
1734 fn get_moved_indexes(
1738 ) -> (Vec<MoveSite>, Vec<Location>) {
1739 fn predecessor_locations<'tcx, 'a>(
1740 body: &'a mir::Body<'tcx>,
1742 ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
1743 if location.statement_index == 0 {
1744 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
1745 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
1747 Either::Right(std::iter::once(Location {
1748 statement_index: location.statement_index - 1,
1754 let mut mpis = vec![mpi];
1755 let move_paths = &self.move_data.move_paths;
1756 mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1758 let mut stack = Vec::new();
1759 let mut back_edge_stack = Vec::new();
1761 predecessor_locations(self.body, location).for_each(|predecessor| {
1762 if location.dominates(predecessor, &self.dominators) {
1763 back_edge_stack.push(predecessor)
1765 stack.push(predecessor);
1769 let mut reached_start = false;
1771 /* Check if the mpi is initialized as an argument */
1772 let mut is_argument = false;
1773 for arg in self.body.args_iter() {
1774 let path = self.move_data.rev_lookup.find_local(arg);
1775 if mpis.contains(&path) {
1780 let mut visited = FxHashSet::default();
1781 let mut move_locations = FxHashSet::default();
1782 let mut reinits = vec![];
1783 let mut result = vec![];
1785 let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
1787 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1788 location, is_back_edge
1791 if !visited.insert(location) {
1797 self.body[location.block].statements.get(location.statement_index).map(|s| &s.kind);
1798 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
1799 // this analysis only tries to find moves explicitly
1800 // written by the user, so we ignore the move-outs
1801 // created by `StorageDead` and at the beginning
1804 // If we are found a use of a.b.c which was in error, then we want to look for
1805 // moves not only of a.b.c but also a.b and a.
1807 // Note that the moves data already includes "parent" paths, so we don't have to
1808 // worry about the other case: that is, if there is a move of a.b.c, it is already
1809 // marked as a move of a.b and a as well, so we will generate the correct errors
1811 for moi in &self.move_data.loc_map[location] {
1812 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
1813 let path = self.move_data.moves[*moi].path;
1814 if mpis.contains(&path) {
1816 "report_use_of_moved_or_uninitialized: found {:?}",
1817 move_paths[path].place
1819 result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1820 move_locations.insert(location);
1822 // Strictly speaking, we could continue our DFS here. There may be
1823 // other moves that can reach the point of error. But it is kind of
1824 // confusing to highlight them.
1832 // drop(a); // <-- current point of error
1835 // Because we stop the DFS here, we only highlight `let c = a`,
1836 // and not `let b = a`. We will of course also report an error at
1837 // `let c = a` which highlights `let b = a` as the move.
1844 let mut any_match = false;
1845 for ii in &self.move_data.init_loc_map[location] {
1846 let init = self.move_data.inits[*ii];
1848 InitKind::Deep | InitKind::NonPanicPathOnly => {
1849 if mpis.contains(&init.path) {
1853 InitKind::Shallow => {
1854 if mpi == init.path {
1861 reinits.push(location);
1867 while let Some(location) = stack.pop() {
1868 if dfs_iter(&mut result, location, false) {
1872 let mut has_predecessor = false;
1873 predecessor_locations(self.body, location).for_each(|predecessor| {
1874 if location.dominates(predecessor, &self.dominators) {
1875 back_edge_stack.push(predecessor)
1877 stack.push(predecessor);
1879 has_predecessor = true;
1882 if !has_predecessor {
1883 reached_start = true;
1886 if (is_argument || !reached_start) && result.is_empty() {
1887 /* Process back edges (moves in future loop iterations) only if
1888 the move path is definitely initialized upon loop entry,
1889 to avoid spurious "in previous iteration" errors.
1890 During DFS, if there's a path from the error back to the start
1891 of the function with no intervening init or move, then the
1892 move path may be uninitialized at loop entry.
1894 while let Some(location) = back_edge_stack.pop() {
1895 if dfs_iter(&mut result, location, true) {
1899 predecessor_locations(self.body, location)
1900 .for_each(|predecessor| back_edge_stack.push(predecessor));
1904 // Check if we can reach these reinits from a move location.
1905 let reinits_reachable = reinits
1908 let mut visited = FxHashSet::default();
1909 let mut stack = vec![*reinit];
1910 while let Some(location) = stack.pop() {
1911 if !visited.insert(location) {
1914 if move_locations.contains(&location) {
1917 stack.extend(predecessor_locations(self.body, location));
1921 .collect::<Vec<Location>>();
1922 (result, reinits_reachable)
1925 pub(crate) fn report_illegal_mutation_of_borrowed(
1928 (place, span): (Place<'tcx>, Span),
1929 loan: &BorrowData<'tcx>,
1931 let loan_spans = self.retrieve_borrow_spans(loan);
1932 let loan_span = loan_spans.args_or_use();
1934 let descr_place = self.describe_any_place(place.as_ref());
1935 if loan.kind == BorrowKind::Shallow {
1936 if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
1937 let mut err = self.cannot_mutate_in_immutable_section(
1944 loan_spans.var_span_label(
1946 format!("borrow occurs due to use{}", loan_spans.describe()),
1947 loan.kind.describe_mutability(),
1950 self.buffer_error(err);
1956 let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
1958 loan_spans.var_span_label(
1960 format!("borrow occurs due to use{}", loan_spans.describe()),
1961 loan.kind.describe_mutability(),
1964 self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
1974 self.explain_deref_coercion(loan, &mut err);
1976 self.buffer_error(err);
1979 fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
1980 let tcx = self.infcx.tcx;
1982 Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
1983 Some((method_did, method_substs)),
1985 &self.body[loan.reserve_location.block].terminator,
1986 rustc_const_eval::util::find_self_call(
1989 loan.assigned_place.local,
1990 loan.reserve_location.block,
1993 if tcx.is_diagnostic_item(sym::deref_method, method_did) {
1995 tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
1996 Instance::resolve(tcx, self.param_env, deref_target, method_substs)
1999 if let Some(Ok(instance)) = deref_target {
2000 let deref_target_ty = instance.ty(tcx, self.param_env);
2002 "borrow occurs due to deref coercion to `{}`",
2005 err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
2011 /// Reports an illegal reassignment; for example, an assignment to
2012 /// (part of) a non-`mut` local that occurs potentially after that
2013 /// local has already been initialized. `place` is the path being
2014 /// assigned; `err_place` is a place providing a reason why
2015 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
2016 /// assignment to `x.f`).
2017 pub(crate) fn report_illegal_reassignment(
2019 _location: Location,
2020 (place, span): (Place<'tcx>, Span),
2021 assigned_span: Span,
2022 err_place: Place<'tcx>,
2024 let (from_arg, local_decl, local_name) = match err_place.as_local() {
2026 self.body.local_kind(local) == LocalKind::Arg,
2027 Some(&self.body.local_decls[local]),
2028 self.local_names[local],
2030 None => (false, None, None),
2033 // If root local is initialized immediately (everything apart from let
2034 // PATTERN;) then make the error refer to that local, rather than the
2035 // place being assigned later.
2036 let (place_description, assigned_span) = match local_decl {
2039 Some(box LocalInfo::User(
2040 ClearCrossCrate::Clear
2041 | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
2042 opt_match_place: None,
2046 | Some(box LocalInfo::StaticRef { .. })
2050 | None => (self.describe_any_place(place.as_ref()), assigned_span),
2051 Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
2054 let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
2055 let msg = if from_arg {
2056 "cannot assign to immutable argument"
2058 "cannot assign twice to immutable variable"
2060 if span != assigned_span && !from_arg {
2061 err.span_label(assigned_span, format!("first assignment to {}", place_description));
2063 if let Some(decl) = local_decl
2064 && let Some(name) = local_name
2065 && decl.can_be_made_mutable()
2067 err.span_suggestion(
2068 decl.source_info.span,
2069 "consider making this binding mutable",
2070 format!("mut {}", name),
2071 Applicability::MachineApplicable,
2074 err.span_label(span, msg);
2075 self.buffer_error(err);
2078 fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
2079 let tcx = self.infcx.tcx;
2080 let (kind, _place_ty) = place.projection.iter().fold(
2081 (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
2082 |(kind, place_ty), &elem| {
2085 ProjectionElem::Deref => match kind {
2086 StorageDeadOrDrop::LocalStorageDead
2087 | StorageDeadOrDrop::BoxedStorageDead => {
2089 place_ty.ty.is_box(),
2090 "Drop of value behind a reference or raw pointer"
2092 StorageDeadOrDrop::BoxedStorageDead
2094 StorageDeadOrDrop::Destructor(_) => kind,
2096 ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
2097 match place_ty.ty.kind() {
2098 ty::Adt(def, _) if def.has_dtor(tcx) => {
2099 // Report the outermost adt with a destructor
2101 StorageDeadOrDrop::Destructor(_) => kind,
2102 StorageDeadOrDrop::LocalStorageDead
2103 | StorageDeadOrDrop::BoxedStorageDead => {
2104 StorageDeadOrDrop::Destructor(place_ty.ty)
2111 ProjectionElem::ConstantIndex { .. }
2112 | ProjectionElem::Subslice { .. }
2113 | ProjectionElem::Index(_) => kind,
2115 place_ty.projection_ty(tcx, elem),
2122 /// Describe the reason for the fake borrow that was assigned to `place`.
2123 fn classify_immutable_section(&self, place: Place<'tcx>) -> Option<&'static str> {
2124 use rustc_middle::mir::visit::Visitor;
2125 struct FakeReadCauseFinder<'tcx> {
2127 cause: Option<FakeReadCause>,
2129 impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
2130 fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
2132 Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
2133 if *place == self.place =>
2135 self.cause = Some(*cause);
2141 let mut visitor = FakeReadCauseFinder { place, cause: None };
2142 visitor.visit_body(&self.body);
2143 match visitor.cause {
2144 Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
2145 Some(FakeReadCause::ForIndex) => Some("indexing expression"),
2150 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
2151 /// borrow of local value that does not live long enough.
2152 fn annotate_argument_and_return_for_borrow(
2154 borrow: &BorrowData<'tcx>,
2155 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2156 // Define a fallback for when we can't match a closure.
2158 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id());
2162 let ty = self.infcx.tcx.type_of(self.mir_def_id());
2164 ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
2165 self.mir_def_id().to_def_id(),
2166 self.infcx.tcx.fn_sig(self.mir_def_id()),
2173 // In order to determine whether we need to annotate, we need to check whether the reserve
2174 // place was an assignment into a temporary.
2176 // If it was, we check whether or not that temporary is eventually assigned into the return
2177 // place. If it was, we can add annotations about the function's return type and arguments
2178 // and it'll make sense.
2179 let location = borrow.reserve_location;
2180 debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
2181 if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
2182 &self.body[location.block].statements.get(location.statement_index)
2184 debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
2185 // Check that the initial assignment of the reserve location is into a temporary.
2186 let mut target = match reservation.as_local() {
2187 Some(local) if self.body.local_kind(local) == LocalKind::Temp => local,
2191 // Next, look through the rest of the block, checking if we are assigning the
2192 // `target` (that is, the place that contains our borrow) to anything.
2193 let mut annotated_closure = None;
2194 for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
2196 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
2199 if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
2200 if let Some(assigned_to) = place.as_local() {
2202 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
2206 // Check if our `target` was captured by a closure.
2207 if let Rvalue::Aggregate(
2208 box AggregateKind::Closure(def_id, substs),
2212 for operand in operands {
2213 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2217 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2221 // Find the local from the operand.
2222 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
2226 if assigned_from_local != target {
2230 // If a closure captured our `target` and then assigned
2231 // into a place then we should annotate the closure in
2232 // case it ends up being assigned into the return place.
2234 self.annotate_fn_sig(*def_id, substs.as_closure().sig());
2236 "annotate_argument_and_return_for_borrow: \
2237 annotated_closure={:?} assigned_from_local={:?} \
2239 annotated_closure, assigned_from_local, assigned_to
2242 if assigned_to == mir::RETURN_PLACE {
2243 // If it was assigned directly into the return place, then
2245 return annotated_closure;
2247 // Otherwise, update the target.
2248 target = assigned_to;
2252 // If none of our closure's operands matched, then skip to the next
2257 // Otherwise, look at other types of assignment.
2258 let assigned_from = match rvalue {
2259 Rvalue::Ref(_, _, assigned_from) => assigned_from,
2260 Rvalue::Use(operand) => match operand {
2261 Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
2269 "annotate_argument_and_return_for_borrow: \
2270 assigned_from={:?}",
2274 // Find the local from the rvalue.
2275 let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { continue };
2277 "annotate_argument_and_return_for_borrow: \
2278 assigned_from_local={:?}",
2279 assigned_from_local,
2282 // Check if our local matches the target - if so, we've assigned our
2283 // borrow to a new place.
2284 if assigned_from_local != target {
2288 // If we assigned our `target` into a new place, then we should
2289 // check if it was the return place.
2291 "annotate_argument_and_return_for_borrow: \
2292 assigned_from_local={:?} assigned_to={:?}",
2293 assigned_from_local, assigned_to
2295 if assigned_to == mir::RETURN_PLACE {
2296 // If it was then return the annotated closure if there was one,
2297 // else, annotate this function.
2298 return annotated_closure.or_else(fallback);
2301 // If we didn't assign into the return place, then we just update
2303 target = assigned_to;
2308 // Check the terminator if we didn't find anything in the statements.
2309 let terminator = &self.body[location.block].terminator();
2311 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2314 if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
2317 if let Some(assigned_to) = destination.as_local() {
2319 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2322 for operand in args {
2323 let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
2327 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2331 if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
2333 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2334 assigned_from_local,
2337 if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
2338 return annotated_closure.or_else(fallback);
2346 // If we haven't found an assignment into the return place, then we need not add
2348 debug!("annotate_argument_and_return_for_borrow: none found");
2352 /// Annotate the first argument and return type of a function signature if they are
2357 sig: ty::PolyFnSig<'tcx>,
2358 ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
2359 debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
2360 let is_closure = self.infcx.tcx.is_closure(did);
2361 let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did.as_local()?);
2362 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
2364 // We need to work out which arguments to highlight. We do this by looking
2365 // at the return type, where there are three cases:
2367 // 1. If there are named arguments, then we should highlight the return type and
2368 // highlight any of the arguments that are also references with that lifetime.
2369 // If there are no arguments that have the same lifetime as the return type,
2370 // then don't highlight anything.
2371 // 2. The return type is a reference with an anonymous lifetime. If this is
2372 // the case, then we can take advantage of (and teach) the lifetime elision
2375 // We know that an error is being reported. So the arguments and return type
2376 // must satisfy the elision rules. Therefore, if there is a single argument
2377 // then that means the return type and first (and only) argument have the same
2378 // lifetime and the borrow isn't meeting that, we can highlight the argument
2381 // If there are multiple arguments then the first argument must be self (else
2382 // it would not satisfy the elision rules), so we can highlight self and the
2384 // 3. The return type is not a reference. In this case, we don't highlight
2386 let return_ty = sig.output();
2387 match return_ty.skip_binder().kind() {
2388 ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
2389 // This is case 1 from above, return type is a named reference so we need to
2390 // search for relevant arguments.
2391 let mut arguments = Vec::new();
2392 for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
2393 if let ty::Ref(argument_region, _, _) = argument.kind() {
2394 if argument_region == return_region {
2395 // Need to use the `rustc_middle::ty` types to compare against the
2396 // `return_region`. Then use the `rustc_hir` type to get only
2397 // the lifetime span.
2398 if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].kind {
2399 // With access to the lifetime, we can get
2401 arguments.push((*argument, lifetime.span));
2403 bug!("ty type is a ref but hir type is not");
2409 // We need to have arguments. This shouldn't happen, but it's worth checking.
2410 if arguments.is_empty() {
2414 // We use a mix of the HIR and the Ty types to get information
2415 // as the HIR doesn't have full types for closure arguments.
2416 let return_ty = sig.output().skip_binder();
2417 let mut return_span = fn_decl.output.span();
2418 if let hir::FnRetTy::Return(ty) = &fn_decl.output {
2419 if let hir::TyKind::Rptr(lifetime, _) = ty.kind {
2420 return_span = lifetime.span;
2424 Some(AnnotatedBorrowFnSignature::NamedFunction {
2430 ty::Ref(_, _, _) if is_closure => {
2431 // This is case 2 from above but only for closures, return type is anonymous
2432 // reference so we select
2433 // the first argument.
2434 let argument_span = fn_decl.inputs.first()?.span;
2435 let argument_ty = sig.inputs().skip_binder().first()?;
2437 // Closure arguments are wrapped in a tuple, so we need to get the first
2439 if let ty::Tuple(elems) = argument_ty.kind() {
2440 let &argument_ty = elems.first()?;
2441 if let ty::Ref(_, _, _) = argument_ty.kind() {
2442 return Some(AnnotatedBorrowFnSignature::Closure {
2451 ty::Ref(_, _, _) => {
2452 // This is also case 2 from above but for functions, return type is still an
2453 // anonymous reference so we select the first argument.
2454 let argument_span = fn_decl.inputs.first()?.span;
2455 let argument_ty = *sig.inputs().skip_binder().first()?;
2457 let return_span = fn_decl.output.span();
2458 let return_ty = sig.output().skip_binder();
2460 // We expect the first argument to be a reference.
2461 match argument_ty.kind() {
2462 ty::Ref(_, _, _) => {}
2466 Some(AnnotatedBorrowFnSignature::AnonymousFunction {
2474 // This is case 3 from above, return type is not a reference so don't highlight
2483 enum AnnotatedBorrowFnSignature<'tcx> {
2485 arguments: Vec<(Ty<'tcx>, Span)>,
2486 return_ty: Ty<'tcx>,
2490 argument_ty: Ty<'tcx>,
2491 argument_span: Span,
2492 return_ty: Ty<'tcx>,
2496 argument_ty: Ty<'tcx>,
2497 argument_span: Span,
2501 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
2502 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2504 pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
2506 &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
2509 format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
2512 cx.get_region_name_for_ty(argument_ty, 0)
2514 &AnnotatedBorrowFnSignature::AnonymousFunction {
2520 let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
2521 diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
2523 let return_ty_name = cx.get_name_for_ty(return_ty, 0);
2524 let types_equal = return_ty_name == argument_ty_name;
2529 if types_equal { "also " } else { "" },
2535 "argument and return type have the same lifetime due to lifetime elision rules",
2538 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2539 lifetime-syntax.html#lifetime-elision>",
2542 cx.get_region_name_for_ty(return_ty, 0)
2544 AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
2545 // Region of return type and arguments checked to be the same earlier.
2546 let region_name = cx.get_region_name_for_ty(*return_ty, 0);
2547 for (_, argument_span) in arguments {
2548 diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
2551 diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,));
2554 "use data from the highlighted arguments which match the `{}` lifetime of \
2565 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr
2566 struct ReferencedStatementsVisitor<'a>(&'a [Span], bool);
2568 impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> {
2569 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
2571 hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => {
2579 /// Given a set of spans representing statements initializing the relevant binding, visit all the
2580 /// function expressions looking for branching code paths that *do not* initialize the binding.
2581 struct ConditionVisitor<'b> {
2584 errors: Vec<(Span, String)>,
2587 impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
2588 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
2590 hir::ExprKind::If(cond, body, None) => {
2591 // `if` expressions with no `else` that initialize the binding might be missing an
2593 let mut v = ReferencedStatementsVisitor(self.spans, false);
2599 "if this `if` condition is `false`, {} is not initialized",
2604 ex.span.shrink_to_hi(),
2605 format!("an `else` arm might be missing here, initializing {}", self.name),
2609 hir::ExprKind::If(cond, body, Some(other)) => {
2610 // `if` expressions where the binding is only initialized in one of the two arms
2611 // might be missing a binding initialization.
2612 let mut a = ReferencedStatementsVisitor(self.spans, false);
2614 let mut b = ReferencedStatementsVisitor(self.spans, false);
2615 b.visit_expr(other);
2617 (true, true) | (false, false) => {}
2619 if other.span.is_desugaring(DesugaringKind::WhileLoop) {
2623 "if this condition isn't met and the `while` loop runs 0 \
2624 times, {} is not initialized",
2630 body.span.shrink_to_hi().until(other.span),
2632 "if the `if` condition is `false` and this `else` arm is \
2633 executed, {} is not initialized",
2643 "if this condition is `true`, {} is not initialized",
2650 hir::ExprKind::Match(e, arms, loop_desugar) => {
2651 // If the binding is initialized in one of the match arms, then the other match
2652 // arms might be missing an initialization.
2653 let results: Vec<bool> = arms
2656 let mut v = ReferencedStatementsVisitor(self.spans, false);
2661 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
2662 for (arm, seen) in arms.iter().zip(results) {
2664 if loop_desugar == hir::MatchSource::ForLoopDesugar {
2668 "if the `for` loop runs 0 times, {} is not initialized ",
2672 } else if let Some(guard) = &arm.guard {
2674 arm.pat.span.to(guard.body().span),
2676 "if this pattern and condition are matched, {} is not \
2685 "if this pattern is matched, {} is not initialized",
2694 // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should
2695 // also be accounted for. For now it is fine, as if we don't find *any* relevant
2696 // branching code paths, we point at the places where the binding *is* initialized for
2700 walk_expr(self, ex);