]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/check_loans.rs
Add test for MIR range matching.
[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::mem_categorization::Categorization;
27 use rustc::middle::region;
28 use rustc::middle::ty;
29 use syntax::ast;
30 use syntax::codemap::Span;
31 use rustc_front::hir;
32
33 use std::rc::Rc;
34
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.
38
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.
43
44     return match helper(loan_path) {
45         Some(new_loan_path) => new_loan_path,
46         None => loan_path.clone()
47     };
48
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) {
54                     v @ Some(_) => v,
55                     None => Some(&**lp_base)
56                 }
57             }
58             LpDowncast(ref lp_base, _) |
59             LpExtend(ref lp_base, _, _) => helper(&**lp_base)
60         }
61     }
62 }
63
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
66     //! a &LoanPath.
67
68     return match helper(loan_path) {
69         Some(new_loan_path) => new_loan_path,
70         None => loan_path.clone()
71     };
72
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) {
78                     v @ Some(_) => v,
79                     None => Some(lp_base.clone())
80                 }
81             }
82             LpDowncast(ref lp_base, _) |
83             LpExtend(ref lp_base, _, _) => helper(lp_base)
84         }
85     }
86 }
87
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<'a, 'tcx>,
94 }
95
96 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
97     fn consume(&mut self,
98                consume_id: ast::NodeId,
99                consume_span: Span,
100                cmt: mc::cmt<'tcx>,
101                mode: euv::ConsumeMode) {
102         debug!("consume(consume_id={}, cmt={:?}, mode={:?})",
103                consume_id, cmt, mode);
104
105         self.consume_common(consume_id, consume_span, cmt, mode);
106     }
107
108     fn matched_pat(&mut self,
109                    _matched_pat: &hir::Pat,
110                    _cmt: mc::cmt,
111                    _mode: euv::MatchMode) { }
112
113     fn consume_pat(&mut self,
114                    consume_pat: &hir::Pat,
115                    cmt: mc::cmt<'tcx>,
116                    mode: euv::ConsumeMode) {
117         debug!("consume_pat(consume_pat={:?}, cmt={:?}, mode={:?})",
118                consume_pat,
119                cmt,
120                mode);
121
122         self.consume_common(consume_pat.id, consume_pat.span, cmt, mode);
123     }
124
125     fn borrow(&mut self,
126               borrow_id: ast::NodeId,
127               borrow_span: Span,
128               cmt: mc::cmt<'tcx>,
129               loan_region: ty::Region,
130               bk: ty::BorrowKind,
131               loan_cause: euv::LoanCause)
132     {
133         debug!("borrow(borrow_id={}, cmt={:?}, loan_region={:?}, \
134                bk={:?}, loan_cause={:?})",
135                borrow_id, cmt, loan_region,
136                bk, loan_cause);
137
138         match opt_loan_path(&cmt) {
139             Some(lp) => {
140                 let moved_value_use_kind = match loan_cause {
141                     euv::ClosureCapture(_) => MovedInCapture,
142                     _ => MovedInUse,
143                 };
144                 self.check_if_path_is_moved(borrow_id, borrow_span, moved_value_use_kind, &lp);
145             }
146             None => { }
147         }
148
149         self.check_for_conflicting_loans(borrow_id);
150     }
151
152     fn mutate(&mut self,
153               assignment_id: ast::NodeId,
154               assignment_span: Span,
155               assignee_cmt: mc::cmt<'tcx>,
156               mode: euv::MutateMode)
157     {
158         debug!("mutate(assignment_id={}, assignee_cmt={:?})",
159                assignment_id, assignee_cmt);
160
161         match opt_loan_path(&assignee_cmt) {
162             Some(lp) => {
163                 match mode {
164                     euv::Init | euv::JustWrite => {
165                         // In a case like `path = 1`, then path does not
166                         // have to be *FULLY* initialized, but we still
167                         // must be careful lest it contains derefs of
168                         // pointers.
169                         self.check_if_assigned_path_is_moved(assignee_cmt.id,
170                                                              assignment_span,
171                                                              MovedInUse,
172                                                              &lp);
173                     }
174                     euv::WriteAndRead => {
175                         // In a case like `path += 1`, then path must be
176                         // fully initialized, since we will read it before
177                         // we write it.
178                         self.check_if_path_is_moved(assignee_cmt.id,
179                                                     assignment_span,
180                                                     MovedInUse,
181                                                     &lp);
182                     }
183                 }
184             }
185             None => { }
186         }
187
188         self.check_assignment(assignment_id, assignment_span, assignee_cmt);
189     }
190
191     fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) { }
192 }
193
194 pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
195                                      dfcx_loans: &LoanDataFlow<'b, 'tcx>,
196                                      move_data: &move_data::FlowedMoveData<'c, 'tcx>,
197                                      all_loans: &[Loan<'tcx>],
198                                      fn_id: ast::NodeId,
199                                      decl: &hir::FnDecl,
200                                      body: &hir::Block) {
201     debug!("check_loans(body id={})", body.id);
202
203     let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
204     let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env), false);
205
206     let mut clcx = CheckLoanCtxt {
207         bccx: bccx,
208         dfcx_loans: dfcx_loans,
209         move_data: move_data,
210         all_loans: all_loans,
211         param_env: &infcx.parameter_environment
212     };
213
214     {
215         let mut euv = euv::ExprUseVisitor::new(&mut clcx, &infcx);
216         euv.walk_fn(decl, body);
217     }
218 }
219
220 #[derive(PartialEq)]
221 enum UseError<'tcx> {
222     UseOk,
223     UseWhileBorrowed(/*loan*/Rc<LoanPath<'tcx>>, /*loan*/Span)
224 }
225
226 fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind,
227                            borrow_kind2: ty::BorrowKind)
228                            -> bool {
229     borrow_kind1 == ty::ImmBorrow && borrow_kind2 == ty::ImmBorrow
230 }
231
232 impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
233     pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.bccx.tcx }
234
235     pub fn each_issued_loan<F>(&self, node: ast::NodeId, mut op: F) -> bool where
236         F: FnMut(&Loan<'tcx>) -> bool,
237     {
238         //! Iterates over each loan that has been issued
239         //! on entrance to `node`, regardless of whether it is
240         //! actually *in scope* at that point.  Sometimes loans
241         //! are issued for future scopes and thus they may have been
242         //! *issued* but not yet be in effect.
243
244         self.dfcx_loans.each_bit_on_entry(node, |loan_index| {
245             let loan = &self.all_loans[loan_index];
246             op(loan)
247         })
248     }
249
250     pub fn each_in_scope_loan<F>(&self, scope: region::CodeExtent, mut op: F) -> bool where
251         F: FnMut(&Loan<'tcx>) -> bool,
252     {
253         //! Like `each_issued_loan()`, but only considers loans that are
254         //! currently in scope.
255
256         let tcx = self.tcx();
257         self.each_issued_loan(scope.node_id(&tcx.region_maps), |loan| {
258             if tcx.region_maps.is_subscope_of(scope, loan.kill_scope) {
259                 op(loan)
260             } else {
261                 true
262             }
263         })
264     }
265
266     fn each_in_scope_loan_affecting_path<F>(&self,
267                                             scope: region::CodeExtent,
268                                             loan_path: &LoanPath<'tcx>,
269                                             mut op: F)
270                                             -> bool where
271         F: FnMut(&Loan<'tcx>) -> bool,
272     {
273         //! Iterates through all of the in-scope loans affecting `loan_path`,
274         //! calling `op`, and ceasing iteration if `false` is returned.
275
276         // First, we check for a loan restricting the path P being used. This
277         // accounts for borrows of P but also borrows of subpaths, like P.a.b.
278         // Consider the following example:
279         //
280         //     let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
281         //     let y = a;          // Conflicts with restriction
282
283         let loan_path = owned_ptr_base_path(loan_path);
284         let cont = self.each_in_scope_loan(scope, |loan| {
285             let mut ret = true;
286             for restr_path in &loan.restricted_paths {
287                 if **restr_path == *loan_path {
288                     if !op(loan) {
289                         ret = false;
290                         break;
291                     }
292                 }
293             }
294             ret
295         });
296
297         if !cont {
298             return false;
299         }
300
301         // Next, we must check for *loans* (not restrictions) on the path P or
302         // any base path. This rejects examples like the following:
303         //
304         //     let x = &mut a.b;
305         //     let y = a.b.c;
306         //
307         // Limiting this search to *loans* and not *restrictions* means that
308         // examples like the following continue to work:
309         //
310         //     let x = &mut a.b;
311         //     let y = a.c;
312
313         let mut loan_path = loan_path;
314         loop {
315             match loan_path.kind {
316                 LpVar(_) | LpUpvar(_) => {
317                     break;
318                 }
319                 LpDowncast(ref lp_base, _) |
320                 LpExtend(ref lp_base, _, _) => {
321                     loan_path = &**lp_base;
322                 }
323             }
324
325             let cont = self.each_in_scope_loan(scope, |loan| {
326                 if *loan.loan_path == *loan_path {
327                     op(loan)
328                 } else {
329                     true
330                 }
331             });
332
333             if !cont {
334                 return false;
335             }
336         }
337
338         return true;
339     }
340
341     pub fn loans_generated_by(&self, node: ast::NodeId) -> Vec<usize> {
342         //! Returns a vector of the loans that are generated as
343         //! we enter `node`.
344
345         let mut result = Vec::new();
346         self.dfcx_loans.each_gen_bit(node, |loan_index| {
347             result.push(loan_index);
348             true
349         });
350         return result;
351     }
352
353     pub fn check_for_conflicting_loans(&self, node: ast::NodeId) {
354         //! Checks to see whether any of the loans that are issued
355         //! on entrance to `node` conflict with loans that have already been
356         //! issued when we enter `node` (for example, we do not
357         //! permit two `&mut` borrows of the same variable).
358         //!
359         //! (Note that some loans can be *issued* without necessarily
360         //! taking effect yet.)
361
362         debug!("check_for_conflicting_loans(node={:?})", node);
363
364         let new_loan_indices = self.loans_generated_by(node);
365         debug!("new_loan_indices = {:?}", new_loan_indices);
366
367         for &new_loan_index in &new_loan_indices {
368             self.each_issued_loan(node, |issued_loan| {
369                 let new_loan = &self.all_loans[new_loan_index];
370                 // Only report an error for the first issued loan that conflicts
371                 // to avoid O(n^2) errors.
372                 self.report_error_if_loans_conflict(issued_loan, new_loan)
373             });
374         }
375
376         for (i, &x) in new_loan_indices.iter().enumerate() {
377             let old_loan = &self.all_loans[x];
378             for &y in &new_loan_indices[(i+1) ..] {
379                 let new_loan = &self.all_loans[y];
380                 self.report_error_if_loans_conflict(old_loan, new_loan);
381             }
382         }
383     }
384
385     pub fn report_error_if_loans_conflict(&self,
386                                           old_loan: &Loan<'tcx>,
387                                           new_loan: &Loan<'tcx>)
388                                           -> bool {
389         //! Checks whether `old_loan` and `new_loan` can safely be issued
390         //! simultaneously.
391
392         debug!("report_error_if_loans_conflict(old_loan={:?}, new_loan={:?})",
393                old_loan,
394                new_loan);
395
396         // Should only be called for loans that are in scope at the same time.
397         assert!(self.tcx().region_maps.scopes_intersect(old_loan.kill_scope,
398                                                         new_loan.kill_scope));
399
400         self.report_error_if_loan_conflicts_with_restriction(
401             old_loan, new_loan, old_loan, new_loan) &&
402         self.report_error_if_loan_conflicts_with_restriction(
403             new_loan, old_loan, old_loan, new_loan)
404     }
405
406     pub fn report_error_if_loan_conflicts_with_restriction(&self,
407                                                            loan1: &Loan<'tcx>,
408                                                            loan2: &Loan<'tcx>,
409                                                            old_loan: &Loan<'tcx>,
410                                                            new_loan: &Loan<'tcx>)
411                                                            -> bool {
412         //! Checks whether the restrictions introduced by `loan1` would
413         //! prohibit `loan2`. Returns false if an error is reported.
414
415         debug!("report_error_if_loan_conflicts_with_restriction(\
416                 loan1={:?}, loan2={:?})",
417                loan1,
418                loan2);
419
420         if compatible_borrow_kinds(loan1.kind, loan2.kind) {
421             return true;
422         }
423
424         let loan2_base_path = owned_ptr_base_path_rc(&loan2.loan_path);
425         for restr_path in &loan1.restricted_paths {
426             if *restr_path != loan2_base_path { continue; }
427
428             // If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
429             // normally generate a rather confusing message (in this case, for multiple mutable
430             // borrows):
431             //
432             //     error: cannot borrow `x.b` as mutable more than once at a time
433             //     note: previous borrow of `x.a` occurs here; the mutable borrow prevents
434             //     subsequent moves, borrows, or modification of `x.a` until the borrow ends
435             //
436             // What we want to do instead is get the 'common ancestor' of the two borrow paths and
437             // use that for most of the message instead, giving is something like this:
438             //
439             //     error: cannot borrow `x` as mutable more than once at a time
440             //     note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
441             //     borrow prevents subsequent moves, borrows, or modification of `x` until the
442             //     borrow ends
443
444             let common = new_loan.loan_path.common(&*old_loan.loan_path);
445             let (nl, ol, new_loan_msg, old_loan_msg) =
446                 if new_loan.loan_path.has_fork(&*old_loan.loan_path) && common.is_some() {
447                     let nl = self.bccx.loan_path_to_string(&common.unwrap());
448                     let ol = nl.clone();
449                     let new_loan_msg = format!(" (here through borrowing `{}`)",
450                                                self.bccx.loan_path_to_string(
451                                                    &*new_loan.loan_path));
452                     let old_loan_msg = format!(" (through borrowing `{}`)",
453                                                self.bccx.loan_path_to_string(
454                                                    &*old_loan.loan_path));
455                     (nl, ol, new_loan_msg, old_loan_msg)
456                 } else {
457                     (self.bccx.loan_path_to_string(&*new_loan.loan_path),
458                      self.bccx.loan_path_to_string(&*old_loan.loan_path),
459                      String::new(), String::new())
460                 };
461
462             let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
463                 "it".to_string()
464             } else {
465                 format!("`{}`", ol)
466             };
467
468             match (new_loan.kind, old_loan.kind) {
469                 (ty::MutBorrow, ty::MutBorrow) => {
470                     span_err!(self.bccx, new_loan.span, E0499,
471                               "cannot borrow `{}`{} as mutable \
472                                more than once at a time",
473                               nl, new_loan_msg);
474                 }
475
476                 (ty::UniqueImmBorrow, _) => {
477                     span_err!(self.bccx, new_loan.span, E0500,
478                               "closure requires unique access to `{}` \
479                                but {} is already borrowed{}",
480                               nl, ol_pronoun, old_loan_msg);
481                 }
482
483                 (_, ty::UniqueImmBorrow) => {
484                     span_err!(self.bccx, new_loan.span, E0501,
485                               "cannot borrow `{}`{} as {} because \
486                                previous closure requires unique access",
487                               nl, new_loan_msg, new_loan.kind.to_user_str());
488                 }
489
490                 (_, _) => {
491                     span_err!(self.bccx, new_loan.span, E0502,
492                               "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(
561                 old_loan.kill_scope.node_id(&self.tcx().region_maps));
562             self.bccx.span_end_note(old_loan_span,
563                                     "previous borrow ends here");
564
565             return false;
566         }
567
568         true
569     }
570
571     fn consume_common(&self,
572                       id: ast::NodeId,
573                       span: Span,
574                       cmt: mc::cmt<'tcx>,
575                       mode: euv::ConsumeMode) {
576         match opt_loan_path(&cmt) {
577             Some(lp) => {
578                 let moved_value_use_kind = match mode {
579                     euv::Copy => {
580                         self.check_for_copy_of_frozen_path(id, span, &*lp);
581                         MovedInUse
582                     }
583                     euv::Move(_) => {
584                         match self.move_data.kind_of_move_of_path(id, &lp) {
585                             None => {
586                                 // Sometimes moves don't have a move kind;
587                                 // this either means that the original move
588                                 // was from something illegal to move,
589                                 // or was moved from referent of an unsafe
590                                 // pointer or something like that.
591                                 MovedInUse
592                             }
593                             Some(move_kind) => {
594                                 self.check_for_move_of_borrowed_path(id, span,
595                                                                      &*lp, move_kind);
596                                 if move_kind == move_data::Captured {
597                                     MovedInCapture
598                                 } else {
599                                     MovedInUse
600                                 }
601                             }
602                         }
603                     }
604                 };
605
606                 self.check_if_path_is_moved(id, span, moved_value_use_kind, &lp);
607             }
608             None => { }
609         }
610     }
611
612     fn check_for_copy_of_frozen_path(&self,
613                                      id: ast::NodeId,
614                                      span: Span,
615                                      copy_path: &LoanPath<'tcx>) {
616         match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
617             UseOk => { }
618             UseWhileBorrowed(loan_path, loan_span) => {
619                 span_err!(self.bccx, span, E0503,
620                           "cannot use `{}` because it was mutably borrowed",
621                           &self.bccx.loan_path_to_string(copy_path));
622                 self.bccx.span_note(
623                     loan_span,
624                     &format!("borrow of `{}` occurs here",
625                             &self.bccx.loan_path_to_string(&*loan_path))
626                     );
627             }
628         }
629     }
630
631     fn check_for_move_of_borrowed_path(&self,
632                                        id: ast::NodeId,
633                                        span: Span,
634                                        move_path: &LoanPath<'tcx>,
635                                        move_kind: move_data::MoveKind) {
636         // We want to detect if there are any loans at all, so we search for
637         // any loans incompatible with MutBorrrow, since all other kinds of
638         // loans are incompatible with that.
639         match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) {
640             UseOk => { }
641             UseWhileBorrowed(loan_path, loan_span) => {
642                 match move_kind {
643                     move_data::Captured =>
644                         span_err!(self.bccx, span, E0504,
645                                   "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                         span_err!(self.bccx, span, E0505,
651                                   "cannot move out of `{}` because it is borrowed",
652                                   &self.bccx.loan_path_to_string(move_path))
653                 };
654
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             self.tcx().region_maps.node_extent(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     /// ```ignore
722     /// let a: i32;
723     /// a = 10; // ok, even though a is uninitialized
724     ///
725     /// struct Point { x: u32, y: u32 }
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, _) | ty::TyEnum(def, _) if def.has_dtor() => {
750                         // In the case where the owner implements drop, then
751                         // the path must be initialized to prevent a case of
752                         // partial reinitialization
753                         //
754                         // FIXME (22079): could refactor via hypothetical
755                         // generalized check_if_path_is_moved
756                         let loan_path = owned_ptr_base_path_rc(lp_base);
757                         self.move_data.each_move_of(id, &loan_path, |_, _| {
758                             self.bccx
759                                 .report_partial_reinitialization_of_uninitialized_structure(
760                                     span,
761                                     &*loan_path);
762                             false
763                         });
764                         return;
765                     },
766                     _ => {},
767                 }
768
769                 // assigning to `P.f` is ok if assigning to `P` is ok
770                 self.check_if_assigned_path_is_moved(id, span,
771                                                      use_kind, lp_base);
772             }
773             LpExtend(ref lp_base, _, LpInterior(_, InteriorElement(..))) |
774             LpExtend(ref lp_base, _, LpDeref(_)) => {
775                 // assigning to `P[i]` requires `P` is initialized
776                 // assigning to `(*P)` requires `P` is initialized
777                 self.check_if_path_is_moved(id, span, use_kind, lp_base);
778             }
779         }
780     }
781
782     fn check_assignment(&self,
783                         assignment_id: ast::NodeId,
784                         assignment_span: Span,
785                         assignee_cmt: mc::cmt<'tcx>) {
786         debug!("check_assignment(assignee_cmt={:?})", assignee_cmt);
787
788         // Check that we don't invalidate any outstanding loans
789         if let Some(loan_path) = opt_loan_path(&assignee_cmt) {
790             let scope = self.tcx().region_maps.node_extent(assignment_id);
791             self.each_in_scope_loan_affecting_path(scope, &*loan_path, |loan| {
792                 self.report_illegal_mutation(assignment_span, &*loan_path, loan);
793                 false
794             });
795         }
796
797         // Check for reassignments to (immutable) local variables. This
798         // needs to be done here instead of in check_loans because we
799         // depend on move data.
800         if let Categorization::Local(local_id) = assignee_cmt.cat {
801             let lp = opt_loan_path(&assignee_cmt).unwrap();
802             self.move_data.each_assignment_of(assignment_id, &lp, |assign| {
803                 if assignee_cmt.mutbl.is_mutable() {
804                     self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
805                 } else {
806                     self.bccx.report_reassigned_immutable_variable(
807                         assignment_span,
808                         &*lp,
809                         assign);
810                 }
811                 false
812             });
813             return
814         }
815     }
816
817     pub fn report_illegal_mutation(&self,
818                                    span: Span,
819                                    loan_path: &LoanPath<'tcx>,
820                                    loan: &Loan) {
821         span_err!(self.bccx, span, E0506,
822                   "cannot assign to `{}` because it is borrowed",
823                   self.bccx.loan_path_to_string(loan_path));
824         self.bccx.span_note(
825             loan.span,
826             &format!("borrow of `{}` occurs here",
827                     self.bccx.loan_path_to_string(loan_path)));
828     }
829 }