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