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