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 BindingForm, BorrowKind, ClearCrossCrate, Field, FakeReadCause, Local,
17 LocalDecl, LocalKind, Location, Operand, Place,
18 ProjectionElem, Rvalue, Statement, StatementKind,
22 use rustc_data_structures::fx::FxHashSet;
23 use rustc_data_structures::sync::Lrc;
24 use rustc_errors::{Applicability, DiagnosticBuilder};
27 use super::borrow_set::BorrowData;
28 use super::{Context, MirBorrowckCtxt};
29 use super::{InitializationRequiringAction, PrefixSet};
31 use dataflow::drop_flag_effects;
32 use dataflow::move_paths::indexes::MoveOutIndex;
33 use dataflow::move_paths::MovePathIndex;
34 use util::borrowck_errors::{BorrowckErrors, Origin};
36 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
37 pub(super) fn report_use_of_moved_or_uninitialized(
40 desired_action: InitializationRequiringAction,
41 (place, span): (&Place<'tcx>, Span),
45 "report_use_of_moved_or_uninitialized: context={:?} desired_action={:?} place={:?} \
47 context, desired_action, place, span, mpi
51 .move_spans(place, context.loc)
52 .or_else(|| self.borrow_spans(span, context.loc));
53 let span = use_spans.args_or_use();
55 let mois = self.get_moved_indexes(context, mpi);
56 debug!("report_use_of_moved_or_uninitialized: mois={:?}", mois);
59 let root_place = self.prefixes(&place, PrefixSet::All).last().unwrap();
61 if self.uninitialized_error_reported.contains(&root_place.clone()) {
63 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
69 self.uninitialized_error_reported.insert(root_place.clone());
71 let item_msg = match self.describe_place_with_options(place, IncludingDowncast(true)) {
72 Some(name) => format!("`{}`", name),
73 None => "value".to_owned(),
75 let mut err = self.tcx.cannot_act_on_uninitialized_variable(
77 desired_action.as_noun(),
79 .describe_place_with_options(place, IncludingDowncast(true))
80 .unwrap_or("_".to_owned()),
83 err.span_label(span, format!("use of possibly uninitialized {}", item_msg));
85 use_spans.var_span_label(
87 format!("{} occurs due to use in closure", desired_action.as_noun()),
90 err.buffer(&mut self.errors_buffer);
92 if let Some((reported_place, _)) = self.move_error_reported.get(&mois) {
93 if self.prefixes(&reported_place, PrefixSet::All).any(|p| p == place) {
94 debug!("report_use_of_moved_or_uninitialized place: error suppressed \
100 let msg = ""; //FIXME: add "partially " or "collaterally "
102 let mut err = self.tcx.cannot_act_on_moved_value(
104 desired_action.as_noun(),
106 self.describe_place_with_options(&place, IncludingDowncast(true)),
110 let mut is_loop_move = false;
112 let move_out = self.move_data.moves[*moi];
113 let moved_place = &self.move_data.move_paths[move_out.path].place;
115 let move_spans = self.move_spans(moved_place, move_out.source);
116 let move_span = move_spans.args_or_use();
118 let move_msg = if move_spans.for_closure() {
124 if span == move_span {
127 format!("value moved{} here in previous iteration of loop", move_msg),
131 err.span_label(move_span, format!("value moved{} here", move_msg));
132 move_spans.var_span_label(&mut err, "variable moved due to use in closure");
136 use_spans.var_span_label(
138 format!("{} occurs due to use in closure", desired_action.as_noun()),
145 "value {} here after move",
146 desired_action.as_verb_in_past_tense()
151 if let Some(ty) = self.retrieve_type_for_place(place) {
152 let needs_note = match ty.sty {
153 ty::Closure(id, _) => {
154 let tables = self.tcx.typeck_tables_of(id);
155 let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
156 let hir_id = self.tcx.hir.node_to_hir_id(node_id);
157 if tables.closure_kind_origins().get(hir_id).is_some() {
167 let mpi = self.move_data.moves[mois[0]].path;
168 let place = &self.move_data.move_paths[mpi].place;
170 if let Some(ty) = self.retrieve_type_for_place(place) {
171 let note_msg = match self
172 .describe_place_with_options(place, IncludingDowncast(true))
174 Some(name) => format!("`{}`", name),
175 None => "value".to_owned(),
179 "move occurs because {} has type `{}`, \
180 which does not implement the `Copy` trait",
187 if let Some((_, mut old_err)) = self.move_error_reported.insert(
191 // Cancel the old error so it doesn't ICE.
197 pub(super) fn report_move_out_while_borrowed(
200 (place, _span): (&Place<'tcx>, Span),
201 borrow: &BorrowData<'tcx>,
204 let value_msg = match self.describe_place(place) {
205 Some(name) => format!("`{}`", name),
206 None => "value".to_owned(),
208 let borrow_msg = match self.describe_place(&borrow.borrowed_place) {
209 Some(name) => format!("`{}`", name),
210 None => "value".to_owned(),
213 let borrow_spans = self.retrieve_borrow_spans(borrow);
214 let borrow_span = borrow_spans.args_or_use();
216 let move_spans = self.move_spans(place, context.loc);
217 let span = move_spans.args_or_use();
219 let mut err = tcx.cannot_move_when_borrowed(
221 &self.describe_place(place).unwrap_or("_".to_owned()),
224 err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
225 err.span_label(span, format!("move out of {} occurs here", value_msg));
227 borrow_spans.var_span_label(&mut err, "borrow occurs due to use in closure");
229 move_spans.var_span_label(&mut err, "move occurs due to use in closure");
231 self.explain_why_borrow_contains_point(context, borrow, None).emit(self.tcx, &mut err);
232 err.buffer(&mut self.errors_buffer);
235 pub(super) fn report_use_while_mutably_borrowed(
238 (place, _span): (&Place<'tcx>, Span),
239 borrow: &BorrowData<'tcx>,
243 let borrow_spans = self.retrieve_borrow_spans(borrow);
244 let borrow_span = borrow_spans.args_or_use();
246 // Conflicting borrows are reported separately, so only check for move
248 let use_spans = self.move_spans(place, context.loc);
249 let span = use_spans.var_or_use();
251 let mut err = tcx.cannot_use_when_mutably_borrowed(
253 &self.describe_place(place).unwrap_or("_".to_owned()),
256 .describe_place(&borrow.borrowed_place)
257 .unwrap_or("_".to_owned()),
261 borrow_spans.var_span_label(&mut err, {
262 let place = &borrow.borrowed_place;
263 let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
265 format!("borrow occurs due to use of `{}` in closure", desc_place)
268 self.explain_why_borrow_contains_point(context, borrow, None).emit(self.tcx, &mut err);
269 err.buffer(&mut self.errors_buffer);
272 pub(super) fn report_conflicting_borrow(
275 (place, span): (&Place<'tcx>, Span),
276 gen_borrow_kind: BorrowKind,
277 issued_borrow: &BorrowData<'tcx>,
279 let issued_spans = self.retrieve_borrow_spans(issued_borrow);
280 let issued_span = issued_spans.args_or_use();
282 let borrow_spans = self.borrow_spans(span, context.loc);
283 let span = borrow_spans.args_or_use();
285 let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
288 // FIXME: supply non-"" `opt_via` when appropriate
289 let mut err = match (
297 (BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt)
298 | (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => tcx
299 .cannot_reborrow_already_borrowed(
312 (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => tcx
313 .cannot_mutably_borrow_multiply(
323 (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => tcx
324 .cannot_uniquely_borrow_by_two_closures(
332 (BorrowKind::Unique, _, _, _, _, _) => tcx.cannot_uniquely_borrow_by_one_closure(
343 (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => tcx
344 .cannot_reborrow_already_uniquely_borrowed(
355 (BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => tcx
356 .cannot_reborrow_already_uniquely_borrowed(
367 (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(),
370 if issued_spans == borrow_spans {
371 borrow_spans.var_span_label(
373 format!("borrows occur due to use of `{}` in closure", desc_place),
376 let borrow_place = &issued_borrow.borrowed_place;
377 let borrow_place_desc = self.describe_place(borrow_place).unwrap_or("_".to_owned());
378 issued_spans.var_span_label(
381 "first borrow occurs due to use of `{}` in closure",
386 borrow_spans.var_span_label(
389 "second borrow occurs due to use of `{}` in closure",
395 self.explain_why_borrow_contains_point(context, issued_borrow, None)
396 .emit(self.tcx, &mut err);
398 err.buffer(&mut self.errors_buffer);
401 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
403 /// This means that some data referenced by `borrow` needs to live
404 /// past the point where the StorageDeadOrDrop of `place` occurs.
405 /// This is usually interpreted as meaning that `place` has too
406 /// short a lifetime. (But sometimes it is more useful to report
407 /// it as a more direct conflict between the execution of a
408 /// `Drop::drop` with an aliasing borrow.)
409 pub(super) fn report_borrowed_value_does_not_live_long_enough(
412 borrow: &BorrowData<'tcx>,
413 place_span: (&Place<'tcx>, Span),
414 kind: Option<WriteKind>,
416 debug!("report_borrowed_value_does_not_live_long_enough(\
417 {:?}, {:?}, {:?}, {:?}\
419 context, borrow, place_span, kind
422 let drop_span = place_span.1;
423 let scope_tree = self.tcx.region_scope_tree(self.mir_def_id);
424 let root_place = self
425 .prefixes(&borrow.borrowed_place, PrefixSet::All)
429 let borrow_spans = self.retrieve_borrow_spans(borrow);
430 let borrow_span = borrow_spans.var_or_use();
432 let proper_span = match *root_place {
433 Place::Local(local) => self.mir.local_decls[local].source_info.span,
438 .access_place_error_reported
439 .contains(&(root_place.clone(), borrow_span))
442 "suppressing access_place error when borrow doesn't live long enough for {:?}",
448 self.access_place_error_reported
449 .insert((root_place.clone(), borrow_span));
451 if let Some(WriteKind::StorageDeadOrDrop(StorageDeadOrDrop::Destructor)) = kind {
452 // If a borrow of path `B` conflicts with drop of `D` (and
453 // we're not in the uninteresting case where `B` is a
454 // prefix of `D`), then report this as a more interesting
455 // destructor conflict.
456 if !borrow.borrowed_place.is_prefix_of(place_span.0) {
457 self.report_borrow_conflicts_with_destructor(context, borrow, place_span, kind);
462 let mut err = match &self.describe_place(&borrow.borrowed_place) {
463 Some(_) if self.is_place_thread_local(root_place) => {
464 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span)
466 Some(name) => self.report_local_value_does_not_live_long_enough(
473 kind.map(|k| (k, place_span.0)),
475 None => self.report_temporary_value_does_not_live_long_enough(
484 borrow_spans.args_span_label(&mut err, "value captured here");
486 err.buffer(&mut self.errors_buffer);
489 fn report_local_value_does_not_live_long_enough(
493 scope_tree: &Lrc<ScopeTree>,
494 borrow: &BorrowData<'tcx>,
497 kind_place: Option<(WriteKind, &Place<'tcx>)>,
498 ) -> DiagnosticBuilder<'cx> {
500 "report_local_value_does_not_live_long_enough(\
501 {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\
503 context, name, scope_tree, borrow, drop_span, borrow_span
506 let mut err = self.tcx.path_does_not_live_long_enough(
508 &format!("`{}`", name),
512 err.span_label(borrow_span, "borrowed value does not live long enough");
515 format!("`{}` dropped here while still borrowed", name),
518 self.explain_why_borrow_contains_point(context, borrow, kind_place)
519 .emit(self.tcx, &mut err);
524 pub(super) fn report_borrow_conflicts_with_destructor(
527 borrow: &BorrowData<'tcx>,
528 (place, drop_span): (&Place<'tcx>, Span),
529 kind: Option<WriteKind>,
532 "report_borrow_conflicts_with_destructor(\
533 {:?}, {:?}, ({:?}, {:?}), {:?}\
535 context, borrow, place, drop_span, kind,
538 let borrow_spans = self.retrieve_borrow_spans(borrow);
539 let borrow_span = borrow_spans.var_or_use();
541 let mut err = self.tcx.cannot_borrow_across_destructor(borrow_span, Origin::Mir);
543 let (what_was_dropped, dropped_ty) = {
544 let desc = match self.describe_place(place) {
545 Some(name) => format!("`{}`", name.as_str()),
546 None => format!("temporary value"),
548 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
552 let label = match dropped_ty.sty {
553 ty::Adt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() => {
554 match self.describe_place(&borrow.borrowed_place) {
556 format!("here, drop of {D} needs exclusive access to `{B}`, \
557 because the type `{T}` implements the `Drop` trait",
558 D=what_was_dropped, T=dropped_ty, B=borrowed),
560 format!("here is drop of {D}; whose type `{T}` implements the `Drop` trait",
561 D=what_was_dropped, T=dropped_ty),
564 _ => format!("drop of {D} occurs here", D=what_was_dropped),
566 err.span_label(drop_span, label);
568 // Only give this note and suggestion if they could be relevant.
569 let explanation = self.explain_why_borrow_contains_point(
570 context, borrow, kind.map(|k| (k, place)),
573 BorrowExplanation::UsedLater {..} |
574 BorrowExplanation::UsedLaterWhenDropped {..} => {
575 err.note("consider using a `let` binding to create a longer lived value");
580 explanation.emit(self.tcx, &mut err);
582 err.buffer(&mut self.errors_buffer);
585 fn report_thread_local_value_does_not_live_long_enough(
589 ) -> DiagnosticBuilder<'cx> {
591 "report_thread_local_value_does_not_live_long_enough(\
594 drop_span, borrow_span
599 .thread_local_value_does_not_live_long_enough(borrow_span, Origin::Mir);
603 "thread-local variables cannot be borrowed beyond the end of the function",
605 err.span_label(drop_span, "end of enclosing function is here");
610 fn report_temporary_value_does_not_live_long_enough(
613 scope_tree: &Lrc<ScopeTree>,
614 borrow: &BorrowData<'tcx>,
617 ) -> DiagnosticBuilder<'cx> {
619 "report_temporary_value_does_not_live_long_enough(\
620 {:?}, {:?}, {:?}, {:?}, {:?}\
622 context, scope_tree, borrow, drop_span, proper_span
627 tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
628 err.span_label(proper_span, "temporary value does not live long enough");
629 err.span_label(drop_span, "temporary value only lives until here");
631 let explanation = self.explain_why_borrow_contains_point(context, borrow, None);
633 BorrowExplanation::UsedLater(..) |
634 BorrowExplanation::UsedLaterInLoop(..) |
635 BorrowExplanation::UsedLaterWhenDropped(..) => {
636 // Only give this note and suggestion if it could be relevant.
637 err.note("consider using a `let` binding to create a longer lived value");
641 explanation.emit(self.tcx, &mut err);
646 fn get_moved_indexes(&mut self, context: Context, mpi: MovePathIndex) -> Vec<MoveOutIndex> {
649 let mut stack = Vec::new();
650 stack.extend(mir.predecessor_locations(context.loc));
652 let mut visited = FxHashSet();
653 let mut result = vec![];
655 'dfs: while let Some(l) = stack.pop() {
657 "report_use_of_moved_or_uninitialized: current_location={:?}",
661 if !visited.insert(l) {
666 let stmt_kind = mir[l.block]
668 .get(l.statement_index)
670 if let Some(StatementKind::StorageDead(..)) = stmt_kind {
671 // this analysis only tries to find moves explicitly
672 // written by the user, so we ignore the move-outs
673 // created by `StorageDead` and at the beginning
676 // If we are found a use of a.b.c which was in error, then we want to look for
677 // moves not only of a.b.c but also a.b and a.
679 // Note that the moves data already includes "parent" paths, so we don't have to
680 // worry about the other case: that is, if there is a move of a.b.c, it is already
681 // marked as a move of a.b and a as well, so we will generate the correct errors
683 let mut mpis = vec![mpi];
684 let move_paths = &self.move_data.move_paths;
685 mpis.extend(move_paths[mpi].parents(move_paths));
687 for moi in &self.move_data.loc_map[l] {
688 debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
689 if mpis.contains(&self.move_data.moves[*moi].path) {
690 debug!("report_use_of_moved_or_uninitialized: found");
693 // Strictly speaking, we could continue our DFS here. There may be
694 // other moves that can reach the point of error. But it is kind of
695 // confusing to highlight them.
703 // drop(a); // <-- current point of error
706 // Because we stop the DFS here, we only highlight `let c = a`,
707 // and not `let b = a`. We will of course also report an error at
708 // `let c = a` which highlights `let b = a` as the move.
715 let mut any_match = false;
716 drop_flag_effects::for_location_inits(self.tcx, self.mir, self.move_data, l, |m| {
725 stack.extend(mir.predecessor_locations(l));
731 pub(super) fn report_illegal_mutation_of_borrowed(
734 (place, span): (&Place<'tcx>, Span),
735 loan: &BorrowData<'tcx>,
737 let loan_spans = self.retrieve_borrow_spans(loan);
738 let loan_span = loan_spans.args_or_use();
741 let mut err = tcx.cannot_assign_to_borrowed(
744 &self.describe_place(place).unwrap_or("_".to_owned()),
748 loan_spans.var_span_label(&mut err, "borrow occurs due to use in closure");
750 self.explain_why_borrow_contains_point(context, loan, None).emit(self.tcx, &mut err);
752 err.buffer(&mut self.errors_buffer);
755 /// Reports an illegal reassignment; for example, an assignment to
756 /// (part of) a non-`mut` local that occurs potentially after that
757 /// local has already been initialized. `place` is the path being
758 /// assigned; `err_place` is a place providing a reason why
759 /// `place` is not mutable (e.g. the non-`mut` local `x` in an
760 /// assignment to `x.f`).
761 pub(super) fn report_illegal_reassignment(
764 (place, span): (&Place<'tcx>, Span),
766 err_place: &Place<'tcx>,
768 let (from_arg, local_decl) = if let Place::Local(local) = *err_place {
769 if let LocalKind::Arg = self.mir.local_kind(local) {
770 (true, Some(&self.mir.local_decls[local]))
772 (false, Some(&self.mir.local_decls[local]))
778 // If root local is initialized immediately (everything apart from let
779 // PATTERN;) then make the error refer to that local, rather than the
780 // place being assigned later.
781 let (place_description, assigned_span) = match local_decl {
783 is_user_variable: Some(ClearCrossCrate::Clear),
788 Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
789 opt_match_place: None,
795 is_user_variable: None,
798 | None => (self.describe_place(place), assigned_span),
799 Some(decl) => (self.describe_place(err_place), decl.source_info.span),
802 let mut err = self.tcx.cannot_reassign_immutable(
804 place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"),
808 let msg = if from_arg {
809 "cannot assign to immutable argument"
811 "cannot assign twice to immutable variable"
813 if span != assigned_span {
815 let value_msg = match place_description {
816 Some(name) => format!("`{}`", name),
817 None => "value".to_owned(),
819 err.span_label(assigned_span, format!("first assignment to {}", value_msg));
822 if let Some(decl) = local_decl {
823 if let Some(name) = decl.name {
824 if decl.can_be_made_mutable() {
825 err.span_suggestion_with_applicability(
826 decl.source_info.span,
827 "make this binding mutable",
828 format!("mut {}", name),
829 Applicability::MachineApplicable,
834 err.span_label(span, msg);
835 err.buffer(&mut self.errors_buffer);
839 pub(super) struct IncludingDowncast(bool);
841 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
842 // End-user visible description of `place` if one can be found. If the
843 // place is a temporary for instance, None will be returned.
844 pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
845 self.describe_place_with_options(place, IncludingDowncast(false))
848 // End-user visible description of `place` if one can be found. If the
849 // place is a temporary for instance, None will be returned.
850 // `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
851 // `Downcast` and `IncludingDowncast` is true
852 pub(super) fn describe_place_with_options(
855 including_downcast: IncludingDowncast,
856 ) -> Option<String> {
857 let mut buf = String::new();
858 match self.append_place_to_string(place, &mut buf, false, &including_downcast) {
864 // Appends end-user visible description of `place` to `buf`.
865 fn append_place_to_string(
870 including_downcast: &IncludingDowncast,
871 ) -> Result<(), ()> {
873 Place::Promoted(_) => {
874 buf.push_str("promoted");
876 Place::Local(local) => {
877 self.append_local_to_string(local, buf)?;
879 Place::Static(ref static_) => {
880 buf.push_str(&self.tcx.item_name(static_.def_id).to_string());
882 Place::Projection(ref proj) => {
884 ProjectionElem::Deref => {
885 let upvar_field_projection =
886 place.is_upvar_field_projection(self.mir, &self.tcx);
887 if let Some(field) = upvar_field_projection {
888 let var_index = field.index();
889 let name = self.mir.upvar_decls[var_index].debug_name.to_string();
890 if self.mir.upvar_decls[var_index].by_ref {
893 buf.push_str(&format!("*{}", &name));
897 self.append_place_to_string(
903 } else if let Place::Local(local) = proj.base {
904 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) =
905 self.mir.local_decls[local].is_user_variable
907 self.append_place_to_string(
915 self.append_place_to_string(
924 self.append_place_to_string(
933 ProjectionElem::Downcast(..) => {
934 self.append_place_to_string(
940 if including_downcast.0 {
944 ProjectionElem::Field(field, _ty) => {
947 let upvar_field_projection =
948 place.is_upvar_field_projection(self.mir, &self.tcx);
949 if let Some(field) = upvar_field_projection {
950 let var_index = field.index();
951 let name = self.mir.upvar_decls[var_index].debug_name.to_string();
954 let field_name = self.describe_field(&proj.base, field);
955 self.append_place_to_string(
961 buf.push_str(&format!(".{}", field_name));
964 ProjectionElem::Index(index) => {
967 self.append_place_to_string(
974 if self.append_local_to_string(index, buf).is_err() {
979 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
981 // Since it isn't possible to borrow an element on a particular index and
982 // then use another while the borrow is held, don't output indices details
983 // to avoid confusing the end-user
984 self.append_place_to_string(
990 buf.push_str(&"[..]");
999 // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
1000 // a name, then `Err` is returned
1001 fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
1002 let local = &self.mir.local_decls[local_index];
1005 buf.push_str(&name.to_string());
1012 // End-user visible description of the `field`nth field of `base`
1013 fn describe_field(&self, base: &Place, field: Field) -> String {
1015 Place::Local(local) => {
1016 let local = &self.mir.local_decls[local];
1017 self.describe_field_from_ty(&local.ty, field)
1019 Place::Promoted(ref prom) => self.describe_field_from_ty(&prom.1, field),
1020 Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field),
1021 Place::Projection(ref proj) => match proj.elem {
1022 ProjectionElem::Deref => self.describe_field(&proj.base, field),
1023 ProjectionElem::Downcast(def, variant_index) => format!(
1025 def.variants[variant_index].fields[field.index()].ident
1027 ProjectionElem::Field(_, field_type) => {
1028 self.describe_field_from_ty(&field_type, field)
1030 ProjectionElem::Index(..)
1031 | ProjectionElem::ConstantIndex { .. }
1032 | ProjectionElem::Subslice { .. } => {
1033 self.describe_field(&proj.base, field).to_string()
1039 // End-user visible description of the `field_index`nth field of `ty`
1040 fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
1042 // If the type is a box, the field is described from the boxed type
1043 self.describe_field_from_ty(&ty.boxed_ty(), field)
1046 ty::Adt(def, _) => if def.is_enum() {
1047 field.index().to_string()
1049 def.non_enum_variant().fields[field.index()]
1053 ty::Tuple(_) => field.index().to_string(),
1054 ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
1055 self.describe_field_from_ty(&ty, field)
1057 ty::Array(ty, _) | ty::Slice(ty) => self.describe_field_from_ty(&ty, field),
1058 ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
1059 // Convert the def-id into a node-id. node-ids are only valid for
1060 // the local code in the current crate, so this returns an `Option` in case
1061 // the closure comes from another crate. But in that case we wouldn't
1062 // be borrowck'ing it, so we can just unwrap:
1063 let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
1064 let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
1066 self.tcx.hir.name(freevar.var_id()).to_string()
1069 // Might need a revision when the fields in trait RFC is implemented
1070 // (https://github.com/rust-lang/rfcs/pull/1546)
1072 "End-user description not implemented for field access on `{:?}`",
1080 // Retrieve type of a place for the current MIR representation
1081 fn retrieve_type_for_place(&self, place: &Place<'tcx>) -> Option<ty::Ty> {
1083 Place::Local(local) => {
1084 let local = &self.mir.local_decls[*local];
1087 Place::Promoted(ref prom) => Some(prom.1),
1088 Place::Static(ref st) => Some(st.ty),
1089 Place::Projection(ref proj) => match proj.elem {
1090 ProjectionElem::Field(_, ty) => Some(ty),
1096 /// Check if a place is a thread-local static.
1097 pub fn is_place_thread_local(&self, place: &Place<'tcx>) -> bool {
1098 if let Place::Static(statik) = place {
1099 let attrs = self.tcx.get_attrs(statik.def_id);
1100 let is_thread_local = attrs.iter().any(|attr| attr.check_name("thread_local"));
1103 "is_place_thread_local: attrs={:?} is_thread_local={:?}",
1104 attrs, is_thread_local
1108 debug!("is_place_thread_local: no");
1113 /// Returns the `FakeReadCause` at this location if it is a `FakeRead` statement.
1114 pub(super) fn retrieve_fake_read_cause_for_location(
1116 location: &Location,
1117 ) -> Option<FakeReadCause> {
1118 let stmt = self.mir.basic_blocks()[location.block]
1120 .get(location.statement_index)?;
1121 if let StatementKind::FakeRead(cause, _) = stmt.kind {
1129 // The span(s) associated to a use of a place.
1130 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1131 pub(super) enum UseSpans {
1132 // The access is caused by capturing a variable for a closure.
1134 // The span of the args of the closure, including the `move` keyword if
1137 // The span of the first use of the captured variable inside the closure.
1140 // This access has a single span associated to it: common case.
1145 pub(super) fn args_or_use(self) -> Span {
1147 UseSpans::ClosureUse {
1150 | UseSpans::OtherUse(span) => span,
1154 pub(super) fn var_or_use(self) -> Span {
1156 UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span,
1160 // Add a span label to the arguments of the closure, if it exists.
1161 pub(super) fn args_span_label(self, err: &mut DiagnosticBuilder, message: impl Into<String>) {
1162 if let UseSpans::ClosureUse { args_span, .. } = self {
1163 err.span_label(args_span, message);
1167 // Add a span label to the use of the captured variable, if it exists.
1168 pub(super) fn var_span_label(self, err: &mut DiagnosticBuilder, message: impl Into<String>) {
1169 if let UseSpans::ClosureUse { var_span, .. } = self {
1170 err.span_label(var_span, message);
1174 pub(super) fn for_closure(self) -> bool {
1176 UseSpans::ClosureUse { .. } => true,
1177 UseSpans::OtherUse(_) => false,
1181 pub(super) fn or_else<F>(self, if_other: F) -> Self
1183 F: FnOnce() -> Self,
1186 closure @ UseSpans::ClosureUse { .. } => closure,
1187 UseSpans::OtherUse(_) => if_other(),
1192 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1193 /// Finds the spans associated to a move or copy of move_place at location.
1194 pub(super) fn move_spans(
1196 moved_place: &Place<'tcx>, // Could also be an upvar.
1199 use self::UseSpans::*;
1200 use rustc::hir::ExprKind::Closure;
1201 use rustc::mir::AggregateKind;
1203 let stmt = match self.mir[location.block]
1205 .get(location.statement_index)
1208 None => return OtherUse(self.mir.source_info(location).span),
1211 if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
1212 if let AggregateKind::Closure(def_id, _) = **kind {
1213 debug!("find_closure_move_span: found closure {:?}", places);
1215 if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
1216 if let Closure(_, _, _, args_span, _) = self.tcx.hir.expect_expr(node_id).node {
1217 if let Some(var_span) = self.tcx.with_freevars(node_id, |freevars| {
1218 for (v, place) in freevars.iter().zip(places) {
1220 Operand::Copy(place) | Operand::Move(place)
1221 if moved_place == place =>
1224 "find_closure_move_span: found captured local {:?}",
1227 return Some(v.span);
1244 return OtherUse(stmt.source_info.span);
1247 /// Finds the span of arguments of a closure (within `maybe_closure_span`)
1248 /// and its usage of the local assigned at `location`.
1249 /// This is done by searching in statements succeeding `location`
1250 /// and originating from `maybe_closure_span`.
1251 pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
1252 use self::UseSpans::*;
1253 use rustc::hir::ExprKind::Closure;
1254 use rustc::mir::AggregateKind;
1256 let local = match self.mir[location.block]
1258 .get(location.statement_index)
1261 kind: StatementKind::Assign(Place::Local(local), _),
1264 _ => return OtherUse(use_span),
1267 if self.mir.local_kind(local) != LocalKind::Temp {
1268 // operands are always temporaries.
1269 return OtherUse(use_span);
1272 for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
1273 if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
1274 if let AggregateKind::Closure(def_id, _) = **kind {
1275 debug!("find_closure_borrow_span: found closure {:?}", places);
1277 return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
1278 let args_span = if let Closure(_, _, _, span, _) =
1279 self.tcx.hir.expect_expr(node_id).node
1283 return OtherUse(use_span);
1287 .with_freevars(node_id, |freevars| {
1288 for (v, place) in freevars.iter().zip(places) {
1290 Operand::Copy(Place::Local(l))
1291 | Operand::Move(Place::Local(l))
1295 "find_closure_borrow_span: found captured local \
1299 return Some(v.span);
1305 }).map(|var_span| ClosureUse {
1308 }).unwrap_or(OtherUse(use_span))
1315 if use_span != stmt.source_info.span {
1323 /// Helper to retrieve span(s) of given borrow from the current MIR
1325 pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData) -> UseSpans {
1326 let span = self.mir.source_info(borrow.reserve_location).span;
1327 self.borrow_spans(span, borrow.reserve_location)