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::infer::InferCtxt;
22 use rustc::middle::expr_use_visitor as euv;
23 use rustc::middle::mem_categorization as mc;
24 use rustc::middle::mem_categorization::Categorization;
25 use rustc::middle::region;
26 use rustc::ty::{self, TyCtxt};
32 use self::restrictions::RestrictionResult;
39 pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
42 move_data::MoveData<'tcx>) {
43 let infcx = bccx.tcx.borrowck_fake_infer_ctxt(body);
44 let mut glcx = GatherLoanCtxt {
47 all_loans: Vec::new(),
48 item_ub: bccx.tcx.region_maps.node_extent(body.node_id),
49 move_data: MoveData::new(),
50 move_error_collector: move_error::MoveErrorCollector::new(),
53 let body = glcx.bccx.tcx.hir.body(body);
54 euv::ExprUseVisitor::new(&mut glcx, &infcx).consume_body(body);
56 glcx.report_potential_errors();
57 let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
58 (all_loans, move_data)
61 struct GatherLoanCtxt<'a, 'tcx: 'a> {
62 bccx: &'a BorrowckCtxt<'a, 'tcx>,
63 infcx: &'a InferCtxt<'a, 'tcx, 'tcx>,
64 move_data: move_data::MoveData<'tcx>,
65 move_error_collector: move_error::MoveErrorCollector<'tcx>,
66 all_loans: Vec<Loan<'tcx>>,
67 /// `item_ub` is used as an upper-bound on the lifetime whenever we
68 /// ask for the scope of an expression categorized as an upvar.
69 item_ub: region::CodeExtent,
72 impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
74 consume_id: ast::NodeId,
77 mode: euv::ConsumeMode) {
78 debug!("consume(consume_id={}, cmt={:?}, mode={:?})",
79 consume_id, cmt, mode);
82 euv::Move(move_reason) => {
83 gather_moves::gather_move_from_expr(
84 self.bccx, &self.move_data, &mut self.move_error_collector,
85 consume_id, cmt, move_reason);
91 fn matched_pat(&mut self,
92 matched_pat: &hir::Pat,
94 mode: euv::MatchMode) {
95 debug!("matched_pat(matched_pat={:?}, cmt={:?}, mode={:?})",
100 if let Categorization::Downcast(..) = cmt.cat {
101 gather_moves::gather_match_variant(
102 self.bccx, &self.move_data, &mut self.move_error_collector,
103 matched_pat, cmt, mode);
107 fn consume_pat(&mut self,
108 consume_pat: &hir::Pat,
110 mode: euv::ConsumeMode) {
111 debug!("consume_pat(consume_pat={:?}, cmt={:?}, mode={:?})",
117 euv::Copy => { return; }
121 gather_moves::gather_move_from_pat(
122 self.bccx, &self.move_data, &mut self.move_error_collector,
127 borrow_id: ast::NodeId,
130 loan_region: &'tcx ty::Region,
132 loan_cause: euv::LoanCause)
134 debug!("borrow(borrow_id={}, cmt={:?}, loan_region={:?}, \
135 bk={:?}, loan_cause={:?})",
136 borrow_id, cmt, loan_region,
139 self.guarantee_valid(borrow_id,
148 assignment_id: ast::NodeId,
149 assignment_span: Span,
150 assignee_cmt: mc::cmt<'tcx>,
151 mode: euv::MutateMode)
153 self.guarantee_assignment_valid(assignment_id,
159 fn decl_without_init(&mut self, id: ast::NodeId, _span: Span) {
160 let ty = self.infcx.tables.borrow().node_id_to_type(id);
161 gather_moves::gather_decl(self.bccx, &self.move_data, id, ty);
165 /// Implements the A-* rules in README.md.
166 fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
168 loan_cause: AliasableViolationKind,
170 req_kind: ty::BorrowKind)
173 let aliasability = cmt.freely_aliasable();
174 debug!("check_aliasability aliasability={:?} req_kind={:?}",
175 aliasability, req_kind);
177 match (aliasability, req_kind) {
178 (mc::Aliasability::NonAliasable, _) => {
179 /* Uniquely accessible path -- OK for `&` and `&mut` */
182 (mc::Aliasability::FreelyAliasable(mc::AliasableStatic), ty::ImmBorrow) => {
183 // Borrow of an immutable static item.
186 (mc::Aliasability::FreelyAliasable(mc::AliasableStaticMut), _) => {
187 // Even touching a static mut is considered unsafe. We assume the
188 // user knows what they're doing in these cases.
191 (mc::Aliasability::FreelyAliasable(alias_cause), ty::UniqueImmBorrow) |
192 (mc::Aliasability::FreelyAliasable(alias_cause), ty::MutBorrow) => {
193 bccx.report_aliasability_violation(
206 /// Implements the M-* rules in README.md.
207 fn check_mutability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
209 cause: AliasableViolationKind,
211 req_kind: ty::BorrowKind)
213 debug!("check_mutability(cause={:?} cmt={:?} req_kind={:?}",
214 cause, cmt, req_kind);
216 ty::UniqueImmBorrow | ty::ImmBorrow => {
218 // I am intentionally leaving this here to help
219 // refactoring if, in the future, we should add new
220 // kinds of mutability.
221 mc::McImmutable | mc::McDeclared | mc::McInherited => {
222 // both imm and mut data can be lent as imm;
223 // for mutable data, this is a freeze
230 // Only mutable data can be lent as mutable.
231 if !cmt.mutbl.is_mutable() {
232 Err(bccx.report(BckError { span: borrow_span,
243 impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
244 pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { self.bccx.tcx }
246 /// Guarantees that `cmt` is assignable, or reports an error.
247 fn guarantee_assignment_valid(&mut self,
248 assignment_id: ast::NodeId,
249 assignment_span: Span,
251 mode: euv::MutateMode) {
253 let opt_lp = opt_loan_path(&cmt);
254 debug!("guarantee_assignment_valid(assignment_id={}, cmt={:?}) opt_lp={:?}",
255 assignment_id, cmt, opt_lp);
257 if let Categorization::Local(..) = cmt.cat {
258 // Only re-assignments to locals require it to be
259 // mutable - this is checked in check_loans.
261 // Check that we don't allow assignments to non-mutable data.
262 if check_mutability(self.bccx, assignment_span, MutabilityViolation,
263 cmt.clone(), ty::MutBorrow).is_err() {
264 return; // reported an error, no sense in reporting more.
268 // Check that we don't allow assignments to aliasable data
269 if check_aliasability(self.bccx, assignment_span, MutabilityViolation,
270 cmt.clone(), ty::MutBorrow).is_err() {
271 return; // reported an error, no sense in reporting more.
276 if let Categorization::Local(..) = cmt.cat {
277 // Only re-assignments to locals require it to be
278 // mutable - this is checked in check_loans.
280 self.mark_loan_path_as_mutated(&lp);
282 gather_moves::gather_assignment(self.bccx, &self.move_data,
283 assignment_id, assignment_span,
287 // This can occur with e.g. `*foo() = 5`. In such
288 // cases, there is no need to check for conflicts
289 // with moves etc, just ignore.
294 /// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or
295 /// reports an error. This may entail taking out loans, which will be added to the
297 fn guarantee_valid(&mut self,
298 borrow_id: ast::NodeId,
301 req_kind: ty::BorrowKind,
302 loan_region: &'tcx ty::Region,
303 cause: euv::LoanCause) {
304 debug!("guarantee_valid(borrow_id={}, cmt={:?}, \
305 req_mutbl={:?}, loan_region={:?})",
311 // a loan for the empty region can never be dereferenced, so
313 if *loan_region == ty::ReEmpty {
317 // Check that the lifetime of the borrow does not exceed
318 // the lifetime of the data being borrowed.
319 if lifetime::guarantee_lifetime(self.bccx, self.item_ub,
320 borrow_span, cause, cmt.clone(), loan_region,
322 return; // reported an error, no sense in reporting more.
325 // Check that we don't allow mutable borrows of non-mutable data.
326 if check_mutability(self.bccx, borrow_span, BorrowViolation(cause),
327 cmt.clone(), req_kind).is_err() {
328 return; // reported an error, no sense in reporting more.
331 // Check that we don't allow mutable borrows of aliasable data.
332 if check_aliasability(self.bccx, borrow_span, BorrowViolation(cause),
333 cmt.clone(), req_kind).is_err() {
334 return; // reported an error, no sense in reporting more.
337 // Compute the restrictions that are required to enforce the
339 let restr = restrictions::compute_restrictions(
340 self.bccx, borrow_span, cause,
341 cmt.clone(), loan_region);
343 debug!("guarantee_valid(): restrictions={:?}", restr);
345 // Create the loan record (if needed).
346 let loan = match restr {
347 RestrictionResult::Safe => {
348 // No restrictions---no loan record necessary
352 RestrictionResult::SafeIf(loan_path, restricted_paths) => {
353 let loan_scope = match *loan_region {
354 ty::ReScope(scope) => scope,
356 ty::ReFree(ref fr) => fr.scope,
358 ty::ReStatic => self.item_ub,
361 ty::ReLateBound(..) |
362 ty::ReEarlyBound(..) |
364 ty::ReSkolemized(..) |
368 "invalid borrow lifetime: {:?}",
372 debug!("loan_scope = {:?}", loan_scope);
374 let borrow_scope = self.tcx().region_maps.node_extent(borrow_id);
375 let gen_scope = self.compute_gen_scope(borrow_scope, loan_scope);
376 debug!("gen_scope = {:?}", gen_scope);
378 let kill_scope = self.compute_kill_scope(loan_scope, &loan_path);
379 debug!("kill_scope = {:?}", kill_scope);
381 if req_kind == ty::MutBorrow {
382 self.mark_loan_path_as_mutated(&loan_path);
386 index: self.all_loans.len(),
387 loan_path: loan_path,
389 gen_scope: gen_scope,
390 kill_scope: kill_scope,
392 restricted_paths: restricted_paths,
398 debug!("guarantee_valid(borrow_id={}), loan={:?}",
401 // let loan_path = loan.loan_path;
402 // let loan_gen_scope = loan.gen_scope;
403 // let loan_kill_scope = loan.kill_scope;
404 self.all_loans.push(loan);
406 // if loan_gen_scope != borrow_id {
407 // FIXME(#6268) Nested method calls
409 // Typically, the scope of the loan includes the point at
410 // which the loan is originated. This
411 // This is a subtle case. See the test case
412 // <compile-fail/borrowck-bad-nested-calls-free.rs>
413 // to see what we are guarding against.
415 //let restr = restrictions::compute_restrictions(
416 // self.bccx, borrow_span, cmt, RESTR_EMPTY);
418 // let all_loans = &mut *self.all_loans; // FIXME(#5074)
420 // index: all_loans.len(),
421 // loan_path: loan_path,
423 // mutbl: ConstMutability,
424 // gen_scope: borrow_id,
425 // kill_scope: kill_scope,
426 // span: borrow_span,
427 // restrictions: restrictions
432 pub fn mark_loan_path_as_mutated(&self, loan_path: &LoanPath) {
433 //! For mutable loans of content whose mutability derives
434 //! from a local variable, mark the mutability decl as necessary.
436 match loan_path.kind {
438 LpUpvar(ty::UpvarId{ var_id: local_id, closure_expr_id: _ }) => {
439 self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
441 LpDowncast(ref base, _) |
442 LpExtend(ref base, mc::McInherited, _) |
443 LpExtend(ref base, mc::McDeclared, _) => {
444 self.mark_loan_path_as_mutated(&base);
446 LpExtend(_, mc::McImmutable, _) => {
452 pub fn compute_gen_scope(&self,
453 borrow_scope: region::CodeExtent,
454 loan_scope: region::CodeExtent)
455 -> region::CodeExtent {
456 //! Determine when to introduce the loan. Typically the loan
457 //! is introduced at the point of the borrow, but in some cases,
458 //! notably method arguments, the loan may be introduced only
459 //! later, once it comes into scope.
461 if self.bccx.tcx.region_maps.is_subscope_of(borrow_scope, loan_scope) {
468 pub fn compute_kill_scope(&self, loan_scope: region::CodeExtent, lp: &LoanPath<'tcx>)
469 -> region::CodeExtent {
470 //! Determine when the loan restrictions go out of scope.
471 //! This is either when the lifetime expires or when the
472 //! local variable which roots the loan-path goes out of scope,
473 //! whichever happens faster.
475 //! It may seem surprising that we might have a loan region
476 //! larger than the variable which roots the loan-path; this can
477 //! come about when variables of `&mut` type are re-borrowed,
478 //! as in this example:
480 //! struct Foo { counter: u32 }
482 //! fn counter<'a>(v: &'a mut Foo) -> &'a mut u32 {
486 //! In this case, the reference (`'a`) outlives the
487 //! variable `v` that hosts it. Note that this doesn't come up
488 //! with immutable `&` pointers, because borrows of such pointers
489 //! do not require restrictions and hence do not cause a loan.
491 let lexical_scope = lp.kill_scope(self.bccx.tcx);
492 let rm = &self.bccx.tcx.region_maps;
493 if rm.is_subscope_of(lexical_scope, loan_scope) {
496 assert!(self.bccx.tcx.region_maps.is_subscope_of(loan_scope, lexical_scope));
501 pub fn report_potential_errors(&self) {
502 self.move_error_collector.report_potential_errors(self.bccx);