1 // Copyright 2012-2013 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 // ----------------------------------------------------------------------
14 // Phase 2 of check: we walk down the tree and check that:
15 // 1. assignments are always made to mutable locations;
16 // 2. loans made in overlapping scopes do not conflict
17 // 3. assignments do not affect things loaned out as immutable
18 // 4. moves do not affect things loaned out in any way
19 use self::UseError
::*;
22 use borrowck
::InteriorKind
::{InteriorElement, InteriorField}
;
23 use rustc
::middle
::expr_use_visitor
as euv
;
24 use rustc
::middle
::expr_use_visitor
::MutateMode
;
25 use rustc
::middle
::mem_categorization
as mc
;
26 use rustc
::middle
::mem_categorization
::Categorization
;
27 use rustc
::middle
::region
;
28 use rustc
::ty
::{self, TyCtxt}
;
35 // FIXME (#16118): These functions are intended to allow the borrow checker to
36 // be less precise in its handling of Box while still allowing moves out of a
37 // Box. They should be removed when Unique is removed from LoanPath.
39 fn owned_ptr_base_path
<'a
, 'tcx
>(loan_path
: &'a LoanPath
<'tcx
>) -> &'a LoanPath
<'tcx
> {
40 //! Returns the base of the leftmost dereference of an Unique in
41 //! `loan_path`. If there is no dereference of an Unique in `loan_path`,
42 //! then it just returns `loan_path` itself.
44 return match helper(loan_path
) {
45 Some(new_loan_path
) => new_loan_path
,
46 None
=> loan_path
.clone()
49 fn helper
<'a
, 'tcx
>(loan_path
: &'a LoanPath
<'tcx
>) -> Option
<&'a LoanPath
<'tcx
>> {
50 match loan_path
.kind
{
51 LpVar(_
) | LpUpvar(_
) => None
,
52 LpExtend(ref lp_base
, _
, LpDeref(mc
::Unique
)) => {
53 match helper(&lp_base
) {
55 None
=> Some(&lp_base
)
58 LpDowncast(ref lp_base
, _
) |
59 LpExtend(ref lp_base
, ..) => helper(&lp_base
)
64 fn owned_ptr_base_path_rc
<'tcx
>(loan_path
: &Rc
<LoanPath
<'tcx
>>) -> Rc
<LoanPath
<'tcx
>> {
65 //! The equivalent of `owned_ptr_base_path` for an &Rc<LoanPath> rather than
68 return match helper(loan_path
) {
69 Some(new_loan_path
) => new_loan_path
,
70 None
=> loan_path
.clone()
73 fn helper
<'tcx
>(loan_path
: &Rc
<LoanPath
<'tcx
>>) -> Option
<Rc
<LoanPath
<'tcx
>>> {
74 match loan_path
.kind
{
75 LpVar(_
) | LpUpvar(_
) => None
,
76 LpExtend(ref lp_base
, _
, LpDeref(mc
::Unique
)) => {
77 match helper(lp_base
) {
79 None
=> Some(lp_base
.clone())
82 LpDowncast(ref lp_base
, _
) |
83 LpExtend(ref lp_base
, ..) => helper(lp_base
)
88 struct CheckLoanCtxt
<'a
, 'tcx
: 'a
> {
89 bccx
: &'a BorrowckCtxt
<'a
, 'tcx
>,
90 dfcx_loans
: &'a LoanDataFlow
<'a
, 'tcx
>,
91 move_data
: &'a move_data
::FlowedMoveData
<'a
, 'tcx
>,
92 all_loans
: &'a
[Loan
<'tcx
>],
93 param_env
: &'a ty
::ParameterEnvironment
<'tcx
>,
96 impl<'a
, 'tcx
> euv
::Delegate
<'tcx
> for CheckLoanCtxt
<'a
, 'tcx
> {
98 consume_id
: ast
::NodeId
,
101 mode
: euv
::ConsumeMode
) {
102 debug
!("consume(consume_id={}, cmt={:?}, mode={:?})",
103 consume_id
, cmt
, mode
);
105 self.consume_common(consume_id
, consume_span
, cmt
, mode
);
108 fn matched_pat(&mut self,
109 _matched_pat
: &hir
::Pat
,
111 _mode
: euv
::MatchMode
) { }
113 fn consume_pat(&mut self,
114 consume_pat
: &hir
::Pat
,
116 mode
: euv
::ConsumeMode
) {
117 debug
!("consume_pat(consume_pat={:?}, cmt={:?}, mode={:?})",
122 self.consume_common(consume_pat
.id
, consume_pat
.span
, cmt
, mode
);
126 borrow_id
: ast
::NodeId
,
129 loan_region
: &'tcx ty
::Region
,
131 loan_cause
: euv
::LoanCause
)
133 debug
!("borrow(borrow_id={}, cmt={:?}, loan_region={:?}, \
134 bk={:?}, loan_cause={:?})",
135 borrow_id
, cmt
, loan_region
,
138 if let Some(lp
) = opt_loan_path(&cmt
) {
139 let moved_value_use_kind
= match loan_cause
{
140 euv
::ClosureCapture(_
) => MovedInCapture
,
143 self.check_if_path_is_moved(borrow_id
, borrow_span
, moved_value_use_kind
, &lp
);
146 self.check_for_conflicting_loans(borrow_id
);
150 assignment_id
: ast
::NodeId
,
151 assignment_span
: Span
,
152 assignee_cmt
: mc
::cmt
<'tcx
>,
153 mode
: euv
::MutateMode
)
155 debug
!("mutate(assignment_id={}, assignee_cmt={:?})",
156 assignment_id
, assignee_cmt
);
158 if let Some(lp
) = opt_loan_path(&assignee_cmt
) {
160 MutateMode
::Init
| MutateMode
::JustWrite
=> {
161 // In a case like `path = 1`, then path does not
162 // have to be *FULLY* initialized, but we still
163 // must be careful lest it contains derefs of
165 self.check_if_assigned_path_is_moved(assignee_cmt
.id
,
170 MutateMode
::WriteAndRead
=> {
171 // In a case like `path += 1`, then path must be
172 // fully initialized, since we will read it before
174 self.check_if_path_is_moved(assignee_cmt
.id
,
181 self.check_assignment(assignment_id
, assignment_span
, assignee_cmt
);
184 fn decl_without_init(&mut self, _id
: ast
::NodeId
, _span
: Span
) { }
187 pub fn check_loans
<'a
, 'b
, 'c
, 'tcx
>(bccx
: &BorrowckCtxt
<'a
, 'tcx
>,
188 dfcx_loans
: &LoanDataFlow
<'b
, 'tcx
>,
189 move_data
: &move_data
::FlowedMoveData
<'c
, 'tcx
>,
190 all_loans
: &[Loan
<'tcx
>],
192 debug
!("check_loans(body id={})", body
.value
.id
);
194 let infcx
= bccx
.tcx
.borrowck_fake_infer_ctxt(body
.id());
195 let mut clcx
= CheckLoanCtxt
{
197 dfcx_loans
: dfcx_loans
,
198 move_data
: move_data
,
199 all_loans
: all_loans
,
200 param_env
: &infcx
.parameter_environment
202 euv
::ExprUseVisitor
::new(&mut clcx
, &infcx
).consume_body(body
);
206 enum UseError
<'tcx
> {
208 UseWhileBorrowed(/*loan*/Rc
<LoanPath
<'tcx
>>, /*loan*/Span
)
211 fn compatible_borrow_kinds(borrow_kind1
: ty
::BorrowKind
,
212 borrow_kind2
: ty
::BorrowKind
)
214 borrow_kind1
== ty
::ImmBorrow
&& borrow_kind2
== ty
::ImmBorrow
217 impl<'a
, 'tcx
> CheckLoanCtxt
<'a
, 'tcx
> {
218 pub fn tcx(&self) -> TyCtxt
<'a
, 'tcx
, 'tcx
> { self.bccx.tcx }
220 pub fn each_issued_loan
<F
>(&self, node
: ast
::NodeId
, mut op
: F
) -> bool
where
221 F
: FnMut(&Loan
<'tcx
>) -> bool
,
223 //! Iterates over each loan that has been issued
224 //! on entrance to `node`, regardless of whether it is
225 //! actually *in scope* at that point. Sometimes loans
226 //! are issued for future scopes and thus they may have been
227 //! *issued* but not yet be in effect.
229 self.dfcx_loans
.each_bit_on_entry(node
, |loan_index
| {
230 let loan
= &self.all_loans
[loan_index
];
235 pub fn each_in_scope_loan
<F
>(&self, scope
: region
::CodeExtent
, mut op
: F
) -> bool
where
236 F
: FnMut(&Loan
<'tcx
>) -> bool
,
238 //! Like `each_issued_loan()`, but only considers loans that are
239 //! currently in scope.
241 let tcx
= self.tcx();
242 self.each_issued_loan(scope
.node_id(&tcx
.region_maps
), |loan
| {
243 if tcx
.region_maps
.is_subscope_of(scope
, loan
.kill_scope
) {
251 fn each_in_scope_loan_affecting_path
<F
>(&self,
252 scope
: region
::CodeExtent
,
253 loan_path
: &LoanPath
<'tcx
>,
256 F
: FnMut(&Loan
<'tcx
>) -> bool
,
258 //! Iterates through all of the in-scope loans affecting `loan_path`,
259 //! calling `op`, and ceasing iteration if `false` is returned.
261 // First, we check for a loan restricting the path P being used. This
262 // accounts for borrows of P but also borrows of subpaths, like P.a.b.
263 // Consider the following example:
265 // let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
266 // let y = a; // Conflicts with restriction
268 let loan_path
= owned_ptr_base_path(loan_path
);
269 let cont
= self.each_in_scope_loan(scope
, |loan
| {
271 for restr_path
in &loan
.restricted_paths
{
272 if **restr_path
== *loan_path
{
286 // Next, we must check for *loans* (not restrictions) on the path P or
287 // any base path. This rejects examples like the following:
292 // Limiting this search to *loans* and not *restrictions* means that
293 // examples like the following continue to work:
298 let mut loan_path
= loan_path
;
300 match loan_path
.kind
{
301 LpVar(_
) | LpUpvar(_
) => {
304 LpDowncast(ref lp_base
, _
) |
305 LpExtend(ref lp_base
, ..) => {
306 loan_path
= &lp_base
;
310 let cont
= self.each_in_scope_loan(scope
, |loan
| {
311 if *loan
.loan_path
== *loan_path
{
326 pub fn loans_generated_by(&self, node
: ast
::NodeId
) -> Vec
<usize> {
327 //! Returns a vector of the loans that are generated as
330 let mut result
= Vec
::new();
331 self.dfcx_loans
.each_gen_bit(node
, |loan_index
| {
332 result
.push(loan_index
);
338 pub fn check_for_conflicting_loans(&self, node
: ast
::NodeId
) {
339 //! Checks to see whether any of the loans that are issued
340 //! on entrance to `node` conflict with loans that have already been
341 //! issued when we enter `node` (for example, we do not
342 //! permit two `&mut` borrows of the same variable).
344 //! (Note that some loans can be *issued* without necessarily
345 //! taking effect yet.)
347 debug
!("check_for_conflicting_loans(node={:?})", node
);
349 let new_loan_indices
= self.loans_generated_by(node
);
350 debug
!("new_loan_indices = {:?}", new_loan_indices
);
352 for &new_loan_index
in &new_loan_indices
{
353 self.each_issued_loan(node
, |issued_loan
| {
354 let new_loan
= &self.all_loans
[new_loan_index
];
355 // Only report an error for the first issued loan that conflicts
356 // to avoid O(n^2) errors.
357 self.report_error_if_loans_conflict(issued_loan
, new_loan
)
361 for (i
, &x
) in new_loan_indices
.iter().enumerate() {
362 let old_loan
= &self.all_loans
[x
];
363 for &y
in &new_loan_indices
[(i
+1) ..] {
364 let new_loan
= &self.all_loans
[y
];
365 self.report_error_if_loans_conflict(old_loan
, new_loan
);
370 pub fn report_error_if_loans_conflict(&self,
371 old_loan
: &Loan
<'tcx
>,
372 new_loan
: &Loan
<'tcx
>)
374 //! Checks whether `old_loan` and `new_loan` can safely be issued
377 debug
!("report_error_if_loans_conflict(old_loan={:?}, new_loan={:?})",
381 // Should only be called for loans that are in scope at the same time.
382 assert
!(self.tcx().region_maps
.scopes_intersect(old_loan
.kill_scope
,
383 new_loan
.kill_scope
));
385 self.report_error_if_loan_conflicts_with_restriction(
386 old_loan
, new_loan
, old_loan
, new_loan
) &&
387 self.report_error_if_loan_conflicts_with_restriction(
388 new_loan
, old_loan
, old_loan
, new_loan
)
391 pub fn report_error_if_loan_conflicts_with_restriction(&self,
394 old_loan
: &Loan
<'tcx
>,
395 new_loan
: &Loan
<'tcx
>)
397 //! Checks whether the restrictions introduced by `loan1` would
398 //! prohibit `loan2`. Returns false if an error is reported.
400 debug
!("report_error_if_loan_conflicts_with_restriction(\
401 loan1={:?}, loan2={:?})",
405 if compatible_borrow_kinds(loan1
.kind
, loan2
.kind
) {
409 let loan2_base_path
= owned_ptr_base_path_rc(&loan2
.loan_path
);
410 for restr_path
in &loan1
.restricted_paths
{
411 if *restr_path
!= loan2_base_path { continue; }
413 // If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
414 // normally generate a rather confusing message (in this case, for multiple mutable
417 // error: cannot borrow `x.b` as mutable more than once at a time
418 // note: previous borrow of `x.a` occurs here; the mutable borrow prevents
419 // subsequent moves, borrows, or modification of `x.a` until the borrow ends
421 // What we want to do instead is get the 'common ancestor' of the two borrow paths and
422 // use that for most of the message instead, giving is something like this:
424 // error: cannot borrow `x` as mutable more than once at a time
425 // note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
426 // borrow prevents subsequent moves, borrows, or modification of `x` until the
429 let common
= new_loan
.loan_path
.common(&old_loan
.loan_path
);
430 let (nl
, ol
, new_loan_msg
, old_loan_msg
) = {
431 if new_loan
.loan_path
.has_fork(&old_loan
.loan_path
) && common
.is_some() {
432 let nl
= self.bccx
.loan_path_to_string(&common
.unwrap());
434 let new_loan_msg
= format
!(" (via `{}`)",
435 self.bccx
.loan_path_to_string(
436 &new_loan
.loan_path
));
437 let old_loan_msg
= format
!(" (via `{}`)",
438 self.bccx
.loan_path_to_string(
439 &old_loan
.loan_path
));
440 (nl
, ol
, new_loan_msg
, old_loan_msg
)
442 (self.bccx
.loan_path_to_string(&new_loan
.loan_path
),
443 self.bccx
.loan_path_to_string(&old_loan
.loan_path
),
449 let ol_pronoun
= if new_loan
.loan_path
== old_loan
.loan_path
{
455 // We want to assemble all the relevant locations for the error.
457 // 1. Where did the new loan occur.
458 // - if due to closure creation, where was the variable used in closure?
459 // 2. Where did old loan occur.
460 // 3. Where does old loan expire.
462 let previous_end_span
=
463 self.tcx().hir
.span(old_loan
.kill_scope
.node_id(&self.tcx().region_maps
))
466 let mut err
= match (new_loan
.kind
, old_loan
.kind
) {
467 (ty
::MutBorrow
, ty
::MutBorrow
) => {
468 let mut err
= struct_span_err
!(self.bccx
, new_loan
.span
, E0499
,
469 "cannot borrow `{}`{} as mutable \
470 more than once at a time",
474 &format
!("first mutable borrow occurs here{}", old_loan_msg
));
477 &format
!("second mutable borrow occurs here{}", new_loan_msg
));
480 &format
!("first borrow ends here"));
484 (ty
::UniqueImmBorrow
, ty
::UniqueImmBorrow
) => {
485 let mut err
= struct_span_err
!(self.bccx
, new_loan
.span
, E0524
,
486 "two closures require unique access to `{}` \
491 &format
!("first closure is constructed here"));
494 &format
!("second closure is constructed here"));
497 &format
!("borrow from first closure ends here"));
501 (ty
::UniqueImmBorrow
, _
) => {
502 let mut err
= struct_span_err
!(self.bccx
, new_loan
.span
, E0500
,
503 "closure requires unique access to `{}` \
504 but {} is already borrowed{}",
505 nl
, ol_pronoun
, old_loan_msg
);
508 &format
!("closure construction occurs here{}", new_loan_msg
));
511 &format
!("borrow occurs here{}", old_loan_msg
));
514 &format
!("borrow ends here"));
518 (_
, ty
::UniqueImmBorrow
) => {
519 let mut err
= struct_span_err
!(self.bccx
, new_loan
.span
, E0501
,
520 "cannot borrow `{}`{} as {} because \
521 previous closure requires unique access",
522 nl
, new_loan_msg
, new_loan
.kind
.to_user_str());
525 &format
!("borrow occurs here{}", new_loan_msg
));
528 &format
!("closure construction occurs here{}", old_loan_msg
));
531 &format
!("borrow from closure ends here"));
536 let mut err
= struct_span_err
!(self.bccx
, new_loan
.span
, E0502
,
537 "cannot borrow `{}`{} as {} because \
538 {} is also borrowed as {}{}",
541 new_loan
.kind
.to_user_str(),
543 old_loan
.kind
.to_user_str(),
547 &format
!("{} borrow occurs here{}",
548 new_loan
.kind
.to_user_str(),
552 &format
!("{} borrow occurs here{}",
553 old_loan
.kind
.to_user_str(),
557 &format
!("{} borrow ends here",
558 old_loan
.kind
.to_user_str()));
563 match new_loan
.cause
{
564 euv
::ClosureCapture(span
) => {
567 &format
!("borrow occurs due to use of `{}` in closure", nl
));
572 match old_loan
.cause
{
573 euv
::ClosureCapture(span
) => {
576 &format
!("previous borrow occurs due to use of `{}` in closure",
589 fn consume_common(&self,
593 mode
: euv
::ConsumeMode
) {
594 if let Some(lp
) = opt_loan_path(&cmt
) {
595 let moved_value_use_kind
= match mode
{
597 self.check_for_copy_of_frozen_path(id
, span
, &lp
);
601 match self.move_data
.kind_of_move_of_path(id
, &lp
) {
603 // Sometimes moves don't have a move kind;
604 // this either means that the original move
605 // was from something illegal to move,
606 // or was moved from referent of an unsafe
607 // pointer or something like that.
611 self.check_for_move_of_borrowed_path(id
, span
,
613 if move_kind
== move_data
::Captured
{
623 self.check_if_path_is_moved(id
, span
, moved_value_use_kind
, &lp
);
627 fn check_for_copy_of_frozen_path(&self,
630 copy_path
: &LoanPath
<'tcx
>) {
631 match self.analyze_restrictions_on_use(id
, copy_path
, ty
::ImmBorrow
) {
633 UseWhileBorrowed(loan_path
, loan_span
) => {
634 struct_span_err
!(self.bccx
, span
, E0503
,
635 "cannot use `{}` because it was mutably borrowed",
636 &self.bccx
.loan_path_to_string(copy_path
))
637 .span_label(loan_span
,
638 &format
!("borrow of `{}` occurs here",
639 &self.bccx
.loan_path_to_string(&loan_path
))
642 &format
!("use of borrowed `{}`",
643 &self.bccx
.loan_path_to_string(&loan_path
)))
649 fn check_for_move_of_borrowed_path(&self,
652 move_path
: &LoanPath
<'tcx
>,
653 move_kind
: move_data
::MoveKind
) {
654 // We want to detect if there are any loans at all, so we search for
655 // any loans incompatible with MutBorrrow, since all other kinds of
656 // loans are incompatible with that.
657 match self.analyze_restrictions_on_use(id
, move_path
, ty
::MutBorrow
) {
659 UseWhileBorrowed(loan_path
, loan_span
) => {
660 let mut err
= match move_kind
{
661 move_data
::Captured
=> {
662 let mut err
= struct_span_err
!(self.bccx
, span
, E0504
,
663 "cannot move `{}` into closure because it is borrowed",
664 &self.bccx
.loan_path_to_string(move_path
));
667 &format
!("borrow of `{}` occurs here",
668 &self.bccx
.loan_path_to_string(&loan_path
))
672 &format
!("move into closure occurs here")
676 move_data
::Declared
|
677 move_data
::MoveExpr
|
678 move_data
::MovePat
=> {
679 let mut err
= struct_span_err
!(self.bccx
, span
, E0505
,
680 "cannot move out of `{}` because it is borrowed",
681 &self.bccx
.loan_path_to_string(move_path
));
684 &format
!("borrow of `{}` occurs here",
685 &self.bccx
.loan_path_to_string(&loan_path
))
689 &format
!("move out of `{}` occurs here",
690 &self.bccx
.loan_path_to_string(move_path
))
701 pub fn analyze_restrictions_on_use(&self,
702 expr_id
: ast
::NodeId
,
703 use_path
: &LoanPath
<'tcx
>,
704 borrow_kind
: ty
::BorrowKind
)
706 debug
!("analyze_restrictions_on_use(expr_id={}, use_path={:?})",
707 self.tcx().hir
.node_to_string(expr_id
),
712 self.each_in_scope_loan_affecting_path(
713 self.tcx().region_maps
.node_extent(expr_id
), use_path
, |loan
| {
714 if !compatible_borrow_kinds(loan
.kind
, borrow_kind
) {
715 ret
= UseWhileBorrowed(loan
.loan_path
.clone(), loan
.span
);
725 /// Reports an error if `expr` (which should be a path)
726 /// is using a moved/uninitialized value
727 fn check_if_path_is_moved(&self,
730 use_kind
: MovedValueUseKind
,
731 lp
: &Rc
<LoanPath
<'tcx
>>) {
732 debug
!("check_if_path_is_moved(id={}, use_kind={:?}, lp={:?})",
735 // FIXME (22079): if you find yourself tempted to cut and paste
736 // the body below and then specializing the error reporting,
737 // consider refactoring this instead!
739 let base_lp
= owned_ptr_base_path_rc(lp
);
740 self.move_data
.each_move_of(id
, &base_lp
, |the_move
, moved_lp
| {
741 self.bccx
.report_use_of_moved_value(
752 /// Reports an error if assigning to `lp` will use a
753 /// moved/uninitialized value. Mainly this is concerned with
754 /// detecting derefs of uninitialized pointers.
760 /// a = 10; // ok, even though a is uninitialized
762 /// struct Point { x: u32, y: u32 }
764 /// p.x = 22; // ok, even though `p` is uninitialized
766 /// let p: Box<Point>;
767 /// (*p).x = 22; // not ok, p is uninitialized, can't deref
769 fn check_if_assigned_path_is_moved(&self,
772 use_kind
: MovedValueUseKind
,
773 lp
: &Rc
<LoanPath
<'tcx
>>)
776 LpVar(_
) | LpUpvar(_
) => {
777 // assigning to `x` does not require that `x` is initialized
779 LpDowncast(ref lp_base
, _
) => {
780 // assigning to `(P->Variant).f` is ok if assigning to `P` is ok
781 self.check_if_assigned_path_is_moved(id
, span
,
784 LpExtend(ref lp_base
, _
, LpInterior(_
, InteriorField(_
))) => {
785 match lp_base
.to_type().sty
{
786 ty
::TyAdt(def
, _
) if def
.has_dtor(self.tcx()) => {
787 // In the case where the owner implements drop, then
788 // the path must be initialized to prevent a case of
789 // partial reinitialization
791 // FIXME (22079): could refactor via hypothetical
792 // generalized check_if_path_is_moved
793 let loan_path
= owned_ptr_base_path_rc(lp_base
);
794 self.move_data
.each_move_of(id
, &loan_path
, |_
, _
| {
796 .report_partial_reinitialization_of_uninitialized_structure(
806 // assigning to `P.f` is ok if assigning to `P` is ok
807 self.check_if_assigned_path_is_moved(id
, span
,
810 LpExtend(ref lp_base
, _
, LpInterior(_
, InteriorElement(..))) |
811 LpExtend(ref lp_base
, _
, LpDeref(_
)) => {
812 // assigning to `P[i]` requires `P` is initialized
813 // assigning to `(*P)` requires `P` is initialized
814 self.check_if_path_is_moved(id
, span
, use_kind
, lp_base
);
819 fn check_assignment(&self,
820 assignment_id
: ast
::NodeId
,
821 assignment_span
: Span
,
822 assignee_cmt
: mc
::cmt
<'tcx
>) {
823 debug
!("check_assignment(assignee_cmt={:?})", assignee_cmt
);
825 // Check that we don't invalidate any outstanding loans
826 if let Some(loan_path
) = opt_loan_path(&assignee_cmt
) {
827 let scope
= self.tcx().region_maps
.node_extent(assignment_id
);
828 self.each_in_scope_loan_affecting_path(scope
, &loan_path
, |loan
| {
829 self.report_illegal_mutation(assignment_span
, &loan_path
, loan
);
834 // Check for reassignments to (immutable) local variables. This
835 // needs to be done here instead of in check_loans because we
836 // depend on move data.
837 if let Categorization
::Local(local_id
) = assignee_cmt
.cat
{
838 let lp
= opt_loan_path(&assignee_cmt
).unwrap();
839 self.move_data
.each_assignment_of(assignment_id
, &lp
, |assign
| {
840 if assignee_cmt
.mutbl
.is_mutable() {
841 self.tcx().used_mut_nodes
.borrow_mut().insert(local_id
);
843 self.bccx
.report_reassigned_immutable_variable(
854 pub fn report_illegal_mutation(&self,
856 loan_path
: &LoanPath
<'tcx
>,
858 struct_span_err
!(self.bccx
, span
, E0506
,
859 "cannot assign to `{}` because it is borrowed",
860 self.bccx
.loan_path_to_string(loan_path
))
861 .span_label(loan
.span
,
862 &format
!("borrow of `{}` occurs here",
863 self.bccx
.loan_path_to_string(loan_path
)))
865 &format
!("assignment to borrowed `{}` occurs here",
866 self.bccx
.loan_path_to_string(loan_path
)))