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;
12 use rustc::middle::region::ScopeTree;
13 use rustc::mir::{BindingForm, BorrowKind, ClearCrossCrate, Field, Local};
14 use rustc::mir::{LocalDecl, LocalKind, Location, Operand, Place};
15 use rustc::mir::{ProjectionElem, Rvalue, Statement, StatementKind};
16 use rustc::mir::VarBindingForm;
17 use rustc::ty::{self, RegionKind};
18 use rustc_data_structures::indexed_vec::Idx;
19 use rustc_data_structures::sync::Lrc;
22 use super::borrow_set::BorrowData;
23 use super::{Context, MirBorrowckCtxt};
24 use super::{InitializationRequiringAction, PrefixSet};
26 use dataflow::move_paths::MovePathIndex;
27 use dataflow::{FlowAtLocation, MovingOutStatements};
28 use util::borrowck_errors::{BorrowckErrors, Origin};
30 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
31 pub(super) fn report_use_of_moved_or_uninitialized(
34 desired_action: InitializationRequiringAction,
35 (place, span): (&Place<'tcx>, Span),
37 curr_move_out: &FlowAtLocation<MovingOutStatements<'_, 'gcx, 'tcx>>,
39 let mois = self.move_data.path_map[mpi]
41 .filter(|moi| curr_move_out.contains(moi))
45 let root_place = self.prefixes(&place, PrefixSet::All).last().unwrap();
47 if self.moved_error_reported.contains(&root_place.clone()) {
49 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
55 self.moved_error_reported.insert(root_place.clone());
57 let item_msg = match self.describe_place_with_options(place, IncludingDowncast(true)) {
58 Some(name) => format!("`{}`", name),
59 None => "value".to_owned(),
61 let mut err = self.tcx
62 .cannot_act_on_uninitialized_variable(
64 desired_action.as_noun(),
66 .describe_place_with_options(place, IncludingDowncast(true))
67 .unwrap_or("_".to_owned()),
70 err.span_label(span, format!("use of possibly uninitialized {}", item_msg));
71 err.buffer(&mut self.errors_buffer);
73 let msg = ""; //FIXME: add "partially " or "collaterally "
75 let mut err = self.tcx.cannot_act_on_moved_value(
77 desired_action.as_noun(),
79 self.describe_place_with_options(&place, IncludingDowncast(true)),
83 let mut is_loop_move = false;
85 let move_msg = ""; //FIXME: add " (into closure)"
88 .source_info(self.move_data.moves[**moi].source)
90 if span == move_span {
93 format!("value moved{} here in previous iteration of loop", move_msg),
97 err.span_label(move_span, format!("value moved{} here", move_msg));
104 "value {} here after move",
105 desired_action.as_verb_in_past_tense()
110 if let Some(ty) = self.retrieve_type_for_place(place) {
111 let needs_note = match ty.sty {
112 ty::TypeVariants::TyClosure(id, _) => {
113 let tables = self.tcx.typeck_tables_of(id);
114 let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
115 let hir_id = self.tcx.hir.node_to_hir_id(node_id);
116 if let Some(_) = tables.closure_kind_origins().get(hir_id) {
126 let mpi = self.move_data.moves[*mois[0]].path;
127 let place = &self.move_data.move_paths[mpi].place;
129 if let Some(ty) = self.retrieve_type_for_place(place) {
130 let note_msg = match self
131 .describe_place_with_options(place, IncludingDowncast(true))
133 Some(name) => format!("`{}`", name),
134 None => "value".to_owned(),
138 "move occurs because {} has type `{}`, \
139 which does not implement the `Copy` trait",
146 err.buffer(&mut self.errors_buffer);
150 pub(super) fn report_move_out_while_borrowed(
153 (place, span): (&Place<'tcx>, Span),
154 borrow: &BorrowData<'tcx>,
157 let value_msg = match self.describe_place(place) {
158 Some(name) => format!("`{}`", name),
159 None => "value".to_owned(),
161 let borrow_msg = match self.describe_place(&borrow.borrowed_place) {
162 Some(name) => format!("`{}`", name),
163 None => "value".to_owned(),
165 let mut err = tcx.cannot_move_when_borrowed(
167 &self.describe_place(place).unwrap_or("_".to_owned()),
171 self.retrieve_borrow_span(borrow),
172 format!("borrow of {} occurs here", borrow_msg),
174 err.span_label(span, format!("move out of {} occurs here", value_msg));
175 self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
176 err.buffer(&mut self.errors_buffer);
179 pub(super) fn report_use_while_mutably_borrowed(
182 (place, span): (&Place<'tcx>, Span),
183 borrow: &BorrowData<'tcx>,
186 let mut err = tcx.cannot_use_when_mutably_borrowed(
188 &self.describe_place(place).unwrap_or("_".to_owned()),
189 self.retrieve_borrow_span(borrow),
191 .describe_place(&borrow.borrowed_place)
192 .unwrap_or("_".to_owned()),
196 self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
197 err.buffer(&mut self.errors_buffer);
200 /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
201 /// the local assigned at `location`.
202 /// This is done by searching in statements succeeding `location`
203 /// and originating from `maybe_closure_span`.
204 pub(super) fn find_closure_span(
206 maybe_closure_span: Span,
208 ) -> Option<(Span, Span)> {
209 use rustc::hir::ExprKind::Closure;
210 use rustc::mir::AggregateKind;
212 let local = match self.mir[location.block]
214 .get(location.statement_index)
217 kind: StatementKind::Assign(Place::Local(local), _),
223 for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
224 if maybe_closure_span != stmt.source_info.span {
228 if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
229 if let AggregateKind::Closure(def_id, _) = **kind {
230 debug!("find_closure_span: found closure {:?}", places);
232 return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
233 let args_span = if let Closure(_, _, _, span, _) =
234 self.tcx.hir.expect_expr(node_id).node
242 .with_freevars(node_id, |freevars| {
243 for (v, place) in freevars.iter().zip(places) {
245 Operand::Copy(Place::Local(l))
246 | Operand::Move(Place::Local(l)) if local == l =>
249 "find_closure_span: found captured local {:?}",
259 .map(|var_span| (args_span, var_span))
270 pub(super) fn report_conflicting_borrow(
273 (place, span): (&Place<'tcx>, Span),
274 gen_borrow_kind: BorrowKind,
275 issued_borrow: &BorrowData<'tcx>,
277 let issued_span = self.retrieve_borrow_span(issued_borrow);
279 let new_closure_span = self.find_closure_span(span, context.loc);
280 let span = new_closure_span.map(|(args, _)| args).unwrap_or(span);
281 let old_closure_span = self.find_closure_span(issued_span, issued_borrow.reserve_location);
282 let issued_span = old_closure_span
283 .map(|(args, _)| args)
284 .unwrap_or(issued_span);
286 let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
289 // FIXME: supply non-"" `opt_via` when appropriate
290 let mut err = match (
298 (BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt)
299 | (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => tcx
300 .cannot_reborrow_already_borrowed(
313 (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => tcx
314 .cannot_mutably_borrow_multiply(
324 (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => tcx
325 .cannot_uniquely_borrow_by_two_closures(
333 (BorrowKind::Unique, _, _, _, _, _) => tcx.cannot_uniquely_borrow_by_one_closure(
344 (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => tcx
345 .cannot_reborrow_already_uniquely_borrowed(
356 (BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => tcx
357 .cannot_reborrow_already_uniquely_borrowed(
368 (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(),
371 if let Some((_, var_span)) = old_closure_span {
372 let place = &issued_borrow.borrowed_place;
373 let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
378 "previous borrow occurs due to use of `{}` in closure",
384 if let Some((_, var_span)) = new_closure_span {
387 format!("borrow occurs due to use of `{}` in closure", desc_place),
391 self.explain_why_borrow_contains_point(context, issued_borrow, None, &mut err);
393 err.buffer(&mut self.errors_buffer);
396 pub(super) fn report_borrowed_value_does_not_live_long_enough(
399 borrow: &BorrowData<'tcx>,
400 place_span: (&Place<'tcx>, Span),
401 kind: Option<WriteKind>,
403 let drop_span = place_span.1;
404 let scope_tree = self.tcx.region_scope_tree(self.mir_def_id);
405 let root_place = self
406 .prefixes(&borrow.borrowed_place, PrefixSet::All)
410 let borrow_span = self.mir.source_info(borrow.reserve_location).span;
411 let proper_span = match *root_place {
412 Place::Local(local) => self.mir.local_decls[local].source_info.span,
417 .access_place_error_reported
418 .contains(&(root_place.clone(), borrow_span))
421 "suppressing access_place error when borrow doesn't live long enough for {:?}",
427 self.access_place_error_reported
428 .insert((root_place.clone(), borrow_span));
430 match (borrow.region, &self.describe_place(&borrow.borrowed_place)) {
431 (RegionKind::ReScope(_), Some(name)) => {
432 self.report_scoped_local_value_does_not_live_long_enough(
442 (RegionKind::ReScope(_), None) => {
443 self.report_scoped_temporary_value_does_not_live_long_enough(
452 (RegionKind::ReEarlyBound(_), Some(name))
453 | (RegionKind::ReFree(_), Some(name))
454 | (RegionKind::ReStatic, Some(name))
455 | (RegionKind::ReEmpty, Some(name))
456 | (RegionKind::ReVar(_), Some(name)) => {
457 self.report_unscoped_local_value_does_not_live_long_enough(
465 kind.map(|k| (k, place_span.0)),
468 (RegionKind::ReEarlyBound(_), None)
469 | (RegionKind::ReFree(_), None)
470 | (RegionKind::ReStatic, None)
471 | (RegionKind::ReEmpty, None)
472 | (RegionKind::ReVar(_), None) => {
473 self.report_unscoped_temporary_value_does_not_live_long_enough(
482 (RegionKind::ReLateBound(_, _), _)
483 | (RegionKind::ReSkolemized(_, _), _)
484 | (RegionKind::ReClosureBound(_), _)
485 | (RegionKind::ReCanonical(_), _)
486 | (RegionKind::ReErased, _) => {
489 "region {:?} does not make sense in this context",
496 fn report_scoped_local_value_does_not_live_long_enough(
500 _scope_tree: &Lrc<ScopeTree>,
501 borrow: &BorrowData<'tcx>,
508 tcx.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name), Origin::Mir);
509 err.span_label(borrow_span, "borrowed value does not live long enough");
512 format!("`{}` dropped here while still borrowed", name),
514 self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
515 err.buffer(&mut self.errors_buffer);
518 fn report_scoped_temporary_value_does_not_live_long_enough(
521 _scope_tree: &Lrc<ScopeTree>,
522 borrow: &BorrowData<'tcx>,
529 tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
530 err.span_label(proper_span, "temporary value does not live long enough");
533 "temporary value dropped here while still borrowed",
535 err.note("consider using a `let` binding to increase its lifetime");
536 self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
537 err.buffer(&mut self.errors_buffer);
540 fn report_unscoped_local_value_does_not_live_long_enough(
544 scope_tree: &Lrc<ScopeTree>,
545 borrow: &BorrowData<'tcx>,
549 kind_place: Option<(WriteKind, &Place<'tcx>)>,
552 "report_unscoped_local_value_does_not_live_long_enough(\
553 {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\
555 context, name, scope_tree, borrow, drop_span, borrow_span
560 tcx.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name), Origin::Mir);
561 err.span_label(borrow_span, "borrowed value does not live long enough");
562 err.span_label(drop_span, "borrowed value only lives until here");
564 self.explain_why_borrow_contains_point(context, borrow, kind_place, &mut err);
565 err.buffer(&mut self.errors_buffer);
568 fn report_unscoped_temporary_value_does_not_live_long_enough(
571 scope_tree: &Lrc<ScopeTree>,
572 borrow: &BorrowData<'tcx>,
578 "report_unscoped_temporary_value_does_not_live_long_enough(\
579 {:?}, {:?}, {:?}, {:?}, {:?}\
581 context, scope_tree, borrow, drop_span, proper_span
586 tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
587 err.span_label(proper_span, "temporary value does not live long enough");
588 err.span_label(drop_span, "temporary value only lives until here");
590 self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
591 err.buffer(&mut self.errors_buffer);
594 pub(super) fn report_illegal_mutation_of_borrowed(
597 (place, span): (&Place<'tcx>, Span),
598 loan: &BorrowData<'tcx>,
601 let mut err = tcx.cannot_assign_to_borrowed(
603 self.retrieve_borrow_span(loan),
604 &self.describe_place(place).unwrap_or("_".to_owned()),
608 self.explain_why_borrow_contains_point(context, loan, None, &mut err);
610 err.buffer(&mut self.errors_buffer);
613 /// Reports an illegal reassignment; for example, an assignment to
614 /// (part of) a non-`mut` local that occurs potentially after that
615 /// local has already been initialized. `place` is the path being
616 /// assigned; `err_place` is a place providing a reason why
617 /// `place` is not mutable (e.g. the non-`mut` local `x` in an
618 /// assignment to `x.f`).
619 pub(super) fn report_illegal_reassignment(
622 (place, span): (&Place<'tcx>, Span),
624 err_place: &Place<'tcx>,
626 let (from_arg, local_decl) = if let Place::Local(local) = *err_place {
627 if let LocalKind::Arg = self.mir.local_kind(local) {
628 (true, Some(&self.mir.local_decls[local]))
630 (false, Some(&self.mir.local_decls[local]))
636 // If root local is initialized immediately (everything apart from let
637 // PATTERN;) then make the error refer to that local, rather than the
638 // place being assigned later.
639 let (place_description, assigned_span) = match local_decl {
640 Some(LocalDecl { is_user_variable: Some(ClearCrossCrate::Clear), .. })
641 | Some(LocalDecl { is_user_variable: Some(ClearCrossCrate::Set(
642 BindingForm::Var(VarBindingForm {
643 opt_match_place: None, ..
645 | Some(LocalDecl { is_user_variable: None, .. })
646 | None => (self.describe_place(place), assigned_span),
647 Some(decl) => (self.describe_place(err_place), decl.source_info.span),
650 let mut err = self.tcx.cannot_reassign_immutable(
652 place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"),
656 let msg = if from_arg {
657 "cannot assign to immutable argument"
659 "cannot assign twice to immutable variable"
661 if span != assigned_span {
663 let value_msg = match place_description {
664 Some(name) => format!("`{}`", name),
665 None => "value".to_owned(),
667 err.span_label(assigned_span, format!("first assignment to {}", value_msg));
670 if let Some(decl) = local_decl {
671 if let Some(name) = decl.name {
672 if decl.can_be_made_mutable() {
674 decl.source_info.span,
675 format!("consider changing this to `mut {}`", name),
680 err.span_label(span, msg);
681 err.buffer(&mut self.errors_buffer);
685 pub(super) struct IncludingDowncast(bool);
687 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
688 // End-user visible description of `place` if one can be found. If the
689 // place is a temporary for instance, None will be returned.
690 pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
691 self.describe_place_with_options(place, IncludingDowncast(false))
694 // End-user visible description of `place` if one can be found. If the
695 // place is a temporary for instance, None will be returned.
696 // `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
697 // `Downcast` and `IncludingDowncast` is true
698 pub(super) fn describe_place_with_options(
701 including_downcast: IncludingDowncast,
702 ) -> Option<String> {
703 let mut buf = String::new();
704 match self.append_place_to_string(place, &mut buf, false, &including_downcast) {
710 // Appends end-user visible description of `place` to `buf`.
711 fn append_place_to_string(
716 including_downcast: &IncludingDowncast,
717 ) -> Result<(), ()> {
719 Place::Promoted(_) => {
720 buf.push_str("promoted");
722 Place::Local(local) => {
723 self.append_local_to_string(local, buf)?;
725 Place::Static(ref static_) => {
726 buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id)));
728 Place::Projection(ref proj) => {
730 ProjectionElem::Deref => {
731 let upvar_field_projection = place.is_upvar_field_projection(
732 self.mir, &self.tcx);
733 if let Some(field) = upvar_field_projection {
734 let var_index = field.index();
735 let name = self.mir.upvar_decls[var_index].debug_name.to_string();
736 if self.mir.upvar_decls[var_index].by_ref {
739 buf.push_str(&format!("*{}", &name));
743 self.append_place_to_string(
749 } else if let Place::Local(local) = proj.base {
750 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard))
751 = self.mir.local_decls[local].is_user_variable {
752 self.append_place_to_string(
760 self.append_place_to_string(
769 self.append_place_to_string(
778 ProjectionElem::Downcast(..) => {
779 self.append_place_to_string(
785 if including_downcast.0 {
789 ProjectionElem::Field(field, _ty) => {
792 let upvar_field_projection = place.is_upvar_field_projection(
793 self.mir, &self.tcx);
794 if let Some(field) = upvar_field_projection {
795 let var_index = field.index();
796 let name = self.mir.upvar_decls[var_index].debug_name.to_string();
799 let field_name = self.describe_field(&proj.base, field);
800 self.append_place_to_string(
806 buf.push_str(&format!(".{}", field_name));
809 ProjectionElem::Index(index) => {
812 self.append_place_to_string(
819 if let Err(_) = self.append_local_to_string(index, buf) {
824 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
826 // Since it isn't possible to borrow an element on a particular index and
827 // then use another while the borrow is held, don't output indices details
828 // to avoid confusing the end-user
829 self.append_place_to_string(
835 buf.push_str(&"[..]");
844 // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
845 // a name, then `Err` is returned
846 fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
847 let local = &self.mir.local_decls[local_index];
850 buf.push_str(&format!("{}", name));
857 // End-user visible description of the `field`nth field of `base`
858 fn describe_field(&self, base: &Place, field: Field) -> String {
860 Place::Local(local) => {
861 let local = &self.mir.local_decls[local];
862 self.describe_field_from_ty(&local.ty, field)
864 Place::Promoted(ref prom) => self.describe_field_from_ty(&prom.1, field),
865 Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field),
866 Place::Projection(ref proj) => match proj.elem {
867 ProjectionElem::Deref => self.describe_field(&proj.base, field),
868 ProjectionElem::Downcast(def, variant_index) => format!(
870 def.variants[variant_index].fields[field.index()].ident
872 ProjectionElem::Field(_, field_type) => {
873 self.describe_field_from_ty(&field_type, field)
875 ProjectionElem::Index(..)
876 | ProjectionElem::ConstantIndex { .. }
877 | ProjectionElem::Subslice { .. } => {
878 format!("{}", self.describe_field(&proj.base, field))
884 // End-user visible description of the `field_index`nth field of `ty`
885 fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
887 // If the type is a box, the field is described from the boxed type
888 self.describe_field_from_ty(&ty.boxed_ty(), field)
891 ty::TyAdt(def, _) => if def.is_enum() {
892 format!("{}", field.index())
894 format!("{}", def.non_enum_variant().fields[field.index()].ident)
896 ty::TyTuple(_) => format!("{}", field.index()),
897 ty::TyRef(_, ty, _) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => {
898 self.describe_field_from_ty(&ty, field)
900 ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field),
901 ty::TyClosure(def_id, _) | ty::TyGenerator(def_id, _, _) => {
902 // Convert the def-id into a node-id. node-ids are only valid for
903 // the local code in the current crate, so this returns an `Option` in case
904 // the closure comes from another crate. But in that case we wouldn't
905 // be borrowck'ing it, so we can just unwrap:
906 let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
907 let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
909 self.tcx.hir.name(freevar.var_id()).to_string()
912 // Might need a revision when the fields in trait RFC is implemented
913 // (https://github.com/rust-lang/rfcs/pull/1546)
915 "End-user description not implemented for field access on `{:?}`",
923 // Retrieve span of given borrow from the current MIR representation
924 crate fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span {
925 self.mir.source_info(borrow.reserve_location).span
928 // Retrieve type of a place for the current MIR representation
929 fn retrieve_type_for_place(&self, place: &Place<'tcx>) -> Option<ty::Ty> {
931 Place::Local(local) => {
932 let local = &self.mir.local_decls[*local];
935 Place::Promoted(ref prom) => Some(prom.1),
936 Place::Static(ref st) => Some(st.ty),
937 Place::Projection(ref proj) => match proj.elem {
938 ProjectionElem::Field(_, ty) => Some(ty),