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