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.
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.
11 // ----------------------------------------------------------------------
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::*;
21 use middle::borrowck::*;
22 use middle::borrowck::LoanPathElem::*;
23 use middle::borrowck::LoanPathKind::*;
24 use middle::expr_use_visitor as euv;
25 use middle::mem_categorization as mc;
29 use syntax::codemap::Span;
30 use util::ppaux::Repr;
34 // FIXME (#16118): These functions are intended to allow the borrow checker to
35 // be less precise in its handling of Box while still allowing moves out of a
36 // Box. They should be removed when OwnedPtr is removed from LoanPath.
38 fn owned_ptr_base_path<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> &'a LoanPath<'tcx> {
39 //! Returns the base of the leftmost dereference of an OwnedPtr in
40 //! `loan_path`. If there is no dereference of an OwnedPtr in `loan_path`,
41 //! then it just returns `loan_path` itself.
43 return match helper(loan_path) {
44 Some(new_loan_path) => new_loan_path,
45 None => loan_path.clone()
48 fn helper<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> Option<&'a LoanPath<'tcx>> {
49 match loan_path.kind {
50 LpVar(_) | LpUpvar(_) => None,
51 LpExtend(ref lp_base, _, LpDeref(mc::OwnedPtr)) => {
52 match helper(&**lp_base) {
54 None => Some(&**lp_base)
57 LpDowncast(ref lp_base, _) |
58 LpExtend(ref lp_base, _, _) => helper(&**lp_base)
63 fn owned_ptr_base_path_rc<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Rc<LoanPath<'tcx>> {
64 //! The equivalent of `owned_ptr_base_path` for an &Rc<LoanPath> rather than
67 return match helper(loan_path) {
68 Some(new_loan_path) => new_loan_path,
69 None => loan_path.clone()
72 fn helper<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Option<Rc<LoanPath<'tcx>>> {
73 match loan_path.kind {
74 LpVar(_) | LpUpvar(_) => None,
75 LpExtend(ref lp_base, _, LpDeref(mc::OwnedPtr)) => {
76 match helper(lp_base) {
78 None => Some(lp_base.clone())
81 LpDowncast(ref lp_base, _) |
82 LpExtend(ref lp_base, _, _) => helper(lp_base)
87 struct CheckLoanCtxt<'a, 'tcx: 'a> {
88 bccx: &'a BorrowckCtxt<'a, 'tcx>,
89 dfcx_loans: &'a LoanDataFlow<'a, 'tcx>,
90 move_data: move_data::FlowedMoveData<'a, 'tcx>,
91 all_loans: &'a [Loan<'tcx>],
94 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
96 consume_id: ast::NodeId,
99 mode: euv::ConsumeMode) {
100 debug!("consume(consume_id={}, cmt={}, mode={})",
101 consume_id, cmt.repr(self.tcx()), mode);
103 self.consume_common(consume_id, consume_span, cmt, mode);
106 fn matched_pat(&mut self,
107 _matched_pat: &ast::Pat,
109 _mode: euv::MatchMode) { }
111 fn consume_pat(&mut self,
112 consume_pat: &ast::Pat,
114 mode: euv::ConsumeMode) {
115 debug!("consume_pat(consume_pat={}, cmt={}, mode={})",
116 consume_pat.repr(self.tcx()),
117 cmt.repr(self.tcx()),
120 self.consume_common(consume_pat.id, consume_pat.span, cmt, mode);
124 borrow_id: ast::NodeId,
127 loan_region: ty::Region,
129 loan_cause: euv::LoanCause)
131 debug!("borrow(borrow_id={}, cmt={}, loan_region={}, \
132 bk={}, loan_cause={})",
133 borrow_id, cmt.repr(self.tcx()), loan_region,
136 match opt_loan_path(&cmt) {
138 let moved_value_use_kind = match loan_cause {
139 euv::ClosureCapture(_) => MovedInCapture,
142 self.check_if_path_is_moved(borrow_id, borrow_span, moved_value_use_kind, &lp);
147 self.check_for_conflicting_loans(region::CodeExtent::from_node_id(borrow_id));
151 assignment_id: ast::NodeId,
152 assignment_span: Span,
153 assignee_cmt: mc::cmt<'tcx>,
154 mode: euv::MutateMode)
156 debug!("mutate(assignment_id={}, assignee_cmt={})",
157 assignment_id, assignee_cmt.repr(self.tcx()));
159 match opt_loan_path(&assignee_cmt) {
162 euv::Init | euv::JustWrite => {
163 // In a case like `path = 1`, then path does not
164 // have to be *FULLY* initialized, but we still
165 // must be careful lest it contains derefs of
167 self.check_if_assigned_path_is_moved(assignee_cmt.id,
172 euv::WriteAndRead => {
173 // In a case like `path += 1`, then path must be
174 // fully initialized, since we will read it before
176 self.check_if_path_is_moved(assignee_cmt.id,
186 self.check_assignment(assignment_id, assignment_span, assignee_cmt, mode);
189 fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) { }
192 pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
193 dfcx_loans: &LoanDataFlow<'b, 'tcx>,
194 move_data: move_data::FlowedMoveData<'c, 'tcx>,
195 all_loans: &[Loan<'tcx>],
198 debug!("check_loans(body id={})", body.id);
200 let mut clcx = CheckLoanCtxt {
202 dfcx_loans: dfcx_loans,
203 move_data: move_data,
204 all_loans: all_loans,
208 let mut euv = euv::ExprUseVisitor::new(&mut clcx, bccx.tcx);
209 euv.walk_fn(decl, body);
213 #[deriving(PartialEq)]
214 enum UseError<'tcx> {
216 UseWhileBorrowed(/*loan*/Rc<LoanPath<'tcx>>, /*loan*/Span)
219 fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind,
220 borrow_kind2: ty::BorrowKind)
222 borrow_kind1 == ty::ImmBorrow && borrow_kind2 == ty::ImmBorrow
225 impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
226 pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.bccx.tcx }
228 pub fn each_issued_loan(&self, scope: region::CodeExtent, op: |&Loan<'tcx>| -> bool)
230 //! Iterates over each loan that has been issued
231 //! on entrance to `scope`, regardless of whether it is
232 //! actually *in scope* at that point. Sometimes loans
233 //! are issued for future scopes and thus they may have been
234 //! *issued* but not yet be in effect.
236 self.dfcx_loans.each_bit_on_entry(scope.node_id(), |loan_index| {
237 let loan = &self.all_loans[loan_index];
242 pub fn each_in_scope_loan(&self,
243 scope: region::CodeExtent,
244 op: |&Loan<'tcx>| -> bool)
246 //! Like `each_issued_loan()`, but only considers loans that are
247 //! currently in scope.
249 let tcx = self.tcx();
250 self.each_issued_loan(scope, |loan| {
251 if tcx.region_maps.is_subscope_of(scope, loan.kill_scope) {
259 fn each_in_scope_loan_affecting_path(&self,
260 scope: region::CodeExtent,
261 loan_path: &LoanPath<'tcx>,
262 op: |&Loan<'tcx>| -> bool)
264 //! Iterates through all of the in-scope loans affecting `loan_path`,
265 //! calling `op`, and ceasing iteration if `false` is returned.
267 // First, we check for a loan restricting the path P being used. This
268 // accounts for borrows of P but also borrows of subpaths, like P.a.b.
269 // Consider the following example:
271 // let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
272 // let y = a; // Conflicts with restriction
274 let loan_path = owned_ptr_base_path(loan_path);
275 let cont = self.each_in_scope_loan(scope, |loan| {
277 for restr_path in loan.restricted_paths.iter() {
278 if **restr_path == *loan_path {
292 // Next, we must check for *loans* (not restrictions) on the path P or
293 // any base path. This rejects examples like the following:
298 // Limiting this search to *loans* and not *restrictions* means that
299 // examples like the following continue to work:
304 let mut loan_path = loan_path;
306 match loan_path.kind {
307 LpVar(_) | LpUpvar(_) => {
310 LpDowncast(ref lp_base, _) |
311 LpExtend(ref lp_base, _, _) => {
312 loan_path = &**lp_base;
316 let cont = self.each_in_scope_loan(scope, |loan| {
317 if *loan.loan_path == *loan_path {
332 pub fn loans_generated_by(&self, scope: region::CodeExtent) -> Vec<uint> {
333 //! Returns a vector of the loans that are generated as
334 //! we enter `scope`.
336 let mut result = Vec::new();
337 self.dfcx_loans.each_gen_bit(scope.node_id(), |loan_index| {
338 result.push(loan_index);
344 pub fn check_for_conflicting_loans(&self, scope: region::CodeExtent) {
345 //! Checks to see whether any of the loans that are issued
346 //! on entrance to `scope` conflict with loans that have already been
347 //! issued when we enter `scope` (for example, we do not
348 //! permit two `&mut` borrows of the same variable).
350 //! (Note that some loans can be *issued* without necessarily
351 //! taking effect yet.)
353 debug!("check_for_conflicting_loans(scope={})", scope);
355 let new_loan_indices = self.loans_generated_by(scope);
356 debug!("new_loan_indices = {}", new_loan_indices);
358 self.each_issued_loan(scope, |issued_loan| {
359 for &new_loan_index in new_loan_indices.iter() {
360 let new_loan = &self.all_loans[new_loan_index];
361 self.report_error_if_loans_conflict(issued_loan, new_loan);
366 for (i, &x) in new_loan_indices.iter().enumerate() {
367 let old_loan = &self.all_loans[x];
368 for &y in new_loan_indices.slice_from(i+1).iter() {
369 let new_loan = &self.all_loans[y];
370 self.report_error_if_loans_conflict(old_loan, new_loan);
375 pub fn report_error_if_loans_conflict(&self,
376 old_loan: &Loan<'tcx>,
377 new_loan: &Loan<'tcx>) {
378 //! Checks whether `old_loan` and `new_loan` can safely be issued
381 debug!("report_error_if_loans_conflict(old_loan={}, new_loan={})",
382 old_loan.repr(self.tcx()),
383 new_loan.repr(self.tcx()));
385 // Should only be called for loans that are in scope at the same time.
386 assert!(self.tcx().region_maps.scopes_intersect(old_loan.kill_scope,
387 new_loan.kill_scope));
389 self.report_error_if_loan_conflicts_with_restriction(
390 old_loan, new_loan, old_loan, new_loan) &&
391 self.report_error_if_loan_conflicts_with_restriction(
392 new_loan, old_loan, old_loan, new_loan);
395 pub fn report_error_if_loan_conflicts_with_restriction(&self,
398 old_loan: &Loan<'tcx>,
399 new_loan: &Loan<'tcx>)
401 //! Checks whether the restrictions introduced by `loan1` would
402 //! prohibit `loan2`. Returns false if an error is reported.
404 debug!("report_error_if_loan_conflicts_with_restriction(\
405 loan1={}, loan2={})",
406 loan1.repr(self.tcx()),
407 loan2.repr(self.tcx()));
409 if compatible_borrow_kinds(loan1.kind, loan2.kind) {
413 let loan2_base_path = owned_ptr_base_path_rc(&loan2.loan_path);
414 for restr_path in loan1.restricted_paths.iter() {
415 if *restr_path != loan2_base_path { continue; }
417 // If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
418 // normally generate a rather confusing message (in this case, for multiple mutable
421 // error: cannot borrow `x.b` as mutable more than once at a time
422 // note: previous borrow of `x.a` occurs here; the mutable borrow prevents
423 // subsequent moves, borrows, or modification of `x.a` until the borrow ends
425 // What we want to do instead is get the 'common ancestor' of the two borrow paths and
426 // use that for most of the message instead, giving is something like this:
428 // error: cannot borrow `x` as mutable more than once at a time
429 // note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
430 // borrow prevents subsequent moves, borrows, or modification of `x` until the
433 let common = new_loan.loan_path.common(&*old_loan.loan_path);
434 let (nl, ol, new_loan_msg, old_loan_msg) =
435 if new_loan.loan_path.has_fork(&*old_loan.loan_path) && common.is_some() {
436 let nl = self.bccx.loan_path_to_string(&common.unwrap());
438 let new_loan_msg = format!(" (here through borrowing `{}`)",
439 self.bccx.loan_path_to_string(
440 &*new_loan.loan_path));
441 let old_loan_msg = format!(" (through borrowing `{}`)",
442 self.bccx.loan_path_to_string(
443 &*old_loan.loan_path));
444 (nl, ol, new_loan_msg, old_loan_msg)
446 (self.bccx.loan_path_to_string(&*new_loan.loan_path),
447 self.bccx.loan_path_to_string(&*old_loan.loan_path),
448 String::new(), String::new())
451 let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
457 match (new_loan.kind, old_loan.kind) {
458 (ty::MutBorrow, ty::MutBorrow) => {
461 format!("cannot borrow `{}`{} as mutable \
462 more than once at a time",
463 nl, new_loan_msg).as_slice())
466 (ty::UniqueImmBorrow, _) => {
469 format!("closure requires unique access to `{}` \
470 but {} is already borrowed{}",
471 nl, ol_pronoun, old_loan_msg).as_slice());
474 (_, ty::UniqueImmBorrow) => {
477 format!("cannot borrow `{}`{} as {} because \
478 previous closure requires unique access",
479 nl, new_loan_msg, new_loan.kind.to_user_str()).as_slice());
485 format!("cannot borrow `{}`{} as {} because \
486 {} is also borrowed as {}{}",
489 new_loan.kind.to_user_str(),
491 old_loan.kind.to_user_str(),
492 old_loan_msg).as_slice());
496 match new_loan.cause {
497 euv::ClosureCapture(span) => {
500 format!("borrow occurs due to use of `{}` in closure",
506 let rule_summary = match old_loan.kind {
508 format!("the mutable borrow prevents subsequent \
509 moves, borrows, or modification of `{0}` \
510 until the borrow ends",
515 format!("the immutable borrow prevents subsequent \
516 moves or mutable borrows of `{0}` \
517 until the borrow ends",
521 ty::UniqueImmBorrow => {
522 format!("the unique capture prevents subsequent \
523 moves or borrows of `{0}` \
524 until the borrow ends",
529 let borrow_summary = match old_loan.cause {
530 euv::ClosureCapture(_) => {
531 format!("previous borrow of `{}` occurs here{} due to \
536 euv::OverloadedOperator(..) |
539 euv::ClosureInvocation(..) |
541 euv::RefBinding(..) |
542 euv::MatchDiscriminant(..) => {
543 format!("previous borrow of `{}` occurs here{}",
550 format!("{}; {}", borrow_summary, rule_summary).as_slice());
552 let old_loan_span = self.tcx().map.span(old_loan.kill_scope.node_id());
553 self.bccx.span_end_note(old_loan_span,
554 "previous borrow ends here");
562 fn is_local_variable_or_arg(&self, cmt: mc::cmt<'tcx>) -> bool {
564 mc::cat_local(_) => true,
569 fn consume_common(&self,
573 mode: euv::ConsumeMode) {
574 match opt_loan_path(&cmt) {
576 let moved_value_use_kind = match mode {
578 self.check_for_copy_of_frozen_path(id, span, &*lp);
582 match self.move_data.kind_of_move_of_path(id, &lp) {
584 // Sometimes moves don't have a move kind;
585 // this either means that the original move
586 // was from something illegal to move,
587 // or was moved from referent of an unsafe
588 // pointer or something like that.
592 self.check_for_move_of_borrowed_path(id, span,
594 if move_kind == move_data::Captured {
604 self.check_if_path_is_moved(id, span, moved_value_use_kind, &lp);
610 fn check_for_copy_of_frozen_path(&self,
613 copy_path: &LoanPath<'tcx>) {
614 match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
616 UseWhileBorrowed(loan_path, loan_span) => {
619 format!("cannot use `{}` because it was mutably borrowed",
620 self.bccx.loan_path_to_string(copy_path).as_slice())
624 format!("borrow of `{}` occurs here",
625 self.bccx.loan_path_to_string(&*loan_path).as_slice())
631 fn check_for_move_of_borrowed_path(&self,
634 move_path: &LoanPath<'tcx>,
635 move_kind: move_data::MoveKind) {
636 // We want to detect if there are any loans at all, so we search for
637 // any loans incompatible with MutBorrrow, since all other kinds of
638 // loans are incompatible with that.
639 match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) {
641 UseWhileBorrowed(loan_path, loan_span) => {
642 let err_message = match move_kind {
643 move_data::Captured =>
644 format!("cannot move `{}` into closure because it is borrowed",
645 self.bccx.loan_path_to_string(move_path).as_slice()),
646 move_data::Declared |
647 move_data::MoveExpr |
648 move_data::MovePat =>
649 format!("cannot move out of `{}` because it is borrowed",
650 self.bccx.loan_path_to_string(move_path).as_slice())
653 self.bccx.span_err(span, err_message.as_slice());
656 format!("borrow of `{}` occurs here",
657 self.bccx.loan_path_to_string(&*loan_path).as_slice())
663 pub fn analyze_restrictions_on_use(&self,
664 expr_id: ast::NodeId,
665 use_path: &LoanPath<'tcx>,
666 borrow_kind: ty::BorrowKind)
668 debug!("analyze_restrictions_on_use(expr_id={}, use_path={})",
669 self.tcx().map.node_to_string(expr_id),
670 use_path.repr(self.tcx()));
674 self.each_in_scope_loan_affecting_path(
675 region::CodeExtent::from_node_id(expr_id), use_path, |loan| {
676 if !compatible_borrow_kinds(loan.kind, borrow_kind) {
677 ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span);
687 /// Reports an error if `expr` (which should be a path)
688 /// is using a moved/uninitialized value
689 fn check_if_path_is_moved(&self,
692 use_kind: MovedValueUseKind,
693 lp: &Rc<LoanPath<'tcx>>) {
694 debug!("check_if_path_is_moved(id={}, use_kind={}, lp={})",
695 id, use_kind, lp.repr(self.bccx.tcx));
696 let base_lp = owned_ptr_base_path_rc(lp);
697 self.move_data.each_move_of(id, &base_lp, |the_move, moved_lp| {
698 self.bccx.report_use_of_moved_value(
708 /// Reports an error if assigning to `lp` will use a
709 /// moved/uninitialized value. Mainly this is concerned with
710 /// detecting derefs of uninitialized pointers.
716 /// a = 10; // ok, even though a is uninitialized
718 /// struct Point { x: uint, y: uint }
720 /// p.x = 22; // ok, even though `p` is uninitialized
723 /// (*p).x = 22; // not ok, p is uninitialized, can't deref
725 fn check_if_assigned_path_is_moved(&self,
728 use_kind: MovedValueUseKind,
729 lp: &Rc<LoanPath<'tcx>>)
732 LpVar(_) | LpUpvar(_) => {
733 // assigning to `x` does not require that `x` is initialized
735 LpDowncast(ref lp_base, _) => {
736 // assigning to `(P->Variant).f` is ok if assigning to `P` is ok
737 self.check_if_assigned_path_is_moved(id, span,
740 LpExtend(ref lp_base, _, LpInterior(_)) => {
741 // assigning to `P.f` is ok if assigning to `P` is ok
742 self.check_if_assigned_path_is_moved(id, span,
745 LpExtend(ref lp_base, _, LpDeref(_)) => {
746 // assigning to `(*P)` requires that `P` be initialized
747 self.check_if_path_is_moved(id, span,
753 fn check_assignment(&self,
754 assignment_id: ast::NodeId,
755 assignment_span: Span,
756 assignee_cmt: mc::cmt<'tcx>,
757 mode: euv::MutateMode) {
758 debug!("check_assignment(assignee_cmt={})", assignee_cmt.repr(self.tcx()));
760 // Mutable values can be assigned, as long as they obey loans
761 // and aliasing restrictions:
762 if assignee_cmt.mutbl.is_mutable() {
763 if check_for_aliasable_mutable_writes(self, assignment_span, assignee_cmt.clone()) {
764 if mode != euv::Init {
765 check_for_assignment_to_borrowed_path(
766 self, assignment_id, assignment_span, assignee_cmt.clone());
767 mark_variable_as_used_mut(self, assignee_cmt);
773 // Initializations are OK.
774 if mode == euv::Init {
778 // For immutable local variables, assignments are legal
779 // if they cannot already have been assigned
780 if self.is_local_variable_or_arg(assignee_cmt.clone()) {
781 assert!(assignee_cmt.mutbl.is_immutable()); // no "const" locals
782 let lp = opt_loan_path(&assignee_cmt).unwrap();
783 self.move_data.each_assignment_of(assignment_id, &lp, |assign| {
784 self.bccx.report_reassigned_immutable_variable(
793 // Otherwise, just a plain error.
794 match assignee_cmt.note {
795 mc::NoteClosureEnv(upvar_id) => {
796 // If this is an `Fn` closure, it simply can't mutate upvars.
797 // If it's an `FnMut` closure, the original variable was declared immutable.
798 // We need to determine which is the case here.
799 let kind = match assignee_cmt.upvar().unwrap().cat {
800 mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
803 if kind == ty::FnUnboxedClosureKind {
806 format!("cannot assign to {}",
807 self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
809 self.tcx().map.span(upvar_id.closure_expr_id),
810 "consider changing this closure to take self by mutable reference");
814 format!("cannot assign to {} {}",
815 assignee_cmt.mutbl.to_user_str(),
816 self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
819 _ => match opt_loan_path(&assignee_cmt) {
823 format!("cannot assign to {} {} `{}`",
824 assignee_cmt.mutbl.to_user_str(),
825 self.bccx.cmt_to_string(&*assignee_cmt),
826 self.bccx.loan_path_to_string(&*lp)).as_slice());
831 format!("cannot assign to {} {}",
832 assignee_cmt.mutbl.to_user_str(),
833 self.bccx.cmt_to_string(&*assignee_cmt)).as_slice());
839 fn mark_variable_as_used_mut<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
840 mut cmt: mc::cmt<'tcx>) {
841 //! If the mutability of the `cmt` being written is inherited
842 //! from a local variable, liveness will
843 //! not have been able to detect that this variable's mutability
844 //! is important, so we must add the variable to the
845 //! `used_mut_nodes` table here.
848 debug!("mark_variable_as_used_mut(cmt={})", cmt.repr(this.tcx()));
849 match cmt.cat.clone() {
850 mc::cat_upvar(mc::Upvar { id: ty::UpvarId { var_id: id, .. }, .. }) |
851 mc::cat_local(id) => {
852 this.tcx().used_mut_nodes.borrow_mut().insert(id);
857 mc::cat_static_item |
858 mc::cat_deref(_, _, mc::UnsafePtr(..)) |
859 mc::cat_deref(_, _, mc::Implicit(..)) => {
860 assert_eq!(cmt.mutbl, mc::McDeclared);
864 mc::cat_deref(_, _, mc::BorrowedPtr(..)) => {
865 assert_eq!(cmt.mutbl, mc::McDeclared);
866 // We need to drill down to upvar if applicable
873 mc::cat_deref(b, _, mc::OwnedPtr) => {
874 assert_eq!(cmt.mutbl, mc::McInherited);
878 mc::cat_downcast(b, _) |
879 mc::cat_interior(b, _) => {
880 assert_eq!(cmt.mutbl, mc::McInherited);
887 fn check_for_aliasable_mutable_writes<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
889 cmt: mc::cmt<'tcx>) -> bool {
890 //! Safety checks related to writes to aliasable, mutable locations
892 let guarantor = cmt.guarantor();
893 debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
894 cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
895 match guarantor.cat {
896 mc::cat_deref(ref b, _, mc::BorrowedPtr(ty::MutBorrow, _)) => {
897 // Statically prohibit writes to `&mut` when aliasable
899 check_for_aliasability_violation(this, span, b.clone());
905 return true; // no errors reported
908 fn check_for_aliasability_violation<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
912 match cmt.freely_aliasable(this.tcx()) {
916 Some(mc::AliasableStaticMut(..)) => {
920 this.bccx.report_aliasability_violation(
929 fn check_for_assignment_to_borrowed_path<'a, 'tcx>(
930 this: &CheckLoanCtxt<'a, 'tcx>,
931 assignment_id: ast::NodeId,
932 assignment_span: Span,
933 assignee_cmt: mc::cmt<'tcx>)
935 //! Check for assignments that violate the terms of an
936 //! outstanding loan.
938 let loan_path = match opt_loan_path(&assignee_cmt) {
940 None => { return; /* no loan path, can't be any loans */ }
943 let scope = region::CodeExtent::from_node_id(assignment_id);
944 this.each_in_scope_loan_affecting_path(scope, &*loan_path, |loan| {
945 this.report_illegal_mutation(assignment_span, &*loan_path, loan);
951 pub fn report_illegal_mutation(&self,
953 loan_path: &LoanPath<'tcx>,
957 format!("cannot assign to `{}` because it is borrowed",
958 self.bccx.loan_path_to_string(loan_path)).as_slice());
961 format!("borrow of `{}` occurs here",
962 self.bccx.loan_path_to_string(loan_path)).as_slice());