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
20 use self::UseError::*;
23 use borrowck::InteriorKind::{InteriorElement, InteriorField};
24 use rustc::middle::expr_use_visitor as euv;
25 use rustc::middle::expr_use_visitor::MutateMode;
26 use rustc::middle::mem_categorization as mc;
27 use rustc::middle::mem_categorization::Categorization;
28 use rustc::middle::region;
29 use rustc::ty::{self, TyCtxt, RegionKind};
34 use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
38 // FIXME (#16118): These functions are intended to allow the borrow checker to
39 // be less precise in its handling of Box while still allowing moves out of a
40 // Box. They should be removed when Unique is removed from LoanPath.
42 fn owned_ptr_base_path<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> &'a LoanPath<'tcx> {
43 //! Returns the base of the leftmost dereference of an Unique in
44 //! `loan_path`. If there is no dereference of an Unique in `loan_path`,
45 //! then it just returns `loan_path` itself.
47 return match helper(loan_path) {
48 Some(new_loan_path) => new_loan_path,
49 None => loan_path.clone()
52 fn helper<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> Option<&'a LoanPath<'tcx>> {
53 match loan_path.kind {
54 LpVar(_) | LpUpvar(_) => None,
55 LpExtend(ref lp_base, _, LpDeref(mc::Unique)) => {
56 match helper(&lp_base) {
58 None => Some(&lp_base)
61 LpDowncast(ref lp_base, _) |
62 LpExtend(ref lp_base, ..) => helper(&lp_base)
67 fn owned_ptr_base_path_rc<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Rc<LoanPath<'tcx>> {
68 //! The equivalent of `owned_ptr_base_path` for an &Rc<LoanPath> rather than
71 return match helper(loan_path) {
72 Some(new_loan_path) => new_loan_path,
73 None => loan_path.clone()
76 fn helper<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Option<Rc<LoanPath<'tcx>>> {
77 match loan_path.kind {
78 LpVar(_) | LpUpvar(_) => None,
79 LpExtend(ref lp_base, _, LpDeref(mc::Unique)) => {
80 match helper(lp_base) {
82 None => Some(lp_base.clone())
85 LpDowncast(ref lp_base, _) |
86 LpExtend(ref lp_base, ..) => helper(lp_base)
91 struct CheckLoanCtxt<'a, 'tcx: 'a> {
92 bccx: &'a BorrowckCtxt<'a, 'tcx>,
93 dfcx_loans: &'a LoanDataFlow<'a, 'tcx>,
94 move_data: &'a move_data::FlowedMoveData<'a, 'tcx>,
95 all_loans: &'a [Loan<'tcx>],
96 movable_generator: bool,
99 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
100 fn consume(&mut self,
101 consume_id: ast::NodeId,
103 cmt: &mc::cmt_<'tcx>,
104 mode: euv::ConsumeMode) {
105 debug!("consume(consume_id={}, cmt={:?}, mode={:?})",
106 consume_id, cmt, mode);
108 let hir_id = self.tcx().hir.node_to_hir_id(consume_id);
109 self.consume_common(hir_id.local_id, consume_span, cmt, mode);
112 fn matched_pat(&mut self,
113 _matched_pat: &hir::Pat,
115 _mode: euv::MatchMode) { }
117 fn consume_pat(&mut self,
118 consume_pat: &hir::Pat,
119 cmt: &mc::cmt_<'tcx>,
120 mode: euv::ConsumeMode) {
121 debug!("consume_pat(consume_pat={:?}, cmt={:?}, mode={:?})",
126 self.consume_common(consume_pat.hir_id.local_id, consume_pat.span, cmt, mode);
130 borrow_id: ast::NodeId,
132 cmt: &mc::cmt_<'tcx>,
133 loan_region: ty::Region<'tcx>,
135 loan_cause: euv::LoanCause)
137 debug!("borrow(borrow_id={}, cmt={:?}, loan_region={:?}, \
138 bk={:?}, loan_cause={:?})",
139 borrow_id, cmt, loan_region,
142 let hir_id = self.tcx().hir.node_to_hir_id(borrow_id);
143 if let Some(lp) = opt_loan_path(cmt) {
144 let moved_value_use_kind = match loan_cause {
145 euv::ClosureCapture(_) => MovedInCapture,
148 self.check_if_path_is_moved(hir_id.local_id, borrow_span, moved_value_use_kind, &lp);
151 self.check_for_conflicting_loans(hir_id.local_id);
153 self.check_for_loans_across_yields(cmt, loan_region, borrow_span);
157 assignment_id: ast::NodeId,
158 assignment_span: Span,
159 assignee_cmt: &mc::cmt_<'tcx>,
160 mode: euv::MutateMode)
162 debug!("mutate(assignment_id={}, assignee_cmt={:?})",
163 assignment_id, assignee_cmt);
165 if let Some(lp) = opt_loan_path(assignee_cmt) {
167 MutateMode::Init | MutateMode::JustWrite => {
168 // In a case like `path = 1`, then path does not
169 // have to be *FULLY* initialized, but we still
170 // must be careful lest it contains derefs of
172 self.check_if_assigned_path_is_moved(assignee_cmt.hir_id.local_id,
177 MutateMode::WriteAndRead => {
178 // In a case like `path += 1`, then path must be
179 // fully initialized, since we will read it before
181 self.check_if_path_is_moved(assignee_cmt.hir_id.local_id,
188 self.check_assignment(self.tcx().hir.node_to_hir_id(assignment_id).local_id,
189 assignment_span, assignee_cmt);
192 fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) { }
195 pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
196 dfcx_loans: &LoanDataFlow<'b, 'tcx>,
197 move_data: &move_data::FlowedMoveData<'c, 'tcx>,
198 all_loans: &[Loan<'tcx>],
200 debug!("check_loans(body id={})", body.value.id);
202 let def_id = bccx.tcx.hir.body_owner_def_id(body.id());
204 let node_id = bccx.tcx.hir.as_local_node_id(def_id).unwrap();
205 let movable_generator = !match bccx.tcx.hir.get(node_id) {
206 Node::Expr(&hir::Expr {
207 node: hir::ExprKind::Closure(.., Some(hir::GeneratorMovability::Static)),
213 let param_env = bccx.tcx.param_env(def_id);
214 let mut clcx = CheckLoanCtxt {
221 let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id);
222 euv::ExprUseVisitor::new(&mut clcx,
225 &bccx.region_scope_tree,
227 Some(rvalue_promotable_map))
232 enum UseError<'tcx> {
234 UseWhileBorrowed(/*loan*/Rc<LoanPath<'tcx>>, /*loan*/Span)
237 fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind,
238 borrow_kind2: ty::BorrowKind)
240 borrow_kind1 == ty::ImmBorrow && borrow_kind2 == ty::ImmBorrow
243 impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
244 pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { self.bccx.tcx }
246 pub fn each_issued_loan<F>(&self, node: hir::ItemLocalId, mut op: F) -> bool where
247 F: FnMut(&Loan<'tcx>) -> bool,
249 //! Iterates over each loan that has been issued
250 //! on entrance to `node`, regardless of whether it is
251 //! actually *in scope* at that point. Sometimes loans
252 //! are issued for future scopes and thus they may have been
253 //! *issued* but not yet be in effect.
255 self.dfcx_loans.each_bit_on_entry(node, |loan_index| {
256 let loan = &self.all_loans[loan_index];
261 pub fn each_in_scope_loan<F>(&self, scope: region::Scope, mut op: F) -> bool where
262 F: FnMut(&Loan<'tcx>) -> bool,
264 //! Like `each_issued_loan()`, but only considers loans that are
265 //! currently in scope.
267 self.each_issued_loan(scope.item_local_id(), |loan| {
268 if self.bccx.region_scope_tree.is_subscope_of(scope, loan.kill_scope) {
276 fn each_in_scope_loan_affecting_path<F>(&self,
277 scope: region::Scope,
278 loan_path: &LoanPath<'tcx>,
281 F: FnMut(&Loan<'tcx>) -> bool,
283 //! Iterates through all of the in-scope loans affecting `loan_path`,
284 //! calling `op`, and ceasing iteration if `false` is returned.
286 // First, we check for a loan restricting the path P being used. This
287 // accounts for borrows of P but also borrows of subpaths, like P.a.b.
288 // Consider the following example:
290 // let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
291 // let y = a; // Conflicts with restriction
293 let loan_path = owned_ptr_base_path(loan_path);
294 let cont = self.each_in_scope_loan(scope, |loan| {
296 for restr_path in &loan.restricted_paths {
297 if **restr_path == *loan_path {
311 // Next, we must check for *loans* (not restrictions) on the path P or
312 // any base path. This rejects examples like the following:
317 // Limiting this search to *loans* and not *restrictions* means that
318 // examples like the following continue to work:
323 let mut loan_path = loan_path;
325 match loan_path.kind {
326 LpVar(_) | LpUpvar(_) => {
329 LpDowncast(ref lp_base, _) |
330 LpExtend(ref lp_base, ..) => {
331 loan_path = &lp_base;
335 let cont = self.each_in_scope_loan(scope, |loan| {
336 if *loan.loan_path == *loan_path {
351 pub fn loans_generated_by(&self, node: hir::ItemLocalId) -> Vec<usize> {
352 //! Returns a vector of the loans that are generated as
355 let mut result = Vec::new();
356 self.dfcx_loans.each_gen_bit(node, |loan_index| {
357 result.push(loan_index);
363 pub fn check_for_loans_across_yields(&self,
364 cmt: &mc::cmt_<'tcx>,
365 loan_region: ty::Region<'tcx>,
367 pub fn borrow_of_local_data<'tcx>(cmt: &mc::cmt_<'tcx>) -> bool {
369 // Borrows of static items is allowed
370 Categorization::StaticItem => false,
371 // Reborrow of already borrowed data is ignored
372 // Any errors will be caught on the initial borrow
373 Categorization::Deref(..) => false,
375 // By-ref upvars has Derefs so they will get ignored.
376 // Generators counts as FnOnce so this leaves only
377 // by-move upvars, which is local data for generators
378 Categorization::Upvar(..) => true,
380 Categorization::Rvalue(region) => {
381 // Rvalues promoted to 'static are no longer local
382 if let RegionKind::ReStatic = *region {
389 // Borrow of local data must be checked
390 Categorization::Local(..) => true,
392 // For interior references and downcasts, find out if the base is local
393 Categorization::Downcast(ref cmt_base, _) |
394 Categorization::Interior(ref cmt_base, _) => borrow_of_local_data(&cmt_base),
398 if !self.movable_generator {
402 if !borrow_of_local_data(cmt) {
406 let scope = match *loan_region {
407 // A concrete region in which we will look for a yield expression
408 RegionKind::ReScope(scope) => scope,
410 // There cannot be yields inside an empty region
411 RegionKind::ReEmpty => return,
413 // Local data cannot have these lifetimes
414 RegionKind::ReEarlyBound(..) |
415 RegionKind::ReLateBound(..) |
416 RegionKind::ReFree(..) |
417 RegionKind::ReStatic => {
420 .sess.delay_span_bug(borrow_span,
421 &format!("unexpected region for local data {:?}",
426 // These cannot exist in borrowck
427 RegionKind::ReVar(..) |
428 RegionKind::ReCanonical(..) |
429 RegionKind::ReSkolemized(..) |
430 RegionKind::ReClosureBound(..) |
431 RegionKind::ReErased => span_bug!(borrow_span,
432 "unexpected region in borrowck {:?}",
436 let body_id = self.bccx.body.value.hir_id.local_id;
438 if self.bccx.region_scope_tree.containing_body(scope) != Some(body_id) {
439 // We are borrowing local data longer than its storage.
440 // This should result in other borrowck errors.
441 self.bccx.tcx.sess.delay_span_bug(borrow_span,
442 "borrowing local data longer than its storage");
446 if let Some(yield_span) = self.bccx
448 .yield_in_scope_for_expr(scope,
452 self.bccx.cannot_borrow_across_generator_yield(borrow_span,
455 self.bccx.signal_error();
459 pub fn check_for_conflicting_loans(&self, node: hir::ItemLocalId) {
460 //! Checks to see whether any of the loans that are issued
461 //! on entrance to `node` conflict with loans that have already been
462 //! issued when we enter `node` (for example, we do not
463 //! permit two `&mut` borrows of the same variable).
465 //! (Note that some loans can be *issued* without necessarily
466 //! taking effect yet.)
468 debug!("check_for_conflicting_loans(node={:?})", node);
470 let new_loan_indices = self.loans_generated_by(node);
471 debug!("new_loan_indices = {:?}", new_loan_indices);
473 for &new_loan_index in &new_loan_indices {
474 self.each_issued_loan(node, |issued_loan| {
475 let new_loan = &self.all_loans[new_loan_index];
476 // Only report an error for the first issued loan that conflicts
477 // to avoid O(n^2) errors.
478 self.report_error_if_loans_conflict(issued_loan, new_loan)
482 for (i, &x) in new_loan_indices.iter().enumerate() {
483 let old_loan = &self.all_loans[x];
484 for &y in &new_loan_indices[(i+1) ..] {
485 let new_loan = &self.all_loans[y];
486 self.report_error_if_loans_conflict(old_loan, new_loan);
491 pub fn report_error_if_loans_conflict(&self,
492 old_loan: &Loan<'tcx>,
493 new_loan: &Loan<'tcx>)
495 //! Checks whether `old_loan` and `new_loan` can safely be issued
498 debug!("report_error_if_loans_conflict(old_loan={:?}, new_loan={:?})",
502 // Should only be called for loans that are in scope at the same time.
503 assert!(self.bccx.region_scope_tree.scopes_intersect(old_loan.kill_scope,
504 new_loan.kill_scope));
506 let err_old_new = self.report_error_if_loan_conflicts_with_restriction(
507 old_loan, new_loan, old_loan, new_loan).err();
508 let err_new_old = self.report_error_if_loan_conflicts_with_restriction(
509 new_loan, old_loan, old_loan, new_loan).err();
511 match (err_old_new, err_new_old) {
512 (Some(mut err), None) | (None, Some(mut err)) => {
514 self.bccx.signal_error();
516 (Some(mut err_old), Some(mut err_new)) => {
518 self.bccx.signal_error();
521 (None, None) => return true,
527 pub fn report_error_if_loan_conflicts_with_restriction(&self,
530 old_loan: &Loan<'tcx>,
531 new_loan: &Loan<'tcx>)
532 -> Result<(), DiagnosticBuilder<'a>> {
533 //! Checks whether the restrictions introduced by `loan1` would
534 //! prohibit `loan2`. Returns false if an error is reported.
536 debug!("report_error_if_loan_conflicts_with_restriction(\
537 loan1={:?}, loan2={:?})",
541 if compatible_borrow_kinds(loan1.kind, loan2.kind) {
545 let loan2_base_path = owned_ptr_base_path_rc(&loan2.loan_path);
546 for restr_path in &loan1.restricted_paths {
547 if *restr_path != loan2_base_path { continue; }
549 // If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
550 // normally generate a rather confusing message (in this case, for multiple mutable
553 // error: cannot borrow `x.b` as mutable more than once at a time
554 // note: previous borrow of `x.a` occurs here; the mutable borrow prevents
555 // subsequent moves, borrows, or modification of `x.a` until the borrow ends
557 // What we want to do instead is get the 'common ancestor' of the two borrow paths and
558 // use that for most of the message instead, giving is something like this:
560 // error: cannot borrow `x` as mutable more than once at a time
561 // note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
562 // borrow prevents subsequent moves, borrows, or modification of `x` until the
565 let common = new_loan.loan_path.common(&old_loan.loan_path);
566 let (nl, ol, new_loan_msg, old_loan_msg) = {
567 if new_loan.loan_path.has_fork(&old_loan.loan_path) && common.is_some() {
568 let nl = self.bccx.loan_path_to_string(&common.unwrap());
570 let new_loan_msg = format!(" (via `{}`)",
571 self.bccx.loan_path_to_string(
572 &new_loan.loan_path));
573 let old_loan_msg = format!(" (via `{}`)",
574 self.bccx.loan_path_to_string(
575 &old_loan.loan_path));
576 (nl, ol, new_loan_msg, old_loan_msg)
578 (self.bccx.loan_path_to_string(&new_loan.loan_path),
579 self.bccx.loan_path_to_string(&old_loan.loan_path),
585 let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
591 // We want to assemble all the relevant locations for the error.
593 // 1. Where did the new loan occur.
594 // - if due to closure creation, where was the variable used in closure?
595 // 2. Where did old loan occur.
596 // 3. Where does old loan expire.
598 let previous_end_span =
599 Some(self.tcx().sess.source_map().end_point(
600 old_loan.kill_scope.span(self.tcx(), &self.bccx.region_scope_tree)));
602 let mut err = match (new_loan.kind, old_loan.kind) {
603 (ty::MutBorrow, ty::MutBorrow) =>
604 self.bccx.cannot_mutably_borrow_multiply(
605 new_loan.span, &nl, &new_loan_msg, old_loan.span, &old_loan_msg,
606 previous_end_span, Origin::Ast),
607 (ty::UniqueImmBorrow, ty::UniqueImmBorrow) =>
608 self.bccx.cannot_uniquely_borrow_by_two_closures(
609 new_loan.span, &nl, old_loan.span, previous_end_span, Origin::Ast),
610 (ty::UniqueImmBorrow, _) =>
611 self.bccx.cannot_uniquely_borrow_by_one_closure(
612 new_loan.span, &nl, &new_loan_msg,
613 old_loan.span, &ol_pronoun, &old_loan_msg, previous_end_span, Origin::Ast),
614 (_, ty::UniqueImmBorrow) => {
615 let new_loan_str = &new_loan.kind.to_user_str();
616 self.bccx.cannot_reborrow_already_uniquely_borrowed(
617 new_loan.span, &nl, &new_loan_msg, new_loan_str,
618 old_loan.span, &old_loan_msg, previous_end_span, Origin::Ast)
621 self.bccx.cannot_reborrow_already_borrowed(
623 &nl, &new_loan_msg, &new_loan.kind.to_user_str(),
624 old_loan.span, &ol_pronoun, &old_loan.kind.to_user_str(), &old_loan_msg,
625 previous_end_span, Origin::Ast)
628 match new_loan.cause {
629 euv::ClosureCapture(span) => {
632 format!("borrow occurs due to use of `{}` in closure", nl));
637 match old_loan.cause {
638 euv::ClosureCapture(span) => {
641 format!("previous borrow occurs due to use of `{}` in closure",
653 fn consume_common(&self,
654 id: hir::ItemLocalId,
656 cmt: &mc::cmt_<'tcx>,
657 mode: euv::ConsumeMode) {
658 if let Some(lp) = opt_loan_path(cmt) {
659 let moved_value_use_kind = match mode {
661 self.check_for_copy_of_frozen_path(id, span, &lp);
665 match self.move_data.kind_of_move_of_path(id, &lp) {
667 // Sometimes moves don't have a move kind;
668 // this either means that the original move
669 // was from something illegal to move,
670 // or was moved from referent of an unsafe
671 // pointer or something like that.
675 self.check_for_move_of_borrowed_path(id, span,
677 if move_kind == move_data::Captured {
687 self.check_if_path_is_moved(id, span, moved_value_use_kind, &lp);
691 fn check_for_copy_of_frozen_path(&self,
692 id: hir::ItemLocalId,
694 copy_path: &LoanPath<'tcx>) {
695 match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
697 UseWhileBorrowed(loan_path, loan_span) => {
698 let desc = self.bccx.loan_path_to_string(copy_path);
699 self.bccx.cannot_use_when_mutably_borrowed(
701 loan_span, &self.bccx.loan_path_to_string(&loan_path),
704 self.bccx.signal_error();
709 fn check_for_move_of_borrowed_path(&self,
710 id: hir::ItemLocalId,
712 move_path: &LoanPath<'tcx>,
713 move_kind: move_data::MoveKind) {
714 // We want to detect if there are any loans at all, so we search for
715 // any loans incompatible with MutBorrrow, since all other kinds of
716 // loans are incompatible with that.
717 match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) {
719 UseWhileBorrowed(loan_path, loan_span) => {
720 let mut err = match move_kind {
721 move_data::Captured => {
722 let mut err = self.bccx.cannot_move_into_closure(
723 span, &self.bccx.loan_path_to_string(move_path), Origin::Ast);
726 format!("borrow of `{}` occurs here",
727 &self.bccx.loan_path_to_string(&loan_path))
731 "move into closure occurs here"
735 move_data::Declared |
736 move_data::MoveExpr |
737 move_data::MovePat => {
738 let desc = self.bccx.loan_path_to_string(move_path);
739 let mut err = self.bccx.cannot_move_when_borrowed(span, &desc, Origin::Ast);
742 format!("borrow of `{}` occurs here",
743 &self.bccx.loan_path_to_string(&loan_path))
747 format!("move out of `{}` occurs here",
748 &self.bccx.loan_path_to_string(move_path))
755 self.bccx.signal_error();
760 pub fn analyze_restrictions_on_use(&self,
761 expr_id: hir::ItemLocalId,
762 use_path: &LoanPath<'tcx>,
763 borrow_kind: ty::BorrowKind)
765 debug!("analyze_restrictions_on_use(expr_id={:?}, use_path={:?})",
770 let scope = region::Scope {
772 data: region::ScopeData::Node
774 self.each_in_scope_loan_affecting_path(
775 scope, use_path, |loan| {
776 if !compatible_borrow_kinds(loan.kind, borrow_kind) {
777 ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span);
787 /// Reports an error if `expr` (which should be a path)
788 /// is using a moved/uninitialized value
789 fn check_if_path_is_moved(&self,
790 id: hir::ItemLocalId,
792 use_kind: MovedValueUseKind,
793 lp: &Rc<LoanPath<'tcx>>) {
794 debug!("check_if_path_is_moved(id={:?}, use_kind={:?}, lp={:?})",
797 // FIXME: if you find yourself tempted to cut and paste
798 // the body below and then specializing the error reporting,
799 // consider refactoring this instead!
801 let base_lp = owned_ptr_base_path_rc(lp);
802 self.move_data.each_move_of(id, &base_lp, |the_move, moved_lp| {
803 self.bccx.report_use_of_moved_value(
813 /// Reports an error if assigning to `lp` will use a
814 /// moved/uninitialized value. Mainly this is concerned with
815 /// detecting derefs of uninitialized pointers.
821 /// a = 10; // ok, even though a is uninitialized
825 /// struct Point { x: u32, y: u32 }
826 /// let mut p: Point;
827 /// p.x = 22; // ok, even though `p` is uninitialized
830 /// ```compile_fail,E0381
831 /// # struct Point { x: u32, y: u32 }
832 /// let mut p: Box<Point>;
833 /// (*p).x = 22; // not ok, p is uninitialized, can't deref
835 fn check_if_assigned_path_is_moved(&self,
836 id: hir::ItemLocalId,
838 use_kind: MovedValueUseKind,
839 lp: &Rc<LoanPath<'tcx>>)
842 LpVar(_) | LpUpvar(_) => {
843 // assigning to `x` does not require that `x` is initialized
845 LpDowncast(ref lp_base, _) => {
846 // assigning to `(P->Variant).f` is ok if assigning to `P` is ok
847 self.check_if_assigned_path_is_moved(id, span,
850 LpExtend(ref lp_base, _, LpInterior(_, InteriorField(_))) => {
851 match lp_base.to_type().sty {
852 ty::Adt(def, _) if def.has_dtor(self.tcx()) => {
853 // In the case where the owner implements drop, then
854 // the path must be initialized to prevent a case of
855 // partial reinitialization
857 // FIXME: could refactor via hypothetical
858 // generalized check_if_path_is_moved
859 let loan_path = owned_ptr_base_path_rc(lp_base);
860 self.move_data.each_move_of(id, &loan_path, |_, _| {
862 .report_partial_reinitialization_of_uninitialized_structure(
872 // assigning to `P.f` is ok if assigning to `P` is ok
873 self.check_if_assigned_path_is_moved(id, span,
876 LpExtend(ref lp_base, _, LpInterior(_, InteriorElement)) |
877 LpExtend(ref lp_base, _, LpDeref(_)) => {
878 // assigning to `P[i]` requires `P` is initialized
879 // assigning to `(*P)` requires `P` is initialized
880 self.check_if_path_is_moved(id, span, use_kind, lp_base);
885 fn check_assignment(&self,
886 assignment_id: hir::ItemLocalId,
887 assignment_span: Span,
888 assignee_cmt: &mc::cmt_<'tcx>) {
889 debug!("check_assignment(assignee_cmt={:?})", assignee_cmt);
891 // Check that we don't invalidate any outstanding loans
892 if let Some(loan_path) = opt_loan_path(assignee_cmt) {
893 let scope = region::Scope {
895 data: region::ScopeData::Node
897 self.each_in_scope_loan_affecting_path(scope, &loan_path, |loan| {
898 self.report_illegal_mutation(assignment_span, &loan_path, loan);
903 // Check for reassignments to (immutable) local variables. This
904 // needs to be done here instead of in check_loans because we
905 // depend on move data.
906 if let Categorization::Local(local_id) = assignee_cmt.cat {
907 let lp = opt_loan_path(assignee_cmt).unwrap();
908 self.move_data.each_assignment_of(assignment_id, &lp, |assign| {
909 if assignee_cmt.mutbl.is_mutable() {
910 let hir_id = self.bccx.tcx.hir.node_to_hir_id(local_id);
911 self.bccx.used_mut_nodes.borrow_mut().insert(hir_id);
913 self.bccx.report_reassigned_immutable_variable(
924 pub fn report_illegal_mutation(&self,
926 loan_path: &LoanPath<'tcx>,
928 self.bccx.cannot_assign_to_borrowed(
929 span, loan.span, &self.bccx.loan_path_to_string(loan_path), Origin::Ast)
931 self.bccx.signal_error();