]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/borrowck/check_loans.rs
auto merge of #12571 : eddyb/rust/kill-callee-id, r=nikomatsakis
[rust.git] / src / librustc / middle / borrowck / check_loans.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 // ----------------------------------------------------------------------
12 // Checking loans
13 //
14 // Phase 2 of check: we walk down the tree and check that:
15 // 1. assignments are always made to mutable locations;
16 // 2. loans made in overlapping scopes do not conflict
17 // 3. assignments do not affect things loaned out as immutable
18 // 4. moves do not affect things loaned out in any way
19
20
21 use mc = middle::mem_categorization;
22 use middle::borrowck::*;
23 use middle::moves;
24 use middle::ty;
25 use syntax::ast;
26 use syntax::ast_util;
27 use syntax::codemap::Span;
28 use syntax::visit::Visitor;
29 use syntax::visit;
30 use util::ppaux::Repr;
31
32 struct CheckLoanCtxt<'a> {
33     bccx: &'a BorrowckCtxt,
34     dfcx_loans: &'a LoanDataFlow,
35     move_data: move_data::FlowedMoveData,
36     all_loans: &'a [Loan],
37 }
38
39 impl<'a> Visitor<()> for CheckLoanCtxt<'a> {
40
41     fn visit_expr(&mut self, ex: &ast::Expr, _: ()) {
42         check_loans_in_expr(self, ex);
43     }
44     fn visit_local(&mut self, l: &ast::Local, _: ()) {
45         check_loans_in_local(self, l);
46     }
47     fn visit_block(&mut self, b: &ast::Block, _: ()) {
48         check_loans_in_block(self, b);
49     }
50     fn visit_pat(&mut self, p: &ast::Pat, _: ()) {
51         check_loans_in_pat(self, p);
52     }
53     fn visit_fn(&mut self, _fk: &visit::FnKind, _fd: &ast::FnDecl,
54                 _b: &ast::Block, _s: Span, _n: ast::NodeId, _: ()) {
55         // Don't process nested items or closures here,
56         // the outer loop will take care of it.
57         return;
58     }
59
60     // FIXME(#10894) should continue recursing
61     fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {}
62 }
63
64 pub fn check_loans(bccx: &BorrowckCtxt,
65                    dfcx_loans: &LoanDataFlow,
66                    move_data: move_data::FlowedMoveData,
67                    all_loans: &[Loan],
68                    body: &ast::Block) {
69     debug!("check_loans(body id={:?})", body.id);
70
71     let mut clcx = CheckLoanCtxt {
72         bccx: bccx,
73         dfcx_loans: dfcx_loans,
74         move_data: move_data,
75         all_loans: all_loans,
76     };
77
78     clcx.visit_block(body, ());
79 }
80
81 #[deriving(Eq)]
82 enum MoveError {
83     MoveOk,
84     MoveWhileBorrowed(/*loan*/@LoanPath, /*loan*/Span)
85 }
86
87 impl<'a> CheckLoanCtxt<'a> {
88     pub fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
89
90     pub fn each_issued_loan(&self, scope_id: ast::NodeId, op: |&Loan| -> bool)
91                             -> bool {
92         //! Iterates over each loan that has been issued
93         //! on entrance to `scope_id`, regardless of whether it is
94         //! actually *in scope* at that point.  Sometimes loans
95         //! are issued for future scopes and thus they may have been
96         //! *issued* but not yet be in effect.
97
98         self.dfcx_loans.each_bit_on_entry_frozen(scope_id, |loan_index| {
99             let loan = &self.all_loans[loan_index];
100             op(loan)
101         })
102     }
103
104     pub fn each_in_scope_loan(&self,
105                               scope_id: ast::NodeId,
106                               op: |&Loan| -> bool)
107                               -> bool {
108         //! Like `each_issued_loan()`, but only considers loans that are
109         //! currently in scope.
110
111         let tcx = self.tcx();
112         self.each_issued_loan(scope_id, |loan| {
113             if tcx.region_maps.is_subscope_of(scope_id, loan.kill_scope) {
114                 op(loan)
115             } else {
116                 true
117             }
118         })
119     }
120
121     pub fn each_in_scope_restriction(&self,
122                                      scope_id: ast::NodeId,
123                                      loan_path: @LoanPath,
124                                      op: |&Loan, &Restriction| -> bool)
125                                      -> bool {
126         //! Iterates through all the in-scope restrictions for the
127         //! given `loan_path`
128
129         self.each_in_scope_loan(scope_id, |loan| {
130             debug!("each_in_scope_restriction found loan: {:?}",
131                    loan.repr(self.tcx()));
132
133             let mut ret = true;
134             for restr in loan.restrictions.iter() {
135                 if restr.loan_path == loan_path {
136                     if !op(loan, restr) {
137                         ret = false;
138                         break;
139                     }
140                 }
141             }
142             ret
143         })
144     }
145
146     pub fn loans_generated_by(&self, scope_id: ast::NodeId) -> ~[uint] {
147         //! Returns a vector of the loans that are generated as
148         //! we encounter `scope_id`.
149
150         let mut result = ~[];
151         self.dfcx_loans.each_gen_bit_frozen(scope_id, |loan_index| {
152             result.push(loan_index);
153             true
154         });
155         return result;
156     }
157
158     pub fn check_for_conflicting_loans(&self, scope_id: ast::NodeId) {
159         //! Checks to see whether any of the loans that are issued
160         //! by `scope_id` conflict with loans that have already been
161         //! issued when we enter `scope_id` (for example, we do not
162         //! permit two `&mut` borrows of the same variable).
163
164         debug!("check_for_conflicting_loans(scope_id={:?})", scope_id);
165
166         let new_loan_indices = self.loans_generated_by(scope_id);
167         debug!("new_loan_indices = {:?}", new_loan_indices);
168
169         self.each_issued_loan(scope_id, |issued_loan| {
170             for &new_loan_index in new_loan_indices.iter() {
171                 let new_loan = &self.all_loans[new_loan_index];
172                 self.report_error_if_loans_conflict(issued_loan, new_loan);
173             }
174             true
175         });
176
177         for (i, &x) in new_loan_indices.iter().enumerate() {
178             let old_loan = &self.all_loans[x];
179             for &y in new_loan_indices.slice_from(i+1).iter() {
180                 let new_loan = &self.all_loans[y];
181                 self.report_error_if_loans_conflict(old_loan, new_loan);
182             }
183         }
184     }
185
186     pub fn report_error_if_loans_conflict(&self,
187                                           old_loan: &Loan,
188                                           new_loan: &Loan) {
189         //! Checks whether `old_loan` and `new_loan` can safely be issued
190         //! simultaneously.
191
192         debug!("report_error_if_loans_conflict(old_loan={}, new_loan={})",
193                old_loan.repr(self.tcx()),
194                new_loan.repr(self.tcx()));
195
196         // Should only be called for loans that are in scope at the same time.
197         assert!(self.tcx().region_maps.scopes_intersect(old_loan.kill_scope,
198                                                         new_loan.kill_scope));
199
200         self.report_error_if_loan_conflicts_with_restriction(
201             old_loan, new_loan, old_loan, new_loan) &&
202         self.report_error_if_loan_conflicts_with_restriction(
203             new_loan, old_loan, old_loan, new_loan);
204     }
205
206     pub fn report_error_if_loan_conflicts_with_restriction(&self,
207                                                            loan1: &Loan,
208                                                            loan2: &Loan,
209                                                            old_loan: &Loan,
210                                                            new_loan: &Loan)
211                                                            -> bool {
212         //! Checks whether the restrictions introduced by `loan1` would
213         //! prohibit `loan2`. Returns false if an error is reported.
214
215         debug!("report_error_if_loan_conflicts_with_restriction(\
216                 loan1={}, loan2={})",
217                loan1.repr(self.tcx()),
218                loan2.repr(self.tcx()));
219
220         // Restrictions that would cause the new loan to be illegal:
221         let illegal_if = match loan2.kind {
222             // Look for restrictions against mutation. These are
223             // generated by all other borrows.
224             ty::MutBorrow => RESTR_MUTATE,
225
226             // Look for restrictions against freezing (immutable borrows).
227             // These are generated by `&mut` borrows.
228             ty::ImmBorrow => RESTR_FREEZE,
229
230             // No matter how the data is borrowed (as `&`, as `&mut`,
231             // or as `&unique imm`) it will always generate a
232             // restriction against mutating the data. So look for those.
233             ty::UniqueImmBorrow => RESTR_MUTATE,
234         };
235         debug!("illegal_if={:?}", illegal_if);
236
237         for restr in loan1.restrictions.iter() {
238             if !restr.set.intersects(illegal_if) { continue; }
239             if restr.loan_path != loan2.loan_path { continue; }
240
241             let old_pronoun = if new_loan.loan_path == old_loan.loan_path {
242                 ~"it"
243             } else {
244                 format!("`{}`",
245                         self.bccx.loan_path_to_str(old_loan.loan_path))
246             };
247
248             match (new_loan.kind, old_loan.kind) {
249                 (ty::MutBorrow, ty::MutBorrow) => {
250                     self.bccx.span_err(
251                         new_loan.span,
252                         format!("cannot borrow `{}` as mutable \
253                                 more than once at a time",
254                                 self.bccx.loan_path_to_str(new_loan.loan_path)));
255                 }
256
257                 (ty::UniqueImmBorrow, _) => {
258                     self.bccx.span_err(
259                         new_loan.span,
260                         format!("closure requires unique access to `{}` \
261                                 but {} is already borrowed",
262                                 self.bccx.loan_path_to_str(new_loan.loan_path),
263                                 old_pronoun));
264                 }
265
266                 (_, ty::UniqueImmBorrow) => {
267                     self.bccx.span_err(
268                         new_loan.span,
269                         format!("cannot borrow `{}` as {} because \
270                                 previous closure requires unique access",
271                                 self.bccx.loan_path_to_str(new_loan.loan_path),
272                                 new_loan.kind.to_user_str()));
273                 }
274
275                 (_, _) => {
276                     self.bccx.span_err(
277                         new_loan.span,
278                         format!("cannot borrow `{}` as {} because \
279                                 {} is also borrowed as {}",
280                                 self.bccx.loan_path_to_str(new_loan.loan_path),
281                                 new_loan.kind.to_user_str(),
282                                 old_pronoun,
283                                 old_loan.kind.to_user_str()));
284                 }
285             }
286
287             match new_loan.cause {
288                 ClosureCapture(span) => {
289                     self.bccx.span_note(
290                         span,
291                         format!("borrow occurs due to use of `{}` in closure",
292                                 self.bccx.loan_path_to_str(new_loan.loan_path)));
293                 }
294                 _ => { }
295             }
296
297             let rule_summary = match old_loan.kind {
298                 ty::MutBorrow => {
299                     format!("the mutable borrow prevents subsequent \
300                             moves, borrows, or modification of `{0}` \
301                             until the borrow ends",
302                             self.bccx.loan_path_to_str(old_loan.loan_path))
303                 }
304
305                 ty::ImmBorrow => {
306                     format!("the immutable borrow prevents subsequent \
307                             moves or mutable borrows of `{0}` \
308                             until the borrow ends",
309                             self.bccx.loan_path_to_str(old_loan.loan_path))
310                 }
311
312                 ty::UniqueImmBorrow => {
313                     format!("the unique capture prevents subsequent \
314                             moves or borrows of `{0}` \
315                             until the borrow ends",
316                             self.bccx.loan_path_to_str(old_loan.loan_path))
317                 }
318             };
319
320             let borrow_summary = match old_loan.cause {
321                 ClosureCapture(_) => {
322                     format!("previous borrow of `{}` occurs here due to \
323                             use in closure",
324                             self.bccx.loan_path_to_str(old_loan.loan_path))
325                 }
326
327                 AddrOf | AutoRef | RefBinding => {
328                     format!("previous borrow of `{}` occurs here",
329                             self.bccx.loan_path_to_str(old_loan.loan_path))
330                 }
331             };
332
333             self.bccx.span_note(
334                 old_loan.span,
335                 format!("{}; {}", borrow_summary, rule_summary));
336
337             let old_loan_span = self.tcx().map.span(old_loan.kill_scope);
338             self.bccx.span_end_note(old_loan_span,
339                                     "previous borrow ends here");
340
341             return false;
342         }
343
344         true
345     }
346
347     pub fn is_local_variable(&self, cmt: mc::cmt) -> bool {
348         match cmt.cat {
349           mc::cat_local(_) => true,
350           _ => false
351         }
352     }
353
354     pub fn check_if_path_is_moved(&self,
355                                   id: ast::NodeId,
356                                   span: Span,
357                                   use_kind: MovedValueUseKind,
358                                   lp: @LoanPath) {
359         /*!
360          * Reports an error if `expr` (which should be a path)
361          * is using a moved/uninitialized value
362          */
363
364         debug!("check_if_path_is_moved(id={:?}, use_kind={:?}, lp={})",
365                id, use_kind, lp.repr(self.bccx.tcx));
366         self.move_data.each_move_of(id, lp, |move, moved_lp| {
367             self.bccx.report_use_of_moved_value(
368                 span,
369                 use_kind,
370                 lp,
371                 move,
372                 moved_lp);
373             false
374         });
375     }
376
377     pub fn check_assignment(&self, expr: &ast::Expr) {
378         // We don't use cat_expr() here because we don't want to treat
379         // auto-ref'd parameters in overloaded operators as rvalues.
380         let adj = {
381             let adjustments = self.bccx.tcx.adjustments.borrow();
382             adjustments.get().find_copy(&expr.id)
383         };
384         let cmt = match adj {
385             None => self.bccx.cat_expr_unadjusted(expr),
386             Some(adj) => self.bccx.cat_expr_autoderefd(expr, adj)
387         };
388
389         debug!("check_assignment(cmt={})", cmt.repr(self.tcx()));
390
391         // Mutable values can be assigned, as long as they obey loans
392         // and aliasing restrictions:
393         if cmt.mutbl.is_mutable() {
394             if check_for_aliasable_mutable_writes(self, expr, cmt) {
395                 if check_for_assignment_to_restricted_or_frozen_location(
396                     self, expr, cmt)
397                 {
398                     // Safe, but record for lint pass later:
399                     mark_variable_as_used_mut(self, cmt);
400                 }
401             }
402             return;
403         }
404
405         // For immutable local variables, assignments are legal
406         // if they cannot already have been assigned
407         if self.is_local_variable(cmt) {
408             assert!(cmt.mutbl.is_immutable()); // no "const" locals
409             let lp = opt_loan_path(cmt).unwrap();
410             self.move_data.each_assignment_of(expr.id, lp, |assign| {
411                 self.bccx.report_reassigned_immutable_variable(
412                     expr.span,
413                     lp,
414                     assign);
415                 false
416             });
417             return;
418         }
419
420         // Otherwise, just a plain error.
421         match opt_loan_path(cmt) {
422             Some(lp) => {
423                 self.bccx.span_err(
424                     expr.span,
425                     format!("cannot assign to {} {} `{}`",
426                             cmt.mutbl.to_user_str(),
427                             self.bccx.cmt_to_str(cmt),
428                             self.bccx.loan_path_to_str(lp)));
429             }
430             None => {
431                 self.bccx.span_err(
432                     expr.span,
433                     format!("cannot assign to {} {}",
434                             cmt.mutbl.to_user_str(),
435                             self.bccx.cmt_to_str(cmt)));
436             }
437         }
438         return;
439
440         fn mark_variable_as_used_mut(this: &CheckLoanCtxt,
441                                      cmt: mc::cmt) {
442             //! If the mutability of the `cmt` being written is inherited
443             //! from a local variable, liveness will
444             //! not have been able to detect that this variable's mutability
445             //! is important, so we must add the variable to the
446             //! `used_mut_nodes` table here.
447
448             let mut cmt = cmt;
449             loop {
450                 debug!("mark_writes_through_upvars_as_used_mut(cmt={})",
451                        cmt.repr(this.tcx()));
452                 match cmt.cat {
453                     mc::cat_local(id) | mc::cat_arg(id) => {
454                         let mut used_mut_nodes = this.tcx()
455                                                      .used_mut_nodes
456                                                      .borrow_mut();
457                         used_mut_nodes.get().insert(id);
458                         return;
459                     }
460
461                     mc::cat_upvar(..) => {
462                         return;
463                     }
464
465                     mc::cat_deref(_, _, mc::GcPtr) => {
466                         assert_eq!(cmt.mutbl, mc::McImmutable);
467                         return;
468                     }
469
470                     mc::cat_rvalue(..) |
471                     mc::cat_static_item |
472                     mc::cat_copied_upvar(..) |
473                     mc::cat_deref(_, _, mc::UnsafePtr(..)) |
474                     mc::cat_deref(_, _, mc::BorrowedPtr(..)) => {
475                         assert_eq!(cmt.mutbl, mc::McDeclared);
476                         return;
477                     }
478
479                     mc::cat_discr(b, _) |
480                     mc::cat_deref(b, _, mc::OwnedPtr) => {
481                         assert_eq!(cmt.mutbl, mc::McInherited);
482                         cmt = b;
483                     }
484
485                     mc::cat_downcast(b) |
486                     mc::cat_interior(b, _) => {
487                         assert_eq!(cmt.mutbl, mc::McInherited);
488                         cmt = b;
489                     }
490                 }
491             }
492         }
493
494         fn check_for_aliasable_mutable_writes(this: &CheckLoanCtxt,
495                                               expr: &ast::Expr,
496                                               cmt: mc::cmt) -> bool {
497             //! Safety checks related to writes to aliasable, mutable locations
498
499             let guarantor = cmt.guarantor();
500             debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
501                    cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
502             match guarantor.cat {
503                 mc::cat_deref(b, _, mc::BorrowedPtr(ty::MutBorrow, _)) => {
504                     // Statically prohibit writes to `&mut` when aliasable
505
506                     check_for_aliasability_violation(this, expr, b);
507                 }
508
509                 _ => {}
510             }
511
512             return true; // no errors reported
513         }
514
515         fn check_for_aliasability_violation(this: &CheckLoanCtxt,
516                                             expr: &ast::Expr,
517                                             cmt: mc::cmt)
518                                             -> bool {
519             match cmt.freely_aliasable() {
520                 None => {
521                     return true;
522                 }
523                 Some(cause) => {
524                     this.bccx.report_aliasability_violation(
525                         expr.span,
526                         MutabilityViolation,
527                         cause);
528                     return false;
529                 }
530             }
531         }
532
533         fn check_for_assignment_to_restricted_or_frozen_location(
534             this: &CheckLoanCtxt,
535             expr: &ast::Expr,
536             cmt: mc::cmt) -> bool
537         {
538             //! Check for assignments that violate the terms of an
539             //! outstanding loan.
540
541             let loan_path = match opt_loan_path(cmt) {
542                 Some(lp) => lp,
543                 None => { return true; /* no loan path, can't be any loans */ }
544             };
545
546             // Start by searching for an assignment to a *restricted*
547             // location. Here is one example of the kind of error caught
548             // by this check:
549             //
550             //    let mut v = ~[1, 2, 3];
551             //    let p = &v;
552             //    v = ~[4];
553             //
554             // In this case, creating `p` triggers a RESTR_MUTATE
555             // restriction on the path `v`.
556             //
557             // Here is a second, more subtle example:
558             //
559             //    let mut v = ~[1, 2, 3];
560             //    let p = &const v[0];
561             //    v[0] = 4;                   // OK
562             //    v[1] = 5;                   // OK
563             //    v = ~[4, 5, 3];             // Error
564             //
565             // In this case, `p` is pointing to `v[0]`, and it is a
566             // `const` pointer in any case. So the first two
567             // assignments are legal (and would be permitted by this
568             // check). However, the final assignment (which is
569             // logically equivalent) is forbidden, because it would
570             // cause the existing `v` array to be freed, thus
571             // invalidating `p`. In the code, this error results
572             // because `gather_loans::restrictions` adds a
573             // `RESTR_MUTATE` restriction whenever the contents of an
574             // owned pointer are borrowed, and hence while `v[*]` is not
575             // restricted from being written, `v` is.
576             let cont = this.each_in_scope_restriction(expr.id,
577                                                       loan_path,
578                                                       |loan, restr| {
579                 if restr.set.intersects(RESTR_MUTATE) {
580                     this.report_illegal_mutation(expr, loan_path, loan);
581                     false
582                 } else {
583                     true
584                 }
585             });
586
587             if !cont { return false }
588
589             // The previous code handled assignments to paths that
590             // have been restricted. This covers paths that have been
591             // directly lent out and their base paths, but does not
592             // cover random extensions of those paths. For example,
593             // the following program is not declared illegal by the
594             // previous check:
595             //
596             //    let mut v = ~[1, 2, 3];
597             //    let p = &v;
598             //    v[0] = 4; // declared error by loop below, not code above
599             //
600             // The reason that this passes the previous check whereas
601             // an assignment like `v = ~[4]` fails is because the assignment
602             // here is to `v[*]`, and the existing restrictions were issued
603             // for `v`, not `v[*]`.
604             //
605             // So in this loop, we walk back up the loan path so long
606             // as the mutability of the path is dependent on a super
607             // path, and check that the super path was not lent out as
608             // mutable or immutable (a const loan is ok).
609             //
610             // Mutability of a path can be dependent on the super path
611             // in two ways. First, it might be inherited mutability.
612             // Second, the pointee of an `&mut` pointer can only be
613             // mutated if it is found in an unaliased location, so we
614             // have to check that the owner location is not borrowed.
615             //
616             // Note that we are *not* checking for any and all
617             // restrictions.  We are only interested in the pointers
618             // that the user created, whereas we add restrictions for
619             // all kinds of paths that are not directly aliased. If we checked
620             // for all restrictions, and not just loans, then the following
621             // valid program would be considered illegal:
622             //
623             //    let mut v = ~[1, 2, 3];
624             //    let p = &const v[0];
625             //    v[1] = 5; // ok
626             //
627             // Here the restriction that `v` not be mutated would be misapplied
628             // to block the subpath `v[1]`.
629             let full_loan_path = loan_path;
630             let mut loan_path = loan_path;
631             loop {
632                 match *loan_path {
633                     // Peel back one layer if, for `loan_path` to be
634                     // mutable, `lp_base` must be mutable. This occurs
635                     // with inherited mutability and with `&mut`
636                     // pointers.
637                     LpExtend(lp_base, mc::McInherited, _) |
638                     LpExtend(lp_base, _, LpDeref(mc::BorrowedPtr(ty::MutBorrow, _))) => {
639                         loan_path = lp_base;
640                     }
641
642                     // Otherwise stop iterating
643                     LpExtend(_, mc::McDeclared, _) |
644                     LpExtend(_, mc::McImmutable, _) |
645                     LpVar(_) => {
646                         return true;
647                     }
648                 }
649
650                 // Check for a non-const loan of `loan_path`
651                 let cont = this.each_in_scope_loan(expr.id, |loan| {
652                     if loan.loan_path == loan_path {
653                         this.report_illegal_mutation(expr, full_loan_path, loan);
654                         false
655                     } else {
656                         true
657                     }
658                 });
659
660                 if !cont { return false }
661             }
662         }
663     }
664
665     pub fn report_illegal_mutation(&self,
666                                    expr: &ast::Expr,
667                                    loan_path: &LoanPath,
668                                    loan: &Loan) {
669         self.bccx.span_err(
670             expr.span,
671             format!("cannot assign to `{}` because it is borrowed",
672                  self.bccx.loan_path_to_str(loan_path)));
673         self.bccx.span_note(
674             loan.span,
675             format!("borrow of `{}` occurs here",
676                  self.bccx.loan_path_to_str(loan_path)));
677     }
678
679     fn check_move_out_from_expr(&self, expr: &ast::Expr) {
680         match expr.node {
681             ast::ExprFnBlock(..) | ast::ExprProc(..) => {
682                 // Moves due to captures are checked in
683                 // check_captured_variables() because it allows
684                 // us to give a more precise error message with
685                 // a more precise span.
686             }
687             _ => {
688                 self.check_move_out_from_id(expr.id, expr.span)
689             }
690         }
691     }
692
693     fn check_move_out_from_id(&self, id: ast::NodeId, span: Span) {
694         self.move_data.each_path_moved_by(id, |_, move_path| {
695             match self.analyze_move_out_from(id, move_path) {
696                 MoveOk => {}
697                 MoveWhileBorrowed(loan_path, loan_span) => {
698                     self.bccx.span_err(
699                         span,
700                         format!("cannot move out of `{}` \
701                                 because it is borrowed",
702                              self.bccx.loan_path_to_str(move_path)));
703                     self.bccx.span_note(
704                         loan_span,
705                         format!("borrow of `{}` occurs here",
706                                 self.bccx.loan_path_to_str(loan_path)));
707                 }
708             }
709             true
710         });
711     }
712
713     fn check_captured_variables(&self,
714                                 closure_id: ast::NodeId,
715                                 span: Span) {
716         let capture_map = self.bccx.capture_map.borrow();
717         let cap_vars = capture_map.get().get(&closure_id);
718         for cap_var in cap_vars.borrow().iter() {
719             let var_id = ast_util::def_id_of_def(cap_var.def).node;
720             let var_path = @LpVar(var_id);
721             self.check_if_path_is_moved(closure_id, span,
722                                         MovedInCapture, var_path);
723             match cap_var.mode {
724                 moves::CapRef | moves::CapCopy => {}
725                 moves::CapMove => {
726                     check_by_move_capture(self, closure_id, cap_var, var_path);
727                 }
728             }
729         }
730         return;
731
732         fn check_by_move_capture(this: &CheckLoanCtxt,
733                                  closure_id: ast::NodeId,
734                                  cap_var: &moves::CaptureVar,
735                                  move_path: @LoanPath) {
736             let move_err = this.analyze_move_out_from(closure_id, move_path);
737             match move_err {
738                 MoveOk => {}
739                 MoveWhileBorrowed(loan_path, loan_span) => {
740                     this.bccx.span_err(
741                         cap_var.span,
742                         format!("cannot move `{}` into closure \
743                                 because it is borrowed",
744                                 this.bccx.loan_path_to_str(move_path)));
745                     this.bccx.span_note(
746                         loan_span,
747                         format!("borrow of `{}` occurs here",
748                                 this.bccx.loan_path_to_str(loan_path)));
749                 }
750             }
751         }
752     }
753
754     pub fn analyze_move_out_from(&self,
755                                  expr_id: ast::NodeId,
756                                  mut move_path: @LoanPath)
757                                  -> MoveError {
758         debug!("analyze_move_out_from(expr_id={:?}, move_path={})",
759                self.tcx().map.node_to_str(expr_id),
760                move_path.repr(self.tcx()));
761
762         // We must check every element of a move path. See
763         // `borrowck-move-subcomponent.rs` for a test case.
764         loop {
765             // check for a conflicting loan:
766             let mut ret = MoveOk;
767             self.each_in_scope_restriction(expr_id, move_path, |loan, _| {
768                 // Any restriction prevents moves.
769                 ret = MoveWhileBorrowed(loan.loan_path, loan.span);
770                 false
771             });
772
773             if ret != MoveOk {
774                 return ret
775             }
776
777             match *move_path {
778                 LpVar(_) => return MoveOk,
779                 LpExtend(subpath, _, _) => move_path = subpath,
780             }
781         }
782     }
783
784     pub fn check_call(&self,
785                       _expr: &ast::Expr,
786                       _callee: Option<@ast::Expr>,
787                       _callee_span: Span,
788                       _args: &[@ast::Expr]) {
789         // NB: This call to check for conflicting loans is not truly
790         // necessary, because the callee_id never issues new loans.
791         // However, I added it for consistency and lest the system
792         // should change in the future.
793         //
794         // FIXME(#6268) nested method calls
795         // self.check_for_conflicting_loans(callee_id);
796     }
797 }
798
799 fn check_loans_in_local<'a>(this: &mut CheckLoanCtxt<'a>,
800                             local: &ast::Local) {
801     visit::walk_local(this, local, ());
802 }
803
804 fn check_loans_in_expr<'a>(this: &mut CheckLoanCtxt<'a>,
805                            expr: &ast::Expr) {
806     visit::walk_expr(this, expr, ());
807
808     debug!("check_loans_in_expr(expr={})",
809            expr.repr(this.tcx()));
810
811     this.check_for_conflicting_loans(expr.id);
812     this.check_move_out_from_expr(expr);
813
814     let method_map = this.bccx.method_map.borrow();
815     match expr.node {
816       ast::ExprPath(..) => {
817           if !this.move_data.is_assignee(expr.id) {
818               let cmt = this.bccx.cat_expr_unadjusted(expr);
819               debug!("path cmt={}", cmt.repr(this.tcx()));
820               let r = opt_loan_path(cmt);
821               for &lp in r.iter() {
822                   this.check_if_path_is_moved(expr.id, expr.span, MovedInUse, lp);
823               }
824           }
825       }
826       ast::ExprFnBlock(..) | ast::ExprProc(..) => {
827           this.check_captured_variables(expr.id, expr.span)
828       }
829       ast::ExprAssign(dest, _) |
830       ast::ExprAssignOp(_, dest, _) => {
831         this.check_assignment(dest);
832       }
833       ast::ExprCall(f, ref args) => {
834         this.check_call(expr, Some(f), f.span, *args);
835       }
836       ast::ExprMethodCall(_, _, ref args) => {
837         this.check_call(expr, None, expr.span, *args);
838       }
839       ast::ExprIndex(_, rval) | ast::ExprBinary(_, _, rval)
840       if method_map.get().contains_key(&expr.id) => {
841         this.check_call(expr, None, expr.span, [rval]);
842       }
843       ast::ExprUnary(_, _) | ast::ExprIndex(_, _)
844       if method_map.get().contains_key(&expr.id) => {
845         this.check_call(expr, None, expr.span, []);
846       }
847       ast::ExprInlineAsm(ref ia) => {
848           for &(_, out) in ia.outputs.iter() {
849               this.check_assignment(out);
850           }
851       }
852       _ => {}
853     }
854 }
855
856 fn check_loans_in_pat<'a>(this: &mut CheckLoanCtxt<'a>,
857                           pat: &ast::Pat)
858 {
859     this.check_for_conflicting_loans(pat.id);
860     this.check_move_out_from_id(pat.id, pat.span);
861     visit::walk_pat(this, pat, ());
862 }
863
864 fn check_loans_in_block<'a>(this: &mut CheckLoanCtxt<'a>,
865                             blk: &ast::Block)
866 {
867     visit::walk_block(this, blk, ());
868     this.check_for_conflicting_loans(blk.id);
869 }