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.
12 use rustc::middle::region::ScopeTree;
13 use rustc::mir::{BorrowKind, Field, Local, Location, Operand};
14 use rustc::mir::{Place, ProjectionElem, Rvalue, Statement, StatementKind};
15 use rustc::ty::{self, RegionKind};
16 use rustc_data_structures::indexed_vec::Idx;
20 use super::{MirBorrowckCtxt, Context};
21 use super::{InitializationRequiringAction, PrefixSet};
22 use dataflow::{ActiveBorrows, BorrowData, FlowAtLocation, MovingOutStatements};
23 use dataflow::move_paths::MovePathIndex;
24 use util::borrowck_errors::{BorrowckErrors, Origin};
26 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
27 pub(super) fn report_use_of_moved_or_uninitialized(
30 desired_action: InitializationRequiringAction,
31 (place, span): (&Place<'tcx>, Span),
33 curr_move_out: &FlowAtLocation<MovingOutStatements<'_, 'gcx, 'tcx>>,
35 let mois = self.move_data.path_map[mpi]
37 .filter(|moi| curr_move_out.contains(moi))
41 let item_msg = match self.describe_place(place) {
42 Some(name) => format!("`{}`", name),
43 None => "value".to_owned(),
46 .cannot_act_on_uninitialized_variable(
48 desired_action.as_noun(),
49 &self.describe_place(place).unwrap_or("_".to_owned()),
52 .span_label(span, format!("use of possibly uninitialized {}", item_msg))
55 let msg = ""; //FIXME: add "partially " or "collaterally "
57 let mut err = self.tcx.cannot_act_on_moved_value(
59 desired_action.as_noun(),
61 &self.describe_place(place).unwrap_or("_".to_owned()),
65 let mut is_loop_move = false;
67 let move_msg = ""; //FIXME: add " (into closure)"
68 let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span;
69 if span == move_span {
72 format!("value moved{} here in previous iteration of loop", move_msg),
76 err.span_label(move_span, format!("value moved{} here", move_msg));
83 "value {} here after move",
84 desired_action.as_verb_in_past_tense()
89 if let Some(ty) = self.retrieve_type_for_place(place) {
90 let needs_note = match ty.sty {
91 ty::TypeVariants::TyClosure(id, _) => {
92 let tables = self.tcx.typeck_tables_of(id);
93 let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
94 let hir_id = self.tcx.hir.node_to_hir_id(node_id);
95 if let Some(_) = tables.closure_kind_origins().get(hir_id) {
105 let note_msg = match self.describe_place(place) {
106 Some(name) => format!("`{}`", name),
107 None => "value".to_owned(),
110 err.note(&format!("move occurs because {} has type `{}`, \
111 which does not implement the `Copy` trait",
120 pub(super) fn report_move_out_while_borrowed(
123 (place, span): (&Place<'tcx>, Span),
124 borrow: &BorrowData<'tcx>,
126 let value_msg = match self.describe_place(place) {
127 Some(name) => format!("`{}`", name),
128 None => "value".to_owned(),
130 let borrow_msg = match self.describe_place(&borrow.borrowed_place) {
131 Some(name) => format!("`{}`", name),
132 None => "value".to_owned(),
134 let mut err = self.tcx.cannot_move_when_borrowed(
136 &self.describe_place(place).unwrap_or("_".to_owned()),
140 self.retrieve_borrow_span(borrow),
141 format!("borrow of {} occurs here", borrow_msg),
143 err.span_label(span, format!("move out of {} occurs here", value_msg));
144 self.explain_why_borrow_contains_point(context, borrow, &mut err);
148 pub(super) fn report_use_while_mutably_borrowed(
151 (place, span): (&Place<'tcx>, Span),
152 borrow: &BorrowData<'tcx>,
154 let mut err = self.tcx.cannot_use_when_mutably_borrowed(
156 &self.describe_place(place).unwrap_or("_".to_owned()),
157 self.retrieve_borrow_span(borrow),
158 &self.describe_place(&borrow.borrowed_place).unwrap_or("_".to_owned()),
162 self.explain_why_borrow_contains_point(context, borrow, &mut err);
167 /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
168 /// the local assigned at `location`.
169 /// This is done by searching in statements succeeding `location`
170 /// and originating from `maybe_closure_span`.
171 fn find_closure_span(
173 maybe_closure_span: Span,
175 ) -> Option<(Span, Span)> {
176 use rustc::hir::ExprClosure;
177 use rustc::mir::AggregateKind;
179 let local = match self.mir[location.block].statements.get(location.statement_index) {
180 Some(&Statement { kind: StatementKind::Assign(Place::Local(local), _), .. }) => local,
184 for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
185 if maybe_closure_span != stmt.source_info.span {
189 if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
190 if let AggregateKind::Closure(def_id, _) = **kind {
191 debug!("find_closure_span: found closure {:?}", places);
193 return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
194 let args_span = if let ExprClosure(_, _, _, span, _) =
195 self.tcx.hir.expect_expr(node_id).node
203 .with_freevars(node_id, |freevars| {
204 for (v, place) in freevars.iter().zip(places) {
206 Operand::Copy(Place::Local(l)) |
207 Operand::Move(Place::Local(l)) if local == l =>
210 "find_closure_span: found captured local {:?}",
220 .map(|var_span| (args_span, var_span))
231 pub(super) fn report_conflicting_borrow(
234 (place, span): (&Place<'tcx>, Span),
235 gen_borrow_kind: BorrowKind,
236 issued_borrow: &BorrowData,
237 end_issued_loan_span: Option<Span>,
239 let issued_span = self.retrieve_borrow_span(issued_borrow);
241 let new_closure_span = self.find_closure_span(span, context.loc);
242 let span = new_closure_span.map(|(args, _)| args).unwrap_or(span);
243 let old_closure_span = self.find_closure_span(issued_span, issued_borrow.location);
244 let issued_span = old_closure_span
245 .map(|(args, _)| args)
246 .unwrap_or(issued_span);
248 let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
250 // FIXME: supply non-"" `opt_via` when appropriate
251 let mut err = match (
259 (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) |
260 (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) => self.tcx
261 .cannot_reborrow_already_borrowed(
270 end_issued_loan_span,
274 (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) => self.tcx
275 .cannot_mutably_borrow_multiply(
281 end_issued_loan_span,
285 (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => self.tcx
286 .cannot_uniquely_borrow_by_two_closures(
290 end_issued_loan_span,
294 (BorrowKind::Unique, _, _, _, _, _) => self.tcx.cannot_uniquely_borrow_by_one_closure(
301 end_issued_loan_span,
305 (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => self.tcx
306 .cannot_reborrow_already_uniquely_borrowed(
313 end_issued_loan_span,
317 (BorrowKind::Mut, _, lft, BorrowKind::Unique, _, _) => self.tcx
318 .cannot_reborrow_already_uniquely_borrowed(
325 end_issued_loan_span,
329 (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(),
332 if let Some((_, var_span)) = old_closure_span {
336 "previous borrow occurs due to use of `{}` in closure",
342 if let Some((_, var_span)) = new_closure_span {
345 format!("borrow occurs due to use of `{}` in closure", desc_place),
349 self.explain_why_borrow_contains_point(context, issued_borrow, &mut err);
354 pub(super) fn report_borrowed_value_does_not_live_long_enough(
357 borrow: &BorrowData<'tcx>,
359 borrows: &ActiveBorrows<'cx, 'gcx, 'tcx>
361 let end_span = borrows.opt_region_end_span(&borrow.region);
362 let scope_tree = borrows.0.scope_tree();
363 let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All).last().unwrap();
366 &Place::Local(local) => {
367 if let Some(_) = self.storage_dead_or_drop_error_reported_l.replace(local) {
368 debug!("report_does_not_live_long_enough({:?}): <suppressed>",
369 (borrow, drop_span));
373 &Place::Static(ref statik) => {
374 if let Some(_) = self.storage_dead_or_drop_error_reported_s
375 .replace(statik.def_id)
377 debug!("report_does_not_live_long_enough({:?}): <suppressed>",
378 (borrow, drop_span));
382 &Place::Projection(_) =>
383 unreachable!("root_place is an unreachable???")
386 let borrow_span = self.mir.source_info(borrow.location).span;
387 let proper_span = match *root_place {
388 Place::Local(local) => self.mir.local_decls[local].source_info.span,
392 match (borrow.region, &self.describe_place(&borrow.borrowed_place)) {
393 (RegionKind::ReScope(_), Some(name)) => {
394 self.report_scoped_local_value_does_not_live_long_enough(
405 (RegionKind::ReScope(_), None) => {
406 self.report_scoped_temporary_value_does_not_live_long_enough(
416 (RegionKind::ReEarlyBound(_), Some(name)) |
417 (RegionKind::ReFree(_), Some(name)) |
418 (RegionKind::ReStatic, Some(name)) |
419 (RegionKind::ReEmpty, Some(name)) |
420 (RegionKind::ReVar(_), Some(name)) => {
421 self.report_unscoped_local_value_does_not_live_long_enough(
432 (RegionKind::ReEarlyBound(_), None) |
433 (RegionKind::ReFree(_), None) |
434 (RegionKind::ReStatic, None) |
435 (RegionKind::ReEmpty, None) |
436 (RegionKind::ReVar(_), None) => {
437 self.report_unscoped_temporary_value_does_not_live_long_enough(
447 (RegionKind::ReLateBound(_, _), _) |
448 (RegionKind::ReSkolemized(_, _), _) |
449 (RegionKind::ReClosureBound(_), _) |
450 (RegionKind::ReErased, _) => {
451 span_bug!(drop_span, "region does not make sense in this context");
456 fn report_scoped_local_value_does_not_live_long_enough(
460 _scope_tree: &Rc<ScopeTree>,
461 borrow: &BorrowData<'tcx>,
465 end_span: Option<Span>,
467 let mut err = self.tcx.path_does_not_live_long_enough(borrow_span,
468 &format!("`{}`", name),
470 err.span_label(borrow_span, "borrowed value does not live long enough");
471 err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
472 if let Some(end) = end_span {
473 err.span_label(end, "borrowed value needs to live until here");
475 self.explain_why_borrow_contains_point(context, borrow, &mut err);
479 fn report_scoped_temporary_value_does_not_live_long_enough(
482 _scope_tree: &Rc<ScopeTree>,
483 borrow: &BorrowData<'tcx>,
487 end_span: Option<Span>,
489 let mut err = self.tcx.path_does_not_live_long_enough(proper_span,
492 err.span_label(proper_span, "temporary value does not live long enough");
493 err.span_label(drop_span, "temporary value dropped here while still borrowed");
494 err.note("consider using a `let` binding to increase its lifetime");
495 if let Some(end) = end_span {
496 err.span_label(end, "temporary value needs to live until here");
498 self.explain_why_borrow_contains_point(context, borrow, &mut err);
502 fn report_unscoped_local_value_does_not_live_long_enough(
506 scope_tree: &Rc<ScopeTree>,
507 borrow: &BorrowData<'tcx>,
511 _end_span: Option<Span>,
513 let mut err = self.tcx.path_does_not_live_long_enough(borrow_span,
514 &format!("`{}`", name),
516 err.span_label(borrow_span, "borrowed value does not live long enough");
517 err.span_label(drop_span, "borrowed value only lives until here");
518 self.tcx.note_and_explain_region(scope_tree, &mut err,
519 "borrowed value must be valid for ",
520 borrow.region, "...");
521 self.explain_why_borrow_contains_point(context, borrow, &mut err);
525 fn report_unscoped_temporary_value_does_not_live_long_enough(
528 scope_tree: &Rc<ScopeTree>,
529 borrow: &BorrowData<'tcx>,
533 _end_span: Option<Span>
535 let mut err = self.tcx.path_does_not_live_long_enough(proper_span,
538 err.span_label(proper_span, "temporary value does not live long enough");
539 err.span_label(drop_span, "temporary value only lives until here");
540 self.tcx.note_and_explain_region(scope_tree, &mut err,
541 "borrowed value must be valid for ",
542 borrow.region, "...");
543 self.explain_why_borrow_contains_point(context, borrow, &mut err);
547 pub(super) fn report_illegal_mutation_of_borrowed(
550 (place, span): (&Place<'tcx>, Span),
553 let mut err = self.tcx.cannot_assign_to_borrowed(
555 self.retrieve_borrow_span(loan),
556 &self.describe_place(place).unwrap_or("_".to_owned()),
560 self.explain_why_borrow_contains_point(context, loan, &mut err);
565 pub(super) fn report_illegal_reassignment(
568 (place, span): (&Place<'tcx>, Span),
571 let mut err = self.tcx.cannot_reassign_immutable(
573 &self.describe_place(place).unwrap_or("_".to_owned()),
576 err.span_label(span, "cannot assign twice to immutable variable");
577 if span != assigned_span {
578 let value_msg = match self.describe_place(place) {
579 Some(name) => format!("`{}`", name),
580 None => "value".to_owned(),
582 err.span_label(assigned_span, format!("first assignment to {}", value_msg));
588 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
589 // End-user visible description of `place` if one can be found. If the
590 // place is a temporary for instance, None will be returned.
591 pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
592 let mut buf = String::new();
593 match self.append_place_to_string(place, &mut buf, false) {
599 // Appends end-user visible description of `place` to `buf`.
600 fn append_place_to_string(
605 ) -> Result<(), ()> {
607 Place::Local(local) => {
608 self.append_local_to_string(local, buf)?;
610 Place::Static(ref static_) => {
611 buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id)));
613 Place::Projection(ref proj) => {
615 ProjectionElem::Deref => {
616 if let Some(field) = self.is_upvar_field_projection(&proj.base) {
617 let var_index = field.index();
618 let name = self.mir.upvar_decls[var_index].debug_name.to_string();
619 if self.mir.upvar_decls[var_index].by_ref {
622 buf.push_str(&format!("*{}", &name));
626 self.append_place_to_string(&proj.base, buf, autoderef)?;
629 self.append_place_to_string(&proj.base, buf, autoderef)?;
633 ProjectionElem::Downcast(..) => {
634 self.append_place_to_string(&proj.base, buf, autoderef)?;
636 ProjectionElem::Field(field, _ty) => {
639 if let Some(field) = self.is_upvar_field_projection(place) {
640 let var_index = field.index();
641 let name = self.mir.upvar_decls[var_index].debug_name.to_string();
644 let field_name = self.describe_field(&proj.base, field);
645 self.append_place_to_string(&proj.base, buf, autoderef)?;
646 buf.push_str(&format!(".{}", field_name));
649 ProjectionElem::Index(index) => {
652 self.append_place_to_string(&proj.base, buf, autoderef)?;
654 if let Err(_) = self.append_local_to_string(index, buf) {
659 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
661 // Since it isn't possible to borrow an element on a particular index and
662 // then use another while the borrow is held, don't output indices details
663 // to avoid confusing the end-user
664 self.append_place_to_string(&proj.base, buf, autoderef)?;
665 buf.push_str(&"[..]");
674 // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
675 // a name, then `Err` is returned
676 fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
677 let local = &self.mir.local_decls[local_index];
680 buf.push_str(&format!("{}", name));
687 // End-user visible description of the `field`nth field of `base`
688 fn describe_field(&self, base: &Place, field: Field) -> String {
690 Place::Local(local) => {
691 let local = &self.mir.local_decls[local];
692 self.describe_field_from_ty(&local.ty, field)
694 Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field),
695 Place::Projection(ref proj) => match proj.elem {
696 ProjectionElem::Deref => self.describe_field(&proj.base, field),
697 ProjectionElem::Downcast(def, variant_index) => {
698 format!("{}", def.variants[variant_index].fields[field.index()].name)
700 ProjectionElem::Field(_, field_type) => {
701 self.describe_field_from_ty(&field_type, field)
703 ProjectionElem::Index(..) |
704 ProjectionElem::ConstantIndex { .. } |
705 ProjectionElem::Subslice { .. } => {
706 format!("{}", self.describe_field(&proj.base, field))
712 // End-user visible description of the `field_index`nth field of `ty`
713 fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
715 // If the type is a box, the field is described from the boxed type
716 self.describe_field_from_ty(&ty.boxed_ty(), field)
719 ty::TyAdt(def, _) => if def.is_enum() {
720 format!("{}", field.index())
722 format!("{}", def.non_enum_variant().fields[field.index()].name)
724 ty::TyTuple(_, _) => format!("{}", field.index()),
725 ty::TyRef(_, tnm) | ty::TyRawPtr(tnm) => {
726 self.describe_field_from_ty(&tnm.ty, field)
728 ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field),
729 ty::TyClosure(closure_def_id, _) => {
730 // Convert the def-id into a node-id. node-ids are only valid for
731 // the local code in the current crate, so this returns an `Option` in case
732 // the closure comes from another crate. But in that case we wouldn't
733 // be borrowck'ing it, so we can just unwrap:
734 let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap();
735 let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
737 self.tcx.hir.name(freevar.var_id()).to_string()
740 // Might need a revision when the fields in trait RFC is implemented
741 // (https://github.com/rust-lang/rfcs/pull/1546)
743 "End-user description not implemented for field access on `{:?}`",
751 // Retrieve span of given borrow from the current MIR representation
752 fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span {
753 self.mir.source_info(borrow.location).span
756 // Retrieve type of a place for the current MIR representation
757 fn retrieve_type_for_place(&self, place: &Place<'tcx>) -> Option<ty::Ty> {
759 Place::Local(local) => {
760 let local = &self.mir.local_decls[*local];
763 Place::Static(ref st) => Some(st.ty),
764 Place::Projection(ref proj) => {
766 ProjectionElem::Field(_, ty) => Some(ty),