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