]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/borrowck/check_loans.rs
auto merge of #12194 : WebeWizard/rust/master, r=cmr
[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_map;
27 use syntax::ast_util;
28 use syntax::codemap::Span;
29 use syntax::parse::token;
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) -> ~[uint] {
149         //! Returns a vector of the loans that are generated as
150         //! we encounter `scope_id`.
151
152         let mut result = ~[];
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 = ast_map::node_span(self.tcx().items,
340                                                    old_loan.kill_scope);
341             self.bccx.span_end_note(old_loan_span,
342                                     "previous borrow ends here");
343
344             return false;
345         }
346
347         true
348     }
349
350     pub fn is_local_variable(&self, cmt: mc::cmt) -> bool {
351         match cmt.cat {
352           mc::cat_local(_) => true,
353           _ => false
354         }
355     }
356
357     pub fn check_if_path_is_moved(&self,
358                                   id: ast::NodeId,
359                                   span: Span,
360                                   use_kind: MovedValueUseKind,
361                                   lp: @LoanPath) {
362         /*!
363          * Reports an error if `expr` (which should be a path)
364          * is using a moved/uninitialized value
365          */
366
367         debug!("check_if_path_is_moved(id={:?}, use_kind={:?}, lp={})",
368                id, use_kind, lp.repr(self.bccx.tcx));
369         self.move_data.each_move_of(id, lp, |move, moved_lp| {
370             self.bccx.report_use_of_moved_value(
371                 span,
372                 use_kind,
373                 lp,
374                 move,
375                 moved_lp);
376             false
377         });
378     }
379
380     pub fn check_assignment(&self, expr: &ast::Expr) {
381         // We don't use cat_expr() here because we don't want to treat
382         // auto-ref'd parameters in overloaded operators as rvalues.
383         let adj = {
384             let adjustments = self.bccx.tcx.adjustments.borrow();
385             adjustments.get().find_copy(&expr.id)
386         };
387         let cmt = match adj {
388             None => self.bccx.cat_expr_unadjusted(expr),
389             Some(adj) => self.bccx.cat_expr_autoderefd(expr, adj)
390         };
391
392         debug!("check_assignment(cmt={})", cmt.repr(self.tcx()));
393
394         // Mutable values can be assigned, as long as they obey loans
395         // and aliasing restrictions:
396         if cmt.mutbl.is_mutable() {
397             if check_for_aliasable_mutable_writes(self, expr, cmt) {
398                 if check_for_assignment_to_restricted_or_frozen_location(
399                     self, expr, cmt)
400                 {
401                     // Safe, but record for lint pass later:
402                     mark_variable_as_used_mut(self, cmt);
403                 }
404             }
405             return;
406         }
407
408         // For immutable local variables, assignments are legal
409         // if they cannot already have been assigned
410         if self.is_local_variable(cmt) {
411             assert!(cmt.mutbl.is_immutable()); // no "const" locals
412             let lp = opt_loan_path(cmt).unwrap();
413             self.move_data.each_assignment_of(expr.id, lp, |assign| {
414                 self.bccx.report_reassigned_immutable_variable(
415                     expr.span,
416                     lp,
417                     assign);
418                 false
419             });
420             return;
421         }
422
423         // Otherwise, just a plain error.
424         match opt_loan_path(cmt) {
425             Some(lp) => {
426                 self.bccx.span_err(
427                     expr.span,
428                     format!("cannot assign to {} {} `{}`",
429                             cmt.mutbl.to_user_str(),
430                             self.bccx.cmt_to_str(cmt),
431                             self.bccx.loan_path_to_str(lp)));
432             }
433             None => {
434                 self.bccx.span_err(
435                     expr.span,
436                     format!("cannot assign to {} {}",
437                             cmt.mutbl.to_user_str(),
438                             self.bccx.cmt_to_str(cmt)));
439             }
440         }
441         return;
442
443         fn mark_variable_as_used_mut(this: &CheckLoanCtxt,
444                                      cmt: mc::cmt) {
445             //! If the mutability of the `cmt` being written is inherited
446             //! from a local variable, liveness will
447             //! not have been able to detect that this variable's mutability
448             //! is important, so we must add the variable to the
449             //! `used_mut_nodes` table here.
450
451             let mut cmt = cmt;
452             loop {
453                 debug!("mark_writes_through_upvars_as_used_mut(cmt={})",
454                        cmt.repr(this.tcx()));
455                 match cmt.cat {
456                     mc::cat_local(id) | mc::cat_arg(id) => {
457                         let mut used_mut_nodes = this.tcx()
458                                                      .used_mut_nodes
459                                                      .borrow_mut();
460                         used_mut_nodes.get().insert(id);
461                         return;
462                     }
463
464                     mc::cat_upvar(..) => {
465                         return;
466                     }
467
468                     mc::cat_deref(_, _, mc::GcPtr) => {
469                         assert_eq!(cmt.mutbl, mc::McImmutable);
470                         return;
471                     }
472
473                     mc::cat_rvalue(..) |
474                     mc::cat_static_item |
475                     mc::cat_copied_upvar(..) |
476                     mc::cat_deref(_, _, mc::UnsafePtr(..)) |
477                     mc::cat_deref(_, _, mc::BorrowedPtr(..)) => {
478                         assert_eq!(cmt.mutbl, mc::McDeclared);
479                         return;
480                     }
481
482                     mc::cat_discr(b, _) |
483                     mc::cat_deref(b, _, mc::OwnedPtr) => {
484                         assert_eq!(cmt.mutbl, mc::McInherited);
485                         cmt = b;
486                     }
487
488                     mc::cat_downcast(b) |
489                     mc::cat_interior(b, _) => {
490                         assert_eq!(cmt.mutbl, mc::McInherited);
491                         cmt = b;
492                     }
493                 }
494             }
495         }
496
497         fn check_for_aliasable_mutable_writes(this: &CheckLoanCtxt,
498                                               expr: &ast::Expr,
499                                               cmt: mc::cmt) -> bool {
500             //! Safety checks related to writes to aliasable, mutable locations
501
502             let guarantor = cmt.guarantor();
503             debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
504                    cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
505             match guarantor.cat {
506                 mc::cat_deref(b, _, mc::BorrowedPtr(ty::MutBorrow, _)) => {
507                     // Statically prohibit writes to `&mut` when aliasable
508
509                     check_for_aliasability_violation(this, expr, b);
510                 }
511
512                 _ => {}
513             }
514
515             return true; // no errors reported
516         }
517
518         fn check_for_aliasability_violation(this: &CheckLoanCtxt,
519                                             expr: &ast::Expr,
520                                             cmt: mc::cmt)
521                                             -> bool {
522             match cmt.freely_aliasable() {
523                 None => {
524                     return true;
525                 }
526                 Some(cause) => {
527                     this.bccx.report_aliasability_violation(
528                         expr.span,
529                         MutabilityViolation,
530                         cause);
531                     return false;
532                 }
533             }
534         }
535
536         fn check_for_assignment_to_restricted_or_frozen_location(
537             this: &CheckLoanCtxt,
538             expr: &ast::Expr,
539             cmt: mc::cmt) -> bool
540         {
541             //! Check for assignments that violate the terms of an
542             //! outstanding loan.
543
544             let loan_path = match opt_loan_path(cmt) {
545                 Some(lp) => lp,
546                 None => { return true; /* no loan path, can't be any loans */ }
547             };
548
549             // Start by searching for an assignment to a *restricted*
550             // location. Here is one example of the kind of error caught
551             // by this check:
552             //
553             //    let mut v = ~[1, 2, 3];
554             //    let p = &v;
555             //    v = ~[4];
556             //
557             // In this case, creating `p` triggers a RESTR_MUTATE
558             // restriction on the path `v`.
559             //
560             // Here is a second, more subtle example:
561             //
562             //    let mut v = ~[1, 2, 3];
563             //    let p = &const v[0];
564             //    v[0] = 4;                   // OK
565             //    v[1] = 5;                   // OK
566             //    v = ~[4, 5, 3];             // Error
567             //
568             // In this case, `p` is pointing to `v[0]`, and it is a
569             // `const` pointer in any case. So the first two
570             // assignments are legal (and would be permitted by this
571             // check). However, the final assignment (which is
572             // logically equivalent) is forbidden, because it would
573             // cause the existing `v` array to be freed, thus
574             // invalidating `p`. In the code, this error results
575             // because `gather_loans::restrictions` adds a
576             // `RESTR_MUTATE` restriction whenever the contents of an
577             // owned pointer are borrowed, and hence while `v[*]` is not
578             // restricted from being written, `v` is.
579             let cont = this.each_in_scope_restriction(expr.id,
580                                                       loan_path,
581                                                       |loan, restr| {
582                 if restr.set.intersects(RESTR_MUTATE) {
583                     this.report_illegal_mutation(expr, loan_path, loan);
584                     false
585                 } else {
586                     true
587                 }
588             });
589
590             if !cont { return false }
591
592             // The previous code handled assignments to paths that
593             // have been restricted. This covers paths that have been
594             // directly lent out and their base paths, but does not
595             // cover random extensions of those paths. For example,
596             // the following program is not declared illegal by the
597             // previous check:
598             //
599             //    let mut v = ~[1, 2, 3];
600             //    let p = &v;
601             //    v[0] = 4; // declared error by loop below, not code above
602             //
603             // The reason that this passes the previous check whereas
604             // an assignment like `v = ~[4]` fails is because the assignment
605             // here is to `v[*]`, and the existing restrictions were issued
606             // for `v`, not `v[*]`.
607             //
608             // So in this loop, we walk back up the loan path so long
609             // as the mutability of the path is dependent on a super
610             // path, and check that the super path was not lent out as
611             // mutable or immutable (a const loan is ok).
612             //
613             // Mutability of a path can be dependent on the super path
614             // in two ways. First, it might be inherited mutability.
615             // Second, the pointee of an `&mut` pointer can only be
616             // mutated if it is found in an unaliased location, so we
617             // have to check that the owner location is not borrowed.
618             //
619             // Note that we are *not* checking for any and all
620             // restrictions.  We are only interested in the pointers
621             // that the user created, whereas we add restrictions for
622             // all kinds of paths that are not directly aliased. If we checked
623             // for all restrictions, and not just loans, then the following
624             // valid program would be considered illegal:
625             //
626             //    let mut v = ~[1, 2, 3];
627             //    let p = &const v[0];
628             //    v[1] = 5; // ok
629             //
630             // Here the restriction that `v` not be mutated would be misapplied
631             // to block the subpath `v[1]`.
632             let full_loan_path = loan_path;
633             let mut loan_path = loan_path;
634             loop {
635                 match *loan_path {
636                     // Peel back one layer if, for `loan_path` to be
637                     // mutable, `lp_base` must be mutable. This occurs
638                     // with inherited mutability and with `&mut`
639                     // pointers.
640                     LpExtend(lp_base, mc::McInherited, _) |
641                     LpExtend(lp_base, _, LpDeref(mc::BorrowedPtr(ty::MutBorrow, _))) => {
642                         loan_path = lp_base;
643                     }
644
645                     // Otherwise stop iterating
646                     LpExtend(_, mc::McDeclared, _) |
647                     LpExtend(_, mc::McImmutable, _) |
648                     LpVar(_) => {
649                         return true;
650                     }
651                 }
652
653                 // Check for a non-const loan of `loan_path`
654                 let cont = this.each_in_scope_loan(expr.id, |loan| {
655                     if loan.loan_path == loan_path {
656                         this.report_illegal_mutation(expr, full_loan_path, loan);
657                         false
658                     } else {
659                         true
660                     }
661                 });
662
663                 if !cont { return false }
664             }
665         }
666     }
667
668     pub fn report_illegal_mutation(&self,
669                                    expr: &ast::Expr,
670                                    loan_path: &LoanPath,
671                                    loan: &Loan) {
672         self.bccx.span_err(
673             expr.span,
674             format!("cannot assign to `{}` because it is borrowed",
675                  self.bccx.loan_path_to_str(loan_path)));
676         self.bccx.span_note(
677             loan.span,
678             format!("borrow of `{}` occurs here",
679                  self.bccx.loan_path_to_str(loan_path)));
680     }
681
682     fn check_move_out_from_expr(&self, expr: &ast::Expr) {
683         match expr.node {
684             ast::ExprFnBlock(..) | ast::ExprProc(..) => {
685                 // Moves due to captures are checked in
686                 // check_captured_variables() because it allows
687                 // us to give a more precise error message with
688                 // a more precise span.
689             }
690             _ => {
691                 self.check_move_out_from_id(expr.id, expr.span)
692             }
693         }
694     }
695
696     fn check_move_out_from_id(&self, id: ast::NodeId, span: Span) {
697         self.move_data.each_path_moved_by(id, |_, move_path| {
698             match self.analyze_move_out_from(id, move_path) {
699                 MoveOk => {}
700                 MoveWhileBorrowed(loan_path, loan_span) => {
701                     self.bccx.span_err(
702                         span,
703                         format!("cannot move out of `{}` \
704                                 because it is borrowed",
705                              self.bccx.loan_path_to_str(move_path)));
706                     self.bccx.span_note(
707                         loan_span,
708                         format!("borrow of `{}` occurs here",
709                                 self.bccx.loan_path_to_str(loan_path)));
710                 }
711             }
712             true
713         });
714     }
715
716     fn check_captured_variables(&self,
717                                 closure_id: ast::NodeId,
718                                 span: Span) {
719         let capture_map = self.bccx.capture_map.borrow();
720         let cap_vars = capture_map.get().get(&closure_id);
721         for cap_var in cap_vars.borrow().iter() {
722             let var_id = ast_util::def_id_of_def(cap_var.def).node;
723             let var_path = @LpVar(var_id);
724             self.check_if_path_is_moved(closure_id, span,
725                                         MovedInCapture, var_path);
726             match cap_var.mode {
727                 moves::CapRef | moves::CapCopy => {}
728                 moves::CapMove => {
729                     check_by_move_capture(self, closure_id, cap_var, var_path);
730                 }
731             }
732         }
733         return;
734
735         fn check_by_move_capture(this: &CheckLoanCtxt,
736                                  closure_id: ast::NodeId,
737                                  cap_var: &moves::CaptureVar,
738                                  move_path: @LoanPath) {
739             let move_err = this.analyze_move_out_from(closure_id, move_path);
740             match move_err {
741                 MoveOk => {}
742                 MoveWhileBorrowed(loan_path, loan_span) => {
743                     this.bccx.span_err(
744                         cap_var.span,
745                         format!("cannot move `{}` into closure \
746                                 because it is borrowed",
747                                 this.bccx.loan_path_to_str(move_path)));
748                     this.bccx.span_note(
749                         loan_span,
750                         format!("borrow of `{}` occurs here",
751                                 this.bccx.loan_path_to_str(loan_path)));
752                 }
753             }
754         }
755     }
756
757     pub fn analyze_move_out_from(&self,
758                                  expr_id: ast::NodeId,
759                                  mut move_path: @LoanPath)
760                                  -> MoveError {
761         debug!("analyze_move_out_from(expr_id={:?}, move_path={})",
762                ast_map::node_id_to_str(self.tcx().items,
763                                        expr_id,
764                                        token::get_ident_interner()),
765                move_path.repr(self.tcx()));
766
767         // We must check every element of a move path. See
768         // `borrowck-move-subcomponent.rs` for a test case.
769         loop {
770             // check for a conflicting loan:
771             let mut ret = MoveOk;
772             self.each_in_scope_restriction(expr_id, move_path, |loan, _| {
773                 // Any restriction prevents moves.
774                 ret = MoveWhileBorrowed(loan.loan_path, loan.span);
775                 false
776             });
777
778             if ret != MoveOk {
779                 return ret
780             }
781
782             match *move_path {
783                 LpVar(_) => return MoveOk,
784                 LpExtend(subpath, _, _) => move_path = subpath,
785             }
786         }
787     }
788
789     pub fn check_call(&self,
790                       _expr: &ast::Expr,
791                       _callee: Option<@ast::Expr>,
792                       _callee_id: ast::NodeId,
793                       _callee_span: Span,
794                       _args: &[@ast::Expr]) {
795         // NB: This call to check for conflicting loans is not truly
796         // necessary, because the callee_id never issues new loans.
797         // However, I added it for consistency and lest the system
798         // should change in the future.
799         //
800         // FIXME(#6268) nested method calls
801         // self.check_for_conflicting_loans(callee_id);
802     }
803 }
804
805 fn check_loans_in_local<'a>(this: &mut CheckLoanCtxt<'a>,
806                             local: &ast::Local) {
807     visit::walk_local(this, local, ());
808 }
809
810 fn check_loans_in_expr<'a>(this: &mut CheckLoanCtxt<'a>,
811                            expr: &ast::Expr) {
812     visit::walk_expr(this, expr, ());
813
814     debug!("check_loans_in_expr(expr={})",
815            expr.repr(this.tcx()));
816
817     this.check_for_conflicting_loans(expr.id);
818     this.check_move_out_from_expr(expr);
819
820     let method_map = this.bccx.method_map.borrow();
821     match expr.node {
822       ast::ExprPath(..) => {
823           if !this.move_data.is_assignee(expr.id) {
824               let cmt = this.bccx.cat_expr_unadjusted(expr);
825               debug!("path cmt={}", cmt.repr(this.tcx()));
826               let r = opt_loan_path(cmt);
827               for &lp in r.iter() {
828                   this.check_if_path_is_moved(expr.id, expr.span, MovedInUse, lp);
829               }
830           }
831       }
832       ast::ExprFnBlock(..) | ast::ExprProc(..) => {
833           this.check_captured_variables(expr.id, expr.span)
834       }
835       ast::ExprAssign(dest, _) |
836       ast::ExprAssignOp(_, _, dest, _) => {
837         this.check_assignment(dest);
838       }
839       ast::ExprCall(f, ref args, _) => {
840         this.check_call(expr, Some(f), f.id, f.span, *args);
841       }
842       ast::ExprMethodCall(callee_id, _, _, ref args, _) => {
843         this.check_call(expr, None, callee_id, expr.span, *args);
844       }
845       ast::ExprIndex(callee_id, _, rval) |
846       ast::ExprBinary(callee_id, _, _, rval)
847       if method_map.get().contains_key(&expr.id) => {
848         this.check_call(expr, None, callee_id, expr.span, [rval]);
849       }
850       ast::ExprUnary(callee_id, _, _) | ast::ExprIndex(callee_id, _, _)
851       if method_map.get().contains_key(&expr.id) => {
852         this.check_call(expr, None, callee_id, expr.span, []);
853       }
854       ast::ExprInlineAsm(ref ia) => {
855           for &(_, out) in ia.outputs.iter() {
856               this.check_assignment(out);
857           }
858       }
859       _ => {}
860     }
861 }
862
863 fn check_loans_in_pat<'a>(this: &mut CheckLoanCtxt<'a>,
864                           pat: &ast::Pat)
865 {
866     this.check_for_conflicting_loans(pat.id);
867     this.check_move_out_from_id(pat.id, pat.span);
868     visit::walk_pat(this, pat, ());
869 }
870
871 fn check_loans_in_block<'a>(this: &mut CheckLoanCtxt<'a>,
872                             blk: &ast::Block)
873 {
874     visit::walk_block(this, blk, ());
875     this.check_for_conflicting_loans(blk.id);
876 }