]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/borrowck/check_loans.rs
/*! -> //!
[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 use self::UseError::*;
20
21 use middle::borrowck::*;
22 use middle::borrowck::LoanPathElem::*;
23 use middle::borrowck::LoanPathKind::*;
24 use middle::expr_use_visitor as euv;
25 use middle::mem_categorization as mc;
26 use middle::region;
27 use middle::ty;
28 use syntax::ast;
29 use syntax::codemap::Span;
30 use util::ppaux::Repr;
31
32 use std::rc::Rc;
33
34 // FIXME (#16118): These functions are intended to allow the borrow checker to
35 // be less precise in its handling of Box while still allowing moves out of a
36 // Box. They should be removed when OwnedPtr is removed from LoanPath.
37
38 fn owned_ptr_base_path<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> &'a LoanPath<'tcx> {
39     //! Returns the base of the leftmost dereference of an OwnedPtr in
40     //! `loan_path`. If there is no dereference of an OwnedPtr in `loan_path`,
41     //! then it just returns `loan_path` itself.
42
43     return match helper(loan_path) {
44         Some(new_loan_path) => new_loan_path,
45         None => loan_path.clone()
46     };
47
48     fn helper<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> Option<&'a LoanPath<'tcx>> {
49         match loan_path.kind {
50             LpVar(_) | LpUpvar(_) => None,
51             LpExtend(ref lp_base, _, LpDeref(mc::OwnedPtr)) => {
52                 match helper(&**lp_base) {
53                     v @ Some(_) => v,
54                     None => Some(&**lp_base)
55                 }
56             }
57             LpDowncast(ref lp_base, _) |
58             LpExtend(ref lp_base, _, _) => helper(&**lp_base)
59         }
60     }
61 }
62
63 fn owned_ptr_base_path_rc<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Rc<LoanPath<'tcx>> {
64     //! The equivalent of `owned_ptr_base_path` for an &Rc<LoanPath> rather than
65     //! a &LoanPath.
66
67     return match helper(loan_path) {
68         Some(new_loan_path) => new_loan_path,
69         None => loan_path.clone()
70     };
71
72     fn helper<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Option<Rc<LoanPath<'tcx>>> {
73         match loan_path.kind {
74             LpVar(_) | LpUpvar(_) => None,
75             LpExtend(ref lp_base, _, LpDeref(mc::OwnedPtr)) => {
76                 match helper(lp_base) {
77                     v @ Some(_) => v,
78                     None => Some(lp_base.clone())
79                 }
80             }
81             LpDowncast(ref lp_base, _) |
82             LpExtend(ref lp_base, _, _) => helper(lp_base)
83         }
84     }
85 }
86
87 struct CheckLoanCtxt<'a, 'tcx: 'a> {
88     bccx: &'a BorrowckCtxt<'a, 'tcx>,
89     dfcx_loans: &'a LoanDataFlow<'a, 'tcx>,
90     move_data: move_data::FlowedMoveData<'a, 'tcx>,
91     all_loans: &'a [Loan<'tcx>],
92 }
93
94 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
95     fn consume(&mut self,
96                consume_id: ast::NodeId,
97                consume_span: Span,
98                cmt: mc::cmt<'tcx>,
99                mode: euv::ConsumeMode) {
100         debug!("consume(consume_id={}, cmt={}, mode={})",
101                consume_id, cmt.repr(self.tcx()), mode);
102
103         self.consume_common(consume_id, consume_span, cmt, mode);
104     }
105
106     fn matched_pat(&mut self,
107                    _matched_pat: &ast::Pat,
108                    _cmt: mc::cmt,
109                    _mode: euv::MatchMode) { }
110
111     fn consume_pat(&mut self,
112                    consume_pat: &ast::Pat,
113                    cmt: mc::cmt<'tcx>,
114                    mode: euv::ConsumeMode) {
115         debug!("consume_pat(consume_pat={}, cmt={}, mode={})",
116                consume_pat.repr(self.tcx()),
117                cmt.repr(self.tcx()),
118                mode);
119
120         self.consume_common(consume_pat.id, consume_pat.span, cmt, mode);
121     }
122
123     fn borrow(&mut self,
124               borrow_id: ast::NodeId,
125               borrow_span: Span,
126               cmt: mc::cmt<'tcx>,
127               loan_region: ty::Region,
128               bk: ty::BorrowKind,
129               loan_cause: euv::LoanCause)
130     {
131         debug!("borrow(borrow_id={}, cmt={}, loan_region={}, \
132                bk={}, loan_cause={})",
133                borrow_id, cmt.repr(self.tcx()), loan_region,
134                bk, loan_cause);
135
136         match opt_loan_path(&cmt) {
137             Some(lp) => {
138                 let moved_value_use_kind = match loan_cause {
139                     euv::ClosureCapture(_) => MovedInCapture,
140                     _ => MovedInUse,
141                 };
142                 self.check_if_path_is_moved(borrow_id, borrow_span, moved_value_use_kind, &lp);
143             }
144             None => { }
145         }
146
147         self.check_for_conflicting_loans(region::CodeExtent::from_node_id(borrow_id));
148     }
149
150     fn mutate(&mut self,
151               assignment_id: ast::NodeId,
152               assignment_span: Span,
153               assignee_cmt: mc::cmt<'tcx>,
154               mode: euv::MutateMode)
155     {
156         debug!("mutate(assignment_id={}, assignee_cmt={})",
157                assignment_id, assignee_cmt.repr(self.tcx()));
158
159         match opt_loan_path(&assignee_cmt) {
160             Some(lp) => {
161                 match mode {
162                     euv::Init | euv::JustWrite => {
163                         // In a case like `path = 1`, then path does not
164                         // have to be *FULLY* initialized, but we still
165                         // must be careful lest it contains derefs of
166                         // pointers.
167                         self.check_if_assigned_path_is_moved(assignee_cmt.id,
168                                                              assignment_span,
169                                                              MovedInUse,
170                                                              &lp);
171                     }
172                     euv::WriteAndRead => {
173                         // In a case like `path += 1`, then path must be
174                         // fully initialized, since we will read it before
175                         // we write it.
176                         self.check_if_path_is_moved(assignee_cmt.id,
177                                                     assignment_span,
178                                                     MovedInUse,
179                                                     &lp);
180                     }
181                 }
182             }
183             None => { }
184         }
185
186         self.check_assignment(assignment_id, assignment_span, assignee_cmt, mode);
187     }
188
189     fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) { }
190 }
191
192 pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
193                                      dfcx_loans: &LoanDataFlow<'b, 'tcx>,
194                                      move_data: move_data::FlowedMoveData<'c, 'tcx>,
195                                      all_loans: &[Loan<'tcx>],
196                                      decl: &ast::FnDecl,
197                                      body: &ast::Block) {
198     debug!("check_loans(body id={})", body.id);
199
200     let mut clcx = CheckLoanCtxt {
201         bccx: bccx,
202         dfcx_loans: dfcx_loans,
203         move_data: move_data,
204         all_loans: all_loans,
205     };
206
207     {
208         let mut euv = euv::ExprUseVisitor::new(&mut clcx, bccx.tcx);
209         euv.walk_fn(decl, body);
210     }
211 }
212
213 #[deriving(PartialEq)]
214 enum UseError<'tcx> {
215     UseOk,
216     UseWhileBorrowed(/*loan*/Rc<LoanPath<'tcx>>, /*loan*/Span)
217 }
218
219 fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind,
220                            borrow_kind2: ty::BorrowKind)
221                            -> bool {
222     borrow_kind1 == ty::ImmBorrow && borrow_kind2 == ty::ImmBorrow
223 }
224
225 impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
226     pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.bccx.tcx }
227
228     pub fn each_issued_loan(&self, scope: region::CodeExtent, op: |&Loan<'tcx>| -> bool)
229                             -> bool {
230         //! Iterates over each loan that has been issued
231         //! on entrance to `scope`, regardless of whether it is
232         //! actually *in scope* at that point.  Sometimes loans
233         //! are issued for future scopes and thus they may have been
234         //! *issued* but not yet be in effect.
235
236         self.dfcx_loans.each_bit_on_entry(scope.node_id(), |loan_index| {
237             let loan = &self.all_loans[loan_index];
238             op(loan)
239         })
240     }
241
242     pub fn each_in_scope_loan(&self,
243                               scope: region::CodeExtent,
244                               op: |&Loan<'tcx>| -> bool)
245                               -> bool {
246         //! Like `each_issued_loan()`, but only considers loans that are
247         //! currently in scope.
248
249         let tcx = self.tcx();
250         self.each_issued_loan(scope, |loan| {
251             if tcx.region_maps.is_subscope_of(scope, loan.kill_scope) {
252                 op(loan)
253             } else {
254                 true
255             }
256         })
257     }
258
259     fn each_in_scope_loan_affecting_path(&self,
260                                          scope: region::CodeExtent,
261                                          loan_path: &LoanPath<'tcx>,
262                                          op: |&Loan<'tcx>| -> bool)
263                                          -> bool {
264         //! Iterates through all of the in-scope loans affecting `loan_path`,
265         //! calling `op`, and ceasing iteration if `false` is returned.
266
267         // First, we check for a loan restricting the path P being used. This
268         // accounts for borrows of P but also borrows of subpaths, like P.a.b.
269         // Consider the following example:
270         //
271         //     let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
272         //     let y = a;          // Conflicts with restriction
273
274         let loan_path = owned_ptr_base_path(loan_path);
275         let cont = self.each_in_scope_loan(scope, |loan| {
276             let mut ret = true;
277             for restr_path in loan.restricted_paths.iter() {
278                 if **restr_path == *loan_path {
279                     if !op(loan) {
280                         ret = false;
281                         break;
282                     }
283                 }
284             }
285             ret
286         });
287
288         if !cont {
289             return false;
290         }
291
292         // Next, we must check for *loans* (not restrictions) on the path P or
293         // any base path. This rejects examples like the following:
294         //
295         //     let x = &mut a.b;
296         //     let y = a.b.c;
297         //
298         // Limiting this search to *loans* and not *restrictions* means that
299         // examples like the following continue to work:
300         //
301         //     let x = &mut a.b;
302         //     let y = a.c;
303
304         let mut loan_path = loan_path;
305         loop {
306             match loan_path.kind {
307                 LpVar(_) | LpUpvar(_) => {
308                     break;
309                 }
310                 LpDowncast(ref lp_base, _) |
311                 LpExtend(ref lp_base, _, _) => {
312                     loan_path = &**lp_base;
313                 }
314             }
315
316             let cont = self.each_in_scope_loan(scope, |loan| {
317                 if *loan.loan_path == *loan_path {
318                     op(loan)
319                 } else {
320                     true
321                 }
322             });
323
324             if !cont {
325                 return false;
326             }
327         }
328
329         return true;
330     }
331
332     pub fn loans_generated_by(&self, scope: region::CodeExtent) -> Vec<uint> {
333         //! Returns a vector of the loans that are generated as
334         //! we enter `scope`.
335
336         let mut result = Vec::new();
337         self.dfcx_loans.each_gen_bit(scope.node_id(), |loan_index| {
338             result.push(loan_index);
339             true
340         });
341         return result;
342     }
343
344     pub fn check_for_conflicting_loans(&self, scope: region::CodeExtent) {
345         //! Checks to see whether any of the loans that are issued
346         //! on entrance to `scope` conflict with loans that have already been
347         //! issued when we enter `scope` (for example, we do not
348         //! permit two `&mut` borrows of the same variable).
349         //!
350         //! (Note that some loans can be *issued* without necessarily
351         //! taking effect yet.)
352
353         debug!("check_for_conflicting_loans(scope={})", scope);
354
355         let new_loan_indices = self.loans_generated_by(scope);
356         debug!("new_loan_indices = {}", new_loan_indices);
357
358         self.each_issued_loan(scope, |issued_loan| {
359             for &new_loan_index in new_loan_indices.iter() {
360                 let new_loan = &self.all_loans[new_loan_index];
361                 self.report_error_if_loans_conflict(issued_loan, new_loan);
362             }
363             true
364         });
365
366         for (i, &x) in new_loan_indices.iter().enumerate() {
367             let old_loan = &self.all_loans[x];
368             for &y in new_loan_indices.slice_from(i+1).iter() {
369                 let new_loan = &self.all_loans[y];
370                 self.report_error_if_loans_conflict(old_loan, new_loan);
371             }
372         }
373     }
374
375     pub fn report_error_if_loans_conflict(&self,
376                                           old_loan: &Loan<'tcx>,
377                                           new_loan: &Loan<'tcx>) {
378         //! Checks whether `old_loan` and `new_loan` can safely be issued
379         //! simultaneously.
380
381         debug!("report_error_if_loans_conflict(old_loan={}, new_loan={})",
382                old_loan.repr(self.tcx()),
383                new_loan.repr(self.tcx()));
384
385         // Should only be called for loans that are in scope at the same time.
386         assert!(self.tcx().region_maps.scopes_intersect(old_loan.kill_scope,
387                                                         new_loan.kill_scope));
388
389         self.report_error_if_loan_conflicts_with_restriction(
390             old_loan, new_loan, old_loan, new_loan) &&
391         self.report_error_if_loan_conflicts_with_restriction(
392             new_loan, old_loan, old_loan, new_loan);
393     }
394
395     pub fn report_error_if_loan_conflicts_with_restriction(&self,
396                                                            loan1: &Loan<'tcx>,
397                                                            loan2: &Loan<'tcx>,
398                                                            old_loan: &Loan<'tcx>,
399                                                            new_loan: &Loan<'tcx>)
400                                                            -> bool {
401         //! Checks whether the restrictions introduced by `loan1` would
402         //! prohibit `loan2`. Returns false if an error is reported.
403
404         debug!("report_error_if_loan_conflicts_with_restriction(\
405                 loan1={}, loan2={})",
406                loan1.repr(self.tcx()),
407                loan2.repr(self.tcx()));
408
409         if compatible_borrow_kinds(loan1.kind, loan2.kind) {
410             return true;
411         }
412
413         let loan2_base_path = owned_ptr_base_path_rc(&loan2.loan_path);
414         for restr_path in loan1.restricted_paths.iter() {
415             if *restr_path != loan2_base_path { continue; }
416
417             // If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
418             // normally generate a rather confusing message (in this case, for multiple mutable
419             // borrows):
420             //
421             //     error: cannot borrow `x.b` as mutable more than once at a time
422             //     note: previous borrow of `x.a` occurs here; the mutable borrow prevents
423             //     subsequent moves, borrows, or modification of `x.a` until the borrow ends
424             //
425             // What we want to do instead is get the 'common ancestor' of the two borrow paths and
426             // use that for most of the message instead, giving is something like this:
427             //
428             //     error: cannot borrow `x` as mutable more than once at a time
429             //     note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
430             //     borrow prevents subsequent moves, borrows, or modification of `x` until the
431             //     borrow ends
432
433             let common = new_loan.loan_path.common(&*old_loan.loan_path);
434             let (nl, ol, new_loan_msg, old_loan_msg) =
435                 if new_loan.loan_path.has_fork(&*old_loan.loan_path) && common.is_some() {
436                     let nl = self.bccx.loan_path_to_string(&common.unwrap());
437                     let ol = nl.clone();
438                     let new_loan_msg = format!(" (here through borrowing `{}`)",
439                                                self.bccx.loan_path_to_string(
440                                                    &*new_loan.loan_path));
441                     let old_loan_msg = format!(" (through borrowing `{}`)",
442                                                self.bccx.loan_path_to_string(
443                                                    &*old_loan.loan_path));
444                     (nl, ol, new_loan_msg, old_loan_msg)
445                 } else {
446                     (self.bccx.loan_path_to_string(&*new_loan.loan_path),
447                      self.bccx.loan_path_to_string(&*old_loan.loan_path),
448                      String::new(), String::new())
449                 };
450
451             let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
452                 "it".to_string()
453             } else {
454                 format!("`{}`", ol)
455             };
456
457             match (new_loan.kind, old_loan.kind) {
458                 (ty::MutBorrow, ty::MutBorrow) => {
459                     self.bccx.span_err(
460                         new_loan.span,
461                         format!("cannot borrow `{}`{} as mutable \
462                                 more than once at a time",
463                                 nl, new_loan_msg).as_slice())
464                 }
465
466                 (ty::UniqueImmBorrow, _) => {
467                     self.bccx.span_err(
468                         new_loan.span,
469                         format!("closure requires unique access to `{}` \
470                                 but {} is already borrowed{}",
471                                 nl, ol_pronoun, old_loan_msg).as_slice());
472                 }
473
474                 (_, ty::UniqueImmBorrow) => {
475                     self.bccx.span_err(
476                         new_loan.span,
477                         format!("cannot borrow `{}`{} as {} because \
478                                 previous closure requires unique access",
479                                 nl, new_loan_msg, new_loan.kind.to_user_str()).as_slice());
480                 }
481
482                 (_, _) => {
483                     self.bccx.span_err(
484                         new_loan.span,
485                         format!("cannot borrow `{}`{} as {} because \
486                                 {} is also borrowed as {}{}",
487                                 nl,
488                                 new_loan_msg,
489                                 new_loan.kind.to_user_str(),
490                                 ol_pronoun,
491                                 old_loan.kind.to_user_str(),
492                                 old_loan_msg).as_slice());
493                 }
494             }
495
496             match new_loan.cause {
497                 euv::ClosureCapture(span) => {
498                     self.bccx.span_note(
499                         span,
500                         format!("borrow occurs due to use of `{}` in closure",
501                                 nl).as_slice());
502                 }
503                 _ => { }
504             }
505
506             let rule_summary = match old_loan.kind {
507                 ty::MutBorrow => {
508                     format!("the mutable borrow prevents subsequent \
509                             moves, borrows, or modification of `{0}` \
510                             until the borrow ends",
511                             ol)
512                 }
513
514                 ty::ImmBorrow => {
515                     format!("the immutable borrow prevents subsequent \
516                             moves or mutable borrows of `{0}` \
517                             until the borrow ends",
518                             ol)
519                 }
520
521                 ty::UniqueImmBorrow => {
522                     format!("the unique capture prevents subsequent \
523                             moves or borrows of `{0}` \
524                             until the borrow ends",
525                             ol)
526                 }
527             };
528
529             let borrow_summary = match old_loan.cause {
530                 euv::ClosureCapture(_) => {
531                     format!("previous borrow of `{}` occurs here{} due to \
532                             use in closure",
533                             ol, old_loan_msg)
534                 }
535
536                 euv::OverloadedOperator(..) |
537                 euv::AddrOf(..) |
538                 euv::AutoRef(..) |
539                 euv::ClosureInvocation(..) |
540                 euv::ForLoop(..) |
541                 euv::RefBinding(..) |
542                 euv::MatchDiscriminant(..) => {
543                     format!("previous borrow of `{}` occurs here{}",
544                             ol, old_loan_msg)
545                 }
546             };
547
548             self.bccx.span_note(
549                 old_loan.span,
550                 format!("{}; {}", borrow_summary, rule_summary).as_slice());
551
552             let old_loan_span = self.tcx().map.span(old_loan.kill_scope.node_id());
553             self.bccx.span_end_note(old_loan_span,
554                                     "previous borrow ends here");
555
556             return false;
557         }
558
559         true
560     }
561
562     fn is_local_variable_or_arg(&self, cmt: mc::cmt<'tcx>) -> bool {
563         match cmt.cat {
564           mc::cat_local(_) => true,
565           _ => false
566         }
567     }
568
569     fn consume_common(&self,
570                       id: ast::NodeId,
571                       span: Span,
572                       cmt: mc::cmt<'tcx>,
573                       mode: euv::ConsumeMode) {
574         match opt_loan_path(&cmt) {
575             Some(lp) => {
576                 let moved_value_use_kind = match mode {
577                     euv::Copy => {
578                         self.check_for_copy_of_frozen_path(id, span, &*lp);
579                         MovedInUse
580                     }
581                     euv::Move(_) => {
582                         match self.move_data.kind_of_move_of_path(id, &lp) {
583                             None => {
584                                 // Sometimes moves don't have a move kind;
585                                 // this either means that the original move
586                                 // was from something illegal to move,
587                                 // or was moved from referent of an unsafe
588                                 // pointer or something like that.
589                                 MovedInUse
590                             }
591                             Some(move_kind) => {
592                                 self.check_for_move_of_borrowed_path(id, span,
593                                                                      &*lp, move_kind);
594                                 if move_kind == move_data::Captured {
595                                     MovedInCapture
596                                 } else {
597                                     MovedInUse
598                                 }
599                             }
600                         }
601                     }
602                 };
603
604                 self.check_if_path_is_moved(id, span, moved_value_use_kind, &lp);
605             }
606             None => { }
607         }
608     }
609
610     fn check_for_copy_of_frozen_path(&self,
611                                      id: ast::NodeId,
612                                      span: Span,
613                                      copy_path: &LoanPath<'tcx>) {
614         match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
615             UseOk => { }
616             UseWhileBorrowed(loan_path, loan_span) => {
617                 self.bccx.span_err(
618                     span,
619                     format!("cannot use `{}` because it was mutably borrowed",
620                             self.bccx.loan_path_to_string(copy_path).as_slice())
621                     .as_slice());
622                 self.bccx.span_note(
623                     loan_span,
624                     format!("borrow of `{}` occurs here",
625                             self.bccx.loan_path_to_string(&*loan_path).as_slice())
626                     .as_slice());
627             }
628         }
629     }
630
631     fn check_for_move_of_borrowed_path(&self,
632                                        id: ast::NodeId,
633                                        span: Span,
634                                        move_path: &LoanPath<'tcx>,
635                                        move_kind: move_data::MoveKind) {
636         // We want to detect if there are any loans at all, so we search for
637         // any loans incompatible with MutBorrrow, since all other kinds of
638         // loans are incompatible with that.
639         match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) {
640             UseOk => { }
641             UseWhileBorrowed(loan_path, loan_span) => {
642                 let err_message = match move_kind {
643                     move_data::Captured =>
644                         format!("cannot move `{}` into closure because it is borrowed",
645                                 self.bccx.loan_path_to_string(move_path).as_slice()),
646                     move_data::Declared |
647                     move_data::MoveExpr |
648                     move_data::MovePat =>
649                         format!("cannot move out of `{}` because it is borrowed",
650                                 self.bccx.loan_path_to_string(move_path).as_slice())
651                 };
652
653                 self.bccx.span_err(span, err_message.as_slice());
654                 self.bccx.span_note(
655                     loan_span,
656                     format!("borrow of `{}` occurs here",
657                             self.bccx.loan_path_to_string(&*loan_path).as_slice())
658                     .as_slice());
659             }
660         }
661     }
662
663     pub fn analyze_restrictions_on_use(&self,
664                                        expr_id: ast::NodeId,
665                                        use_path: &LoanPath<'tcx>,
666                                        borrow_kind: ty::BorrowKind)
667                                        -> UseError<'tcx> {
668         debug!("analyze_restrictions_on_use(expr_id={}, use_path={})",
669                self.tcx().map.node_to_string(expr_id),
670                use_path.repr(self.tcx()));
671
672         let mut ret = UseOk;
673
674         self.each_in_scope_loan_affecting_path(
675             region::CodeExtent::from_node_id(expr_id), use_path, |loan| {
676             if !compatible_borrow_kinds(loan.kind, borrow_kind) {
677                 ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span);
678                 false
679             } else {
680                 true
681             }
682         });
683
684         return ret;
685     }
686
687     /// Reports an error if `expr` (which should be a path)
688     /// is using a moved/uninitialized value
689     fn check_if_path_is_moved(&self,
690                               id: ast::NodeId,
691                               span: Span,
692                               use_kind: MovedValueUseKind,
693                               lp: &Rc<LoanPath<'tcx>>) {
694         debug!("check_if_path_is_moved(id={}, use_kind={}, lp={})",
695                id, use_kind, lp.repr(self.bccx.tcx));
696         let base_lp = owned_ptr_base_path_rc(lp);
697         self.move_data.each_move_of(id, &base_lp, |the_move, moved_lp| {
698             self.bccx.report_use_of_moved_value(
699                 span,
700                 use_kind,
701                 &**lp,
702                 the_move,
703                 moved_lp);
704             false
705         });
706     }
707
708     /// Reports an error if assigning to `lp` will use a
709     /// moved/uninitialized value. Mainly this is concerned with
710     /// detecting derefs of uninitialized pointers.
711     ///
712     /// For example:
713     ///
714     /// ```
715     /// let a: int;
716     /// a = 10; // ok, even though a is uninitialized
717     ///
718     /// struct Point { x: uint, y: uint }
719     /// let p: Point;
720     /// p.x = 22; // ok, even though `p` is uninitialized
721     ///
722     /// let p: ~Point;
723     /// (*p).x = 22; // not ok, p is uninitialized, can't deref
724     /// ```
725     fn check_if_assigned_path_is_moved(&self,
726                                        id: ast::NodeId,
727                                        span: Span,
728                                        use_kind: MovedValueUseKind,
729                                        lp: &Rc<LoanPath<'tcx>>)
730     {
731         match lp.kind {
732             LpVar(_) | LpUpvar(_) => {
733                 // assigning to `x` does not require that `x` is initialized
734             }
735             LpDowncast(ref lp_base, _) => {
736                 // assigning to `(P->Variant).f` is ok if assigning to `P` is ok
737                 self.check_if_assigned_path_is_moved(id, span,
738                                                      use_kind, lp_base);
739             }
740             LpExtend(ref lp_base, _, LpInterior(_)) => {
741                 // assigning to `P.f` is ok if assigning to `P` is ok
742                 self.check_if_assigned_path_is_moved(id, span,
743                                                      use_kind, lp_base);
744             }
745             LpExtend(ref lp_base, _, LpDeref(_)) => {
746                 // assigning to `(*P)` requires that `P` be initialized
747                 self.check_if_path_is_moved(id, span,
748                                             use_kind, lp_base);
749             }
750         }
751     }
752
753     fn check_assignment(&self,
754                         assignment_id: ast::NodeId,
755                         assignment_span: Span,
756                         assignee_cmt: mc::cmt<'tcx>,
757                         mode: euv::MutateMode) {
758         debug!("check_assignment(assignee_cmt={})", assignee_cmt.repr(self.tcx()));
759
760         // Mutable values can be assigned, as long as they obey loans
761         // and aliasing restrictions:
762         if assignee_cmt.mutbl.is_mutable() {
763             if check_for_aliasable_mutable_writes(self, assignment_span, assignee_cmt.clone()) {
764                 if mode != euv::Init {
765                     check_for_assignment_to_borrowed_path(
766                         self, assignment_id, assignment_span, assignee_cmt.clone());
767                     mark_variable_as_used_mut(self, assignee_cmt);
768                 }
769             }
770             return;
771         }
772
773         // Initializations are OK.
774         if mode == euv::Init {
775             return
776         }
777
778         // For immutable local variables, assignments are legal
779         // if they cannot already have been assigned
780         if self.is_local_variable_or_arg(assignee_cmt.clone()) {
781             assert!(assignee_cmt.mutbl.is_immutable()); // no "const" locals
782             let lp = opt_loan_path(&assignee_cmt).unwrap();
783             self.move_data.each_assignment_of(assignment_id, &lp, |assign| {
784                 self.bccx.report_reassigned_immutable_variable(
785                     assignment_span,
786                     &*lp,
787                     assign);
788                 false
789             });
790             return;
791         }
792
793         // Otherwise, just a plain error.
794         match assignee_cmt.note {
795             mc::NoteClosureEnv(upvar_id) => {
796                 // If this is an `Fn` closure, it simply can't mutate upvars.
797                 // If it's an `FnMut` closure, the original variable was declared immutable.
798                 // We need to determine which is the case here.
799                 let kind = match assignee_cmt.upvar().unwrap().cat {
800                     mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
801                     _ => unreachable!()
802                 };
803                 if kind == ty::FnUnboxedClosureKind {
804                     self.bccx.span_err(
805                         assignment_span,
806                         format!("cannot assign to {}",
807                                 self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
808                     self.bccx.span_help(
809                         self.tcx().map.span(upvar_id.closure_expr_id),
810                         "consider changing this closure to take self by mutable reference");
811                 } else {
812                     self.bccx.span_err(
813                         assignment_span,
814                         format!("cannot assign to {} {}",
815                                 assignee_cmt.mutbl.to_user_str(),
816                                 self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
817                 }
818             }
819             _ => match opt_loan_path(&assignee_cmt) {
820                 Some(lp) => {
821                     self.bccx.span_err(
822                         assignment_span,
823                         format!("cannot assign to {} {} `{}`",
824                                 assignee_cmt.mutbl.to_user_str(),
825                                 self.bccx.cmt_to_string(&*assignee_cmt),
826                                 self.bccx.loan_path_to_string(&*lp)).as_slice());
827                 }
828                 None => {
829                     self.bccx.span_err(
830                         assignment_span,
831                         format!("cannot assign to {} {}",
832                                 assignee_cmt.mutbl.to_user_str(),
833                                 self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
834                 }
835             }
836         }
837         return;
838
839         fn mark_variable_as_used_mut<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
840                                                mut cmt: mc::cmt<'tcx>) {
841             //! If the mutability of the `cmt` being written is inherited
842             //! from a local variable, liveness will
843             //! not have been able to detect that this variable's mutability
844             //! is important, so we must add the variable to the
845             //! `used_mut_nodes` table here.
846
847             loop {
848                 debug!("mark_variable_as_used_mut(cmt={})", cmt.repr(this.tcx()));
849                 match cmt.cat.clone() {
850                     mc::cat_upvar(mc::Upvar { id: ty::UpvarId { var_id: id, .. }, .. }) |
851                     mc::cat_local(id) => {
852                         this.tcx().used_mut_nodes.borrow_mut().insert(id);
853                         return;
854                     }
855
856                     mc::cat_rvalue(..) |
857                     mc::cat_static_item |
858                     mc::cat_deref(_, _, mc::UnsafePtr(..)) |
859                     mc::cat_deref(_, _, mc::Implicit(..)) => {
860                         assert_eq!(cmt.mutbl, mc::McDeclared);
861                         return;
862                     }
863
864                     mc::cat_deref(_, _, mc::BorrowedPtr(..)) => {
865                         assert_eq!(cmt.mutbl, mc::McDeclared);
866                         // We need to drill down to upvar if applicable
867                         match cmt.upvar() {
868                             Some(b) => cmt = b,
869                             None => return
870                         }
871                     }
872
873                     mc::cat_deref(b, _, mc::OwnedPtr) => {
874                         assert_eq!(cmt.mutbl, mc::McInherited);
875                         cmt = b;
876                     }
877
878                     mc::cat_downcast(b, _) |
879                     mc::cat_interior(b, _) => {
880                         assert_eq!(cmt.mutbl, mc::McInherited);
881                         cmt = b;
882                     }
883                 }
884             }
885         }
886
887         fn check_for_aliasable_mutable_writes<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
888                                                         span: Span,
889                                                         cmt: mc::cmt<'tcx>) -> bool {
890             //! Safety checks related to writes to aliasable, mutable locations
891
892             let guarantor = cmt.guarantor();
893             debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
894                    cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
895             match guarantor.cat {
896                 mc::cat_deref(ref b, _, mc::BorrowedPtr(ty::MutBorrow, _)) => {
897                     // Statically prohibit writes to `&mut` when aliasable
898
899                     check_for_aliasability_violation(this, span, b.clone());
900                 }
901
902                 _ => {}
903             }
904
905             return true; // no errors reported
906         }
907
908         fn check_for_aliasability_violation<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
909                                                       span: Span,
910                                                       cmt: mc::cmt<'tcx>)
911                                                       -> bool {
912             match cmt.freely_aliasable(this.tcx()) {
913                 None => {
914                     return true;
915                 }
916                 Some(mc::AliasableStaticMut(..)) => {
917                     return true;
918                 }
919                 Some(cause) => {
920                     this.bccx.report_aliasability_violation(
921                         span,
922                         MutabilityViolation,
923                         cause);
924                     return false;
925                 }
926             }
927         }
928
929         fn check_for_assignment_to_borrowed_path<'a, 'tcx>(
930             this: &CheckLoanCtxt<'a, 'tcx>,
931             assignment_id: ast::NodeId,
932             assignment_span: Span,
933             assignee_cmt: mc::cmt<'tcx>)
934         {
935             //! Check for assignments that violate the terms of an
936             //! outstanding loan.
937
938             let loan_path = match opt_loan_path(&assignee_cmt) {
939                 Some(lp) => lp,
940                 None => { return; /* no loan path, can't be any loans */ }
941             };
942
943             let scope = region::CodeExtent::from_node_id(assignment_id);
944             this.each_in_scope_loan_affecting_path(scope, &*loan_path, |loan| {
945                 this.report_illegal_mutation(assignment_span, &*loan_path, loan);
946                 false
947             });
948         }
949     }
950
951     pub fn report_illegal_mutation(&self,
952                                    span: Span,
953                                    loan_path: &LoanPath<'tcx>,
954                                    loan: &Loan) {
955         self.bccx.span_err(
956             span,
957             format!("cannot assign to `{}` because it is borrowed",
958                     self.bccx.loan_path_to_string(loan_path)).as_slice());
959         self.bccx.span_note(
960             loan.span,
961             format!("borrow of `{}` occurs here",
962                     self.bccx.loan_path_to_string(loan_path)).as_slice());
963     }
964 }
965