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::infer;
23 use rustc::middle::mem_categorization as mc;
24 use rustc::middle::mem_categorization::Categorization;
25 use rustc::middle::region;
26 use rustc::middle::ty;
29 use syntax::codemap::Span;
30 use syntax::ast::NodeId;
32 use rustc_front::hir::Expr;
33 use rustc_front::intravisit;
34 use rustc_front::intravisit::Visitor;
36 use self::restrictions::RestrictionResult;
43 pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
48 move_data::MoveData<'tcx>) {
49 let mut glcx = GatherLoanCtxt {
51 all_loans: Vec::new(),
52 item_ub: bccx.tcx.region_maps.node_extent(body.id),
53 move_data: MoveData::new(),
54 move_error_collector: move_error::MoveErrorCollector::new(),
57 let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
58 let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env), false);
60 let mut euv = euv::ExprUseVisitor::new(&mut glcx, &infcx);
61 euv.walk_fn(decl, body);
64 glcx.report_potential_errors();
65 let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
66 (all_loans, move_data)
69 struct GatherLoanCtxt<'a, 'tcx: 'a> {
70 bccx: &'a BorrowckCtxt<'a, 'tcx>,
71 move_data: move_data::MoveData<'tcx>,
72 move_error_collector: move_error::MoveErrorCollector<'tcx>,
73 all_loans: Vec<Loan<'tcx>>,
74 /// `item_ub` is used as an upper-bound on the lifetime whenever we
75 /// ask for the scope of an expression categorized as an upvar.
76 item_ub: region::CodeExtent,
79 impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
81 consume_id: ast::NodeId,
84 mode: euv::ConsumeMode) {
85 debug!("consume(consume_id={}, cmt={:?}, mode={:?})",
86 consume_id, cmt, mode);
89 euv::Move(move_reason) => {
90 gather_moves::gather_move_from_expr(
91 self.bccx, &self.move_data, &mut self.move_error_collector,
92 consume_id, cmt, move_reason);
98 fn matched_pat(&mut self,
99 matched_pat: &hir::Pat,
101 mode: euv::MatchMode) {
102 debug!("matched_pat(matched_pat={:?}, cmt={:?}, mode={:?})",
107 if let Categorization::Downcast(..) = cmt.cat {
108 gather_moves::gather_match_variant(
109 self.bccx, &self.move_data, &mut self.move_error_collector,
110 matched_pat, cmt, mode);
114 fn consume_pat(&mut self,
115 consume_pat: &hir::Pat,
117 mode: euv::ConsumeMode) {
118 debug!("consume_pat(consume_pat={:?}, cmt={:?}, mode={:?})",
124 euv::Copy => { return; }
128 gather_moves::gather_move_from_pat(
129 self.bccx, &self.move_data, &mut self.move_error_collector,
134 borrow_id: ast::NodeId,
137 loan_region: ty::Region,
139 loan_cause: euv::LoanCause)
141 debug!("borrow(borrow_id={}, cmt={:?}, loan_region={:?}, \
142 bk={:?}, loan_cause={:?})",
143 borrow_id, cmt, loan_region,
146 self.guarantee_valid(borrow_id,
155 assignment_id: ast::NodeId,
156 assignment_span: Span,
157 assignee_cmt: mc::cmt<'tcx>,
158 mode: euv::MutateMode)
160 self.guarantee_assignment_valid(assignment_id,
166 fn decl_without_init(&mut self, id: ast::NodeId, span: Span) {
167 gather_moves::gather_decl(self.bccx, &self.move_data, id, span, id);
171 /// Implements the A-* rules in README.md.
172 fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
174 loan_cause: AliasableViolationKind,
176 req_kind: ty::BorrowKind)
179 let aliasability = cmt.freely_aliasable(bccx.tcx);
180 debug!("check_aliasability aliasability={:?} req_kind={:?}",
181 aliasability, req_kind);
183 match (aliasability, req_kind) {
184 (mc::Aliasability::NonAliasable, _) => {
185 /* Uniquely accessible path -- OK for `&` and `&mut` */
188 (mc::Aliasability::FreelyAliasable(mc::AliasableStatic), ty::ImmBorrow) => {
189 // Borrow of an immutable static item.
192 (mc::Aliasability::FreelyAliasable(mc::AliasableStaticMut), _) => {
193 // Even touching a static mut is considered unsafe. We assume the
194 // user knows what they're doing in these cases.
197 (mc::Aliasability::ImmutableUnique(_), ty::MutBorrow) => {
198 bccx.report_aliasability_violation(
201 mc::AliasableReason::UnaliasableImmutable);
204 (mc::Aliasability::FreelyAliasable(alias_cause), ty::UniqueImmBorrow) |
205 (mc::Aliasability::FreelyAliasable(alias_cause), ty::MutBorrow) => {
206 bccx.report_aliasability_violation(
218 /// Implements the M-* rules in README.md.
219 fn check_mutability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
221 cause: AliasableViolationKind,
223 req_kind: ty::BorrowKind)
225 debug!("check_mutability(cause={:?} cmt={:?} req_kind={:?}",
226 cause, cmt, req_kind);
228 ty::UniqueImmBorrow | ty::ImmBorrow => {
230 // I am intentionally leaving this here to help
231 // refactoring if, in the future, we should add new
232 // kinds of mutability.
233 mc::McImmutable | mc::McDeclared | mc::McInherited => {
234 // both imm and mut data can be lent as imm;
235 // for mutable data, this is a freeze
242 // Only mutable data can be lent as mutable.
243 if !cmt.mutbl.is_mutable() {
244 Err(bccx.report(BckError { span: borrow_span,
255 impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
256 pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.bccx.tcx }
258 /// Guarantees that `cmt` is assignable, or reports an error.
259 fn guarantee_assignment_valid(&mut self,
260 assignment_id: ast::NodeId,
261 assignment_span: Span,
263 mode: euv::MutateMode) {
265 let opt_lp = opt_loan_path(&cmt);
266 debug!("guarantee_assignment_valid(assignment_id={}, cmt={:?}) opt_lp={:?}",
267 assignment_id, cmt, opt_lp);
269 if let Categorization::Local(..) = cmt.cat {
270 // Only re-assignments to locals require it to be
271 // mutable - this is checked in check_loans.
273 // Check that we don't allow assignments to non-mutable data.
274 if check_mutability(self.bccx, assignment_span, MutabilityViolation,
275 cmt.clone(), ty::MutBorrow).is_err() {
276 return; // reported an error, no sense in reporting more.
280 // Check that we don't allow assignments to aliasable data
281 if check_aliasability(self.bccx, assignment_span, MutabilityViolation,
282 cmt.clone(), ty::MutBorrow).is_err() {
283 return; // reported an error, no sense in reporting more.
288 if let Categorization::Local(..) = cmt.cat {
289 // Only re-assignments to locals require it to be
290 // mutable - this is checked in check_loans.
292 self.mark_loan_path_as_mutated(&lp);
294 gather_moves::gather_assignment(self.bccx, &self.move_data,
295 assignment_id, assignment_span,
299 // This can occur with e.g. `*foo() = 5`. In such
300 // cases, there is no need to check for conflicts
301 // with moves etc, just ignore.
306 /// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or
307 /// reports an error. This may entail taking out loans, which will be added to the
309 fn guarantee_valid(&mut self,
310 borrow_id: ast::NodeId,
313 req_kind: ty::BorrowKind,
314 loan_region: ty::Region,
315 cause: euv::LoanCause) {
316 debug!("guarantee_valid(borrow_id={}, cmt={:?}, \
317 req_mutbl={:?}, loan_region={:?})",
323 // a loan for the empty region can never be dereferenced, so
325 if loan_region == ty::ReEmpty {
329 // Check that the lifetime of the borrow does not exceed
330 // the lifetime of the data being borrowed.
331 if lifetime::guarantee_lifetime(self.bccx, self.item_ub,
332 borrow_span, cause, cmt.clone(), loan_region,
334 return; // reported an error, no sense in reporting more.
337 // Check that we don't allow mutable borrows of non-mutable data.
338 if check_mutability(self.bccx, borrow_span, BorrowViolation(cause),
339 cmt.clone(), req_kind).is_err() {
340 return; // reported an error, no sense in reporting more.
343 // Check that we don't allow mutable borrows of aliasable data.
344 if check_aliasability(self.bccx, borrow_span, BorrowViolation(cause),
345 cmt.clone(), req_kind).is_err() {
346 return; // reported an error, no sense in reporting more.
349 // Compute the restrictions that are required to enforce the
351 let restr = restrictions::compute_restrictions(
352 self.bccx, borrow_span, cause,
353 cmt.clone(), loan_region);
355 debug!("guarantee_valid(): restrictions={:?}", restr);
357 // Create the loan record (if needed).
358 let loan = match restr {
359 RestrictionResult::Safe => {
360 // No restrictions---no loan record necessary
364 RestrictionResult::SafeIf(loan_path, restricted_paths) => {
365 let loan_scope = match loan_region {
366 ty::ReScope(scope) => scope,
368 ty::ReFree(ref fr) => fr.scope,
370 ty::ReStatic => self.item_ub,
373 ty::ReLateBound(..) |
374 ty::ReEarlyBound(..) |
376 ty::ReSkolemized(..) => {
377 self.tcx().sess.span_bug(
379 &format!("invalid borrow lifetime: {:?}",
383 debug!("loan_scope = {:?}", loan_scope);
385 let borrow_scope = self.tcx().region_maps.node_extent(borrow_id);
386 let gen_scope = self.compute_gen_scope(borrow_scope, loan_scope);
387 debug!("gen_scope = {:?}", gen_scope);
389 let kill_scope = self.compute_kill_scope(loan_scope, &*loan_path);
390 debug!("kill_scope = {:?}", kill_scope);
392 if req_kind == ty::MutBorrow {
393 self.mark_loan_path_as_mutated(&*loan_path);
397 index: self.all_loans.len(),
398 loan_path: loan_path,
400 gen_scope: gen_scope,
401 kill_scope: kill_scope,
403 restricted_paths: restricted_paths,
409 debug!("guarantee_valid(borrow_id={}), loan={:?}",
412 // let loan_path = loan.loan_path;
413 // let loan_gen_scope = loan.gen_scope;
414 // let loan_kill_scope = loan.kill_scope;
415 self.all_loans.push(loan);
417 // if loan_gen_scope != borrow_id {
418 // FIXME(#6268) Nested method calls
420 // Typically, the scope of the loan includes the point at
421 // which the loan is originated. This
422 // This is a subtle case. See the test case
423 // <compile-fail/borrowck-bad-nested-calls-free.rs>
424 // to see what we are guarding against.
426 //let restr = restrictions::compute_restrictions(
427 // self.bccx, borrow_span, cmt, RESTR_EMPTY);
429 // let all_loans = &mut *self.all_loans; // FIXME(#5074)
431 // index: all_loans.len(),
432 // loan_path: loan_path,
434 // mutbl: ConstMutability,
435 // gen_scope: borrow_id,
436 // kill_scope: kill_scope,
437 // span: borrow_span,
438 // restrictions: restrictions
443 pub fn mark_loan_path_as_mutated(&self, loan_path: &LoanPath) {
444 //! For mutable loans of content whose mutability derives
445 //! from a local variable, mark the mutability decl as necessary.
447 match loan_path.kind {
449 LpUpvar(ty::UpvarId{ var_id: local_id, closure_expr_id: _ }) => {
450 self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
452 LpDowncast(ref base, _) |
453 LpExtend(ref base, mc::McInherited, _) |
454 LpExtend(ref base, mc::McDeclared, _) => {
455 self.mark_loan_path_as_mutated(&**base);
457 LpExtend(_, mc::McImmutable, _) => {
463 pub fn compute_gen_scope(&self,
464 borrow_scope: region::CodeExtent,
465 loan_scope: region::CodeExtent)
466 -> region::CodeExtent {
467 //! Determine when to introduce the loan. Typically the loan
468 //! is introduced at the point of the borrow, but in some cases,
469 //! notably method arguments, the loan may be introduced only
470 //! later, once it comes into scope.
472 if self.bccx.tcx.region_maps.is_subscope_of(borrow_scope, loan_scope) {
479 pub fn compute_kill_scope(&self, loan_scope: region::CodeExtent, lp: &LoanPath<'tcx>)
480 -> region::CodeExtent {
481 //! Determine when the loan restrictions go out of scope.
482 //! This is either when the lifetime expires or when the
483 //! local variable which roots the loan-path goes out of scope,
484 //! whichever happens faster.
486 //! It may seem surprising that we might have a loan region
487 //! larger than the variable which roots the loan-path; this can
488 //! come about when variables of `&mut` type are re-borrowed,
489 //! as in this example:
491 //! struct Foo { counter: u32 }
493 //! fn counter<'a>(v: &'a mut Foo) -> &'a mut u32 {
497 //! In this case, the reference (`'a`) outlives the
498 //! variable `v` that hosts it. Note that this doesn't come up
499 //! with immutable `&` pointers, because borrows of such pointers
500 //! do not require restrictions and hence do not cause a loan.
502 let lexical_scope = lp.kill_scope(self.bccx.tcx);
503 let rm = &self.bccx.tcx.region_maps;
504 if rm.is_subscope_of(lexical_scope, loan_scope) {
507 assert!(self.bccx.tcx.region_maps.is_subscope_of(loan_scope, lexical_scope));
512 pub fn report_potential_errors(&self) {
513 self.move_error_collector.report_potential_errors(self.bccx);
517 /// Context used while gathering loans on static initializers
519 /// This visitor walks static initializer's expressions and makes
520 /// sure the loans being taken are sound.
521 struct StaticInitializerCtxt<'a, 'tcx: 'a> {
522 bccx: &'a BorrowckCtxt<'a, 'tcx>,
525 impl<'a, 'tcx, 'v> Visitor<'v> for StaticInitializerCtxt<'a, 'tcx> {
526 fn visit_expr(&mut self, ex: &Expr) {
527 if let hir::ExprAddrOf(mutbl, ref base) = ex.node {
528 let infcx = infer::new_infer_ctxt(self.bccx.tcx, &self.bccx.tcx.tables, None, false);
529 let mc = mc::MemCategorizationContext::new(&infcx);
530 let base_cmt = mc.cat_expr(&**base).unwrap();
531 let borrow_kind = ty::BorrowKind::from_mutbl(mutbl);
532 // Check that we don't allow borrows of unsafe static items.
533 if check_aliasability(self.bccx, ex.span,
534 BorrowViolation(euv::AddrOf),
535 base_cmt, borrow_kind).is_err() {
536 return; // reported an error, no sense in reporting more.
540 intravisit::walk_expr(self, ex);
544 pub fn gather_loans_in_static_initializer(bccx: &mut BorrowckCtxt, expr: &hir::Expr) {
546 debug!("gather_loans_in_static_initializer(expr={:?})", expr);
548 let mut sicx = StaticInitializerCtxt {
552 sicx.visit_expr(expr);