]> git.proxmox.com Git - rustc.git/blame - src/librustc/middle/borrowck/check_loans.rs
Imported Upstream version 0.7
[rustc.git] / src / librustc / middle / borrowck / check_loans.rs
CommitLineData
223e47cc
LB
1// Copyright 2012 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
223e47cc 20
970d7e83
LB
21use std::hashmap::HashSet;
22use std::uint;
23use mc = middle::mem_categorization;
24use middle::borrowck::*;
223e47cc 25use middle::moves;
223e47cc 26use middle::ty;
970d7e83 27use syntax::ast::{m_mutbl, m_imm, m_const};
223e47cc
LB
28use syntax::ast;
29use syntax::ast_util;
30use syntax::codemap::span;
223e47cc 31use syntax::visit;
970d7e83 32use util::ppaux::Repr;
223e47cc 33
970d7e83 34struct CheckLoanCtxt<'self> {
223e47cc 35 bccx: @BorrowckCtxt,
970d7e83
LB
36 dfcx_loans: &'self LoanDataFlow,
37 move_data: move_data::FlowedMoveData,
38 all_loans: &'self [Loan],
39 reported: @mut HashSet<ast::node_id>,
223e47cc
LB
40}
41
42pub fn check_loans(bccx: @BorrowckCtxt,
970d7e83
LB
43 dfcx_loans: &LoanDataFlow,
44 move_data: move_data::FlowedMoveData,
45 all_loans: &[Loan],
46 body: &ast::blk) {
47 debug!("check_loans(body id=%?)", body.node.id);
48
223e47cc
LB
49 let clcx = @mut CheckLoanCtxt {
50 bccx: bccx,
970d7e83
LB
51 dfcx_loans: dfcx_loans,
52 move_data: move_data,
53 all_loans: all_loans,
54 reported: @mut HashSet::new(),
223e47cc 55 };
970d7e83 56
223e47cc
LB
57 let vt = visit::mk_vt(@visit::Visitor {visit_expr: check_loans_in_expr,
58 visit_local: check_loans_in_local,
59 visit_block: check_loans_in_block,
970d7e83 60 visit_pat: check_loans_in_pat,
223e47cc
LB
61 visit_fn: check_loans_in_fn,
62 .. *visit::default_visitor()});
970d7e83 63 (vt.visit_block)(body, (clcx, vt));
223e47cc
LB
64}
65
970d7e83
LB
66enum MoveError {
67 MoveOk,
68 MoveWhileBorrowed(/*move*/@LoanPath, /*loan*/@LoanPath, /*loan*/span)
223e47cc
LB
69}
70
970d7e83
LB
71impl<'self> CheckLoanCtxt<'self> {
72 pub fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
73
74 pub fn each_issued_loan(&self,
75 scope_id: ast::node_id,
76 op: &fn(&Loan) -> bool)
77 -> bool {
78 //! Iterates over each loan that has been issued
79 //! on entrance to `scope_id`, regardless of whether it is
80 //! actually *in scope* at that point. Sometimes loans
81 //! are issued for future scopes and thus they may have been
82 //! *issued* but not yet be in effect.
83
84 for self.dfcx_loans.each_bit_on_entry_frozen(scope_id) |loan_index| {
85 let loan = &self.all_loans[loan_index];
86 if !op(loan) {
87 return false;
223e47cc
LB
88 }
89 }
970d7e83 90 return true;
223e47cc
LB
91 }
92
970d7e83
LB
93 pub fn each_in_scope_loan(&self,
94 scope_id: ast::node_id,
95 op: &fn(&Loan) -> bool)
96 -> bool {
97 //! Like `each_issued_loan()`, but only considers loans that are
98 //! currently in scope.
99
100 let region_maps = self.tcx().region_maps;
101 for self.each_issued_loan(scope_id) |loan| {
102 if region_maps.is_subscope_of(scope_id, loan.kill_scope) {
103 if !op(loan) {
104 return false;
223e47cc
LB
105 }
106 }
223e47cc 107 }
970d7e83 108 return true;
223e47cc
LB
109 }
110
970d7e83
LB
111 pub fn each_in_scope_restriction(&self,
112 scope_id: ast::node_id,
113 loan_path: @LoanPath,
114 op: &fn(&Loan, &Restriction) -> bool)
115 -> bool {
116 //! Iterates through all the in-scope restrictions for the
117 //! given `loan_path`
118
119 for self.each_in_scope_loan(scope_id) |loan| {
120 for loan.restrictions.iter().advance |restr| {
121 if restr.loan_path == loan_path {
122 if !op(loan, restr) {
123 return false;
223e47cc
LB
124 }
125 }
126 }
223e47cc 127 }
970d7e83 128 return true;
223e47cc
LB
129 }
130
970d7e83
LB
131 pub fn loans_generated_by(&self, scope_id: ast::node_id) -> ~[uint] {
132 //! Returns a vector of the loans that are generated as
133 //! we encounter `scope_id`.
134
135 let mut result = ~[];
136 for self.dfcx_loans.each_gen_bit_frozen(scope_id) |loan_index| {
137 result.push(loan_index);
223e47cc 138 }
970d7e83 139 return result;
223e47cc
LB
140 }
141
970d7e83
LB
142 pub fn check_for_conflicting_loans(&mut self, scope_id: ast::node_id) {
143 //! Checks to see whether any of the loans that are issued
144 //! by `scope_id` conflict with loans that have already been
145 //! issued when we enter `scope_id` (for example, we do not
146 //! permit two `&mut` borrows of the same variable).
223e47cc 147
223e47cc
LB
148 debug!("check_for_conflicting_loans(scope_id=%?)", scope_id);
149
970d7e83
LB
150 let new_loan_indices = self.loans_generated_by(scope_id);
151 debug!("new_loan_indices = %?", new_loan_indices);
223e47cc 152
970d7e83
LB
153 for self.each_issued_loan(scope_id) |issued_loan| {
154 for new_loan_indices.iter().advance |&new_loan_index| {
155 let new_loan = &self.all_loans[new_loan_index];
156 self.report_error_if_loans_conflict(issued_loan, new_loan);
223e47cc
LB
157 }
158 }
159
970d7e83
LB
160 for uint::range(0, new_loan_indices.len()) |i| {
161 let old_loan = &self.all_loans[new_loan_indices[i]];
162 for uint::range(i+1, new_loan_indices.len()) |j| {
163 let new_loan = &self.all_loans[new_loan_indices[j]];
164 self.report_error_if_loans_conflict(old_loan, new_loan);
223e47cc
LB
165 }
166 }
167 }
168
970d7e83
LB
169 pub fn report_error_if_loans_conflict(&self,
170 old_loan: &Loan,
171 new_loan: &Loan) {
172 //! Checks whether `old_loan` and `new_loan` can safely be issued
173 //! simultaneously.
174
175 debug!("report_error_if_loans_conflict(old_loan=%s, new_loan=%s)",
176 old_loan.repr(self.tcx()),
177 new_loan.repr(self.tcx()));
178
179 // Should only be called for loans that are in scope at the same time.
180 let region_maps = self.tcx().region_maps;
181 assert!(region_maps.scopes_intersect(old_loan.kill_scope,
182 new_loan.kill_scope));
183
184 self.report_error_if_loan_conflicts_with_restriction(
185 old_loan, new_loan, old_loan, new_loan) &&
186 self.report_error_if_loan_conflicts_with_restriction(
187 new_loan, old_loan, old_loan, new_loan);
188 }
223e47cc 189
970d7e83
LB
190 pub fn report_error_if_loan_conflicts_with_restriction(&self,
191 loan1: &Loan,
192 loan2: &Loan,
193 old_loan: &Loan,
194 new_loan: &Loan)
195 -> bool {
196 //! Checks whether the restrictions introduced by `loan1` would
197 //! prohibit `loan2`. Returns false if an error is reported.
198
199 debug!("report_error_if_loan_conflicts_with_restriction(\
200 loan1=%s, loan2=%s)",
201 loan1.repr(self.tcx()),
202 loan2.repr(self.tcx()));
203
204 // Restrictions that would cause the new loan to be illegal:
205 let illegal_if = match loan2.mutbl {
206 m_mutbl => RESTR_ALIAS | RESTR_FREEZE | RESTR_CLAIM,
207 m_imm => RESTR_ALIAS | RESTR_FREEZE,
208 m_const => RESTR_ALIAS,
209 };
210 debug!("illegal_if=%?", illegal_if);
223e47cc 211
970d7e83
LB
212 for loan1.restrictions.iter().advance |restr| {
213 if !restr.set.intersects(illegal_if) { loop; }
214 if restr.loan_path != loan2.loan_path { loop; }
215
216 match (new_loan.mutbl, old_loan.mutbl) {
217 (m_mutbl, m_mutbl) => {
218 self.bccx.span_err(
219 new_loan.span,
220 fmt!("cannot borrow `%s` as mutable \
221 more than once at a time",
222 self.bccx.loan_path_to_str(new_loan.loan_path)));
223 self.bccx.span_note(
224 old_loan.span,
225 fmt!("second borrow of `%s` as mutable occurs here",
226 self.bccx.loan_path_to_str(new_loan.loan_path)));
227 return false;
228 }
229
230 _ => {
231 self.bccx.span_err(
232 new_loan.span,
233 fmt!("cannot borrow `%s` as %s because \
234 it is also borrowed as %s"
235 self.bccx.loan_path_to_str(new_loan.loan_path),
236 self.bccx.mut_to_str(new_loan.mutbl),
237 self.bccx.mut_to_str(old_loan.mutbl)));
238 self.bccx.span_note(
239 old_loan.span,
240 fmt!("second borrow of `%s` occurs here",
241 self.bccx.loan_path_to_str(new_loan.loan_path)));
242 return false;
243 }
223e47cc
LB
244 }
245 }
970d7e83
LB
246
247 true
223e47cc
LB
248 }
249
970d7e83 250 pub fn is_local_variable(&self, cmt: mc::cmt) -> bool {
223e47cc 251 match cmt.cat {
970d7e83 252 mc::cat_local(_) => true,
223e47cc
LB
253 _ => false
254 }
255 }
256
970d7e83
LB
257 pub fn check_if_path_is_moved(&self,
258 id: ast::node_id,
259 span: span,
260 use_kind: MovedValueUseKind,
261 lp: @LoanPath) {
262 /*!
263 * Reports an error if `expr` (which should be a path)
264 * is using a moved/uninitialized value
265 */
266
267 debug!("check_if_path_is_moved(id=%?, use_kind=%?, lp=%s)",
268 id, use_kind, lp.repr(self.bccx.tcx));
269 for self.move_data.each_move_of(id, lp) |move, moved_lp| {
270 self.bccx.report_use_of_moved_value(
271 span,
272 use_kind,
273 lp,
274 move,
275 moved_lp);
276 return;
277 }
278 }
279
280 pub fn check_assignment(&self, expr: @ast::expr) {
223e47cc
LB
281 // We don't use cat_expr() here because we don't want to treat
282 // auto-ref'd parameters in overloaded operators as rvalues.
970d7e83
LB
283 let cmt = match self.bccx.tcx.adjustments.find(&expr.id) {
284 None => self.bccx.cat_expr_unadjusted(expr),
285 Some(&adj) => self.bccx.cat_expr_autoderefd(expr, adj)
223e47cc
LB
286 };
287
970d7e83
LB
288 debug!("check_assignment(cmt=%s)", cmt.repr(self.tcx()));
289
290 // Mutable values can be assigned, as long as they obey loans
291 // and aliasing restrictions:
292 if cmt.mutbl.is_mutable() {
293 if check_for_aliasable_mutable_writes(self, expr, cmt) {
294 if check_for_assignment_to_restricted_or_frozen_location(
295 self, expr, cmt)
296 {
297 // Safe, but record for lint pass later:
298 mark_variable_as_used_mut(self, cmt);
223e47cc
LB
299 }
300 }
970d7e83 301 return;
223e47cc
LB
302 }
303
970d7e83
LB
304 // For immutable local variables, assignments are legal
305 // if they cannot already have been assigned
306 if self.is_local_variable(cmt) {
307 assert!(cmt.mutbl.is_immutable()); // no "const" locals
308 let lp = opt_loan_path(cmt).get();
309 for self.move_data.each_assignment_of(expr.id, lp) |assign| {
310 self.bccx.report_reassigned_immutable_variable(
311 expr.span,
312 lp,
313 assign);
314 return;
223e47cc 315 }
970d7e83 316 return;
223e47cc
LB
317 }
318
970d7e83
LB
319 // Otherwise, just a plain error.
320 self.bccx.span_err(
321 expr.span,
322 fmt!("cannot assign to %s %s"
323 cmt.mutbl.to_user_str(),
324 self.bccx.cmt_to_str(cmt)));
325 return;
326
327 fn mark_variable_as_used_mut(this: &CheckLoanCtxt,
328 cmt: mc::cmt) {
329 //! If the mutability of the `cmt` being written is inherited
330 //! from a local variable, liveness will
331 //! not have been able to detect that this variable's mutability
332 //! is important, so we must add the variable to the
333 //! `used_mut_nodes` table here.
334
335 let mut cmt = cmt;
336 loop {
337 debug!("mark_writes_through_upvars_as_used_mut(cmt=%s)",
338 cmt.repr(this.tcx()));
339 match cmt.cat {
340 mc::cat_local(id) |
341 mc::cat_arg(id) |
342 mc::cat_self(id) => {
343 this.tcx().used_mut_nodes.insert(id);
344 return;
345 }
223e47cc 346
970d7e83
LB
347 mc::cat_stack_upvar(b) => {
348 cmt = b;
349 }
223e47cc 350
970d7e83
LB
351 mc::cat_rvalue |
352 mc::cat_static_item |
353 mc::cat_implicit_self |
354 mc::cat_copied_upvar(*) |
355 mc::cat_deref(_, _, mc::unsafe_ptr(*)) |
356 mc::cat_deref(_, _, mc::gc_ptr(*)) |
357 mc::cat_deref(_, _, mc::region_ptr(*)) => {
358 assert_eq!(cmt.mutbl, mc::McDeclared);
359 return;
360 }
223e47cc 361
970d7e83
LB
362 mc::cat_discr(b, _) |
363 mc::cat_deref(b, _, mc::uniq_ptr(*)) => {
364 assert_eq!(cmt.mutbl, mc::McInherited);
365 cmt = b;
366 }
367
368 mc::cat_downcast(b) |
369 mc::cat_interior(b, _) => {
370 if cmt.mutbl == mc::McInherited {
371 cmt = b;
372 } else {
373 return; // field declared as mutable or some such
374 }
223e47cc 375 }
223e47cc
LB
376 }
377 }
223e47cc 378 }
223e47cc 379
970d7e83
LB
380 fn check_for_aliasable_mutable_writes(this: &CheckLoanCtxt,
381 expr: @ast::expr,
382 cmt: mc::cmt) -> bool {
383 //! Safety checks related to writes to aliasable, mutable locations
384
385 let guarantor = cmt.guarantor();
386 debug!("check_for_aliasable_mutable_writes(cmt=%s, guarantor=%s)",
387 cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
388 match guarantor.cat {
389 mc::cat_deref(b, _, mc::region_ptr(m_mutbl, _)) => {
390 // Statically prohibit writes to `&mut` when aliasable
391
392 match b.freely_aliasable() {
393 None => {}
394 Some(cause) => {
395 this.bccx.report_aliasability_violation(
396 expr.span,
397 MutabilityViolation,
398 cause);
399 }
400 }
223e47cc 401 }
970d7e83
LB
402
403 mc::cat_deref(_, deref_count, mc::gc_ptr(ast::m_mutbl)) => {
404 // Dynamically check writes to `@mut`
405
406 let key = root_map_key {
407 id: guarantor.id,
408 derefs: deref_count
409 };
410 debug!("Inserting write guard at %?", key);
411 this.bccx.write_guard_map.insert(key);
412 }
413
414 _ => {}
223e47cc 415 }
223e47cc 416
970d7e83 417 return true; // no errors reported
223e47cc 418 }
223e47cc 419
970d7e83
LB
420 fn check_for_assignment_to_restricted_or_frozen_location(
421 this: &CheckLoanCtxt,
422 expr: @ast::expr,
423 cmt: mc::cmt) -> bool
424 {
425 //! Check for assignments that violate the terms of an
426 //! outstanding loan.
427
428 let loan_path = match opt_loan_path(cmt) {
429 Some(lp) => lp,
430 None => { return true; /* no loan path, can't be any loans */ }
431 };
432
433 // Start by searching for an assignment to a *restricted*
434 // location. Here is one example of the kind of error caught
435 // by this check:
436 //
437 // let mut v = ~[1, 2, 3];
438 // let p = &v;
439 // v = ~[4];
440 //
441 // In this case, creating `p` triggers a RESTR_MUTATE
442 // restriction on the path `v`.
443 //
444 // Here is a second, more subtle example:
445 //
446 // let mut v = ~[1, 2, 3];
447 // let p = &const v[0];
448 // v[0] = 4; // OK
449 // v[1] = 5; // OK
450 // v = ~[4, 5, 3]; // Error
451 //
452 // In this case, `p` is pointing to `v[0]`, and it is a
453 // `const` pointer in any case. So the first two
454 // assignments are legal (and would be permitted by this
455 // check). However, the final assignment (which is
456 // logically equivalent) is forbidden, because it would
457 // cause the existing `v` array to be freed, thus
458 // invalidating `p`. In the code, this error results
459 // because `gather_loans::restrictions` adds a
460 // `RESTR_MUTATE` restriction whenever the contents of an
461 // owned pointer are borrowed, and hence while `v[*]` is not
462 // restricted from being written, `v` is.
463 for this.each_in_scope_restriction(expr.id, loan_path)
464 |loan, restr|
465 {
466 if restr.set.intersects(RESTR_MUTATE) {
467 this.report_illegal_mutation(expr, loan_path, loan);
468 return false;
469 }
470 }
471
472 // The previous code handled assignments to paths that
473 // have been restricted. This covers paths that have been
474 // directly lent out and their base paths, but does not
475 // cover random extensions of those paths. For example,
476 // the following program is not declared illegal by the
477 // previous check:
478 //
479 // let mut v = ~[1, 2, 3];
480 // let p = &v;
481 // v[0] = 4; // declared error by loop below, not code above
482 //
483 // The reason that this passes the previous check whereas
484 // an assignment like `v = ~[4]` fails is because the assignment
485 // here is to `v[*]`, and the existing restrictions were issued
486 // for `v`, not `v[*]`.
487 //
488 // So in this loop, we walk back up the loan path so long
489 // as the mutability of the path is dependent on a super
490 // path, and check that the super path was not lent out as
491 // mutable or immutable (a const loan is ok).
492 //
493 // Note that we are *not* checking for any and all
494 // restrictions. We are only interested in the pointers
495 // that the user created, whereas we add restrictions for
496 // all kinds of paths that are not directly aliased. If we checked
497 // for all restrictions, and not just loans, then the following
498 // valid program would be considered illegal:
499 //
500 // let mut v = ~[1, 2, 3];
501 // let p = &const v[0];
502 // v[1] = 5; // ok
503 //
504 // Here the restriction that `v` not be mutated would be misapplied
505 // to block the subpath `v[1]`.
506 let full_loan_path = loan_path;
507 let mut loan_path = loan_path;
508 loop {
509 match *loan_path {
510 // Peel back one layer if `loan_path` has
511 // inherited mutability
512 LpExtend(lp_base, mc::McInherited, _) => {
513 loan_path = lp_base;
514 }
515
516 // Otherwise stop iterating
517 LpExtend(_, mc::McDeclared, _) |
518 LpExtend(_, mc::McImmutable, _) |
519 LpExtend(_, mc::McReadOnly, _) |
520 LpVar(_) => {
521 return true;
522 }
523 }
524
525 // Check for a non-const loan of `loan_path`
526 for this.each_in_scope_loan(expr.id) |loan| {
527 if loan.loan_path == loan_path && loan.mutbl != m_const {
528 this.report_illegal_mutation(expr, full_loan_path, loan);
529 return false;
530 }
531 }
223e47cc 532 }
223e47cc
LB
533 }
534 }
535
970d7e83
LB
536 pub fn report_illegal_mutation(&self,
537 expr: @ast::expr,
538 loan_path: &LoanPath,
539 loan: &Loan) {
540 self.bccx.span_err(
541 expr.span,
542 fmt!("cannot assign to `%s` because it is borrowed",
543 self.bccx.loan_path_to_str(loan_path)));
544 self.bccx.span_note(
545 loan.span,
546 fmt!("borrow of `%s` occurs here",
547 self.bccx.loan_path_to_str(loan_path)));
548 }
549
550 pub fn check_move_out_from_expr(&self, ex: @ast::expr) {
223e47cc
LB
551 match ex.node {
552 ast::expr_paren(*) => {
553 /* In the case of an expr_paren(), the expression inside
554 * the parens will also be marked as being moved. Ignore
555 * the parents then so as not to report duplicate errors. */
556 }
557 _ => {
558 let cmt = self.bccx.cat_expr(ex);
559 match self.analyze_move_out_from_cmt(cmt) {
560 MoveOk => {}
970d7e83 561 MoveWhileBorrowed(move_path, loan_path, loan_span) => {
223e47cc
LB
562 self.bccx.span_err(
563 cmt.span,
970d7e83
LB
564 fmt!("cannot move out of `%s` \
565 because it is borrowed",
566 self.bccx.loan_path_to_str(move_path)));
223e47cc 567 self.bccx.span_note(
970d7e83
LB
568 loan_span,
569 fmt!("borrow of `%s` occurs here",
570 self.bccx.loan_path_to_str(loan_path)));
223e47cc
LB
571 }
572 }
573 }
574 }
575 }
576
970d7e83
LB
577 pub fn analyze_move_out_from_cmt(&self, cmt: mc::cmt) -> MoveError {
578 debug!("analyze_move_out_from_cmt(cmt=%s)", cmt.repr(self.tcx()));
223e47cc 579
970d7e83 580 // FIXME(#4384) inadequare if/when we permit `move a.b`
223e47cc
LB
581
582 // check for a conflicting loan:
970d7e83
LB
583 let r = opt_loan_path(cmt);
584 for r.iter().advance |&lp| {
585 for self.each_in_scope_restriction(cmt.id, lp) |loan, _| {
586 // Any restriction prevents moves.
587 return MoveWhileBorrowed(lp, loan.loan_path, loan.span);
223e47cc
LB
588 }
589 }
590
970d7e83 591 MoveOk
223e47cc
LB
592 }
593
970d7e83
LB
594 pub fn check_call(&mut self,
595 _expr: @ast::expr,
596 _callee: Option<@ast::expr>,
597 _callee_id: ast::node_id,
598 _callee_span: span,
599 _args: &[@ast::expr]) {
600 // NB: This call to check for conflicting loans is not truly
601 // necessary, because the callee_id never issues new loans.
602 // However, I added it for consistency and lest the system
603 // should change in the future.
604 //
605 // FIXME(#6268) nested method calls
606 // self.check_for_conflicting_loans(callee_id);
223e47cc
LB
607 }
608}
609
970d7e83
LB
610fn check_loans_in_fn<'a>(fk: &visit::fn_kind,
611 decl: &ast::fn_decl,
612 body: &ast::blk,
613 sp: span,
614 id: ast::node_id,
615 (this, visitor): (@mut CheckLoanCtxt<'a>,
616 visit::vt<@mut CheckLoanCtxt<'a>>)) {
223e47cc 617 match *fk {
970d7e83
LB
618 visit::fk_item_fn(*) |
619 visit::fk_method(*) => {
620 // Don't process nested items.
621 return;
223e47cc
LB
622 }
623
970d7e83
LB
624 visit::fk_anon(*) |
625 visit::fk_fn_block(*) => {
626 check_captured_variables(this, id, sp);
223e47cc
LB
627 }
628 }
629
970d7e83
LB
630 visit::visit_fn(fk, decl, body, sp, id, (this, visitor));
631
632 fn check_captured_variables(this: @mut CheckLoanCtxt,
633 closure_id: ast::node_id,
634 span: span) {
635 let cap_vars = this.bccx.capture_map.get(&closure_id);
636 for cap_vars.iter().advance |cap_var| {
637 match cap_var.mode {
638 moves::CapRef | moves::CapCopy => {
639 let var_id = ast_util::def_id_of_def(cap_var.def).node;
640 let lp = @LpVar(var_id);
641 this.check_if_path_is_moved(closure_id, span,
642 MovedInCapture, lp);
223e47cc 643 }
970d7e83
LB
644 moves::CapMove => {
645 check_by_move_capture(this, closure_id, cap_var);
223e47cc
LB
646 }
647 }
223e47cc 648 }
970d7e83
LB
649 return;
650
651 fn check_by_move_capture(this: @mut CheckLoanCtxt,
652 closure_id: ast::node_id,
653 cap_var: &moves::CaptureVar) {
654 let var_id = ast_util::def_id_of_def(cap_var.def).node;
655 let ty = ty::node_id_to_type(this.tcx(), var_id);
656 let cmt = this.bccx.cat_def(closure_id, cap_var.span,
657 ty, cap_var.def);
658 let move_err = this.analyze_move_out_from_cmt(cmt);
659 match move_err {
660 MoveOk => {}
661 MoveWhileBorrowed(move_path, loan_path, loan_span) => {
662 this.bccx.span_err(
663 cap_var.span,
664 fmt!("cannot move `%s` into closure \
665 because it is borrowed",
666 this.bccx.loan_path_to_str(move_path)));
667 this.bccx.span_note(
668 loan_span,
669 fmt!("borrow of `%s` occurs here",
670 this.bccx.loan_path_to_str(loan_path)));
223e47cc
LB
671 }
672 }
223e47cc
LB
673 }
674 }
675}
676
970d7e83
LB
677fn check_loans_in_local<'a>(local: @ast::local,
678 (this, vt): (@mut CheckLoanCtxt<'a>,
679 visit::vt<@mut CheckLoanCtxt<'a>>)) {
680 visit::visit_local(local, (this, vt));
223e47cc
LB
681}
682
970d7e83
LB
683fn check_loans_in_expr<'a>(expr: @ast::expr,
684 (this, vt): (@mut CheckLoanCtxt<'a>,
685 visit::vt<@mut CheckLoanCtxt<'a>>)) {
686 visit::visit_expr(expr, (this, vt));
223e47cc 687
970d7e83
LB
688 debug!("check_loans_in_expr(expr=%s)",
689 expr.repr(this.tcx()));
223e47cc 690
970d7e83
LB
691 this.check_for_conflicting_loans(expr.id);
692
693 if this.bccx.moves_map.contains(&expr.id) {
694 this.check_move_out_from_expr(expr);
223e47cc
LB
695 }
696
697 match expr.node {
970d7e83
LB
698 ast::expr_self |
699 ast::expr_path(*) => {
700 if !this.move_data.is_assignee(expr.id) {
701 let cmt = this.bccx.cat_expr_unadjusted(expr);
702 debug!("path cmt=%s", cmt.repr(this.tcx()));
703 let r = opt_loan_path(cmt);
704 for r.iter().advance |&lp| {
705 this.check_if_path_is_moved(expr.id, expr.span, MovedInUse, lp);
706 }
707 }
223e47cc
LB
708 }
709 ast::expr_assign(dest, _) |
970d7e83
LB
710 ast::expr_assign_op(_, _, dest, _) => {
711 this.check_assignment(dest);
223e47cc
LB
712 }
713 ast::expr_call(f, ref args, _) => {
970d7e83 714 this.check_call(expr, Some(f), f.id, f.span, *args);
223e47cc 715 }
970d7e83
LB
716 ast::expr_method_call(callee_id, _, _, _, ref args, _) => {
717 this.check_call(expr, None, callee_id, expr.span, *args);
223e47cc 718 }
970d7e83
LB
719 ast::expr_index(callee_id, _, rval) |
720 ast::expr_binary(callee_id, _, _, rval)
721 if this.bccx.method_map.contains_key(&expr.id) => {
722 this.check_call(expr,
223e47cc 723 None,
970d7e83 724 callee_id,
223e47cc 725 expr.span,
970d7e83 726 [rval]);
223e47cc 727 }
970d7e83
LB
728 ast::expr_unary(callee_id, _, _) | ast::expr_index(callee_id, _, _)
729 if this.bccx.method_map.contains_key(&expr.id) => {
730 this.check_call(expr,
223e47cc 731 None,
970d7e83 732 callee_id,
223e47cc 733 expr.span,
970d7e83 734 []);
223e47cc
LB
735 }
736 _ => { }
737 }
223e47cc
LB
738}
739
970d7e83
LB
740fn check_loans_in_pat<'a>(pat: @ast::pat,
741 (this, vt): (@mut CheckLoanCtxt<'a>,
742 visit::vt<@mut CheckLoanCtxt<'a>>))
743{
744 this.check_for_conflicting_loans(pat.id);
745
746 // Note: moves out of pattern bindings are not checked by
747 // the borrow checker, at least not directly. What happens
748 // is that if there are any moved bindings, the discriminant
749 // will be considered a move, and this will be checked as
750 // normal. Then, in `middle::check_match`, we will check
751 // that no move occurs in a binding that is underneath an
752 // `@` or `&`. Together these give the same guarantees as
753 // `check_move_out_from_expr()` without requiring us to
754 // rewalk the patterns and rebuild the pattern
755 // categorizations.
756
757 visit::visit_pat(pat, (this, vt));
223e47cc
LB
758}
759
970d7e83
LB
760fn check_loans_in_block<'a>(blk: &ast::blk,
761 (this, vt): (@mut CheckLoanCtxt<'a>,
762 visit::vt<@mut CheckLoanCtxt<'a>>))
763{
764 visit::visit_block(blk, (this, vt));
765 this.check_for_conflicting_loans(blk.node.id);
766}