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