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