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