]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/check_loans.rs
Auto merge of #26378 - arielb1:unused-mut, r=pnkfelix
[rust.git] / src / librustc_borrowck / borrowck / check_loans.rs
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.
4 //
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.
10
11 // ----------------------------------------------------------------------
12 // Checking loans
13 //
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::*;
20
21 use borrowck::*;
22 use borrowck::InteriorKind::{InteriorElement, InteriorField};
23 use rustc::middle::expr_use_visitor as euv;
24 use rustc::middle::infer;
25 use rustc::middle::mem_categorization as mc;
26 use rustc::middle::region;
27 use rustc::middle::ty;
28 use syntax::ast;
29 use syntax::codemap::Span;
30
31 use std::rc::Rc;
32
33 // FIXME (#16118): These functions are intended to allow the borrow checker to
34 // be less precise in its handling of Box while still allowing moves out of a
35 // Box. They should be removed when Unique is removed from LoanPath.
36
37 fn owned_ptr_base_path<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> &'a LoanPath<'tcx> {
38     //! Returns the base of the leftmost dereference of an Unique in
39     //! `loan_path`. If there is no dereference of an Unique in `loan_path`,
40     //! then it just returns `loan_path` itself.
41
42     return match helper(loan_path) {
43         Some(new_loan_path) => new_loan_path,
44         None => loan_path.clone()
45     };
46
47     fn helper<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> Option<&'a LoanPath<'tcx>> {
48         match loan_path.kind {
49             LpVar(_) | LpUpvar(_) => None,
50             LpExtend(ref lp_base, _, LpDeref(mc::Unique)) => {
51                 match helper(&**lp_base) {
52                     v @ Some(_) => v,
53                     None => Some(&**lp_base)
54                 }
55             }
56             LpDowncast(ref lp_base, _) |
57             LpExtend(ref lp_base, _, _) => helper(&**lp_base)
58         }
59     }
60 }
61
62 fn owned_ptr_base_path_rc<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Rc<LoanPath<'tcx>> {
63     //! The equivalent of `owned_ptr_base_path` for an &Rc<LoanPath> rather than
64     //! a &LoanPath.
65
66     return match helper(loan_path) {
67         Some(new_loan_path) => new_loan_path,
68         None => loan_path.clone()
69     };
70
71     fn helper<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Option<Rc<LoanPath<'tcx>>> {
72         match loan_path.kind {
73             LpVar(_) | LpUpvar(_) => None,
74             LpExtend(ref lp_base, _, LpDeref(mc::Unique)) => {
75                 match helper(lp_base) {
76                     v @ Some(_) => v,
77                     None => Some(lp_base.clone())
78                 }
79             }
80             LpDowncast(ref lp_base, _) |
81             LpExtend(ref lp_base, _, _) => helper(lp_base)
82         }
83     }
84 }
85
86 struct CheckLoanCtxt<'a, 'tcx: 'a> {
87     bccx: &'a BorrowckCtxt<'a, 'tcx>,
88     dfcx_loans: &'a LoanDataFlow<'a, 'tcx>,
89     move_data: move_data::FlowedMoveData<'a, 'tcx>,
90     all_loans: &'a [Loan<'tcx>],
91     param_env: &'a ty::ParameterEnvironment<'a, 'tcx>,
92 }
93
94 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
95     fn consume(&mut self,
96                consume_id: ast::NodeId,
97                consume_span: Span,
98                cmt: mc::cmt<'tcx>,
99                mode: euv::ConsumeMode) {
100         debug!("consume(consume_id={}, cmt={:?}, mode={:?})",
101                consume_id, cmt, mode);
102
103         self.consume_common(consume_id, consume_span, cmt, mode);
104     }
105
106     fn matched_pat(&mut self,
107                    _matched_pat: &ast::Pat,
108                    _cmt: mc::cmt,
109                    _mode: euv::MatchMode) { }
110
111     fn consume_pat(&mut self,
112                    consume_pat: &ast::Pat,
113                    cmt: mc::cmt<'tcx>,
114                    mode: euv::ConsumeMode) {
115         debug!("consume_pat(consume_pat={:?}, cmt={:?}, mode={:?})",
116                consume_pat,
117                cmt,
118                mode);
119
120         self.consume_common(consume_pat.id, consume_pat.span, cmt, mode);
121     }
122
123     fn borrow(&mut self,
124               borrow_id: ast::NodeId,
125               borrow_span: Span,
126               cmt: mc::cmt<'tcx>,
127               loan_region: ty::Region,
128               bk: ty::BorrowKind,
129               loan_cause: euv::LoanCause)
130     {
131         debug!("borrow(borrow_id={}, cmt={:?}, loan_region={:?}, \
132                bk={:?}, loan_cause={:?})",
133                borrow_id, cmt, loan_region,
134                bk, loan_cause);
135
136         match opt_loan_path(&cmt) {
137             Some(lp) => {
138                 let moved_value_use_kind = match loan_cause {
139                     euv::ClosureCapture(_) => MovedInCapture,
140                     _ => MovedInUse,
141                 };
142                 self.check_if_path_is_moved(borrow_id, borrow_span, moved_value_use_kind, &lp);
143             }
144             None => { }
145         }
146
147         self.check_for_conflicting_loans(region::CodeExtent::from_node_id(borrow_id));
148     }
149
150     fn mutate(&mut self,
151               assignment_id: ast::NodeId,
152               assignment_span: Span,
153               assignee_cmt: mc::cmt<'tcx>,
154               mode: euv::MutateMode)
155     {
156         debug!("mutate(assignment_id={}, assignee_cmt={:?})",
157                assignment_id, assignee_cmt);
158
159         match opt_loan_path(&assignee_cmt) {
160             Some(lp) => {
161                 match mode {
162                     euv::Init | euv::JustWrite => {
163                         // In a case like `path = 1`, then path does not
164                         // have to be *FULLY* initialized, but we still
165                         // must be careful lest it contains derefs of
166                         // pointers.
167                         self.check_if_assigned_path_is_moved(assignee_cmt.id,
168                                                              assignment_span,
169                                                              MovedInUse,
170                                                              &lp);
171                     }
172                     euv::WriteAndRead => {
173                         // In a case like `path += 1`, then path must be
174                         // fully initialized, since we will read it before
175                         // we write it.
176                         self.check_if_path_is_moved(assignee_cmt.id,
177                                                     assignment_span,
178                                                     MovedInUse,
179                                                     &lp);
180                     }
181                 }
182             }
183             None => { }
184         }
185
186         self.check_assignment(assignment_id, assignment_span, assignee_cmt);
187     }
188
189     fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) { }
190 }
191
192 pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
193                                      dfcx_loans: &LoanDataFlow<'b, 'tcx>,
194                                      move_data: move_data::FlowedMoveData<'c, 'tcx>,
195                                      all_loans: &[Loan<'tcx>],
196                                      fn_id: ast::NodeId,
197                                      decl: &ast::FnDecl,
198                                      body: &ast::Block) {
199     debug!("check_loans(body id={})", body.id);
200
201     let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
202     let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env), false);
203
204     let mut clcx = CheckLoanCtxt {
205         bccx: bccx,
206         dfcx_loans: dfcx_loans,
207         move_data: move_data,
208         all_loans: all_loans,
209         param_env: &infcx.parameter_environment
210     };
211
212     {
213         let mut euv = euv::ExprUseVisitor::new(&mut clcx, &infcx);
214         euv.walk_fn(decl, body);
215     }
216 }
217
218 #[derive(PartialEq)]
219 enum UseError<'tcx> {
220     UseOk,
221     UseWhileBorrowed(/*loan*/Rc<LoanPath<'tcx>>, /*loan*/Span)
222 }
223
224 fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind,
225                            borrow_kind2: ty::BorrowKind)
226                            -> bool {
227     borrow_kind1 == ty::ImmBorrow && borrow_kind2 == ty::ImmBorrow
228 }
229
230 impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
231     pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.bccx.tcx }
232
233     pub fn each_issued_loan<F>(&self, scope: region::CodeExtent, mut op: F) -> bool where
234         F: FnMut(&Loan<'tcx>) -> bool,
235     {
236         //! Iterates over each loan that has been issued
237         //! on entrance to `scope`, regardless of whether it is
238         //! actually *in scope* at that point.  Sometimes loans
239         //! are issued for future scopes and thus they may have been
240         //! *issued* but not yet be in effect.
241
242         self.dfcx_loans.each_bit_on_entry(scope.node_id(), |loan_index| {
243             let loan = &self.all_loans[loan_index];
244             op(loan)
245         })
246     }
247
248     pub fn each_in_scope_loan<F>(&self, scope: region::CodeExtent, mut op: F) -> bool where
249         F: FnMut(&Loan<'tcx>) -> bool,
250     {
251         //! Like `each_issued_loan()`, but only considers loans that are
252         //! currently in scope.
253
254         let tcx = self.tcx();
255         self.each_issued_loan(scope, |loan| {
256             if tcx.region_maps.is_subscope_of(scope, loan.kill_scope) {
257                 op(loan)
258             } else {
259                 true
260             }
261         })
262     }
263
264     fn each_in_scope_loan_affecting_path<F>(&self,
265                                             scope: region::CodeExtent,
266                                             loan_path: &LoanPath<'tcx>,
267                                             mut op: F)
268                                             -> bool where
269         F: FnMut(&Loan<'tcx>) -> bool,
270     {
271         //! Iterates through all of the in-scope loans affecting `loan_path`,
272         //! calling `op`, and ceasing iteration if `false` is returned.
273
274         // First, we check for a loan restricting the path P being used. This
275         // accounts for borrows of P but also borrows of subpaths, like P.a.b.
276         // Consider the following example:
277         //
278         //     let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
279         //     let y = a;          // Conflicts with restriction
280
281         let loan_path = owned_ptr_base_path(loan_path);
282         let cont = self.each_in_scope_loan(scope, |loan| {
283             let mut ret = true;
284             for restr_path in &loan.restricted_paths {
285                 if **restr_path == *loan_path {
286                     if !op(loan) {
287                         ret = false;
288                         break;
289                     }
290                 }
291             }
292             ret
293         });
294
295         if !cont {
296             return false;
297         }
298
299         // Next, we must check for *loans* (not restrictions) on the path P or
300         // any base path. This rejects examples like the following:
301         //
302         //     let x = &mut a.b;
303         //     let y = a.b.c;
304         //
305         // Limiting this search to *loans* and not *restrictions* means that
306         // examples like the following continue to work:
307         //
308         //     let x = &mut a.b;
309         //     let y = a.c;
310
311         let mut loan_path = loan_path;
312         loop {
313             match loan_path.kind {
314                 LpVar(_) | LpUpvar(_) => {
315                     break;
316                 }
317                 LpDowncast(ref lp_base, _) |
318                 LpExtend(ref lp_base, _, _) => {
319                     loan_path = &**lp_base;
320                 }
321             }
322
323             let cont = self.each_in_scope_loan(scope, |loan| {
324                 if *loan.loan_path == *loan_path {
325                     op(loan)
326                 } else {
327                     true
328                 }
329             });
330
331             if !cont {
332                 return false;
333             }
334         }
335
336         return true;
337     }
338
339     pub fn loans_generated_by(&self, scope: region::CodeExtent) -> Vec<usize> {
340         //! Returns a vector of the loans that are generated as
341         //! we enter `scope`.
342
343         let mut result = Vec::new();
344         self.dfcx_loans.each_gen_bit(scope.node_id(), |loan_index| {
345             result.push(loan_index);
346             true
347         });
348         return result;
349     }
350
351     pub fn check_for_conflicting_loans(&self, scope: region::CodeExtent) {
352         //! Checks to see whether any of the loans that are issued
353         //! on entrance to `scope` conflict with loans that have already been
354         //! issued when we enter `scope` (for example, we do not
355         //! permit two `&mut` borrows of the same variable).
356         //!
357         //! (Note that some loans can be *issued* without necessarily
358         //! taking effect yet.)
359
360         debug!("check_for_conflicting_loans(scope={:?})", scope);
361
362         let new_loan_indices = self.loans_generated_by(scope);
363         debug!("new_loan_indices = {:?}", new_loan_indices);
364
365         self.each_issued_loan(scope, |issued_loan| {
366             for &new_loan_index in &new_loan_indices {
367                 let new_loan = &self.all_loans[new_loan_index];
368                 self.report_error_if_loans_conflict(issued_loan, new_loan);
369             }
370             true
371         });
372
373         for (i, &x) in new_loan_indices.iter().enumerate() {
374             let old_loan = &self.all_loans[x];
375             for &y in &new_loan_indices[(i+1) ..] {
376                 let new_loan = &self.all_loans[y];
377                 self.report_error_if_loans_conflict(old_loan, new_loan);
378             }
379         }
380     }
381
382     pub fn report_error_if_loans_conflict(&self,
383                                           old_loan: &Loan<'tcx>,
384                                           new_loan: &Loan<'tcx>) {
385         //! Checks whether `old_loan` and `new_loan` can safely be issued
386         //! simultaneously.
387
388         debug!("report_error_if_loans_conflict(old_loan={:?}, new_loan={:?})",
389                old_loan,
390                new_loan);
391
392         // Should only be called for loans that are in scope at the same time.
393         assert!(self.tcx().region_maps.scopes_intersect(old_loan.kill_scope,
394                                                         new_loan.kill_scope));
395
396         self.report_error_if_loan_conflicts_with_restriction(
397             old_loan, new_loan, old_loan, new_loan) &&
398         self.report_error_if_loan_conflicts_with_restriction(
399             new_loan, old_loan, old_loan, new_loan);
400     }
401
402     pub fn report_error_if_loan_conflicts_with_restriction(&self,
403                                                            loan1: &Loan<'tcx>,
404                                                            loan2: &Loan<'tcx>,
405                                                            old_loan: &Loan<'tcx>,
406                                                            new_loan: &Loan<'tcx>)
407                                                            -> bool {
408         //! Checks whether the restrictions introduced by `loan1` would
409         //! prohibit `loan2`. Returns false if an error is reported.
410
411         debug!("report_error_if_loan_conflicts_with_restriction(\
412                 loan1={:?}, loan2={:?})",
413                loan1,
414                loan2);
415
416         if compatible_borrow_kinds(loan1.kind, loan2.kind) {
417             return true;
418         }
419
420         let loan2_base_path = owned_ptr_base_path_rc(&loan2.loan_path);
421         for restr_path in &loan1.restricted_paths {
422             if *restr_path != loan2_base_path { continue; }
423
424             // If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
425             // normally generate a rather confusing message (in this case, for multiple mutable
426             // borrows):
427             //
428             //     error: cannot borrow `x.b` as mutable more than once at a time
429             //     note: previous borrow of `x.a` occurs here; the mutable borrow prevents
430             //     subsequent moves, borrows, or modification of `x.a` until the borrow ends
431             //
432             // What we want to do instead is get the 'common ancestor' of the two borrow paths and
433             // use that for most of the message instead, giving is something like this:
434             //
435             //     error: cannot borrow `x` as mutable more than once at a time
436             //     note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
437             //     borrow prevents subsequent moves, borrows, or modification of `x` until the
438             //     borrow ends
439
440             let common = new_loan.loan_path.common(&*old_loan.loan_path);
441             let (nl, ol, new_loan_msg, old_loan_msg) =
442                 if new_loan.loan_path.has_fork(&*old_loan.loan_path) && common.is_some() {
443                     let nl = self.bccx.loan_path_to_string(&common.unwrap());
444                     let ol = nl.clone();
445                     let new_loan_msg = format!(" (here through borrowing `{}`)",
446                                                self.bccx.loan_path_to_string(
447                                                    &*new_loan.loan_path));
448                     let old_loan_msg = format!(" (through borrowing `{}`)",
449                                                self.bccx.loan_path_to_string(
450                                                    &*old_loan.loan_path));
451                     (nl, ol, new_loan_msg, old_loan_msg)
452                 } else {
453                     (self.bccx.loan_path_to_string(&*new_loan.loan_path),
454                      self.bccx.loan_path_to_string(&*old_loan.loan_path),
455                      String::new(), String::new())
456                 };
457
458             let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
459                 "it".to_string()
460             } else {
461                 format!("`{}`", ol)
462             };
463
464             match (new_loan.kind, old_loan.kind) {
465                 (ty::MutBorrow, ty::MutBorrow) => {
466                     self.bccx.span_err(
467                         new_loan.span,
468                         &format!("cannot borrow `{}`{} as mutable \
469                                 more than once at a time",
470                                 nl, new_loan_msg))
471                 }
472
473                 (ty::UniqueImmBorrow, _) => {
474                     self.bccx.span_err(
475                         new_loan.span,
476                         &format!("closure requires unique access to `{}` \
477                                 but {} is already borrowed{}",
478                                 nl, ol_pronoun, old_loan_msg));
479                 }
480
481                 (_, ty::UniqueImmBorrow) => {
482                     self.bccx.span_err(
483                         new_loan.span,
484                         &format!("cannot borrow `{}`{} as {} because \
485                                 previous closure requires unique access",
486                                 nl, new_loan_msg, new_loan.kind.to_user_str()));
487                 }
488
489                 (_, _) => {
490                     self.bccx.span_err(
491                         new_loan.span,
492                         &format!("cannot borrow `{}`{} as {} because \
493                                 {} is also borrowed as {}{}",
494                                 nl,
495                                 new_loan_msg,
496                                 new_loan.kind.to_user_str(),
497                                 ol_pronoun,
498                                 old_loan.kind.to_user_str(),
499                                 old_loan_msg));
500                 }
501             }
502
503             match new_loan.cause {
504                 euv::ClosureCapture(span) => {
505                     self.bccx.span_note(
506                         span,
507                         &format!("borrow occurs due to use of `{}` in closure",
508                                 nl));
509                 }
510                 _ => { }
511             }
512
513             let rule_summary = match old_loan.kind {
514                 ty::MutBorrow => {
515                     format!("the mutable borrow prevents subsequent \
516                             moves, borrows, or modification of `{0}` \
517                             until the borrow ends",
518                             ol)
519                 }
520
521                 ty::ImmBorrow => {
522                     format!("the immutable borrow prevents subsequent \
523                             moves or mutable borrows of `{0}` \
524                             until the borrow ends",
525                             ol)
526                 }
527
528                 ty::UniqueImmBorrow => {
529                     format!("the unique capture prevents subsequent \
530                             moves or borrows of `{0}` \
531                             until the borrow ends",
532                             ol)
533                 }
534             };
535
536             let borrow_summary = match old_loan.cause {
537                 euv::ClosureCapture(_) => {
538                     format!("previous borrow of `{}` occurs here{} due to \
539                             use in closure",
540                             ol, old_loan_msg)
541                 }
542
543                 euv::OverloadedOperator(..) |
544                 euv::AddrOf(..) |
545                 euv::AutoRef(..) |
546                 euv::AutoUnsafe(..) |
547                 euv::ClosureInvocation(..) |
548                 euv::ForLoop(..) |
549                 euv::RefBinding(..) |
550                 euv::MatchDiscriminant(..) => {
551                     format!("previous borrow of `{}` occurs here{}",
552                             ol, old_loan_msg)
553                 }
554             };
555
556             self.bccx.span_note(
557                 old_loan.span,
558                 &format!("{}; {}", borrow_summary, rule_summary));
559
560             let old_loan_span = self.tcx().map.span(old_loan.kill_scope.node_id());
561             self.bccx.span_end_note(old_loan_span,
562                                     "previous borrow ends here");
563
564             return false;
565         }
566
567         true
568     }
569
570     fn consume_common(&self,
571                       id: ast::NodeId,
572                       span: Span,
573                       cmt: mc::cmt<'tcx>,
574                       mode: euv::ConsumeMode) {
575         match opt_loan_path(&cmt) {
576             Some(lp) => {
577                 let moved_value_use_kind = match mode {
578                     euv::Copy => {
579                         self.check_for_copy_of_frozen_path(id, span, &*lp);
580                         MovedInUse
581                     }
582                     euv::Move(_) => {
583                         match self.move_data.kind_of_move_of_path(id, &lp) {
584                             None => {
585                                 // Sometimes moves don't have a move kind;
586                                 // this either means that the original move
587                                 // was from something illegal to move,
588                                 // or was moved from referent of an unsafe
589                                 // pointer or something like that.
590                                 MovedInUse
591                             }
592                             Some(move_kind) => {
593                                 self.check_for_move_of_borrowed_path(id, span,
594                                                                      &*lp, move_kind);
595                                 if move_kind == move_data::Captured {
596                                     MovedInCapture
597                                 } else {
598                                     MovedInUse
599                                 }
600                             }
601                         }
602                     }
603                 };
604
605                 self.check_if_path_is_moved(id, span, moved_value_use_kind, &lp);
606             }
607             None => { }
608         }
609     }
610
611     fn check_for_copy_of_frozen_path(&self,
612                                      id: ast::NodeId,
613                                      span: Span,
614                                      copy_path: &LoanPath<'tcx>) {
615         match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
616             UseOk => { }
617             UseWhileBorrowed(loan_path, loan_span) => {
618                 self.bccx.span_err(
619                     span,
620                     &format!("cannot use `{}` because it was mutably borrowed",
621                             &self.bccx.loan_path_to_string(copy_path))
622                     );
623                 self.bccx.span_note(
624                     loan_span,
625                     &format!("borrow of `{}` occurs here",
626                             &self.bccx.loan_path_to_string(&*loan_path))
627                     );
628             }
629         }
630     }
631
632     fn check_for_move_of_borrowed_path(&self,
633                                        id: ast::NodeId,
634                                        span: Span,
635                                        move_path: &LoanPath<'tcx>,
636                                        move_kind: move_data::MoveKind) {
637         // We want to detect if there are any loans at all, so we search for
638         // any loans incompatible with MutBorrrow, since all other kinds of
639         // loans are incompatible with that.
640         match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) {
641             UseOk => { }
642             UseWhileBorrowed(loan_path, loan_span) => {
643                 let err_message = match move_kind {
644                     move_data::Captured =>
645                         format!("cannot move `{}` into closure because it is borrowed",
646                                 &self.bccx.loan_path_to_string(move_path)),
647                     move_data::Declared |
648                     move_data::MoveExpr |
649                     move_data::MovePat =>
650                         format!("cannot move out of `{}` because it is borrowed",
651                                 &self.bccx.loan_path_to_string(move_path))
652                 };
653
654                 self.bccx.span_err(span, &err_message[..]);
655                 self.bccx.span_note(
656                     loan_span,
657                     &format!("borrow of `{}` occurs here",
658                             &self.bccx.loan_path_to_string(&*loan_path))
659                     );
660             }
661         }
662     }
663
664     pub fn analyze_restrictions_on_use(&self,
665                                        expr_id: ast::NodeId,
666                                        use_path: &LoanPath<'tcx>,
667                                        borrow_kind: ty::BorrowKind)
668                                        -> UseError<'tcx> {
669         debug!("analyze_restrictions_on_use(expr_id={}, use_path={:?})",
670                self.tcx().map.node_to_string(expr_id),
671                use_path);
672
673         let mut ret = UseOk;
674
675         self.each_in_scope_loan_affecting_path(
676             region::CodeExtent::from_node_id(expr_id), use_path, |loan| {
677             if !compatible_borrow_kinds(loan.kind, borrow_kind) {
678                 ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span);
679                 false
680             } else {
681                 true
682             }
683         });
684
685         return ret;
686     }
687
688     /// Reports an error if `expr` (which should be a path)
689     /// is using a moved/uninitialized value
690     fn check_if_path_is_moved(&self,
691                               id: ast::NodeId,
692                               span: Span,
693                               use_kind: MovedValueUseKind,
694                               lp: &Rc<LoanPath<'tcx>>) {
695         debug!("check_if_path_is_moved(id={}, use_kind={:?}, lp={:?})",
696                id, use_kind, lp);
697
698         // FIXME (22079): if you find yourself tempted to cut and paste
699         // the body below and then specializing the error reporting,
700         // consider refactoring this instead!
701
702         let base_lp = owned_ptr_base_path_rc(lp);
703         self.move_data.each_move_of(id, &base_lp, |the_move, moved_lp| {
704             self.bccx.report_use_of_moved_value(
705                 span,
706                 use_kind,
707                 &**lp,
708                 the_move,
709                 moved_lp,
710                 self.param_env);
711             false
712         });
713     }
714
715     /// Reports an error if assigning to `lp` will use a
716     /// moved/uninitialized value. Mainly this is concerned with
717     /// detecting derefs of uninitialized pointers.
718     ///
719     /// For example:
720     ///
721     /// ```
722     /// let a: int;
723     /// a = 10; // ok, even though a is uninitialized
724     ///
725     /// struct Point { x: usize, y: usize }
726     /// let p: Point;
727     /// p.x = 22; // ok, even though `p` is uninitialized
728     ///
729     /// let p: Box<Point>;
730     /// (*p).x = 22; // not ok, p is uninitialized, can't deref
731     /// ```
732     fn check_if_assigned_path_is_moved(&self,
733                                        id: ast::NodeId,
734                                        span: Span,
735                                        use_kind: MovedValueUseKind,
736                                        lp: &Rc<LoanPath<'tcx>>)
737     {
738         match lp.kind {
739             LpVar(_) | LpUpvar(_) => {
740                 // assigning to `x` does not require that `x` is initialized
741             }
742             LpDowncast(ref lp_base, _) => {
743                 // assigning to `(P->Variant).f` is ok if assigning to `P` is ok
744                 self.check_if_assigned_path_is_moved(id, span,
745                                                      use_kind, lp_base);
746             }
747             LpExtend(ref lp_base, _, LpInterior(InteriorField(_))) => {
748                 match lp_base.to_type().sty {
749                     ty::TyStruct(def_id, _) | ty::TyEnum(def_id, _) => {
750                         if self.tcx().has_dtor(def_id) {
751                             // In the case where the owner implements drop, then
752                             // the path must be initialized to prevent a case of
753                             // partial reinitialization
754                             //
755                             // FIXME (22079): could refactor via hypothetical
756                             // generalized check_if_path_is_moved
757                             let loan_path = owned_ptr_base_path_rc(lp_base);
758                             self.move_data.each_move_of(id, &loan_path, |_, _| {
759                                 self.bccx
760                                     .report_partial_reinitialization_of_uninitialized_structure(
761                                         span,
762                                         &*loan_path);
763                                 false
764                             });
765                             return;
766                         }
767                     },
768                     _ => {},
769                 }
770
771                 // assigning to `P.f` is ok if assigning to `P` is ok
772                 self.check_if_assigned_path_is_moved(id, span,
773                                                      use_kind, lp_base);
774             }
775             LpExtend(ref lp_base, _, LpInterior(InteriorElement(..))) |
776             LpExtend(ref lp_base, _, LpDeref(_)) => {
777                 // assigning to `P[i]` requires `P` is initialized
778                 // assigning to `(*P)` requires `P` is initialized
779                 self.check_if_path_is_moved(id, span, use_kind, lp_base);
780             }
781         }
782     }
783
784     fn check_assignment(&self,
785                         assignment_id: ast::NodeId,
786                         assignment_span: Span,
787                         assignee_cmt: mc::cmt<'tcx>) {
788         debug!("check_assignment(assignee_cmt={:?})", assignee_cmt);
789
790         // Check that we don't invalidate any outstanding loans
791         if let Some(loan_path) = opt_loan_path(&assignee_cmt) {
792             let scope = region::CodeExtent::from_node_id(assignment_id);
793             self.each_in_scope_loan_affecting_path(scope, &*loan_path, |loan| {
794                 self.report_illegal_mutation(assignment_span, &*loan_path, loan);
795                 false
796             });
797         }
798
799         // Check for reassignments to (immutable) local variables. This
800         // needs to be done here instead of in check_loans because we
801         // depend on move data.
802         if let mc::cat_local(local_id) = assignee_cmt.cat {
803             let lp = opt_loan_path(&assignee_cmt).unwrap();
804             self.move_data.each_assignment_of(assignment_id, &lp, |assign| {
805                 if assignee_cmt.mutbl.is_mutable() {
806                     self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
807                 } else {
808                     self.bccx.report_reassigned_immutable_variable(
809                         assignment_span,
810                         &*lp,
811                         assign);
812                 }
813                 false
814             });
815             return
816         }
817     }
818
819     pub fn report_illegal_mutation(&self,
820                                    span: Span,
821                                    loan_path: &LoanPath<'tcx>,
822                                    loan: &Loan) {
823         self.bccx.span_err(
824             span,
825             &format!("cannot assign to `{}` because it is borrowed",
826                     self.bccx.loan_path_to_string(loan_path)));
827         self.bccx.span_note(
828             loan.span,
829             &format!("borrow of `{}` occurs here",
830                     self.bccx.loan_path_to_string(loan_path)));
831     }
832 }