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
::{BorrowKind, Field, Local, LocalKind, 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
;
17 use rustc_data_structures
::sync
::Lrc
;
20 use super::borrow_set
::BorrowData
;
21 use super::{Context, MirBorrowckCtxt}
;
22 use super::{InitializationRequiringAction, PrefixSet}
;
24 use dataflow
::move_paths
::MovePathIndex
;
25 use dataflow
::{FlowAtLocation, MovingOutStatements}
;
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
).last().unwrap();
45 if self.moved_error_reported
.contains(&root_place
.clone()) {
47 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
53 self.moved_error_reported
.insert(root_place
.clone());
55 let item_msg
= match self.describe_place(place
) {
56 Some(name
) => format
!("`{}`", name
),
57 None
=> "value".to_owned(),
60 .cannot_act_on_uninitialized_variable(
62 desired_action
.as_noun(),
63 &self.describe_place(place
).unwrap_or("_".to_owned()),
66 .span_label(span
, format
!("use of possibly uninitialized {}", item_msg
))
69 let msg
= ""; //FIXME: add "partially " or "collaterally "
71 let mut err
= self.tcx
.cannot_act_on_moved_value(
73 desired_action
.as_noun(),
75 &self.describe_place(place
).unwrap_or("_".to_owned()),
79 let mut is_loop_move
= false;
81 let move_msg
= ""; //FIXME: add " (into closure)"
82 let move_span
= self.mir
.source_info(self.move_data
.moves
[*moi
].source
).span
;
83 if span
== move_span
{
86 format
!("value moved{} here in previous iteration of loop", move_msg
),
90 err
.span_label(move_span
, format
!("value moved{} here", move_msg
));
97 "value {} here after move",
98 desired_action
.as_verb_in_past_tense()
103 if let Some(ty
) = self.retrieve_type_for_place(place
) {
104 let needs_note
= match ty
.sty
{
105 ty
::TypeVariants
::TyClosure(id
, _
) => {
106 let tables
= self.tcx
.typeck_tables_of(id
);
107 let node_id
= self.tcx
.hir
.as_local_node_id(id
).unwrap();
108 let hir_id
= self.tcx
.hir
.node_to_hir_id(node_id
);
109 if let Some(_
) = tables
.closure_kind_origins().get(hir_id
) {
119 let note_msg
= match self.describe_place(place
) {
120 Some(name
) => format
!("`{}`", name
),
121 None
=> "value".to_owned(),
125 "move occurs because {} has type `{}`, \
126 which does not implement the `Copy` trait",
136 pub(super) fn report_move_out_while_borrowed(
139 (place
, span
): (&Place
<'tcx
>, Span
),
140 borrow
: &BorrowData
<'tcx
>,
143 let value_msg
= match self.describe_place(place
) {
144 Some(name
) => format
!("`{}`", name
),
145 None
=> "value".to_owned(),
147 let borrow_msg
= match self.describe_place(&borrow
.borrowed_place
) {
148 Some(name
) => format
!("`{}`", name
),
149 None
=> "value".to_owned(),
151 let mut err
= tcx
.cannot_move_when_borrowed(
153 &self.describe_place(place
).unwrap_or("_".to_owned()),
157 self.retrieve_borrow_span(borrow
),
158 format
!("borrow of {} occurs here", borrow_msg
),
160 err
.span_label(span
, format
!("move out of {} occurs here", value_msg
));
161 self.explain_why_borrow_contains_point(context
, borrow
, None
, &mut err
);
165 pub(super) fn report_use_while_mutably_borrowed(
168 (place
, span
): (&Place
<'tcx
>, Span
),
169 borrow
: &BorrowData
<'tcx
>,
172 let mut err
= tcx
.cannot_use_when_mutably_borrowed(
174 &self.describe_place(place
).unwrap_or("_".to_owned()),
175 self.retrieve_borrow_span(borrow
),
177 .describe_place(&borrow
.borrowed_place
)
178 .unwrap_or("_".to_owned()),
182 self.explain_why_borrow_contains_point(context
, borrow
, None
, &mut err
);
187 /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
188 /// the local assigned at `location`.
189 /// This is done by searching in statements succeeding `location`
190 /// and originating from `maybe_closure_span`.
191 fn find_closure_span(
193 maybe_closure_span
: Span
,
195 ) -> Option
<(Span
, Span
)> {
196 use rustc
::hir
::ExprClosure
;
197 use rustc
::mir
::AggregateKind
;
199 let local
= match self.mir
[location
.block
]
201 .get(location
.statement_index
)
204 kind
: StatementKind
::Assign(Place
::Local(local
), _
),
210 for stmt
in &self.mir
[location
.block
].statements
[location
.statement_index
+ 1..] {
211 if maybe_closure_span
!= stmt
.source_info
.span
{
215 if let StatementKind
::Assign(_
, Rvalue
::Aggregate(ref kind
, ref places
)) = stmt
.kind
{
216 if let AggregateKind
::Closure(def_id
, _
) = **kind
{
217 debug
!("find_closure_span: found closure {:?}", places
);
219 return if let Some(node_id
) = self.tcx
.hir
.as_local_node_id(def_id
) {
220 let args_span
= if let ExprClosure(_
, _
, _
, span
, _
) =
221 self.tcx
.hir
.expect_expr(node_id
).node
229 .with_freevars(node_id
, |freevars
| {
230 for (v
, place
) in freevars
.iter().zip(places
) {
232 Operand
::Copy(Place
::Local(l
))
233 | Operand
::Move(Place
::Local(l
)) if local
== l
=>
236 "find_closure_span: found captured local {:?}",
246 .map(|var_span
| (args_span
, var_span
))
257 pub(super) fn report_conflicting_borrow(
260 (place
, span
): (&Place
<'tcx
>, Span
),
261 gen_borrow_kind
: BorrowKind
,
262 issued_borrow
: &BorrowData
<'tcx
>,
264 let issued_span
= self.retrieve_borrow_span(issued_borrow
);
266 let new_closure_span
= self.find_closure_span(span
, context
.loc
);
267 let span
= new_closure_span
.map(|(args
, _
)| args
).unwrap_or(span
);
268 let old_closure_span
= self.find_closure_span(issued_span
, issued_borrow
.reserve_location
);
269 let issued_span
= old_closure_span
270 .map(|(args
, _
)| args
)
271 .unwrap_or(issued_span
);
273 let desc_place
= self.describe_place(place
).unwrap_or("_".to_owned());
276 // FIXME: supply non-"" `opt_via` when appropriate
277 let mut err
= match (
285 (BorrowKind
::Shared
, lft
, _
, BorrowKind
::Mut { .. }
, _
, rgt
)
286 | (BorrowKind
::Mut { .. }
, _
, lft
, BorrowKind
::Shared
, rgt
, _
) => tcx
287 .cannot_reborrow_already_borrowed(
300 (BorrowKind
::Mut { .. }
, _
, _
, BorrowKind
::Mut { .. }
, _
, _
) => tcx
301 .cannot_mutably_borrow_multiply(
311 (BorrowKind
::Unique
, _
, _
, BorrowKind
::Unique
, _
, _
) => tcx
312 .cannot_uniquely_borrow_by_two_closures(
320 (BorrowKind
::Unique
, _
, _
, _
, _
, _
) => tcx
.cannot_uniquely_borrow_by_one_closure(
331 (BorrowKind
::Shared
, lft
, _
, BorrowKind
::Unique
, _
, _
) => tcx
332 .cannot_reborrow_already_uniquely_borrowed(
343 (BorrowKind
::Mut { .. }
, _
, lft
, BorrowKind
::Unique
, _
, _
) => tcx
344 .cannot_reborrow_already_uniquely_borrowed(
355 (BorrowKind
::Shared
, _
, _
, BorrowKind
::Shared
, _
, _
) => unreachable
!(),
358 if let Some((_
, var_span
)) = old_closure_span
{
362 "previous borrow occurs due to use of `{}` in closure",
368 if let Some((_
, var_span
)) = new_closure_span
{
371 format
!("borrow occurs due to use of `{}` in closure", desc_place
),
375 self.explain_why_borrow_contains_point(context
, issued_borrow
, None
, &mut err
);
380 pub(super) fn report_borrowed_value_does_not_live_long_enough(
383 borrow
: &BorrowData
<'tcx
>,
384 place_span
: (&Place
<'tcx
>, Span
),
385 kind
: Option
<WriteKind
>,
387 let drop_span
= place_span
.1;
388 let scope_tree
= self.tcx
.region_scope_tree(self.mir_def_id
);
389 let root_place
= self
390 .prefixes(&borrow
.borrowed_place
, PrefixSet
::All
)
394 let borrow_span
= self.mir
.source_info(borrow
.reserve_location
).span
;
395 let proper_span
= match *root_place
{
396 Place
::Local(local
) => self.mir
.local_decls
[local
].source_info
.span
,
401 .access_place_error_reported
402 .contains(&(root_place
.clone(), borrow_span
))
405 "suppressing access_place error when borrow doesn't live long enough for {:?}",
411 self.access_place_error_reported
412 .insert((root_place
.clone(), borrow_span
));
414 match (borrow
.region
, &self.describe_place(&borrow
.borrowed_place
)) {
415 (RegionKind
::ReScope(_
), Some(name
)) => {
416 self.report_scoped_local_value_does_not_live_long_enough(
426 (RegionKind
::ReScope(_
), None
) => {
427 self.report_scoped_temporary_value_does_not_live_long_enough(
436 (RegionKind
::ReEarlyBound(_
), Some(name
))
437 | (RegionKind
::ReFree(_
), Some(name
))
438 | (RegionKind
::ReStatic
, Some(name
))
439 | (RegionKind
::ReEmpty
, Some(name
))
440 | (RegionKind
::ReVar(_
), Some(name
)) => {
441 self.report_unscoped_local_value_does_not_live_long_enough(
449 kind
.map(|k
| (k
, place_span
.0)),
452 (RegionKind
::ReEarlyBound(_
), None
)
453 | (RegionKind
::ReFree(_
), None
)
454 | (RegionKind
::ReStatic
, None
)
455 | (RegionKind
::ReEmpty
, None
)
456 | (RegionKind
::ReVar(_
), None
) => {
457 self.report_unscoped_temporary_value_does_not_live_long_enough(
466 (RegionKind
::ReLateBound(_
, _
), _
)
467 | (RegionKind
::ReSkolemized(_
, _
), _
)
468 | (RegionKind
::ReClosureBound(_
), _
)
469 | (RegionKind
::ReCanonical(_
), _
)
470 | (RegionKind
::ReErased
, _
) => {
473 "region {:?} does not make sense in this context",
480 fn report_scoped_local_value_does_not_live_long_enough(
484 _scope_tree
: &Lrc
<ScopeTree
>,
485 borrow
: &BorrowData
<'tcx
>,
492 tcx
.path_does_not_live_long_enough(borrow_span
, &format
!("`{}`", name
), Origin
::Mir
);
493 err
.span_label(borrow_span
, "borrowed value does not live long enough");
496 format
!("`{}` dropped here while still borrowed", name
),
498 self.explain_why_borrow_contains_point(context
, borrow
, None
, &mut err
);
502 fn report_scoped_temporary_value_does_not_live_long_enough(
505 _scope_tree
: &Lrc
<ScopeTree
>,
506 borrow
: &BorrowData
<'tcx
>,
513 tcx
.path_does_not_live_long_enough(proper_span
, "borrowed value", Origin
::Mir
);
514 err
.span_label(proper_span
, "temporary value does not live long enough");
517 "temporary value dropped here while still borrowed",
519 err
.note("consider using a `let` binding to increase its lifetime");
520 self.explain_why_borrow_contains_point(context
, borrow
, None
, &mut err
);
524 fn report_unscoped_local_value_does_not_live_long_enough(
528 scope_tree
: &Lrc
<ScopeTree
>,
529 borrow
: &BorrowData
<'tcx
>,
533 kind_place
: Option
<(WriteKind
, &Place
<'tcx
>)>,
536 "report_unscoped_local_value_does_not_live_long_enough(\
537 {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\
539 context
, name
, scope_tree
, borrow
, drop_span
, borrow_span
544 tcx
.path_does_not_live_long_enough(borrow_span
, &format
!("`{}`", name
), Origin
::Mir
);
545 err
.span_label(borrow_span
, "borrowed value does not live long enough");
546 err
.span_label(drop_span
, "borrowed value only lives until here");
548 self.explain_why_borrow_contains_point(context
, borrow
, kind_place
, &mut err
);
552 fn report_unscoped_temporary_value_does_not_live_long_enough(
555 scope_tree
: &Lrc
<ScopeTree
>,
556 borrow
: &BorrowData
<'tcx
>,
562 "report_unscoped_temporary_value_does_not_live_long_enough(\
563 {:?}, {:?}, {:?}, {:?}, {:?}\
565 context
, scope_tree
, borrow
, drop_span
, proper_span
570 tcx
.path_does_not_live_long_enough(proper_span
, "borrowed value", Origin
::Mir
);
571 err
.span_label(proper_span
, "temporary value does not live long enough");
572 err
.span_label(drop_span
, "temporary value only lives until here");
574 self.explain_why_borrow_contains_point(context
, borrow
, None
, &mut err
);
578 pub(super) fn report_illegal_mutation_of_borrowed(
581 (place
, span
): (&Place
<'tcx
>, Span
),
582 loan
: &BorrowData
<'tcx
>,
585 let mut err
= tcx
.cannot_assign_to_borrowed(
587 self.retrieve_borrow_span(loan
),
588 &self.describe_place(place
).unwrap_or("_".to_owned()),
592 self.explain_why_borrow_contains_point(context
, loan
, None
, &mut err
);
597 /// Reports an illegal reassignment; for example, an assignment to
598 /// (part of) a non-`mut` local that occurs potentially after that
599 /// local has already been initialized. `place` is the path being
600 /// assigned; `err_place` is a place providing a reason why
601 /// `place` is not mutable (e.g. the non-`mut` local `x` in an
602 /// assignment to `x.f`).
603 pub(super) fn report_illegal_reassignment(
606 (place
, span
): (&Place
<'tcx
>, Span
),
608 err_place
: &Place
<'tcx
>,
610 let is_arg
= if let Place
::Local(local
) = place
{
611 if let LocalKind
::Arg
= self.mir
.local_kind(*local
) {
620 let mut err
= self.tcx
.cannot_reassign_immutable(
622 &self.describe_place(place
).unwrap_or("_".to_owned()),
626 let msg
= if is_arg
{
627 "cannot assign to immutable argument"
629 "cannot assign twice to immutable variable"
631 if span
!= assigned_span
{
633 let value_msg
= match self.describe_place(place
) {
634 Some(name
) => format
!("`{}`", name
),
635 None
=> "value".to_owned(),
637 err
.span_label(assigned_span
, format
!("first assignment to {}", value_msg
));
640 if let Place
::Local(local
) = err_place
{
641 let local_decl
= &self.mir
.local_decls
[*local
];
642 if let Some(name
) = local_decl
.name
{
643 if local_decl
.can_be_made_mutable() {
644 err
.span_label(local_decl
.source_info
.span
,
645 format
!("consider changing this to `mut {}`", name
));
649 err
.span_label(span
, msg
);
654 impl<'cx
, 'gcx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'gcx
, 'tcx
> {
655 // End-user visible description of `place` if one can be found. If the
656 // place is a temporary for instance, None will be returned.
657 pub(super) fn describe_place(&self, place
: &Place
<'tcx
>) -> Option
<String
> {
658 let mut buf
= String
::new();
659 match self.append_place_to_string(place
, &mut buf
, false) {
665 // Appends end-user visible description of `place` to `buf`.
666 fn append_place_to_string(
671 ) -> Result
<(), ()> {
673 Place
::Local(local
) => {
674 self.append_local_to_string(local
, buf
)?
;
676 Place
::Static(ref static_
) => {
677 buf
.push_str(&format
!("{}", &self.tcx
.item_name(static_
.def_id
)));
679 Place
::Projection(ref proj
) => {
681 ProjectionElem
::Deref
=> {
682 if let Some(field
) = self.is_upvar_field_projection(&proj
.base
) {
683 let var_index
= field
.index();
684 let name
= self.mir
.upvar_decls
[var_index
].debug_name
.to_string();
685 if self.mir
.upvar_decls
[var_index
].by_ref
{
688 buf
.push_str(&format
!("*{}", &name
));
692 self.append_place_to_string(&proj
.base
, buf
, autoderef
)?
;
695 self.append_place_to_string(&proj
.base
, buf
, autoderef
)?
;
699 ProjectionElem
::Downcast(..) => {
700 self.append_place_to_string(&proj
.base
, buf
, autoderef
)?
;
702 ProjectionElem
::Field(field
, _ty
) => {
705 if let Some(field
) = self.is_upvar_field_projection(place
) {
706 let var_index
= field
.index();
707 let name
= self.mir
.upvar_decls
[var_index
].debug_name
.to_string();
710 let field_name
= self.describe_field(&proj
.base
, field
);
711 self.append_place_to_string(&proj
.base
, buf
, autoderef
)?
;
712 buf
.push_str(&format
!(".{}", field_name
));
715 ProjectionElem
::Index(index
) => {
718 self.append_place_to_string(&proj
.base
, buf
, autoderef
)?
;
720 if let Err(_
) = self.append_local_to_string(index
, buf
) {
725 ProjectionElem
::ConstantIndex { .. }
| ProjectionElem
::Subslice { .. }
=> {
727 // Since it isn't possible to borrow an element on a particular index and
728 // then use another while the borrow is held, don't output indices details
729 // to avoid confusing the end-user
730 self.append_place_to_string(&proj
.base
, buf
, autoderef
)?
;
731 buf
.push_str(&"[..]");
740 // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
741 // a name, then `Err` is returned
742 fn append_local_to_string(&self, local_index
: Local
, buf
: &mut String
) -> Result
<(), ()> {
743 let local
= &self.mir
.local_decls
[local_index
];
746 buf
.push_str(&format
!("{}", name
));
753 // End-user visible description of the `field`nth field of `base`
754 fn describe_field(&self, base
: &Place
, field
: Field
) -> String
{
756 Place
::Local(local
) => {
757 let local
= &self.mir
.local_decls
[local
];
758 self.describe_field_from_ty(&local
.ty
, field
)
760 Place
::Static(ref static_
) => self.describe_field_from_ty(&static_
.ty
, field
),
761 Place
::Projection(ref proj
) => match proj
.elem
{
762 ProjectionElem
::Deref
=> self.describe_field(&proj
.base
, field
),
763 ProjectionElem
::Downcast(def
, variant_index
) => format
!(
765 def
.variants
[variant_index
].fields
[field
.index()].ident
767 ProjectionElem
::Field(_
, field_type
) => {
768 self.describe_field_from_ty(&field_type
, field
)
770 ProjectionElem
::Index(..)
771 | ProjectionElem
::ConstantIndex { .. }
772 | ProjectionElem
::Subslice { .. }
=> {
773 format
!("{}", self.describe_field(&proj
.base
, field
))
779 // End-user visible description of the `field_index`nth field of `ty`
780 fn describe_field_from_ty(&self, ty
: &ty
::Ty
, field
: Field
) -> String
{
782 // If the type is a box, the field is described from the boxed type
783 self.describe_field_from_ty(&ty
.boxed_ty(), field
)
786 ty
::TyAdt(def
, _
) => if def
.is_enum() {
787 format
!("{}", field
.index())
789 format
!("{}", def
.non_enum_variant().fields
[field
.index()].ident
)
791 ty
::TyTuple(_
) => format
!("{}", field
.index()),
792 ty
::TyRef(_
, ty
, _
) | ty
::TyRawPtr(ty
::TypeAndMut { ty, .. }
) => {
793 self.describe_field_from_ty(&ty
, field
)
795 ty
::TyArray(ty
, _
) | ty
::TySlice(ty
) => self.describe_field_from_ty(&ty
, field
),
796 ty
::TyClosure(def_id
, _
) | ty
::TyGenerator(def_id
, _
, _
) => {
797 // Convert the def-id into a node-id. node-ids are only valid for
798 // the local code in the current crate, so this returns an `Option` in case
799 // the closure comes from another crate. But in that case we wouldn't
800 // be borrowck'ing it, so we can just unwrap:
801 let node_id
= self.tcx
.hir
.as_local_node_id(def_id
).unwrap();
802 let freevar
= self.tcx
.with_freevars(node_id
, |fv
| fv
[field
.index()]);
804 self.tcx
.hir
.name(freevar
.var_id()).to_string()
807 // Might need a revision when the fields in trait RFC is implemented
808 // (https://github.com/rust-lang/rfcs/pull/1546)
810 "End-user description not implemented for field access on `{:?}`",
818 // Retrieve span of given borrow from the current MIR representation
819 crate fn retrieve_borrow_span(&self, borrow
: &BorrowData
) -> Span
{
820 self.mir
.source_info(borrow
.reserve_location
).span
823 // Retrieve type of a place for the current MIR representation
824 fn retrieve_type_for_place(&self, place
: &Place
<'tcx
>) -> Option
<ty
::Ty
> {
826 Place
::Local(local
) => {
827 let local
= &self.mir
.local_decls
[*local
];
830 Place
::Static(ref st
) => Some(st
.ty
),
831 Place
::Projection(ref proj
) => match proj
.elem
{
832 ProjectionElem
::Field(_
, ty
) => Some(ty
),