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