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 borrowck::InteriorKind::{InteriorElement, InteriorField};
23 use rustc::middle::expr_use_visitor as euv;
24 use rustc::middle::infer;
25 use rustc::middle::mem_categorization as mc;
26 use rustc::middle::region;
27 use rustc::middle::ty;
29 use syntax::codemap::Span;
33 // FIXME (#16118): These functions are intended to allow the borrow checker to
34 // be less precise in its handling of Box while still allowing moves out of a
35 // Box. They should be removed when Unique is removed from LoanPath.
37 fn owned_ptr_base_path<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> &'a LoanPath<'tcx> {
38 //! Returns the base of the leftmost dereference of an Unique in
39 //! `loan_path`. If there is no dereference of an Unique in `loan_path`,
40 //! then it just returns `loan_path` itself.
42 return match helper(loan_path) {
43 Some(new_loan_path) => new_loan_path,
44 None => loan_path.clone()
47 fn helper<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> Option<&'a LoanPath<'tcx>> {
48 match loan_path.kind {
49 LpVar(_) | LpUpvar(_) => None,
50 LpExtend(ref lp_base, _, LpDeref(mc::Unique)) => {
51 match helper(&**lp_base) {
53 None => Some(&**lp_base)
56 LpDowncast(ref lp_base, _) |
57 LpExtend(ref lp_base, _, _) => helper(&**lp_base)
62 fn owned_ptr_base_path_rc<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Rc<LoanPath<'tcx>> {
63 //! The equivalent of `owned_ptr_base_path` for an &Rc<LoanPath> rather than
66 return match helper(loan_path) {
67 Some(new_loan_path) => new_loan_path,
68 None => loan_path.clone()
71 fn helper<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Option<Rc<LoanPath<'tcx>>> {
72 match loan_path.kind {
73 LpVar(_) | LpUpvar(_) => None,
74 LpExtend(ref lp_base, _, LpDeref(mc::Unique)) => {
75 match helper(lp_base) {
77 None => Some(lp_base.clone())
80 LpDowncast(ref lp_base, _) |
81 LpExtend(ref lp_base, _, _) => helper(lp_base)
86 struct CheckLoanCtxt<'a, 'tcx: 'a> {
87 bccx: &'a BorrowckCtxt<'a, 'tcx>,
88 dfcx_loans: &'a LoanDataFlow<'a, 'tcx>,
89 move_data: move_data::FlowedMoveData<'a, 'tcx>,
90 all_loans: &'a [Loan<'tcx>],
91 param_env: &'a ty::ParameterEnvironment<'a, '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, 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={:?})",
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, 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);
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);
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>],
199 debug!("check_loans(body id={})", body.id);
201 let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
202 let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env), false);
204 let mut clcx = CheckLoanCtxt {
206 dfcx_loans: dfcx_loans,
207 move_data: move_data,
208 all_loans: all_loans,
209 param_env: &infcx.parameter_environment
213 let mut euv = euv::ExprUseVisitor::new(&mut clcx, &infcx);
214 euv.walk_fn(decl, body);
219 enum UseError<'tcx> {
221 UseWhileBorrowed(/*loan*/Rc<LoanPath<'tcx>>, /*loan*/Span)
224 fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind,
225 borrow_kind2: ty::BorrowKind)
227 borrow_kind1 == ty::ImmBorrow && borrow_kind2 == ty::ImmBorrow
230 impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
231 pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.bccx.tcx }
233 pub fn each_issued_loan<F>(&self, scope: region::CodeExtent, mut op: F) -> bool where
234 F: FnMut(&Loan<'tcx>) -> bool,
236 //! Iterates over each loan that has been issued
237 //! on entrance to `scope`, regardless of whether it is
238 //! actually *in scope* at that point. Sometimes loans
239 //! are issued for future scopes and thus they may have been
240 //! *issued* but not yet be in effect.
242 self.dfcx_loans.each_bit_on_entry(scope.node_id(), |loan_index| {
243 let loan = &self.all_loans[loan_index];
248 pub fn each_in_scope_loan<F>(&self, scope: region::CodeExtent, mut op: F) -> bool where
249 F: FnMut(&Loan<'tcx>) -> bool,
251 //! Like `each_issued_loan()`, but only considers loans that are
252 //! currently in scope.
254 let tcx = self.tcx();
255 self.each_issued_loan(scope, |loan| {
256 if tcx.region_maps.is_subscope_of(scope, loan.kill_scope) {
264 fn each_in_scope_loan_affecting_path<F>(&self,
265 scope: region::CodeExtent,
266 loan_path: &LoanPath<'tcx>,
269 F: FnMut(&Loan<'tcx>) -> bool,
271 //! Iterates through all of the in-scope loans affecting `loan_path`,
272 //! calling `op`, and ceasing iteration if `false` is returned.
274 // First, we check for a loan restricting the path P being used. This
275 // accounts for borrows of P but also borrows of subpaths, like P.a.b.
276 // Consider the following example:
278 // let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
279 // let y = a; // Conflicts with restriction
281 let loan_path = owned_ptr_base_path(loan_path);
282 let cont = self.each_in_scope_loan(scope, |loan| {
284 for restr_path in &loan.restricted_paths {
285 if **restr_path == *loan_path {
299 // Next, we must check for *loans* (not restrictions) on the path P or
300 // any base path. This rejects examples like the following:
305 // Limiting this search to *loans* and not *restrictions* means that
306 // examples like the following continue to work:
311 let mut loan_path = loan_path;
313 match loan_path.kind {
314 LpVar(_) | LpUpvar(_) => {
317 LpDowncast(ref lp_base, _) |
318 LpExtend(ref lp_base, _, _) => {
319 loan_path = &**lp_base;
323 let cont = self.each_in_scope_loan(scope, |loan| {
324 if *loan.loan_path == *loan_path {
339 pub fn loans_generated_by(&self, scope: region::CodeExtent) -> Vec<usize> {
340 //! Returns a vector of the loans that are generated as
341 //! we enter `scope`.
343 let mut result = Vec::new();
344 self.dfcx_loans.each_gen_bit(scope.node_id(), |loan_index| {
345 result.push(loan_index);
351 pub fn check_for_conflicting_loans(&self, scope: region::CodeExtent) {
352 //! Checks to see whether any of the loans that are issued
353 //! on entrance to `scope` conflict with loans that have already been
354 //! issued when we enter `scope` (for example, we do not
355 //! permit two `&mut` borrows of the same variable).
357 //! (Note that some loans can be *issued* without necessarily
358 //! taking effect yet.)
360 debug!("check_for_conflicting_loans(scope={:?})", scope);
362 let new_loan_indices = self.loans_generated_by(scope);
363 debug!("new_loan_indices = {:?}", new_loan_indices);
365 self.each_issued_loan(scope, |issued_loan| {
366 for &new_loan_index in &new_loan_indices {
367 let new_loan = &self.all_loans[new_loan_index];
368 self.report_error_if_loans_conflict(issued_loan, new_loan);
373 for (i, &x) in new_loan_indices.iter().enumerate() {
374 let old_loan = &self.all_loans[x];
375 for &y in &new_loan_indices[(i+1) ..] {
376 let new_loan = &self.all_loans[y];
377 self.report_error_if_loans_conflict(old_loan, new_loan);
382 pub fn report_error_if_loans_conflict(&self,
383 old_loan: &Loan<'tcx>,
384 new_loan: &Loan<'tcx>) {
385 //! Checks whether `old_loan` and `new_loan` can safely be issued
388 debug!("report_error_if_loans_conflict(old_loan={:?}, new_loan={:?})",
392 // Should only be called for loans that are in scope at the same time.
393 assert!(self.tcx().region_maps.scopes_intersect(old_loan.kill_scope,
394 new_loan.kill_scope));
396 self.report_error_if_loan_conflicts_with_restriction(
397 old_loan, new_loan, old_loan, new_loan) &&
398 self.report_error_if_loan_conflicts_with_restriction(
399 new_loan, old_loan, old_loan, new_loan);
402 pub fn report_error_if_loan_conflicts_with_restriction(&self,
405 old_loan: &Loan<'tcx>,
406 new_loan: &Loan<'tcx>)
408 //! Checks whether the restrictions introduced by `loan1` would
409 //! prohibit `loan2`. Returns false if an error is reported.
411 debug!("report_error_if_loan_conflicts_with_restriction(\
412 loan1={:?}, loan2={:?})",
416 if compatible_borrow_kinds(loan1.kind, loan2.kind) {
420 let loan2_base_path = owned_ptr_base_path_rc(&loan2.loan_path);
421 for restr_path in &loan1.restricted_paths {
422 if *restr_path != loan2_base_path { continue; }
424 // If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
425 // normally generate a rather confusing message (in this case, for multiple mutable
428 // error: cannot borrow `x.b` as mutable more than once at a time
429 // note: previous borrow of `x.a` occurs here; the mutable borrow prevents
430 // subsequent moves, borrows, or modification of `x.a` until the borrow ends
432 // What we want to do instead is get the 'common ancestor' of the two borrow paths and
433 // use that for most of the message instead, giving is something like this:
435 // error: cannot borrow `x` as mutable more than once at a time
436 // note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
437 // borrow prevents subsequent moves, borrows, or modification of `x` until the
440 let common = new_loan.loan_path.common(&*old_loan.loan_path);
441 let (nl, ol, new_loan_msg, old_loan_msg) =
442 if new_loan.loan_path.has_fork(&*old_loan.loan_path) && common.is_some() {
443 let nl = self.bccx.loan_path_to_string(&common.unwrap());
445 let new_loan_msg = format!(" (here through borrowing `{}`)",
446 self.bccx.loan_path_to_string(
447 &*new_loan.loan_path));
448 let old_loan_msg = format!(" (through borrowing `{}`)",
449 self.bccx.loan_path_to_string(
450 &*old_loan.loan_path));
451 (nl, ol, new_loan_msg, old_loan_msg)
453 (self.bccx.loan_path_to_string(&*new_loan.loan_path),
454 self.bccx.loan_path_to_string(&*old_loan.loan_path),
455 String::new(), String::new())
458 let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
464 match (new_loan.kind, old_loan.kind) {
465 (ty::MutBorrow, ty::MutBorrow) => {
468 &format!("cannot borrow `{}`{} as mutable \
469 more than once at a time",
473 (ty::UniqueImmBorrow, _) => {
476 &format!("closure requires unique access to `{}` \
477 but {} is already borrowed{}",
478 nl, ol_pronoun, old_loan_msg));
481 (_, ty::UniqueImmBorrow) => {
484 &format!("cannot borrow `{}`{} as {} because \
485 previous closure requires unique access",
486 nl, new_loan_msg, new_loan.kind.to_user_str()));
492 &format!("cannot borrow `{}`{} as {} because \
493 {} is also borrowed as {}{}",
496 new_loan.kind.to_user_str(),
498 old_loan.kind.to_user_str(),
503 match new_loan.cause {
504 euv::ClosureCapture(span) => {
507 &format!("borrow occurs due to use of `{}` in closure",
513 let rule_summary = match old_loan.kind {
515 format!("the mutable borrow prevents subsequent \
516 moves, borrows, or modification of `{0}` \
517 until the borrow ends",
522 format!("the immutable borrow prevents subsequent \
523 moves or mutable borrows of `{0}` \
524 until the borrow ends",
528 ty::UniqueImmBorrow => {
529 format!("the unique capture prevents subsequent \
530 moves or borrows of `{0}` \
531 until the borrow ends",
536 let borrow_summary = match old_loan.cause {
537 euv::ClosureCapture(_) => {
538 format!("previous borrow of `{}` occurs here{} due to \
543 euv::OverloadedOperator(..) |
546 euv::AutoUnsafe(..) |
547 euv::ClosureInvocation(..) |
549 euv::RefBinding(..) |
550 euv::MatchDiscriminant(..) => {
551 format!("previous borrow of `{}` occurs here{}",
558 &format!("{}; {}", borrow_summary, rule_summary));
560 let old_loan_span = self.tcx().map.span(old_loan.kill_scope.node_id());
561 self.bccx.span_end_note(old_loan_span,
562 "previous borrow ends here");
570 fn consume_common(&self,
574 mode: euv::ConsumeMode) {
575 match opt_loan_path(&cmt) {
577 let moved_value_use_kind = match mode {
579 self.check_for_copy_of_frozen_path(id, span, &*lp);
583 match self.move_data.kind_of_move_of_path(id, &lp) {
585 // Sometimes moves don't have a move kind;
586 // this either means that the original move
587 // was from something illegal to move,
588 // or was moved from referent of an unsafe
589 // pointer or something like that.
593 self.check_for_move_of_borrowed_path(id, span,
595 if move_kind == move_data::Captured {
605 self.check_if_path_is_moved(id, span, moved_value_use_kind, &lp);
611 fn check_for_copy_of_frozen_path(&self,
614 copy_path: &LoanPath<'tcx>) {
615 match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
617 UseWhileBorrowed(loan_path, loan_span) => {
620 &format!("cannot use `{}` because it was mutably borrowed",
621 &self.bccx.loan_path_to_string(copy_path))
625 &format!("borrow of `{}` occurs here",
626 &self.bccx.loan_path_to_string(&*loan_path))
632 fn check_for_move_of_borrowed_path(&self,
635 move_path: &LoanPath<'tcx>,
636 move_kind: move_data::MoveKind) {
637 // We want to detect if there are any loans at all, so we search for
638 // any loans incompatible with MutBorrrow, since all other kinds of
639 // loans are incompatible with that.
640 match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) {
642 UseWhileBorrowed(loan_path, loan_span) => {
643 let err_message = match move_kind {
644 move_data::Captured =>
645 format!("cannot move `{}` into closure because it is borrowed",
646 &self.bccx.loan_path_to_string(move_path)),
647 move_data::Declared |
648 move_data::MoveExpr |
649 move_data::MovePat =>
650 format!("cannot move out of `{}` because it is borrowed",
651 &self.bccx.loan_path_to_string(move_path))
654 self.bccx.span_err(span, &err_message[..]);
657 &format!("borrow of `{}` occurs here",
658 &self.bccx.loan_path_to_string(&*loan_path))
664 pub fn analyze_restrictions_on_use(&self,
665 expr_id: ast::NodeId,
666 use_path: &LoanPath<'tcx>,
667 borrow_kind: ty::BorrowKind)
669 debug!("analyze_restrictions_on_use(expr_id={}, use_path={:?})",
670 self.tcx().map.node_to_string(expr_id),
675 self.each_in_scope_loan_affecting_path(
676 region::CodeExtent::from_node_id(expr_id), use_path, |loan| {
677 if !compatible_borrow_kinds(loan.kind, borrow_kind) {
678 ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span);
688 /// Reports an error if `expr` (which should be a path)
689 /// is using a moved/uninitialized value
690 fn check_if_path_is_moved(&self,
693 use_kind: MovedValueUseKind,
694 lp: &Rc<LoanPath<'tcx>>) {
695 debug!("check_if_path_is_moved(id={}, use_kind={:?}, lp={:?})",
698 // FIXME (22079): if you find yourself tempted to cut and paste
699 // the body below and then specializing the error reporting,
700 // consider refactoring this instead!
702 let base_lp = owned_ptr_base_path_rc(lp);
703 self.move_data.each_move_of(id, &base_lp, |the_move, moved_lp| {
704 self.bccx.report_use_of_moved_value(
715 /// Reports an error if assigning to `lp` will use a
716 /// moved/uninitialized value. Mainly this is concerned with
717 /// detecting derefs of uninitialized pointers.
723 /// a = 10; // ok, even though a is uninitialized
725 /// struct Point { x: usize, y: usize }
727 /// p.x = 22; // ok, even though `p` is uninitialized
729 /// let p: Box<Point>;
730 /// (*p).x = 22; // not ok, p is uninitialized, can't deref
732 fn check_if_assigned_path_is_moved(&self,
735 use_kind: MovedValueUseKind,
736 lp: &Rc<LoanPath<'tcx>>)
739 LpVar(_) | LpUpvar(_) => {
740 // assigning to `x` does not require that `x` is initialized
742 LpDowncast(ref lp_base, _) => {
743 // assigning to `(P->Variant).f` is ok if assigning to `P` is ok
744 self.check_if_assigned_path_is_moved(id, span,
747 LpExtend(ref lp_base, _, LpInterior(InteriorField(_))) => {
748 match lp_base.to_type().sty {
749 ty::TyStruct(def_id, _) | ty::TyEnum(def_id, _) => {
750 if self.tcx().has_dtor(def_id) {
751 // In the case where the owner implements drop, then
752 // the path must be initialized to prevent a case of
753 // partial reinitialization
755 // FIXME (22079): could refactor via hypothetical
756 // generalized check_if_path_is_moved
757 let loan_path = owned_ptr_base_path_rc(lp_base);
758 self.move_data.each_move_of(id, &loan_path, |_, _| {
760 .report_partial_reinitialization_of_uninitialized_structure(
771 // assigning to `P.f` is ok if assigning to `P` is ok
772 self.check_if_assigned_path_is_moved(id, span,
775 LpExtend(ref lp_base, _, LpInterior(InteriorElement(..))) |
776 LpExtend(ref lp_base, _, LpDeref(_)) => {
777 // assigning to `P[i]` requires `P` is initialized
778 // assigning to `(*P)` requires `P` is initialized
779 self.check_if_path_is_moved(id, span, use_kind, lp_base);
784 fn check_assignment(&self,
785 assignment_id: ast::NodeId,
786 assignment_span: Span,
787 assignee_cmt: mc::cmt<'tcx>) {
788 debug!("check_assignment(assignee_cmt={:?})", assignee_cmt);
790 // Check that we don't invalidate any outstanding loans
791 if let Some(loan_path) = opt_loan_path(&assignee_cmt) {
792 let scope = region::CodeExtent::from_node_id(assignment_id);
793 self.each_in_scope_loan_affecting_path(scope, &*loan_path, |loan| {
794 self.report_illegal_mutation(assignment_span, &*loan_path, loan);
799 // Check for reassignments to (immutable) local variables. This
800 // needs to be done here instead of in check_loans because we
801 // depend on move data.
802 if let mc::cat_local(local_id) = assignee_cmt.cat {
803 let lp = opt_loan_path(&assignee_cmt).unwrap();
804 self.move_data.each_assignment_of(assignment_id, &lp, |assign| {
805 if assignee_cmt.mutbl.is_mutable() {
806 self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
808 self.bccx.report_reassigned_immutable_variable(
819 pub fn report_illegal_mutation(&self,
821 loan_path: &LoanPath<'tcx>,
825 &format!("cannot assign to `{}` because it is borrowed",
826 self.bccx.loan_path_to_string(loan_path)));
829 &format!("borrow of `{}` occurs here",
830 self.bccx.loan_path_to_string(loan_path)));