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
::*;
22 use rustc
::middle
::expr_use_visitor
as euv
;
23 use rustc
::middle
::mem_categorization
as mc
;
24 use rustc
::middle
::region
;
25 use rustc
::middle
::ty
;
26 use rustc
::util
::ppaux
::Repr
;
28 use syntax
::codemap
::Span
;
32 // FIXME (#16118): These functions are intended to allow the borrow checker to
33 // be less precise in its handling of Box while still allowing moves out of a
34 // Box. They should be removed when Unique is removed from LoanPath.
36 fn owned_ptr_base_path
<'a
, 'tcx
>(loan_path
: &'a LoanPath
<'tcx
>) -> &'a LoanPath
<'tcx
> {
37 //! Returns the base of the leftmost dereference of an Unique in
38 //! `loan_path`. If there is no dereference of an Unique in `loan_path`,
39 //! then it just returns `loan_path` itself.
41 return match helper(loan_path
) {
42 Some(new_loan_path
) => new_loan_path
,
43 None
=> loan_path
.clone()
46 fn helper
<'a
, 'tcx
>(loan_path
: &'a LoanPath
<'tcx
>) -> Option
<&'a LoanPath
<'tcx
>> {
47 match loan_path
.kind
{
48 LpVar(_
) | LpUpvar(_
) => None
,
49 LpExtend(ref lp_base
, _
, LpDeref(mc
::Unique
)) => {
50 match helper(&**lp_base
) {
52 None
=> Some(&**lp_base
)
55 LpDowncast(ref lp_base
, _
) |
56 LpExtend(ref lp_base
, _
, _
) => helper(&**lp_base
)
61 fn owned_ptr_base_path_rc
<'tcx
>(loan_path
: &Rc
<LoanPath
<'tcx
>>) -> Rc
<LoanPath
<'tcx
>> {
62 //! The equivalent of `owned_ptr_base_path` for an &Rc<LoanPath> rather than
65 return match helper(loan_path
) {
66 Some(new_loan_path
) => new_loan_path
,
67 None
=> loan_path
.clone()
70 fn helper
<'tcx
>(loan_path
: &Rc
<LoanPath
<'tcx
>>) -> Option
<Rc
<LoanPath
<'tcx
>>> {
71 match loan_path
.kind
{
72 LpVar(_
) | LpUpvar(_
) => None
,
73 LpExtend(ref lp_base
, _
, LpDeref(mc
::Unique
)) => {
74 match helper(lp_base
) {
76 None
=> Some(lp_base
.clone())
79 LpDowncast(ref lp_base
, _
) |
80 LpExtend(ref lp_base
, _
, _
) => helper(lp_base
)
85 struct CheckLoanCtxt
<'a
, 'tcx
: 'a
> {
86 bccx
: &'a BorrowckCtxt
<'a
, 'tcx
>,
87 dfcx_loans
: &'a LoanDataFlow
<'a
, 'tcx
>,
88 move_data
: move_data
::FlowedMoveData
<'a
, 'tcx
>,
89 all_loans
: &'a
[Loan
<'tcx
>],
90 param_env
: &'a ty
::ParameterEnvironment
<'a
, 'tcx
>,
93 impl<'a
, 'tcx
> euv
::Delegate
<'tcx
> for CheckLoanCtxt
<'a
, 'tcx
> {
95 consume_id
: ast
::NodeId
,
98 mode
: euv
::ConsumeMode
) {
99 debug
!("consume(consume_id={}, cmt={}, mode={:?})",
100 consume_id
, cmt
.repr(self.tcx()), mode
);
102 self.consume_common(consume_id
, consume_span
, cmt
, mode
);
105 fn matched_pat(&mut self,
106 _matched_pat
: &ast
::Pat
,
108 _mode
: euv
::MatchMode
) { }
110 fn consume_pat(&mut self,
111 consume_pat
: &ast
::Pat
,
113 mode
: euv
::ConsumeMode
) {
114 debug
!("consume_pat(consume_pat={}, cmt={}, mode={:?})",
115 consume_pat
.repr(self.tcx()),
116 cmt
.repr(self.tcx()),
119 self.consume_common(consume_pat
.id
, consume_pat
.span
, cmt
, mode
);
123 borrow_id
: ast
::NodeId
,
126 loan_region
: ty
::Region
,
128 loan_cause
: euv
::LoanCause
)
130 debug
!("borrow(borrow_id={}, cmt={}, loan_region={:?}, \
131 bk={:?}, loan_cause={:?})",
132 borrow_id
, cmt
.repr(self.tcx()), loan_region
,
135 match opt_loan_path(&cmt
) {
137 let moved_value_use_kind
= match loan_cause
{
138 euv
::ClosureCapture(_
) => MovedInCapture
,
141 self.check_if_path_is_moved(borrow_id
, borrow_span
, moved_value_use_kind
, &lp
);
146 self.check_for_conflicting_loans(region
::CodeExtent
::from_node_id(borrow_id
));
150 assignment_id
: ast
::NodeId
,
151 assignment_span
: Span
,
152 assignee_cmt
: mc
::cmt
<'tcx
>,
153 mode
: euv
::MutateMode
)
155 debug
!("mutate(assignment_id={}, assignee_cmt={})",
156 assignment_id
, assignee_cmt
.repr(self.tcx()));
158 match opt_loan_path(&assignee_cmt
) {
161 euv
::Init
| euv
::JustWrite
=> {
162 // In a case like `path = 1`, then path does not
163 // have to be *FULLY* initialized, but we still
164 // must be careful lest it contains derefs of
166 self.check_if_assigned_path_is_moved(assignee_cmt
.id
,
171 euv
::WriteAndRead
=> {
172 // In a case like `path += 1`, then path must be
173 // fully initialized, since we will read it before
175 self.check_if_path_is_moved(assignee_cmt
.id
,
185 self.check_assignment(assignment_id
, assignment_span
, assignee_cmt
, mode
);
188 fn decl_without_init(&mut self, _id
: ast
::NodeId
, _span
: Span
) { }
191 pub fn check_loans
<'a
, 'b
, 'c
, 'tcx
>(bccx
: &BorrowckCtxt
<'a
, 'tcx
>,
192 dfcx_loans
: &LoanDataFlow
<'b
, 'tcx
>,
193 move_data
: move_data
::FlowedMoveData
<'c
, 'tcx
>,
194 all_loans
: &[Loan
<'tcx
>],
198 debug
!("check_loans(body id={})", body
.id
);
200 let param_env
= ty
::ParameterEnvironment
::for_item(bccx
.tcx
, fn_id
);
202 let mut clcx
= CheckLoanCtxt
{
204 dfcx_loans
: dfcx_loans
,
205 move_data
: move_data
,
206 all_loans
: all_loans
,
207 param_env
: ¶m_env
,
211 let mut euv
= euv
::ExprUseVisitor
::new(&mut clcx
, ¶m_env
);
212 euv
.walk_fn(decl
, body
);
217 enum UseError
<'tcx
> {
219 UseWhileBorrowed(/*loan*/Rc
<LoanPath
<'tcx
>>, /*loan*/Span
)
222 fn compatible_borrow_kinds(borrow_kind1
: ty
::BorrowKind
,
223 borrow_kind2
: ty
::BorrowKind
)
225 borrow_kind1
== ty
::ImmBorrow
&& borrow_kind2
== ty
::ImmBorrow
228 impl<'a
, 'tcx
> CheckLoanCtxt
<'a
, 'tcx
> {
229 pub fn tcx(&self) -> &'a ty
::ctxt
<'tcx
> { self.bccx.tcx }
231 pub fn each_issued_loan
<F
>(&self, scope
: region
::CodeExtent
, mut op
: F
) -> bool
where
232 F
: FnMut(&Loan
<'tcx
>) -> bool
,
234 //! Iterates over each loan that has been issued
235 //! on entrance to `scope`, regardless of whether it is
236 //! actually *in scope* at that point. Sometimes loans
237 //! are issued for future scopes and thus they may have been
238 //! *issued* but not yet be in effect.
240 self.dfcx_loans
.each_bit_on_entry(scope
.node_id(), |loan_index
| {
241 let loan
= &self.all_loans
[loan_index
];
246 pub fn each_in_scope_loan
<F
>(&self, scope
: region
::CodeExtent
, mut op
: F
) -> bool
where
247 F
: FnMut(&Loan
<'tcx
>) -> bool
,
249 //! Like `each_issued_loan()`, but only considers loans that are
250 //! currently in scope.
252 let tcx
= self.tcx();
253 self.each_issued_loan(scope
, |loan
| {
254 if tcx
.region_maps
.is_subscope_of(scope
, loan
.kill_scope
) {
262 fn each_in_scope_loan_affecting_path
<F
>(&self,
263 scope
: region
::CodeExtent
,
264 loan_path
: &LoanPath
<'tcx
>,
267 F
: FnMut(&Loan
<'tcx
>) -> bool
,
269 //! Iterates through all of the in-scope loans affecting `loan_path`,
270 //! calling `op`, and ceasing iteration if `false` is returned.
272 // First, we check for a loan restricting the path P being used. This
273 // accounts for borrows of P but also borrows of subpaths, like P.a.b.
274 // Consider the following example:
276 // let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
277 // let y = a; // Conflicts with restriction
279 let loan_path
= owned_ptr_base_path(loan_path
);
280 let cont
= self.each_in_scope_loan(scope
, |loan
| {
282 for restr_path
in loan
.restricted_paths
.iter() {
283 if **restr_path
== *loan_path
{
297 // Next, we must check for *loans* (not restrictions) on the path P or
298 // any base path. This rejects examples like the following:
303 // Limiting this search to *loans* and not *restrictions* means that
304 // examples like the following continue to work:
309 let mut loan_path
= loan_path
;
311 match loan_path
.kind
{
312 LpVar(_
) | LpUpvar(_
) => {
315 LpDowncast(ref lp_base
, _
) |
316 LpExtend(ref lp_base
, _
, _
) => {
317 loan_path
= &**lp_base
;
321 let cont
= self.each_in_scope_loan(scope
, |loan
| {
322 if *loan
.loan_path
== *loan_path
{
337 pub fn loans_generated_by(&self, scope
: region
::CodeExtent
) -> Vec
<uint
> {
338 //! Returns a vector of the loans that are generated as
339 //! we enter `scope`.
341 let mut result
= Vec
::new();
342 self.dfcx_loans
.each_gen_bit(scope
.node_id(), |loan_index
| {
343 result
.push(loan_index
);
349 pub fn check_for_conflicting_loans(&self, scope
: region
::CodeExtent
) {
350 //! Checks to see whether any of the loans that are issued
351 //! on entrance to `scope` conflict with loans that have already been
352 //! issued when we enter `scope` (for example, we do not
353 //! permit two `&mut` borrows of the same variable).
355 //! (Note that some loans can be *issued* without necessarily
356 //! taking effect yet.)
358 debug
!("check_for_conflicting_loans(scope={:?})", scope
);
360 let new_loan_indices
= self.loans_generated_by(scope
);
361 debug
!("new_loan_indices = {:?}", new_loan_indices
);
363 self.each_issued_loan(scope
, |issued_loan
| {
364 for &new_loan_index
in new_loan_indices
.iter() {
365 let new_loan
= &self.all_loans
[new_loan_index
];
366 self.report_error_if_loans_conflict(issued_loan
, new_loan
);
371 for (i
, &x
) in new_loan_indices
.iter().enumerate() {
372 let old_loan
= &self.all_loans
[x
];
373 for &y
in new_loan_indices
.slice_from(i
+1).iter() {
374 let new_loan
= &self.all_loans
[y
];
375 self.report_error_if_loans_conflict(old_loan
, new_loan
);
380 pub fn report_error_if_loans_conflict(&self,
381 old_loan
: &Loan
<'tcx
>,
382 new_loan
: &Loan
<'tcx
>) {
383 //! Checks whether `old_loan` and `new_loan` can safely be issued
386 debug
!("report_error_if_loans_conflict(old_loan={}, new_loan={})",
387 old_loan
.repr(self.tcx()),
388 new_loan
.repr(self.tcx()));
390 // Should only be called for loans that are in scope at the same time.
391 assert
!(self.tcx().region_maps
.scopes_intersect(old_loan
.kill_scope
,
392 new_loan
.kill_scope
));
394 self.report_error_if_loan_conflicts_with_restriction(
395 old_loan
, new_loan
, old_loan
, new_loan
) &&
396 self.report_error_if_loan_conflicts_with_restriction(
397 new_loan
, old_loan
, old_loan
, new_loan
);
400 pub fn report_error_if_loan_conflicts_with_restriction(&self,
403 old_loan
: &Loan
<'tcx
>,
404 new_loan
: &Loan
<'tcx
>)
406 //! Checks whether the restrictions introduced by `loan1` would
407 //! prohibit `loan2`. Returns false if an error is reported.
409 debug
!("report_error_if_loan_conflicts_with_restriction(\
410 loan1={}, loan2={})",
411 loan1
.repr(self.tcx()),
412 loan2
.repr(self.tcx()));
414 if compatible_borrow_kinds(loan1
.kind
, loan2
.kind
) {
418 let loan2_base_path
= owned_ptr_base_path_rc(&loan2
.loan_path
);
419 for restr_path
in loan1
.restricted_paths
.iter() {
420 if *restr_path
!= loan2_base_path { continue; }
422 // If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
423 // normally generate a rather confusing message (in this case, for multiple mutable
426 // error: cannot borrow `x.b` as mutable more than once at a time
427 // note: previous borrow of `x.a` occurs here; the mutable borrow prevents
428 // subsequent moves, borrows, or modification of `x.a` until the borrow ends
430 // What we want to do instead is get the 'common ancestor' of the two borrow paths and
431 // use that for most of the message instead, giving is something like this:
433 // error: cannot borrow `x` as mutable more than once at a time
434 // note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
435 // borrow prevents subsequent moves, borrows, or modification of `x` until the
438 let common
= new_loan
.loan_path
.common(&*old_loan
.loan_path
);
439 let (nl
, ol
, new_loan_msg
, old_loan_msg
) =
440 if new_loan
.loan_path
.has_fork(&*old_loan
.loan_path
) && common
.is_some() {
441 let nl
= self.bccx
.loan_path_to_string(&common
.unwrap());
443 let new_loan_msg
= format
!(" (here through borrowing `{}`)",
444 self.bccx
.loan_path_to_string(
445 &*new_loan
.loan_path
));
446 let old_loan_msg
= format
!(" (through borrowing `{}`)",
447 self.bccx
.loan_path_to_string(
448 &*old_loan
.loan_path
));
449 (nl
, ol
, new_loan_msg
, old_loan_msg
)
451 (self.bccx
.loan_path_to_string(&*new_loan
.loan_path
),
452 self.bccx
.loan_path_to_string(&*old_loan
.loan_path
),
453 String
::new(), String
::new())
456 let ol_pronoun
= if new_loan
.loan_path
== old_loan
.loan_path
{
462 match (new_loan
.kind
, old_loan
.kind
) {
463 (ty
::MutBorrow
, ty
::MutBorrow
) => {
466 &format
!("cannot borrow `{}`{} as mutable \
467 more than once at a time",
471 (ty
::UniqueImmBorrow
, _
) => {
474 &format
!("closure requires unique access to `{}` \
475 but {} is already borrowed{}",
476 nl
, ol_pronoun
, old_loan_msg
)[]);
479 (_
, ty
::UniqueImmBorrow
) => {
482 &format
!("cannot borrow `{}`{} as {} because \
483 previous closure requires unique access",
484 nl
, new_loan_msg
, new_loan
.kind
.to_user_str())[]);
490 &format
!("cannot borrow `{}`{} as {} because \
491 {} is also borrowed as {}{}",
494 new_loan
.kind
.to_user_str(),
496 old_loan
.kind
.to_user_str(),
501 match new_loan
.cause
{
502 euv
::ClosureCapture(span
) => {
505 &format
!("borrow occurs due to use of `{}` in closure",
511 let rule_summary
= match old_loan
.kind
{
513 format
!("the mutable borrow prevents subsequent \
514 moves, borrows, or modification of `{0}` \
515 until the borrow ends",
520 format
!("the immutable borrow prevents subsequent \
521 moves or mutable borrows of `{0}` \
522 until the borrow ends",
526 ty
::UniqueImmBorrow
=> {
527 format
!("the unique capture prevents subsequent \
528 moves or borrows of `{0}` \
529 until the borrow ends",
534 let borrow_summary
= match old_loan
.cause
{
535 euv
::ClosureCapture(_
) => {
536 format
!("previous borrow of `{}` occurs here{} due to \
541 euv
::OverloadedOperator(..) |
544 euv
::ClosureInvocation(..) |
546 euv
::RefBinding(..) |
547 euv
::MatchDiscriminant(..) => {
548 format
!("previous borrow of `{}` occurs here{}",
555 &format
!("{}; {}", borrow_summary
, rule_summary
)[]);
557 let old_loan_span
= self.tcx().map
.span(old_loan
.kill_scope
.node_id());
558 self.bccx
.span_end_note(old_loan_span
,
559 "previous borrow ends here");
567 fn is_local_variable_or_arg(&self, cmt
: mc
::cmt
<'tcx
>) -> bool
{
569 mc
::cat_local(_
) => true,
574 fn consume_common(&self,
578 mode
: euv
::ConsumeMode
) {
579 match opt_loan_path(&cmt
) {
581 let moved_value_use_kind
= match mode
{
583 self.check_for_copy_of_frozen_path(id
, span
, &*lp
);
587 match self.move_data
.kind_of_move_of_path(id
, &lp
) {
589 // Sometimes moves don't have a move kind;
590 // this either means that the original move
591 // was from something illegal to move,
592 // or was moved from referent of an unsafe
593 // pointer or something like that.
597 self.check_for_move_of_borrowed_path(id
, span
,
599 if move_kind
== move_data
::Captured
{
609 self.check_if_path_is_moved(id
, span
, moved_value_use_kind
, &lp
);
615 fn check_for_copy_of_frozen_path(&self,
618 copy_path
: &LoanPath
<'tcx
>) {
619 match self.analyze_restrictions_on_use(id
, copy_path
, ty
::ImmBorrow
) {
621 UseWhileBorrowed(loan_path
, loan_span
) => {
624 &format
!("cannot use `{}` because it was mutably borrowed",
625 &self.bccx
.loan_path_to_string(copy_path
)[])
629 &format
!("borrow of `{}` occurs here",
630 &self.bccx
.loan_path_to_string(&*loan_path
)[])
636 fn check_for_move_of_borrowed_path(&self,
639 move_path
: &LoanPath
<'tcx
>,
640 move_kind
: move_data
::MoveKind
) {
641 // We want to detect if there are any loans at all, so we search for
642 // any loans incompatible with MutBorrrow, since all other kinds of
643 // loans are incompatible with that.
644 match self.analyze_restrictions_on_use(id
, move_path
, ty
::MutBorrow
) {
646 UseWhileBorrowed(loan_path
, loan_span
) => {
647 let err_message
= match move_kind
{
648 move_data
::Captured
=>
649 format
!("cannot move `{}` into closure because it is borrowed",
650 &self.bccx
.loan_path_to_string(move_path
)[]),
651 move_data
::Declared
|
652 move_data
::MoveExpr
|
653 move_data
::MovePat
=>
654 format
!("cannot move out of `{}` because it is borrowed",
655 &self.bccx
.loan_path_to_string(move_path
)[])
658 self.bccx
.span_err(span
, &err_message
[]);
661 &format
!("borrow of `{}` occurs here",
662 &self.bccx
.loan_path_to_string(&*loan_path
)[])
668 pub fn analyze_restrictions_on_use(&self,
669 expr_id
: ast
::NodeId
,
670 use_path
: &LoanPath
<'tcx
>,
671 borrow_kind
: ty
::BorrowKind
)
673 debug
!("analyze_restrictions_on_use(expr_id={}, use_path={})",
674 self.tcx().map
.node_to_string(expr_id
),
675 use_path
.repr(self.tcx()));
679 self.each_in_scope_loan_affecting_path(
680 region
::CodeExtent
::from_node_id(expr_id
), use_path
, |loan
| {
681 if !compatible_borrow_kinds(loan
.kind
, borrow_kind
) {
682 ret
= UseWhileBorrowed(loan
.loan_path
.clone(), loan
.span
);
692 /// Reports an error if `expr` (which should be a path)
693 /// is using a moved/uninitialized value
694 fn check_if_path_is_moved(&self,
697 use_kind
: MovedValueUseKind
,
698 lp
: &Rc
<LoanPath
<'tcx
>>) {
699 debug
!("check_if_path_is_moved(id={}, use_kind={:?}, lp={})",
700 id
, use_kind
, lp
.repr(self.bccx
.tcx
));
701 let base_lp
= owned_ptr_base_path_rc(lp
);
702 self.move_data
.each_move_of(id
, &base_lp
, |the_move
, moved_lp
| {
703 self.bccx
.report_use_of_moved_value(
714 /// Reports an error if assigning to `lp` will use a
715 /// moved/uninitialized value. Mainly this is concerned with
716 /// detecting derefs of uninitialized pointers.
722 /// a = 10; // ok, even though a is uninitialized
724 /// struct Point { x: uint, y: uint }
726 /// p.x = 22; // ok, even though `p` is uninitialized
729 /// (*p).x = 22; // not ok, p is uninitialized, can't deref
731 fn check_if_assigned_path_is_moved(&self,
734 use_kind
: MovedValueUseKind
,
735 lp
: &Rc
<LoanPath
<'tcx
>>)
738 LpVar(_
) | LpUpvar(_
) => {
739 // assigning to `x` does not require that `x` is initialized
741 LpDowncast(ref lp_base
, _
) => {
742 // assigning to `(P->Variant).f` is ok if assigning to `P` is ok
743 self.check_if_assigned_path_is_moved(id
, span
,
746 LpExtend(ref lp_base
, _
, LpInterior(_
)) => {
747 // assigning to `P.f` is ok if assigning to `P` is ok
748 self.check_if_assigned_path_is_moved(id
, span
,
751 LpExtend(ref lp_base
, _
, LpDeref(_
)) => {
752 // assigning to `(*P)` requires that `P` be initialized
753 self.check_if_path_is_moved(id
, span
,
759 fn check_assignment(&self,
760 assignment_id
: ast
::NodeId
,
761 assignment_span
: Span
,
762 assignee_cmt
: mc
::cmt
<'tcx
>,
763 mode
: euv
::MutateMode
) {
764 debug
!("check_assignment(assignee_cmt={})", assignee_cmt
.repr(self.tcx()));
766 // Mutable values can be assigned, as long as they obey loans
767 // and aliasing restrictions:
768 if assignee_cmt
.mutbl
.is_mutable() {
769 if check_for_aliasable_mutable_writes(self, assignment_span
, assignee_cmt
.clone()) {
770 if mode
!= euv
::Init
{
771 check_for_assignment_to_borrowed_path(
772 self, assignment_id
, assignment_span
, assignee_cmt
.clone());
773 mark_variable_as_used_mut(self, assignee_cmt
);
779 // Initializations are OK.
780 if mode
== euv
::Init
{
784 // For immutable local variables, assignments are legal
785 // if they cannot already have been assigned
786 if self.is_local_variable_or_arg(assignee_cmt
.clone()) {
787 assert
!(assignee_cmt
.mutbl
.is_immutable()); // no "const" locals
788 let lp
= opt_loan_path(&assignee_cmt
).unwrap();
789 self.move_data
.each_assignment_of(assignment_id
, &lp
, |assign
| {
790 self.bccx
.report_reassigned_immutable_variable(
799 // Otherwise, just a plain error.
800 match assignee_cmt
.note
{
801 mc
::NoteClosureEnv(upvar_id
) => {
802 // If this is an `Fn` closure, it simply can't mutate upvars.
803 // If it's an `FnMut` closure, the original variable was declared immutable.
804 // We need to determine which is the case here.
805 let kind
= match assignee_cmt
.upvar().unwrap().cat
{
806 mc
::cat_upvar(mc
::Upvar { kind, .. }
) => kind
,
809 if kind
== ty
::FnUnboxedClosureKind
{
812 &format
!("cannot assign to {}",
813 self.bccx
.cmt_to_string(&*assignee_cmt
))[]);
815 self.tcx().map
.span(upvar_id
.closure_expr_id
),
816 "consider changing this closure to take self by mutable reference");
820 &format
!("cannot assign to {} {}",
821 assignee_cmt
.mutbl
.to_user_str(),
822 self.bccx
.cmt_to_string(&*assignee_cmt
))[]);
825 _
=> match opt_loan_path(&assignee_cmt
) {
829 &format
!("cannot assign to {} {} `{}`",
830 assignee_cmt
.mutbl
.to_user_str(),
831 self.bccx
.cmt_to_string(&*assignee_cmt
),
832 self.bccx
.loan_path_to_string(&*lp
))[]);
837 &format
!("cannot assign to {} {}",
838 assignee_cmt
.mutbl
.to_user_str(),
839 self.bccx
.cmt_to_string(&*assignee_cmt
))[]);
845 fn mark_variable_as_used_mut
<'a
, 'tcx
>(this
: &CheckLoanCtxt
<'a
, 'tcx
>,
846 mut cmt
: mc
::cmt
<'tcx
>) {
847 //! If the mutability of the `cmt` being written is inherited
848 //! from a local variable, liveness will
849 //! not have been able to detect that this variable's mutability
850 //! is important, so we must add the variable to the
851 //! `used_mut_nodes` table here.
854 debug
!("mark_variable_as_used_mut(cmt={})", cmt
.repr(this
.tcx()));
855 match cmt
.cat
.clone() {
856 mc
::cat_upvar(mc
::Upvar { id: ty::UpvarId { var_id: id, .. }
, .. }) |
857 mc
::cat_local(id
) => {
858 this
.tcx().used_mut_nodes
.borrow_mut().insert(id
);
863 mc
::cat_static_item
|
864 mc
::cat_deref(_
, _
, mc
::UnsafePtr(..)) |
865 mc
::cat_deref(_
, _
, mc
::Implicit(..)) => {
866 assert_eq
!(cmt
.mutbl
, mc
::McDeclared
);
870 mc
::cat_deref(_
, _
, mc
::BorrowedPtr(..)) => {
871 assert_eq
!(cmt
.mutbl
, mc
::McDeclared
);
872 // We need to drill down to upvar if applicable
879 mc
::cat_deref(b
, _
, mc
::Unique
) => {
880 assert_eq
!(cmt
.mutbl
, mc
::McInherited
);
884 mc
::cat_downcast(b
, _
) |
885 mc
::cat_interior(b
, _
) => {
886 assert_eq
!(cmt
.mutbl
, mc
::McInherited
);
893 fn check_for_aliasable_mutable_writes
<'a
, 'tcx
>(this
: &CheckLoanCtxt
<'a
, 'tcx
>,
895 cmt
: mc
::cmt
<'tcx
>) -> bool
{
896 //! Safety checks related to writes to aliasable, mutable locations
898 let guarantor
= cmt
.guarantor();
899 debug
!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
900 cmt
.repr(this
.tcx()), guarantor
.repr(this
.tcx()));
901 if let mc
::cat_deref(ref b
, _
, mc
::BorrowedPtr(ty
::MutBorrow
, _
)) = guarantor
.cat
{
902 // Statically prohibit writes to `&mut` when aliasable
903 check_for_aliasability_violation(this
, span
, b
.clone());
906 return true; // no errors reported
909 fn check_for_aliasability_violation
<'a
, 'tcx
>(this
: &CheckLoanCtxt
<'a
, 'tcx
>,
913 match cmt
.freely_aliasable(this
.tcx()) {
917 Some(mc
::AliasableStaticMut(..)) => {
921 this
.bccx
.report_aliasability_violation(
930 fn check_for_assignment_to_borrowed_path
<'a
, 'tcx
>(
931 this
: &CheckLoanCtxt
<'a
, 'tcx
>,
932 assignment_id
: ast
::NodeId
,
933 assignment_span
: Span
,
934 assignee_cmt
: mc
::cmt
<'tcx
>)
936 //! Check for assignments that violate the terms of an
937 //! outstanding loan.
939 let loan_path
= match opt_loan_path(&assignee_cmt
) {
941 None
=> { return; /* no loan path, can't be any loans */ }
944 let scope
= region
::CodeExtent
::from_node_id(assignment_id
);
945 this
.each_in_scope_loan_affecting_path(scope
, &*loan_path
, |loan
| {
946 this
.report_illegal_mutation(assignment_span
, &*loan_path
, loan
);
952 pub fn report_illegal_mutation(&self,
954 loan_path
: &LoanPath
<'tcx
>,
958 &format
!("cannot assign to `{}` because it is borrowed",
959 self.bccx
.loan_path_to_string(loan_path
))[]);
962 &format
!("borrow of `{}` occurs here",
963 self.bccx
.loan_path_to_string(loan_path
))[]);