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