1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use borrow_check::{WriteKind, StorageDeadOrDrop};
12 use borrow_check::prefixes::IsPrefixOf;
13 use borrow_check::nll::explain_borrow::BorrowExplanation;
14 use rustc::middle::region::ScopeTree;
16 AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, FakeReadCause, Field, Local,
17 LocalDecl, LocalKind, Location, Operand, Place, ProjectionElem, Rvalue, Statement,
18 StatementKind, VarBindingForm,
20 use rustc::hir::def_id::DefId;
22 use rustc_data_structures::fx::FxHashSet;
23 use rustc_data_structures::sync::Lrc;
24 use rustc_errors::{Applicability, DiagnosticBuilder};
25 use rustc::util::ppaux::with_highlight_region_for_bound_region;
28 use super::borrow_set::BorrowData;
29 use super::{Context, MirBorrowckCtxt};
30 use super::{InitializationRequiringAction, PrefixSet};
32 use dataflow::drop_flag_effects;
33 use dataflow::move_paths::indexes::MoveOutIndex;
34 use dataflow::move_paths::MovePathIndex;
35 use util::borrowck_errors::{BorrowckErrors, Origin};
37 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
38 pub(super) fn report_use_of_moved_or_uninitialized(
41 desired_action: InitializationRequiringAction,
42 (place, span): (&Place<'tcx>, Span),
46 "report_use_of_moved_or_uninitialized: context={:?} desired_action={:?} place={:?} \
48 context, desired_action, place, span, mpi
52 .move_spans(place, context.loc)
53 .or_else(|| self.borrow_spans(span, context.loc));
54 let span = use_spans.args_or_use();
56 let mois = self.get_moved_indexes(context, mpi);
57 debug!("report_use_of_moved_or_uninitialized: mois={:?}", mois);
60 let root_place = self.prefixes(&place, PrefixSet::All).last().unwrap();
62 if self.uninitialized_error_reported.contains(&root_place.clone()) {
64 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
70 self.uninitialized_error_reported.insert(root_place.clone());
72 let item_msg = match self.describe_place_with_options(place, IncludingDowncast(true)) {
73 Some(name) => format!("`{}`", name),
74 None => "value".to_owned(),
76 let mut err = self.infcx.tcx.cannot_act_on_uninitialized_variable(
78 desired_action.as_noun(),
80 .describe_place_with_options(place, IncludingDowncast(true))
81 .unwrap_or("_".to_owned()),
84 err.span_label(span, format!("use of possibly uninitialized {}", item_msg));
86 use_spans.var_span_label(
88 format!("{} occurs due to use in closure", desired_action.as_noun()),
91 err.buffer(&mut self.errors_buffer);
93 if let Some((reported_place, _)) = self.move_error_reported.get(&mois) {
94 if self.prefixes(&reported_place, PrefixSet::All).any(|p| p == place) {
95 debug!("report_use_of_moved_or_uninitialized place: error suppressed \
101 let msg = ""; //FIXME: add "partially " or "collaterally "
103 let mut err = self.infcx.tcx.cannot_act_on_moved_value(
105 desired_action.as_noun(),
107 self.describe_place_with_options(&place, IncludingDowncast(true)),
111 let mut is_loop_move = false;
113 let move_out = self.move_data.moves[*moi];
114 let moved_place = &self.move_data.move_paths[move_out.path].place;
116 let move_spans = self.move_spans(moved_place, move_out.source);
117 let move_span = move_spans.args_or_use();
119 let move_msg = if move_spans.for_closure() {
125 if span == move_span {
128 format!("value moved{} here in previous iteration of loop", move_msg),
132 err.span_label(move_span, format!("value moved{} here", move_msg));
133 move_spans.var_span_label(&mut err, "variable moved due to use in closure");
137 use_spans.var_span_label(
139 format!("{} occurs due to use in closure", desired_action.as_noun()),
146 "value {} here after move",
147 desired_action.as_verb_in_past_tense()
152 if let Some(ty) = self.retrieve_type_for_place(place) {
153 let needs_note = match ty.sty {
154 ty::Closure(id, _) => {
155 let tables = self.infcx.tcx.typeck_tables_of(id);
156 let node_id = self.infcx.tcx.hir.as_local_node_id(id).unwrap();
157 let hir_id = self.infcx.tcx.hir.node_to_hir_id(node_id);
158 if tables.closure_kind_origins().get(hir_id).is_some() {
168 let mpi = self.move_data.moves[mois[0]].path;
169 let place = &self.move_data.move_paths[mpi].place;
171 if let Some(ty) = self.retrieve_type_for_place(place) {
172 let note_msg = match self
173 .describe_place_with_options(place, IncludingDowncast(true))
175 Some(name) => format!("`{}`", name),
176 None => "value".to_owned(),
180 "move occurs because {} has type `{}`, \
181 which does not implement the `Copy` trait",
188 if let Some((_, mut old_err)) = self.move_error_reported.insert(
192 // Cancel the old error so it doesn't ICE.
198 pub(super) fn report_move_out_while_borrowed(
201 (place, _span): (&Place<'tcx>, Span),
202 borrow: &BorrowData<'tcx>,
204 let tcx = self.infcx.tcx;
205 let value_msg = match self.describe_place(place) {
206 Some(name) => format!("`{}`", name),
207 None => "value".to_owned(),
209 let borrow_msg = match self.describe_place(&borrow.borrowed_place) {
210 Some(name) => format!("`{}`", name),
211 None => "value".to_owned(),
214 let borrow_spans = self.retrieve_borrow_spans(borrow);
215 let borrow_span = borrow_spans.args_or_use();
217 let move_spans = self.move_spans(place, context.loc);
218 let span = move_spans.args_or_use();
220 let mut err = tcx.cannot_move_when_borrowed(
222 &self.describe_place(place).unwrap_or("_".to_owned()),
225 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
226 err.span_label(span, format!("move out of {} occurs here", value_msg));
228 borrow_spans.var_span_label(&mut err, "borrow occurs due to use in closure");
230 move_spans.var_span_label(&mut err, "move occurs due to use in closure");
232 self.explain_why_borrow_contains_point(context, borrow, None)
233 .emit(self.infcx.tcx, &mut err);
234 err.buffer(&mut self.errors_buffer);
237 pub(super) fn report_use_while_mutably_borrowed(
240 (place, _span): (&Place<'tcx>, Span),
241 borrow: &BorrowData<'tcx>,
243 let tcx = self.infcx.tcx;
245 let borrow_spans = self.retrieve_borrow_spans(borrow);
246 let borrow_span = borrow_spans.args_or_use();
248 // Conflicting borrows are reported separately, so only check for move
250 let use_spans = self.move_spans(place, context.loc);
251 let span = use_spans.var_or_use();
253 let mut err = tcx.cannot_use_when_mutably_borrowed(
255 &self.describe_place(place).unwrap_or("_".to_owned()),
258 .describe_place(&borrow.borrowed_place)
259 .unwrap_or("_".to_owned()),
263 borrow_spans.var_span_label(&mut err, {
264 let place = &borrow.borrowed_place;
265 let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
267 format!("borrow occurs due to use of `{}` in closure", desc_place)
270 self.explain_why_borrow_contains_point(context, borrow, None)
271 .emit(self.infcx.tcx, &mut err);
272 err.buffer(&mut self.errors_buffer);
275 pub(super) fn report_conflicting_borrow(
278 (place, span): (&Place<'tcx>, Span),
279 gen_borrow_kind: BorrowKind,
280 issued_borrow: &BorrowData<'tcx>,
282 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
283 let issued_span = issued_spans.args_or_use();
285 let borrow_spans = self.borrow_spans(span, context.loc);
286 let span = borrow_spans.args_or_use();
288 let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
289 let tcx = self.infcx.tcx;
291 // FIXME: supply non-"" `opt_via` when appropriate
292 let mut err = match (
300 (BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt)
301 | (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => tcx
302 .cannot_reborrow_already_borrowed(
315 (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => tcx
316 .cannot_mutably_borrow_multiply(
326 (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => tcx
327 .cannot_uniquely_borrow_by_two_closures(
335 (BorrowKind::Unique, _, _, _, _, _) => tcx.cannot_uniquely_borrow_by_one_closure(
346 (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => tcx
347 .cannot_reborrow_already_uniquely_borrowed(
358 (BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => tcx
359 .cannot_reborrow_already_uniquely_borrowed(
370 (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(),
373 if issued_spans == borrow_spans {
374 borrow_spans.var_span_label(
376 format!("borrows occur due to use of `{}` in closure", desc_place),
379 let borrow_place = &issued_borrow.borrowed_place;
380 let borrow_place_desc = self.describe_place(borrow_place).unwrap_or("_".to_owned());
381 issued_spans.var_span_label(
384 "first borrow occurs due to use of `{}` in closure",
389 borrow_spans.var_span_label(
392 "second borrow occurs due to use of `{}` in closure",
398 self.explain_why_borrow_contains_point(context, issued_borrow, None)
399 .emit(self.infcx.tcx, &mut err);
401 err.buffer(&mut self.errors_buffer);
404 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
406 /// This means that some data referenced by `borrow` needs to live
407 /// past the point where the StorageDeadOrDrop of `place` occurs.
408 /// This is usually interpreted as meaning that `place` has too
409 /// short a lifetime. (But sometimes it is more useful to report
410 /// it as a more direct conflict between the execution of a
411 /// `Drop::drop` with an aliasing borrow.)
412 pub(super) fn report_borrowed_value_does_not_live_long_enough(
415 borrow: &BorrowData<'tcx>,
416 place_span: (&Place<'tcx>, Span),
417 kind: Option<WriteKind>,
419 debug!("report_borrowed_value_does_not_live_long_enough(\
420 {:?}, {:?}, {:?}, {:?}\
422 context, borrow, place_span, kind
425 let drop_span = place_span.1;
426 let scope_tree = self.infcx.tcx.region_scope_tree(self.mir_def_id);
427 let root_place = self
428 .prefixes(&borrow.borrowed_place, PrefixSet::All)
432 let borrow_spans = self.retrieve_borrow_spans(borrow);
433 let borrow_span = borrow_spans.var_or_use();
435 let proper_span = match *root_place {
436 Place::Local(local) => self.mir.local_decls[local].source_info.span,
441 .access_place_error_reported
442 .contains(&(root_place.clone(), borrow_span))
445 "suppressing access_place error when borrow doesn't live long enough for {:?}",
451 self.access_place_error_reported
452 .insert((root_place.clone(), borrow_span));
454 if let Some(WriteKind::StorageDeadOrDrop(StorageDeadOrDrop::Destructor)) = kind {
455 // If a borrow of path `B` conflicts with drop of `D` (and
456 // we're not in the uninteresting case where `B` is a
457 // prefix of `D`), then report this as a more interesting
458 // destructor conflict.
459 if !borrow.borrowed_place.is_prefix_of(place_span.0) {
460 self.report_borrow_conflicts_with_destructor(context, borrow, place_span, kind);
465 let err = match &self.describe_place(&borrow.borrowed_place) {
466 Some(_) if self.is_place_thread_local(root_place) =>
467 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span),
468 Some(name) => self.report_local_value_does_not_live_long_enough(
475 kind.map(|k| (k, place_span.0)),
477 None => self.report_temporary_value_does_not_live_long_enough(
487 err.buffer(&mut self.errors_buffer);
490 fn report_local_value_does_not_live_long_enough(
494 scope_tree: &Lrc<ScopeTree>,
495 borrow: &BorrowData<'tcx>,
497 borrow_spans: UseSpans,
498 kind_place: Option<(WriteKind, &Place<'tcx>)>,
499 ) -> DiagnosticBuilder<'cx> {
501 "report_local_value_does_not_live_long_enough(\
502 {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\
504 context, name, scope_tree, borrow, drop_span, borrow_spans
507 let borrow_span = borrow_spans.var_or_use();
508 let mut err = self.infcx.tcx.path_does_not_live_long_enough(
510 &format!("`{}`", name),
514 let explanation = self.explain_why_borrow_contains_point(context, borrow, kind_place);
515 if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
516 let region_name = annotation.emit(&mut err);
520 format!("`{}` would have to be valid for `{}`", name, region_name)
523 if let Some(fn_node_id) = self.infcx.tcx.hir.as_local_node_id(self.mir_def_id) {
527 "...but `{}` is only valid for the duration of the `{}` function, so it \
528 is dropped here while still borrowed",
530 self.infcx.tcx.hir.name(fn_node_id),
535 "functions cannot return a borrow to data owned within the function's scope, \
536 functions can only return borrows to data passed as arguments",
539 "to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-\
540 references-and-borrowing.html#dangling-references>",
545 format!("...but `{}` dropped here while still borrowed", name)
549 if let BorrowExplanation::MustBeValidFor(..) = explanation { } else {
550 explanation.emit(self.infcx.tcx, &mut err);
553 err.span_label(borrow_span, "borrowed value does not live long enough");
554 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
556 borrow_spans.args_span_label(&mut err, "value captured here");
558 explanation.emit(self.infcx.tcx, &mut err);
564 pub(super) fn report_borrow_conflicts_with_destructor(
567 borrow: &BorrowData<'tcx>,
568 (place, drop_span): (&Place<'tcx>, Span),
569 kind: Option<WriteKind>,
572 "report_borrow_conflicts_with_destructor(\
573 {:?}, {:?}, ({:?}, {:?}), {:?}\
575 context, borrow, place, drop_span, kind,
578 let borrow_spans = self.retrieve_borrow_spans(borrow);
579 let borrow_span = borrow_spans.var_or_use();
581 let mut err = self.infcx.tcx.cannot_borrow_across_destructor(borrow_span, Origin::Mir);
583 let (what_was_dropped, dropped_ty) = {
584 let desc = match self.describe_place(place) {
585 Some(name) => format!("`{}`", name.as_str()),
586 None => format!("temporary value"),
588 let ty = place.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx);
592 let label = match dropped_ty.sty {
593 ty::Adt(adt, _) if adt.has_dtor(self.infcx.tcx) && !adt.is_box() => {
594 match self.describe_place(&borrow.borrowed_place) {
596 format!("here, drop of {D} needs exclusive access to `{B}`, \
597 because the type `{T}` implements the `Drop` trait",
598 D=what_was_dropped, T=dropped_ty, B=borrowed),
600 format!("here is drop of {D}; whose type `{T}` implements the `Drop` trait",
601 D=what_was_dropped, T=dropped_ty),
604 _ => format!("drop of {D} occurs here", D=what_was_dropped),
606 err.span_label(drop_span, label);
608 // Only give this note and suggestion if they could be relevant.
609 let explanation = self.explain_why_borrow_contains_point(
610 context, borrow, kind.map(|k| (k, place)),
613 BorrowExplanation::UsedLater {..} |
614 BorrowExplanation::UsedLaterWhenDropped {..} => {
615 err.note("consider using a `let` binding to create a longer lived value");
620 explanation.emit(self.infcx.tcx, &mut err);
622 err.buffer(&mut self.errors_buffer);
625 fn report_thread_local_value_does_not_live_long_enough(
629 ) -> DiagnosticBuilder<'cx> {
631 "report_thread_local_value_does_not_live_long_enough(\
634 drop_span, borrow_span
637 let mut err = self.infcx.tcx.thread_local_value_does_not_live_long_enough(
638 borrow_span, Origin::Mir
643 "thread-local variables cannot be borrowed beyond the end of the function",
645 err.span_label(drop_span, "end of enclosing function is here");
650 fn report_temporary_value_does_not_live_long_enough(
653 scope_tree: &Lrc<ScopeTree>,
654 borrow: &BorrowData<'tcx>,
656 borrow_spans: UseSpans,
658 ) -> DiagnosticBuilder<'cx> {
660 "report_temporary_value_does_not_live_long_enough(\
661 {:?}, {:?}, {:?}, {:?}, {:?}\
663 context, scope_tree, borrow, drop_span, proper_span
666 let tcx = self.infcx.tcx;
668 tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
669 err.span_label(proper_span, "temporary value does not live long enough");
670 err.span_label(drop_span, "temporary value only lives until here");
672 let explanation = self.explain_why_borrow_contains_point(context, borrow, None);
674 BorrowExplanation::UsedLater(..) |
675 BorrowExplanation::UsedLaterInLoop(..) |
676 BorrowExplanation::UsedLaterWhenDropped(..) => {
677 // Only give this note and suggestion if it could be relevant.
678 err.note("consider using a `let` binding to create a longer lived value");
682 explanation.emit(self.infcx.tcx, &mut err);
684 borrow_spans.args_span_label(&mut err, "value captured here");
689 fn get_moved_indexes(&mut self, context: Context, mpi: MovePathIndex) -> Vec<MoveOutIndex> {
692 let mut stack = Vec::new();
693 stack.extend(mir.predecessor_locations(context.loc));
695 let mut visited = FxHashSet();
696 let mut result = vec![];
698 'dfs: while let Some(l) = stack.pop() {
700 "report_use_of_moved_or_uninitialized: current_location={:?}",
704 if !visited.insert(l) {
709 let stmt_kind = mir[l.block]
711 .get(l.statement_index)
713 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
714 // this analysis only tries to find moves explicitly
715 // written by the user, so we ignore the move-outs
716 // created by `StorageDead` and at the beginning
719 // If we are found a use of a.b.c which was in error, then we want to look for
720 // moves not only of a.b.c but also a.b and a.
722 // Note that the moves data already includes "parent" paths, so we don't have to
723 // worry about the other case: that is, if there is a move of a.b.c, it is already
724 // marked as a move of a.b and a as well, so we will generate the correct errors
726 let mut mpis = vec![mpi];
727 let move_paths = &self.move_data.move_paths;
728 mpis.extend(move_paths[mpi].parents(move_paths));
730 for moi in &self.move_data.loc_map[l] {
731 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
732 if mpis.contains(&self.move_data.moves[*moi].path) {
733 debug!("report_use_of_moved_or_uninitialized: found");
736 // Strictly speaking, we could continue our DFS here. There may be
737 // other moves that can reach the point of error. But it is kind of
738 // confusing to highlight them.
746 // drop(a); // <-- current point of error
749 // Because we stop the DFS here, we only highlight `let c = a`,
750 // and not `let b = a`. We will of course also report an error at
751 // `let c = a` which highlights `let b = a` as the move.
758 let mut any_match = false;
759 drop_flag_effects::for_location_inits(self.infcx.tcx, self.mir, self.move_data, l, |m| {
768 stack.extend(mir.predecessor_locations(l));
774 pub(super) fn report_illegal_mutation_of_borrowed(
777 (place, span): (&Place<'tcx>, Span),
778 loan: &BorrowData<'tcx>,
780 let loan_spans = self.retrieve_borrow_spans(loan);
781 let loan_span = loan_spans.args_or_use();
783 let tcx = self.infcx.tcx;
784 let mut err = tcx.cannot_assign_to_borrowed(
787 &self.describe_place(place).unwrap_or("_".to_owned()),
791 loan_spans.var_span_label(&mut err, "borrow occurs due to use in closure");
793 self.explain_why_borrow_contains_point(context, loan, None).emit(self.infcx.tcx, &mut err);
795 err.buffer(&mut self.errors_buffer);
798 /// Reports an illegal reassignment; for example, an assignment to
799 /// (part of) a non-`mut` local that occurs potentially after that
800 /// local has already been initialized. `place` is the path being
801 /// assigned; `err_place` is a place providing a reason why
802 /// `place` is not mutable (e.g. the non-`mut` local `x` in an
803 /// assignment to `x.f`).
804 pub(super) fn report_illegal_reassignment(
807 (place, span): (&Place<'tcx>, Span),
809 err_place: &Place<'tcx>,
811 let (from_arg, local_decl) = if let Place::Local(local) = *err_place {
812 if let LocalKind::Arg = self.mir.local_kind(local) {
813 (true, Some(&self.mir.local_decls[local]))
815 (false, Some(&self.mir.local_decls[local]))
821 // If root local is initialized immediately (everything apart from let
822 // PATTERN;) then make the error refer to that local, rather than the
823 // place being assigned later.
824 let (place_description, assigned_span) = match local_decl {
826 is_user_variable: Some(ClearCrossCrate::Clear),
831 Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
832 opt_match_place: None,
838 is_user_variable: None,
841 | None => (self.describe_place(place), assigned_span),
842 Some(decl) => (self.describe_place(err_place), decl.source_info.span),
845 let mut err = self.infcx.tcx.cannot_reassign_immutable(
847 place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"),
851 let msg = if from_arg {
852 "cannot assign to immutable argument"
854 "cannot assign twice to immutable variable"
856 if span != assigned_span {
858 let value_msg = match place_description {
859 Some(name) => format!("`{}`", name),
860 None => "value".to_owned(),
862 err.span_label(assigned_span, format!("first assignment to {}", value_msg));
865 if let Some(decl) = local_decl {
866 if let Some(name) = decl.name {
867 if decl.can_be_made_mutable() {
868 err.span_suggestion_with_applicability(
869 decl.source_info.span,
870 "make this binding mutable",
871 format!("mut {}", name),
872 Applicability::MachineApplicable,
877 err.span_label(span, msg);
878 err.buffer(&mut self.errors_buffer);
882 pub(super) struct IncludingDowncast(bool);
884 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
885 // End-user visible description of `place` if one can be found. If the
886 // place is a temporary for instance, None will be returned.
887 pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
888 self.describe_place_with_options(place, IncludingDowncast(false))
891 // End-user visible description of `place` if one can be found. If the
892 // place is a temporary for instance, None will be returned.
893 // `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
894 // `Downcast` and `IncludingDowncast` is true
895 pub(super) fn describe_place_with_options(
898 including_downcast: IncludingDowncast,
899 ) -> Option<String> {
900 let mut buf = String::new();
901 match self.append_place_to_string(place, &mut buf, false, &including_downcast) {
907 // Appends end-user visible description of `place` to `buf`.
908 fn append_place_to_string(
913 including_downcast: &IncludingDowncast,
914 ) -> Result<(), ()> {
916 Place::Promoted(_) => {
917 buf.push_str("promoted");
919 Place::Local(local) => {
920 self.append_local_to_string(local, buf)?;
922 Place::Static(ref static_) => {
923 buf.push_str(&self.infcx.tcx.item_name(static_.def_id).to_string());
925 Place::Projection(ref proj) => {
927 ProjectionElem::Deref => {
928 let upvar_field_projection =
929 place.is_upvar_field_projection(self.mir, &self.infcx.tcx);
930 if let Some(field) = upvar_field_projection {
931 let var_index = field.index();
932 let name = self.mir.upvar_decls[var_index].debug_name.to_string();
933 if self.mir.upvar_decls[var_index].by_ref {
936 buf.push_str(&format!("*{}", &name));
940 self.append_place_to_string(
946 } else if let Place::Local(local) = proj.base {
947 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) =
948 self.mir.local_decls[local].is_user_variable
950 self.append_place_to_string(
958 self.append_place_to_string(
967 self.append_place_to_string(
976 ProjectionElem::Downcast(..) => {
977 self.append_place_to_string(
983 if including_downcast.0 {
987 ProjectionElem::Field(field, _ty) => {
990 let upvar_field_projection =
991 place.is_upvar_field_projection(self.mir, &self.infcx.tcx);
992 if let Some(field) = upvar_field_projection {
993 let var_index = field.index();
994 let name = self.mir.upvar_decls[var_index].debug_name.to_string();
997 let field_name = self.describe_field(&proj.base, field);
998 self.append_place_to_string(
1002 &including_downcast,
1004 buf.push_str(&format!(".{}", field_name));
1007 ProjectionElem::Index(index) => {
1010 self.append_place_to_string(
1014 &including_downcast,
1017 if self.append_local_to_string(index, buf).is_err() {
1022 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
1024 // Since it isn't possible to borrow an element on a particular index and
1025 // then use another while the borrow is held, don't output indices details
1026 // to avoid confusing the end-user
1027 self.append_place_to_string(
1031 &including_downcast,
1033 buf.push_str(&"[..]");
1042 // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
1043 // a name, then `Err` is returned
1044 fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
1045 let local = &self.mir.local_decls[local_index];
1048 buf.push_str(&name.to_string());
1055 // End-user visible description of the `field`nth field of `base`
1056 fn describe_field(&self, base: &Place, field: Field) -> String {
1058 Place::Local(local) => {
1059 let local = &self.mir.local_decls[local];
1060 self.describe_field_from_ty(&local.ty, field)
1062 Place::Promoted(ref prom) => self.describe_field_from_ty(&prom.1, field),
1063 Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field),
1064 Place::Projection(ref proj) => match proj.elem {
1065 ProjectionElem::Deref => self.describe_field(&proj.base, field),
1066 ProjectionElem::Downcast(def, variant_index) => format!(
1068 def.variants[variant_index].fields[field.index()].ident
1070 ProjectionElem::Field(_, field_type) => {
1071 self.describe_field_from_ty(&field_type, field)
1073 ProjectionElem::Index(..)
1074 | ProjectionElem::ConstantIndex { .. }
1075 | ProjectionElem::Subslice { .. } => {
1076 self.describe_field(&proj.base, field).to_string()
1082 // End-user visible description of the `field_index`nth field of `ty`
1083 fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
1085 // If the type is a box, the field is described from the boxed type
1086 self.describe_field_from_ty(&ty.boxed_ty(), field)
1089 ty::Adt(def, _) => if def.is_enum() {
1090 field.index().to_string()
1092 def.non_enum_variant().fields[field.index()]
1096 ty::Tuple(_) => field.index().to_string(),
1097 ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
1098 self.describe_field_from_ty(&ty, field)
1100 ty::Array(ty, _) | ty::Slice(ty) => self.describe_field_from_ty(&ty, field),
1101 ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
1102 // Convert the def-id into a node-id. node-ids are only valid for
1103 // the local code in the current crate, so this returns an `Option` in case
1104 // the closure comes from another crate. But in that case we wouldn't
1105 // be borrowck'ing it, so we can just unwrap:
1106 let node_id = self.infcx.tcx.hir.as_local_node_id(def_id).unwrap();
1107 let freevar = self.infcx.tcx.with_freevars(node_id, |fv| fv[field.index()]);
1109 self.infcx.tcx.hir.name(freevar.var_id()).to_string()
1112 // Might need a revision when the fields in trait RFC is implemented
1113 // (https://github.com/rust-lang/rfcs/pull/1546)
1115 "End-user description not implemented for field access on `{:?}`",
1123 // Retrieve type of a place for the current MIR representation
1124 fn retrieve_type_for_place(&self, place: &Place<'tcx>) -> Option<ty::Ty> {
1126 Place::Local(local) => {
1127 let local = &self.mir.local_decls[*local];
1130 Place::Promoted(ref prom) => Some(prom.1),
1131 Place::Static(ref st) => Some(st.ty),
1132 Place::Projection(ref proj) => match proj.elem {
1133 ProjectionElem::Field(_, ty) => Some(ty),
1139 /// Check if a place is a thread-local static.
1140 pub fn is_place_thread_local(&self, place: &Place<'tcx>) -> bool {
1141 if let Place::Static(statik) = place {
1142 let attrs = self.infcx.tcx.get_attrs(statik.def_id);
1143 let is_thread_local = attrs.iter().any(|attr| attr.check_name("thread_local"));
1146 "is_place_thread_local: attrs={:?} is_thread_local={:?}",
1147 attrs, is_thread_local
1151 debug!("is_place_thread_local: no");
1156 /// Returns the `FakeReadCause` at this location if it is a `FakeRead` statement.
1157 pub(super) fn retrieve_fake_read_cause_for_location(
1159 location: &Location,
1160 ) -> Option<FakeReadCause> {
1161 let stmt = self.mir.basic_blocks()[location.block]
1163 .get(location.statement_index)?;
1164 if let StatementKind::FakeRead(cause, _) = stmt.kind {
1171 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
1172 /// borrow of local value that does not live long enough.
1173 fn annotate_argument_and_return_for_borrow(
1175 borrow: &BorrowData<'tcx>,
1176 ) -> Option<AnnotatedBorrowFnSignature> {
1177 // There are two cases that need handled: when a closure is involved and
1178 // when a closure is not involved.
1179 let location = borrow.reserve_location;
1180 let is_closure = self.infcx.tcx.is_closure(self.mir_def_id);
1182 match self.mir[location.block].statements.get(location.statement_index) {
1183 // When a closure is involved, we expect the reserve location to be an assignment
1184 // to a temporary local, which will be followed by a closure.
1186 kind: StatementKind::Assign(Place::Local(local), _),
1188 }) if self.mir.local_kind(local) == LocalKind::Temp => {
1189 // Look for the statements within this block after assigning to a local to see
1190 // if we have a closure. If we do, then annotate it.
1191 for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
1192 if let StatementKind::Assign(
1195 box AggregateKind::Closure(def_id, substs),
1199 return self.annotate_fn_sig(
1201 self.infcx.closure_sig(def_id, substs)
1209 // If this is not the case, then return if we're currently on a closure (as we
1210 // don't have a substs to get the PolyFnSig) or attempt to get the arguments
1211 // and return type of the function.
1215 let ty = self.infcx.tcx.type_of(self.mir_def_id);
1217 ty::TyKind::FnDef(_, _) | ty::TyKind::FnPtr(_) =>
1218 self.annotate_fn_sig(
1220 self.infcx.tcx.fn_sig(self.mir_def_id)
1227 /// Annotate the first argument and return type of a function signature if they are
1232 sig: ty::PolyFnSig<'tcx>,
1233 ) -> Option<AnnotatedBorrowFnSignature> {
1234 let is_closure = self.infcx.tcx.is_closure(did);
1235 let fn_node_id = self.infcx.tcx.hir.as_local_node_id(did)?;
1236 let fn_decl = self.infcx.tcx.hir.fn_decl(fn_node_id)?;
1238 // If there is one argument and this error is being reported, that means
1239 // that the lifetime of the borrow could not be made to match the single
1240 // argument's lifetime. We can highlight it.
1242 // If there is more than one argument and this error is being reported, that
1243 // means there must be a self parameter - as otherwise there would be an error
1244 // from lifetime elision and not this. So we highlight the self parameter.
1245 let argument_span = fn_decl.inputs.first()?.span;
1246 let argument_ty = sig.inputs().skip_binder().first()?;
1248 // Closure arguments are wrapped in a tuple, so we need to get the first
1250 let argument_ty = if let ty::TyKind::Tuple(elems) = argument_ty.sty {
1251 let argument_ty = elems.first()?;
1252 if let ty::TyKind::Ref(_, _, _) = argument_ty.sty {
1261 Some(AnnotatedBorrowFnSignature::Closure {
1265 } else if let ty::TyKind::Ref(argument_region, _, _) = argument_ty.sty {
1266 // We only consider the return type for functions.
1267 let return_span = fn_decl.output.span();
1269 let return_ty = sig.output();
1270 let (return_region, return_ty) = if let ty::TyKind::Ref(
1272 ) = return_ty.skip_binder().sty {
1273 (return_region, *return_ty.skip_binder())
1278 Some(AnnotatedBorrowFnSignature::Function {
1283 regions_equal: return_region == argument_region,
1292 enum AnnotatedBorrowFnSignature<'tcx> {
1294 argument_ty: ty::Ty<'tcx>,
1295 argument_span: Span,
1296 return_ty: ty::Ty<'tcx>,
1298 regions_equal: bool,
1301 argument_ty: ty::Ty<'tcx>,
1302 argument_span: Span,
1306 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
1309 diag: &mut DiagnosticBuilder<'_>
1311 let (argument_ty, argument_span) = match self {
1312 AnnotatedBorrowFnSignature::Function {
1316 } => (argument_ty, argument_span),
1317 AnnotatedBorrowFnSignature::Closure {
1320 } => (argument_ty, argument_span),
1323 let (argument_region_name, argument_ty_name) = (
1324 self.get_region_name_for_ty(argument_ty, 0),
1325 self.get_name_for_ty(argument_ty, 0),
1329 format!("has type `{}`", argument_ty_name)
1332 // Only emit labels for the return value when we're annotating a function.
1333 if let AnnotatedBorrowFnSignature::Function {
1339 let counter = if *regions_equal { 0 } else { 1 };
1340 let (return_region_name, return_ty_name) = (
1341 self.get_region_name_for_ty(return_ty, counter),
1342 self.get_name_for_ty(return_ty, counter)
1345 let types_equal = return_ty_name == argument_ty_name;
1350 if types_equal { "also " } else { "" },
1357 argument_region_name
1361 /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
1362 /// name where required.
1363 fn get_name_for_ty(&self, ty: ty::Ty<'tcx>, counter: usize) -> String {
1364 // We need to add synthesized lifetimes where appropriate. We do
1365 // this by hooking into the pretty printer and telling it to label the
1366 // lifetimes without names with the value `'0`.
1368 ty::TyKind::Ref(ty::RegionKind::ReLateBound(_, br), _, _) |
1369 ty::TyKind::Ref(ty::RegionKind::ReSkolemized(_, br), _, _) =>
1370 with_highlight_region_for_bound_region(*br, counter, || format!("{}", ty)),
1371 _ => format!("{}", ty),
1375 /// Return the name of the provided `Ty` (that must be a reference)'s region with a
1376 /// synthesized lifetime name where required.
1377 fn get_region_name_for_ty(&self, ty: ty::Ty<'tcx>, counter: usize) -> String {
1379 ty::TyKind::Ref(region, _, _) => match region {
1380 ty::RegionKind::ReLateBound(_, br) |
1381 ty::RegionKind::ReSkolemized(_, br) =>
1382 with_highlight_region_for_bound_region(*br, counter, || format!("{}", region)),
1383 _ => format!("{}", region),
1385 _ => bug!("ty for annotation of borrow region is not a reference"),
1390 // The span(s) associated to a use of a place.
1391 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1392 pub(super) enum UseSpans {
1393 // The access is caused by capturing a variable for a closure.
1395 // The span of the args of the closure, including the `move` keyword if
1398 // The span of the first use of the captured variable inside the closure.
1401 // This access has a single span associated to it: common case.
1406 pub(super) fn args_or_use(self) -> Span {
1408 UseSpans::ClosureUse {
1411 | UseSpans::OtherUse(span) => span,
1415 pub(super) fn var_or_use(self) -> Span {
1417 UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span,
1421 // Add a span label to the arguments of the closure, if it exists.
1422 pub(super) fn args_span_label(self, err: &mut DiagnosticBuilder, message: impl Into<String>) {
1423 if let UseSpans::ClosureUse { args_span, .. } = self {
1424 err.span_label(args_span, message);
1428 // Add a span label to the use of the captured variable, if it exists.
1429 pub(super) fn var_span_label(self, err: &mut DiagnosticBuilder, message: impl Into<String>) {
1430 if let UseSpans::ClosureUse { var_span, .. } = self {
1431 err.span_label(var_span, message);
1435 pub(super) fn for_closure(self) -> bool {
1437 UseSpans::ClosureUse { .. } => true,
1438 UseSpans::OtherUse(_) => false,
1442 pub(super) fn or_else<F>(self, if_other: F) -> Self
1444 F: FnOnce() -> Self,
1447 closure @ UseSpans::ClosureUse { .. } => closure,
1448 UseSpans::OtherUse(_) => if_other(),
1453 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1454 /// Finds the spans associated to a move or copy of move_place at location.
1455 pub(super) fn move_spans(
1457 moved_place: &Place<'tcx>, // Could also be an upvar.
1460 use self::UseSpans::*;
1461 use rustc::hir::ExprKind::Closure;
1462 use rustc::mir::AggregateKind;
1464 let stmt = match self.mir[location.block]
1466 .get(location.statement_index)
1469 None => return OtherUse(self.mir.source_info(location).span),
1472 if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
1473 if let AggregateKind::Closure(def_id, _) = **kind {
1474 debug!("find_closure_move_span: found closure {:?}", places);
1476 if let Some(node_id) = self.infcx.tcx.hir.as_local_node_id(def_id) {
1478 _, _, _, args_span, _
1479 ) = self.infcx.tcx.hir.expect_expr(node_id).node {
1480 if let Some(var_span) = self.infcx.tcx.with_freevars(node_id, |freevars| {
1481 for (v, place) in freevars.iter().zip(places) {
1483 Operand::Copy(place) | Operand::Move(place)
1484 if moved_place == place =>
1487 "find_closure_move_span: found captured local {:?}",
1490 return Some(v.span);
1507 return OtherUse(stmt.source_info.span);
1510 /// Finds the span of arguments of a closure (within `maybe_closure_span`)
1511 /// and its usage of the local assigned at `location`.
1512 /// This is done by searching in statements succeeding `location`
1513 /// and originating from `maybe_closure_span`.
1514 pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
1515 use self::UseSpans::*;
1516 use rustc::hir::ExprKind::Closure;
1518 let local = match self.mir[location.block]
1520 .get(location.statement_index)
1523 kind: StatementKind::Assign(Place::Local(local), _),
1526 _ => return OtherUse(use_span),
1529 if self.mir.local_kind(local) != LocalKind::Temp {
1530 // operands are always temporaries.
1531 return OtherUse(use_span);
1534 for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
1535 if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
1536 if let AggregateKind::Closure(def_id, _) = **kind {
1537 debug!("find_closure_borrow_span: found closure {:?}", places);
1539 return if let Some(node_id) = self.infcx.tcx.hir.as_local_node_id(def_id) {
1540 let args_span = if let Closure(_, _, _, span, _) =
1541 self.infcx.tcx.hir.expect_expr(node_id).node
1545 return OtherUse(use_span);
1549 .with_freevars(node_id, |freevars| {
1550 for (v, place) in freevars.iter().zip(places) {
1552 Operand::Copy(Place::Local(l))
1553 | Operand::Move(Place::Local(l))
1557 "find_closure_borrow_span: found captured local \
1561 return Some(v.span);
1567 }).map(|var_span| ClosureUse {
1570 }).unwrap_or(OtherUse(use_span))
1577 if use_span != stmt.source_info.span {
1585 /// Helper to retrieve span(s) of given borrow from the current MIR
1587 pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData) -> UseSpans {
1588 let span = self.mir.source_info(borrow.reserve_location).span;
1589 self.borrow_spans(span, borrow.reserve_location)