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;
13 use rustc::middle::region::ScopeTree;
14 use rustc::mir::{BorrowKind, Field, Local, LocalKind, Location, Operand};
15 use rustc::mir::{Place, ProjectionElem, Rvalue, Statement, StatementKind};
16 use rustc::ty::{self, RegionKind};
17 use rustc_data_structures::indexed_vec::Idx;
18 use rustc_data_structures::sync::Lrc;
20 use super::{Context, MirBorrowckCtxt};
21 use super::{InitializationRequiringAction, PrefixSet};
22 use super::borrow_set::BorrowData;
24 use dataflow::{FlowAtLocation, MovingOutStatements};
25 use dataflow::move_paths::MovePathIndex;
26 use util::borrowck_errors::{BorrowckErrors, Origin};
28 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
29 pub(super) fn report_use_of_moved_or_uninitialized(
32 desired_action: InitializationRequiringAction,
33 (place, span): (&Place<'tcx>, Span),
35 curr_move_out: &FlowAtLocation<MovingOutStatements<'_, 'gcx, 'tcx>>,
37 let mois = self.move_data.path_map[mpi]
39 .filter(|moi| curr_move_out.contains(moi))
43 let root_place = self.prefixes(&place, PrefixSet::All)
47 if self.moved_error_reported
48 .contains(&root_place.clone())
51 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
57 self.moved_error_reported
58 .insert(root_place.clone());
60 let item_msg = match self.describe_place(place) {
61 Some(name) => format!("`{}`", name),
62 None => "value".to_owned(),
65 .cannot_act_on_uninitialized_variable(
67 desired_action.as_noun(),
68 &self.describe_place(place).unwrap_or("_".to_owned()),
71 .span_label(span, format!("use of possibly uninitialized {}", item_msg))
74 let msg = ""; //FIXME: add "partially " or "collaterally "
76 let mut err = self.tcx.cannot_act_on_moved_value(
78 desired_action.as_noun(),
80 &self.describe_place(place).unwrap_or("_".to_owned()),
84 let mut is_loop_move = false;
86 let move_msg = ""; //FIXME: add " (into closure)"
87 let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span;
88 if span == move_span {
91 format!("value moved{} here in previous iteration of loop", move_msg),
95 err.span_label(move_span, format!("value moved{} here", move_msg));
102 "value {} here after move",
103 desired_action.as_verb_in_past_tense()
108 if let Some(ty) = self.retrieve_type_for_place(place) {
109 let needs_note = match ty.sty {
110 ty::TypeVariants::TyClosure(id, _) => {
111 let tables = self.tcx.typeck_tables_of(id);
112 let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
113 let hir_id = self.tcx.hir.node_to_hir_id(node_id);
114 if let Some(_) = tables.closure_kind_origins().get(hir_id) {
124 let note_msg = match self.describe_place(place) {
125 Some(name) => format!("`{}`", name),
126 None => "value".to_owned(),
130 "move occurs because {} has type `{}`, \
131 which does not implement the `Copy` trait",
141 pub(super) fn report_move_out_while_borrowed(
144 (place, span): (&Place<'tcx>, Span),
145 borrow: &BorrowData<'tcx>,
148 let value_msg = match self.describe_place(place) {
149 Some(name) => format!("`{}`", name),
150 None => "value".to_owned(),
152 let borrow_msg = match self.describe_place(&borrow.borrowed_place) {
153 Some(name) => format!("`{}`", name),
154 None => "value".to_owned(),
156 let mut err = tcx.cannot_move_when_borrowed(
158 &self.describe_place(place).unwrap_or("_".to_owned()),
162 self.retrieve_borrow_span(borrow),
163 format!("borrow of {} occurs here", borrow_msg),
165 err.span_label(span, format!("move out of {} occurs here", value_msg));
166 self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
170 pub(super) fn report_use_while_mutably_borrowed(
173 (place, span): (&Place<'tcx>, Span),
174 borrow: &BorrowData<'tcx>,
177 let mut err = tcx.cannot_use_when_mutably_borrowed(
179 &self.describe_place(place).unwrap_or("_".to_owned()),
180 self.retrieve_borrow_span(borrow),
181 &self.describe_place(&borrow.borrowed_place)
182 .unwrap_or("_".to_owned()),
186 self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
191 /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
192 /// the local assigned at `location`.
193 /// This is done by searching in statements succeeding `location`
194 /// and originating from `maybe_closure_span`.
195 fn find_closure_span(
197 maybe_closure_span: Span,
199 ) -> Option<(Span, Span)> {
200 use rustc::hir::ExprClosure;
201 use rustc::mir::AggregateKind;
203 let local = match self.mir[location.block]
205 .get(location.statement_index)
208 kind: StatementKind::Assign(Place::Local(local), _),
214 for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
215 if maybe_closure_span != stmt.source_info.span {
219 if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
220 if let AggregateKind::Closure(def_id, _) = **kind {
221 debug!("find_closure_span: found closure {:?}", places);
223 return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
224 let args_span = if let ExprClosure(_, _, _, span, _) =
225 self.tcx.hir.expect_expr(node_id).node
233 .with_freevars(node_id, |freevars| {
234 for (v, place) in freevars.iter().zip(places) {
236 Operand::Copy(Place::Local(l))
237 | Operand::Move(Place::Local(l)) if local == l =>
240 "find_closure_span: found captured local {:?}",
250 .map(|var_span| (args_span, var_span))
261 pub(super) fn report_conflicting_borrow(
264 (place, span): (&Place<'tcx>, Span),
265 gen_borrow_kind: BorrowKind,
266 issued_borrow: &BorrowData<'tcx>,
268 let issued_span = self.retrieve_borrow_span(issued_borrow);
270 let new_closure_span = self.find_closure_span(span, context.loc);
271 let span = new_closure_span.map(|(args, _)| args).unwrap_or(span);
272 let old_closure_span = self.find_closure_span(issued_span, issued_borrow.reserve_location);
273 let issued_span = old_closure_span
274 .map(|(args, _)| args)
275 .unwrap_or(issued_span);
277 let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
280 // FIXME: supply non-"" `opt_via` when appropriate
281 let mut err = match (
289 (BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt)
290 | (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => {
291 tcx.cannot_reborrow_already_borrowed(
305 (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => {
306 tcx.cannot_mutably_borrow_multiply(
317 (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => {
318 tcx.cannot_uniquely_borrow_by_two_closures(
327 (BorrowKind::Unique, _, _, _, _, _) => tcx.cannot_uniquely_borrow_by_one_closure(
338 (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => {
339 tcx.cannot_reborrow_already_uniquely_borrowed(
351 (BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => {
352 tcx.cannot_reborrow_already_uniquely_borrowed(
364 (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(),
367 if let Some((_, var_span)) = old_closure_span {
371 "previous borrow occurs due to use of `{}` in closure",
377 if let Some((_, var_span)) = new_closure_span {
380 format!("borrow occurs due to use of `{}` in closure", desc_place),
384 self.explain_why_borrow_contains_point(context, issued_borrow, None, &mut err);
389 pub(super) fn report_borrowed_value_does_not_live_long_enough(
392 borrow: &BorrowData<'tcx>,
393 place_span: (&Place<'tcx>, Span),
394 kind: Option<WriteKind>,
396 let drop_span = place_span.1;
397 let scope_tree = self.tcx.region_scope_tree(self.mir_def_id);
398 let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All)
402 let borrow_span = self.mir.source_info(borrow.reserve_location).span;
403 let proper_span = match *root_place {
404 Place::Local(local) => self.mir.local_decls[local].source_info.span,
408 if self.access_place_error_reported
409 .contains(&(root_place.clone(), borrow_span))
412 "suppressing access_place error when borrow doesn't live long enough for {:?}",
418 self.access_place_error_reported
419 .insert((root_place.clone(), borrow_span));
421 match (borrow.region, &self.describe_place(&borrow.borrowed_place)) {
422 (RegionKind::ReScope(_), Some(name)) => {
423 self.report_scoped_local_value_does_not_live_long_enough(
433 (RegionKind::ReScope(_), None) => {
434 self.report_scoped_temporary_value_does_not_live_long_enough(
443 (RegionKind::ReEarlyBound(_), Some(name))
444 | (RegionKind::ReFree(_), Some(name))
445 | (RegionKind::ReStatic, Some(name))
446 | (RegionKind::ReEmpty, Some(name))
447 | (RegionKind::ReVar(_), Some(name)) => {
448 self.report_unscoped_local_value_does_not_live_long_enough(
456 kind.map(|k| (k, place_span.0)),
459 (RegionKind::ReEarlyBound(_), None)
460 | (RegionKind::ReFree(_), None)
461 | (RegionKind::ReStatic, None)
462 | (RegionKind::ReEmpty, None)
463 | (RegionKind::ReVar(_), None) => {
464 self.report_unscoped_temporary_value_does_not_live_long_enough(
473 (RegionKind::ReLateBound(_, _), _)
474 | (RegionKind::ReSkolemized(_, _), _)
475 | (RegionKind::ReClosureBound(_), _)
476 | (RegionKind::ReCanonical(_), _)
477 | (RegionKind::ReErased, _) => {
478 span_bug!(drop_span, "region {:?} does not make sense in this context",
484 fn report_scoped_local_value_does_not_live_long_enough(
488 _scope_tree: &Lrc<ScopeTree>,
489 borrow: &BorrowData<'tcx>,
496 tcx.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name), Origin::Mir);
497 err.span_label(borrow_span, "borrowed value does not live long enough");
500 format!("`{}` dropped here while still borrowed", name),
502 self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
506 fn report_scoped_temporary_value_does_not_live_long_enough(
509 _scope_tree: &Lrc<ScopeTree>,
510 borrow: &BorrowData<'tcx>,
517 tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
518 err.span_label(proper_span, "temporary value does not live long enough");
521 "temporary value dropped here while still borrowed",
523 err.note("consider using a `let` binding to increase its lifetime");
524 self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
528 fn report_unscoped_local_value_does_not_live_long_enough(
532 scope_tree: &Lrc<ScopeTree>,
533 borrow: &BorrowData<'tcx>,
537 kind_place: Option<(WriteKind, &Place<'tcx>)>,
540 "report_unscoped_local_value_does_not_live_long_enough(\
541 {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\
543 context, name, scope_tree, borrow, drop_span, borrow_span
548 tcx.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name), Origin::Mir);
549 err.span_label(borrow_span, "borrowed value does not live long enough");
550 err.span_label(drop_span, "borrowed value only lives until here");
552 self.explain_why_borrow_contains_point(context, borrow, kind_place, &mut err);
556 fn report_unscoped_temporary_value_does_not_live_long_enough(
559 scope_tree: &Lrc<ScopeTree>,
560 borrow: &BorrowData<'tcx>,
566 "report_unscoped_temporary_value_does_not_live_long_enough(\
567 {:?}, {:?}, {:?}, {:?}, {:?}\
569 context, scope_tree, borrow, drop_span, proper_span
574 tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
575 err.span_label(proper_span, "temporary value does not live long enough");
576 err.span_label(drop_span, "temporary value only lives until here");
578 self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
582 pub(super) fn report_illegal_mutation_of_borrowed(
585 (place, span): (&Place<'tcx>, Span),
586 loan: &BorrowData<'tcx>,
589 let mut err = tcx.cannot_assign_to_borrowed(
591 self.retrieve_borrow_span(loan),
592 &self.describe_place(place).unwrap_or("_".to_owned()),
596 self.explain_why_borrow_contains_point(context, loan, None, &mut err);
601 /// Reports an illegal reassignment; for example, an assignment to
602 /// (part of) a non-`mut` local that occurs potentially after that
603 /// local has already been initialized. `place` is the path being
604 /// assigned; `err_place` is a place providing a reason why
605 /// `place` is not mutable (e.g. the non-`mut` local `x` in an
606 /// assignment to `x.f`).
607 pub(super) fn report_illegal_reassignment(
610 (place, span): (&Place<'tcx>, Span),
612 err_place: &Place<'tcx>,
614 let is_arg = if let Place::Local(local) = place {
615 if let LocalKind::Arg = self.mir.local_kind(*local) {
624 let mut err = self.tcx.cannot_reassign_immutable(
626 &self.describe_place(place).unwrap_or("_".to_owned()),
630 let msg = if is_arg {
631 "cannot assign to immutable argument"
633 "cannot assign twice to immutable variable"
635 if span != assigned_span {
637 let value_msg = match self.describe_place(place) {
638 Some(name) => format!("`{}`", name),
639 None => "value".to_owned(),
641 err.span_label(assigned_span, format!("first assignment to {}", value_msg));
644 if let Place::Local(local) = err_place {
645 let local_decl = &self.mir.local_decls[*local];
646 if let Some(name) = local_decl.name {
647 if local_decl.can_be_made_mutable() {
648 err.span_label(local_decl.source_info.span,
649 format!("consider changing this to `mut {}`", name));
653 err.span_label(span, msg);
658 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
659 // End-user visible description of `place` if one can be found. If the
660 // place is a temporary for instance, None will be returned.
661 pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
662 let mut buf = String::new();
663 match self.append_place_to_string(place, &mut buf, false) {
669 // Appends end-user visible description of `place` to `buf`.
670 fn append_place_to_string(
675 ) -> Result<(), ()> {
677 Place::Local(local) => {
678 self.append_local_to_string(local, buf)?;
680 Place::Static(ref static_) => {
681 buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id)));
683 Place::Projection(ref proj) => {
685 ProjectionElem::Deref => {
686 if let Some(field) = self.is_upvar_field_projection(&proj.base) {
687 let var_index = field.index();
688 let name = self.mir.upvar_decls[var_index].debug_name.to_string();
689 if self.mir.upvar_decls[var_index].by_ref {
692 buf.push_str(&format!("*{}", &name));
696 self.append_place_to_string(&proj.base, buf, autoderef)?;
699 self.append_place_to_string(&proj.base, buf, autoderef)?;
703 ProjectionElem::Downcast(..) => {
704 self.append_place_to_string(&proj.base, buf, autoderef)?;
706 ProjectionElem::Field(field, _ty) => {
709 if let Some(field) = self.is_upvar_field_projection(place) {
710 let var_index = field.index();
711 let name = self.mir.upvar_decls[var_index].debug_name.to_string();
714 let field_name = self.describe_field(&proj.base, field);
715 self.append_place_to_string(&proj.base, buf, autoderef)?;
716 buf.push_str(&format!(".{}", field_name));
719 ProjectionElem::Index(index) => {
722 self.append_place_to_string(&proj.base, buf, autoderef)?;
724 if let Err(_) = self.append_local_to_string(index, buf) {
729 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
731 // Since it isn't possible to borrow an element on a particular index and
732 // then use another while the borrow is held, don't output indices details
733 // to avoid confusing the end-user
734 self.append_place_to_string(&proj.base, buf, autoderef)?;
735 buf.push_str(&"[..]");
744 // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
745 // a name, then `Err` is returned
746 fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
747 let local = &self.mir.local_decls[local_index];
750 buf.push_str(&format!("{}", name));
757 // End-user visible description of the `field`nth field of `base`
758 fn describe_field(&self, base: &Place, field: Field) -> String {
760 Place::Local(local) => {
761 let local = &self.mir.local_decls[local];
762 self.describe_field_from_ty(&local.ty, field)
764 Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field),
765 Place::Projection(ref proj) => match proj.elem {
766 ProjectionElem::Deref => self.describe_field(&proj.base, field),
767 ProjectionElem::Downcast(def, variant_index) => {
768 format!("{}", def.variants[variant_index].fields[field.index()].ident)
770 ProjectionElem::Field(_, field_type) => {
771 self.describe_field_from_ty(&field_type, field)
773 ProjectionElem::Index(..)
774 | ProjectionElem::ConstantIndex { .. }
775 | ProjectionElem::Subslice { .. } => {
776 format!("{}", self.describe_field(&proj.base, field))
782 // End-user visible description of the `field_index`nth field of `ty`
783 fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
785 // If the type is a box, the field is described from the boxed type
786 self.describe_field_from_ty(&ty.boxed_ty(), field)
789 ty::TyAdt(def, _) => if def.is_enum() {
790 format!("{}", field.index())
792 format!("{}", def.non_enum_variant().fields[field.index()].ident)
794 ty::TyTuple(_) => format!("{}", field.index()),
795 ty::TyRef(_, ty, _) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => {
796 self.describe_field_from_ty(&ty, field)
798 ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field),
799 ty::TyClosure(def_id, _) | ty::TyGenerator(def_id, _, _) => {
800 // Convert the def-id into a node-id. node-ids are only valid for
801 // the local code in the current crate, so this returns an `Option` in case
802 // the closure comes from another crate. But in that case we wouldn't
803 // be borrowck'ing it, so we can just unwrap:
804 let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
805 let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
807 self.tcx.hir.name(freevar.var_id()).to_string()
810 // Might need a revision when the fields in trait RFC is implemented
811 // (https://github.com/rust-lang/rfcs/pull/1546)
813 "End-user description not implemented for field access on `{:?}`",
821 // Retrieve span of given borrow from the current MIR representation
822 crate fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span {
823 self.mir.source_info(borrow.reserve_location).span
826 // Retrieve type of a place for the current MIR representation
827 fn retrieve_type_for_place(&self, place: &Place<'tcx>) -> Option<ty::Ty> {
829 Place::Local(local) => {
830 let local = &self.mir.local_decls[*local];
833 Place::Static(ref st) => Some(st.ty),
834 Place::Projection(ref proj) => match proj.elem {
835 ProjectionElem::Field(_, ty) => Some(ty),