1 // Copyright 2012-2014 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 // The borrow check proceeds in two phases. In phase one, we gather the full
15 // set of loans that are required at any point. These are sorted according to
16 // their associated scopes. In phase two, checking loans, we will then make
17 // sure that all of these loans are honored.
20 use borrowck::move_data::MoveData;
21 use rustc::middle::expr_use_visitor as euv;
22 use rustc::middle::mem_categorization as mc;
23 use rustc::middle::mem_categorization::Categorization;
24 use rustc::middle::region;
25 use rustc::ty::{self, TyCtxt};
31 use self::restrictions::RestrictionResult;
38 pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
40 -> (Vec<Loan<'tcx>>, move_data::MoveData<'tcx>) {
41 let def_id = bccx.tcx.hir.body_owner_def_id(body);
42 let param_env = bccx.tcx.param_env(def_id);
43 let mut glcx = GatherLoanCtxt {
45 all_loans: Vec::new(),
46 item_ub: region::Scope::Node(bccx.tcx.hir.body(body).value.hir_id.local_id),
47 move_data: MoveData::default(),
48 move_error_collector: move_error::MoveErrorCollector::new(),
51 let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id);
52 euv::ExprUseVisitor::new(&mut glcx,
55 &bccx.region_scope_tree,
57 Some(rvalue_promotable_map))
58 .consume_body(bccx.body);
60 glcx.report_potential_errors();
61 let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
62 (all_loans, move_data)
65 struct GatherLoanCtxt<'a, 'tcx: 'a> {
66 bccx: &'a BorrowckCtxt<'a, 'tcx>,
67 move_data: move_data::MoveData<'tcx>,
68 move_error_collector: move_error::MoveErrorCollector<'tcx>,
69 all_loans: Vec<Loan<'tcx>>,
70 /// `item_ub` is used as an upper-bound on the lifetime whenever we
71 /// ask for the scope of an expression categorized as an upvar.
72 item_ub: region::Scope,
75 impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
77 consume_id: ast::NodeId,
80 mode: euv::ConsumeMode) {
81 debug!("consume(consume_id={}, cmt={:?}, mode={:?})",
82 consume_id, cmt, mode);
85 euv::Move(move_reason) => {
86 gather_moves::gather_move_from_expr(
87 self.bccx, &self.move_data, &mut self.move_error_collector,
88 self.bccx.tcx.hir.node_to_hir_id(consume_id).local_id, cmt, move_reason);
94 fn matched_pat(&mut self,
95 matched_pat: &hir::Pat,
97 mode: euv::MatchMode) {
98 debug!("matched_pat(matched_pat={:?}, cmt={:?}, mode={:?})",
104 fn consume_pat(&mut self,
105 consume_pat: &hir::Pat,
106 cmt: &mc::cmt_<'tcx>,
107 mode: euv::ConsumeMode) {
108 debug!("consume_pat(consume_pat={:?}, cmt={:?}, mode={:?})",
114 euv::Copy => { return; }
118 gather_moves::gather_move_from_pat(
119 self.bccx, &self.move_data, &mut self.move_error_collector,
124 borrow_id: ast::NodeId,
126 cmt: &mc::cmt_<'tcx>,
127 loan_region: ty::Region<'tcx>,
129 loan_cause: euv::LoanCause)
131 debug!("borrow(borrow_id={}, cmt={:?}, loan_region={:?}, \
132 bk={:?}, loan_cause={:?})",
133 borrow_id, cmt, loan_region,
135 let hir_id = self.bccx.tcx.hir.node_to_hir_id(borrow_id);
136 self.guarantee_valid(hir_id.local_id,
145 assignment_id: ast::NodeId,
146 assignment_span: Span,
147 assignee_cmt: &mc::cmt_<'tcx>,
150 self.guarantee_assignment_valid(assignment_id,
155 fn decl_without_init(&mut self, id: ast::NodeId, _span: Span) {
158 .node_id_to_type(self.bccx.tcx.hir.node_to_hir_id(id));
159 gather_moves::gather_decl(self.bccx, &self.move_data, id, ty);
163 /// Implements the A-* rules in README.md.
164 fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
166 loan_cause: AliasableViolationKind,
167 cmt: &mc::cmt_<'tcx>,
168 req_kind: ty::BorrowKind)
171 let aliasability = cmt.freely_aliasable();
172 debug!("check_aliasability aliasability={:?} req_kind={:?}",
173 aliasability, req_kind);
175 match (aliasability, req_kind) {
176 (mc::Aliasability::NonAliasable, _) => {
177 /* Uniquely accessible path -- OK for `&` and `&mut` */
180 (mc::Aliasability::FreelyAliasable(mc::AliasableStatic), ty::ImmBorrow) => {
181 // Borrow of an immutable static item.
184 (mc::Aliasability::FreelyAliasable(mc::AliasableStaticMut), _) => {
185 // Even touching a static mut is considered unsafe. We assume the
186 // user knows what they're doing in these cases.
189 (mc::Aliasability::FreelyAliasable(alias_cause), ty::UniqueImmBorrow) |
190 (mc::Aliasability::FreelyAliasable(alias_cause), ty::MutBorrow) => {
191 bccx.report_aliasability_violation(
204 /// Implements the M-* rules in README.md.
205 fn check_mutability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
207 cause: AliasableViolationKind,
208 cmt: &mc::cmt_<'tcx>,
209 req_kind: ty::BorrowKind)
211 debug!("check_mutability(cause={:?} cmt={:?} req_kind={:?}",
212 cause, cmt, req_kind);
214 ty::UniqueImmBorrow | ty::ImmBorrow => {
216 // I am intentionally leaving this here to help
217 // refactoring if, in the future, we should add new
218 // kinds of mutability.
219 mc::McImmutable | mc::McDeclared | mc::McInherited => {
220 // both imm and mut data can be lent as imm;
221 // for mutable data, this is a freeze
228 // Only mutable data can be lent as mutable.
229 if !cmt.mutbl.is_mutable() {
230 Err(bccx.report(BckError { span: borrow_span,
241 impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
242 pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { self.bccx.tcx }
244 /// Guarantees that `cmt` is assignable, or reports an error.
245 fn guarantee_assignment_valid(&mut self,
246 assignment_id: ast::NodeId,
247 assignment_span: Span,
248 cmt: &mc::cmt_<'tcx>) {
250 let opt_lp = opt_loan_path(cmt);
251 debug!("guarantee_assignment_valid(assignment_id={}, cmt={:?}) opt_lp={:?}",
252 assignment_id, cmt, opt_lp);
254 if let Categorization::Local(..) = cmt.cat {
255 // Only re-assignments to locals require it to be
256 // mutable - this is checked in check_loans.
258 // Check that we don't allow assignments to non-mutable data.
259 if check_mutability(self.bccx, assignment_span, MutabilityViolation,
260 cmt, ty::MutBorrow).is_err() {
261 return; // reported an error, no sense in reporting more.
265 // Check that we don't allow assignments to aliasable data
266 if check_aliasability(self.bccx, assignment_span, MutabilityViolation,
267 cmt, ty::MutBorrow).is_err() {
268 return; // reported an error, no sense in reporting more.
273 if let Categorization::Local(..) = cmt.cat {
274 // Only re-assignments to locals require it to be
275 // mutable - this is checked in check_loans.
277 self.mark_loan_path_as_mutated(&lp);
279 gather_moves::gather_assignment(self.bccx, &self.move_data,
280 self.bccx.tcx.hir.node_to_hir_id(assignment_id)
286 // This can occur with e.g. `*foo() = 5`. In such
287 // cases, there is no need to check for conflicts
288 // with moves etc, just ignore.
293 /// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or
294 /// reports an error. This may entail taking out loans, which will be added to the
296 fn guarantee_valid(&mut self,
297 borrow_id: hir::ItemLocalId,
299 cmt: &mc::cmt_<'tcx>,
300 req_kind: ty::BorrowKind,
301 loan_region: ty::Region<'tcx>,
302 cause: euv::LoanCause) {
303 debug!("guarantee_valid(borrow_id={:?}, cmt={:?}, \
304 req_mutbl={:?}, loan_region={:?})",
310 // a loan for the empty region can never be dereferenced, so
312 if *loan_region == ty::ReEmpty {
316 // Check that the lifetime of the borrow does not exceed
317 // the lifetime of the data being borrowed.
318 if lifetime::guarantee_lifetime(self.bccx, self.item_ub,
319 borrow_span, cause, cmt, loan_region).is_err() {
320 return; // reported an error, no sense in reporting more.
323 // Check that we don't allow mutable borrows of non-mutable data.
324 if check_mutability(self.bccx, borrow_span, BorrowViolation(cause),
325 cmt, req_kind).is_err() {
326 return; // reported an error, no sense in reporting more.
329 // Check that we don't allow mutable borrows of aliasable data.
330 if check_aliasability(self.bccx, borrow_span, BorrowViolation(cause),
331 cmt, req_kind).is_err() {
332 return; // reported an error, no sense in reporting more.
335 // Compute the restrictions that are required to enforce the
337 let restr = restrictions::compute_restrictions(
338 self.bccx, borrow_span, cause, &cmt, loan_region);
340 debug!("guarantee_valid(): restrictions={:?}", restr);
342 // Create the loan record (if needed).
343 let loan = match restr {
344 RestrictionResult::Safe => {
345 // No restrictions---no loan record necessary
349 RestrictionResult::SafeIf(loan_path, restricted_paths) => {
350 let loan_scope = match *loan_region {
351 ty::ReScope(scope) => scope,
353 ty::ReEarlyBound(ref br) => {
354 self.bccx.region_scope_tree.early_free_scope(self.tcx(), br)
357 ty::ReFree(ref fr) => {
358 self.bccx.region_scope_tree.free_scope(self.tcx(), fr)
361 ty::ReStatic => self.item_ub,
365 ty::ReClosureBound(..) |
366 ty::ReLateBound(..) |
368 ty::ReSkolemized(..) |
372 "invalid borrow lifetime: {:?}",
376 debug!("loan_scope = {:?}", loan_scope);
378 let borrow_scope = region::Scope::Node(borrow_id);
379 let gen_scope = self.compute_gen_scope(borrow_scope, loan_scope);
380 debug!("gen_scope = {:?}", gen_scope);
382 let kill_scope = self.compute_kill_scope(loan_scope, &loan_path);
383 debug!("kill_scope = {:?}", kill_scope);
385 if req_kind == ty::MutBorrow {
386 self.mark_loan_path_as_mutated(&loan_path);
390 index: self.all_loans.len(),
402 debug!("guarantee_valid(borrow_id={:?}), loan={:?}",
405 // let loan_path = loan.loan_path;
406 // let loan_gen_scope = loan.gen_scope;
407 // let loan_kill_scope = loan.kill_scope;
408 self.all_loans.push(loan);
410 // if loan_gen_scope != borrow_id {
411 // FIXME(https://github.com/rust-lang/rfcs/issues/811) Nested method calls
413 // Typically, the scope of the loan includes the point at
414 // which the loan is originated. This
415 // This is a subtle case. See the test case
416 // <compile-fail/borrowck-bad-nested-calls-free.rs>
417 // to see what we are guarding against.
419 //let restr = restrictions::compute_restrictions(
420 // self.bccx, borrow_span, cmt, RESTR_EMPTY);
423 // index: self.all_loans.len(),
426 // mutbl: ConstMutability,
427 // gen_scope: borrow_id,
429 // span: borrow_span,
435 pub fn mark_loan_path_as_mutated(&self, loan_path: &LoanPath) {
436 //! For mutable loans of content whose mutability derives
437 //! from a local variable, mark the mutability decl as necessary.
439 let mut wrapped_path = Some(loan_path);
440 let mut through_borrow = false;
442 while let Some(current_path) = wrapped_path {
443 wrapped_path = match current_path.kind {
446 let hir_id = self.bccx.tcx.hir.node_to_hir_id(local_id);
447 self.bccx.used_mut_nodes.borrow_mut().insert(hir_id);
451 LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => {
452 self.bccx.used_mut_nodes.borrow_mut().insert(var_id);
455 LpExtend(ref base, mc::McInherited, LpDeref(pointer_kind)) |
456 LpExtend(ref base, mc::McDeclared, LpDeref(pointer_kind)) => {
457 if pointer_kind != mc::Unique {
458 through_borrow = true;
462 LpDowncast(ref base, _) |
463 LpExtend(ref base, mc::McInherited, _) |
464 LpExtend(ref base, mc::McDeclared, _) => {
467 LpExtend(_, mc::McImmutable, _) => {
476 pub fn compute_gen_scope(&self,
477 borrow_scope: region::Scope,
478 loan_scope: region::Scope)
480 //! Determine when to introduce the loan. Typically the loan
481 //! is introduced at the point of the borrow, but in some cases,
482 //! notably method arguments, the loan may be introduced only
483 //! later, once it comes into scope.
485 if self.bccx.region_scope_tree.is_subscope_of(borrow_scope, loan_scope) {
492 pub fn compute_kill_scope(&self, loan_scope: region::Scope, lp: &LoanPath<'tcx>)
494 //! Determine when the loan restrictions go out of scope.
495 //! This is either when the lifetime expires or when the
496 //! local variable which roots the loan-path goes out of scope,
497 //! whichever happens faster.
499 //! It may seem surprising that we might have a loan region
500 //! larger than the variable which roots the loan-path; this can
501 //! come about when variables of `&mut` type are re-borrowed,
502 //! as in this example:
504 //! struct Foo { counter: u32 }
506 //! fn counter<'a>(v: &'a mut Foo) -> &'a mut u32 {
510 //! In this case, the reference (`'a`) outlives the
511 //! variable `v` that hosts it. Note that this doesn't come up
512 //! with immutable `&` pointers, because borrows of such pointers
513 //! do not require restrictions and hence do not cause a loan.
515 let lexical_scope = lp.kill_scope(self.bccx);
516 if self.bccx.region_scope_tree.is_subscope_of(lexical_scope, loan_scope) {
519 assert!(self.bccx.region_scope_tree.is_subscope_of(loan_scope, lexical_scope));
524 pub fn report_potential_errors(&self) {
525 self.move_error_collector.report_potential_errors(self.bccx);