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::region;
24 use rustc::middle::ty;
25 use rustc::util::ppaux::{Repr};
27 use syntax::codemap::Span;
29 use syntax::visit::Visitor;
30 use syntax::ast::{Expr, FnDecl, Block, NodeId, Pat};
37 pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
42 move_data::MoveData<'tcx>) {
43 let mut glcx = GatherLoanCtxt {
45 all_loans: Vec::new(),
46 item_ub: region::CodeExtent::from_node_id(body.id),
47 move_data: MoveData::new(),
48 move_error_collector: move_error::MoveErrorCollector::new(),
51 let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
54 let mut euv = euv::ExprUseVisitor::new(&mut glcx, ¶m_env);
55 euv.walk_fn(decl, body);
58 glcx.report_potential_errors();
59 let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
60 (all_loans, move_data)
63 struct GatherLoanCtxt<'a, 'tcx: 'a> {
64 bccx: &'a BorrowckCtxt<'a, 'tcx>,
65 move_data: move_data::MoveData<'tcx>,
66 move_error_collector: move_error::MoveErrorCollector<'tcx>,
67 all_loans: Vec<Loan<'tcx>>,
68 /// `item_ub` is used as an upper-bound on the lifetime whenever we
69 /// ask for the scope of an expression categorized as an upvar.
70 item_ub: region::CodeExtent,
73 impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
75 consume_id: ast::NodeId,
78 mode: euv::ConsumeMode) {
79 debug!("consume(consume_id={}, cmt={}, mode={:?})",
80 consume_id, cmt.repr(self.tcx()), mode);
83 euv::Move(move_reason) => {
84 gather_moves::gather_move_from_expr(
85 self.bccx, &self.move_data, &self.move_error_collector,
86 consume_id, cmt, move_reason);
92 fn matched_pat(&mut self,
93 matched_pat: &ast::Pat,
95 mode: euv::MatchMode) {
96 debug!("matched_pat(matched_pat={}, cmt={}, mode={:?})",
97 matched_pat.repr(self.tcx()),
101 if let mc::cat_downcast(..) = cmt.cat {
102 gather_moves::gather_match_variant(
103 self.bccx, &self.move_data, &self.move_error_collector,
104 matched_pat, cmt, mode);
108 fn consume_pat(&mut self,
109 consume_pat: &ast::Pat,
111 mode: euv::ConsumeMode) {
112 debug!("consume_pat(consume_pat={}, cmt={}, mode={:?})",
113 consume_pat.repr(self.tcx()),
114 cmt.repr(self.tcx()),
118 euv::Copy => { return; }
122 gather_moves::gather_move_from_pat(
123 self.bccx, &self.move_data, &self.move_error_collector,
128 borrow_id: ast::NodeId,
131 loan_region: ty::Region,
133 loan_cause: euv::LoanCause)
135 debug!("borrow(borrow_id={}, cmt={}, loan_region={:?}, \
136 bk={:?}, loan_cause={:?})",
137 borrow_id, cmt.repr(self.tcx()), loan_region,
140 self.guarantee_valid(borrow_id,
149 assignment_id: ast::NodeId,
150 assignment_span: Span,
151 assignee_cmt: mc::cmt<'tcx>,
152 mode: euv::MutateMode)
154 debug!("mutate(assignment_id={}, assignee_cmt={})",
155 assignment_id, assignee_cmt.repr(self.tcx()));
157 match opt_loan_path(&assignee_cmt) {
159 gather_moves::gather_assignment(self.bccx, &self.move_data,
160 assignment_id, assignment_span,
161 lp, assignee_cmt.id, mode);
164 // This can occur with e.g. `*foo() = 5`. In such
165 // cases, there is no need to check for conflicts
166 // with moves etc, just ignore.
171 fn decl_without_init(&mut self, id: ast::NodeId, span: Span) {
172 gather_moves::gather_decl(self.bccx, &self.move_data, id, span, id);
176 /// Implements the A-* rules in doc.rs.
177 fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
179 loan_cause: euv::LoanCause,
181 req_kind: ty::BorrowKind)
184 match (cmt.freely_aliasable(bccx.tcx), req_kind) {
186 /* Uniquely accessible path -- OK for `&` and `&mut` */
189 (Some(mc::AliasableStatic(safety)), ty::ImmBorrow) => {
190 // Borrow of an immutable static item:
192 mc::InteriorUnsafe => {
193 // If the static item contains an Unsafe<T>, it has interior
194 // mutability. In such cases, another phase of the compiler
195 // will ensure that the type is `Sync` and then trans will
196 // not put it in rodata, so this is ok to allow.
199 mc::InteriorSafe => {
200 // Immutable static can be borrowed, no problem.
205 (Some(mc::AliasableStaticMut(..)), _) => {
206 // Even touching a static mut is considered unsafe. We assume the
207 // user knows what they're doing in these cases.
210 (Some(alias_cause), ty::UniqueImmBorrow) |
211 (Some(alias_cause), ty::MutBorrow) => {
212 bccx.report_aliasability_violation(
214 BorrowViolation(loan_cause),
224 impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
225 pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.bccx.tcx }
227 /// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or
228 /// reports an error. This may entail taking out loans, which will be added to the
230 fn guarantee_valid(&mut self,
231 borrow_id: ast::NodeId,
234 req_kind: ty::BorrowKind,
235 loan_region: ty::Region,
236 cause: euv::LoanCause) {
237 debug!("guarantee_valid(borrow_id={}, cmt={}, \
238 req_mutbl={:?}, loan_region={:?})",
240 cmt.repr(self.tcx()),
244 // a loan for the empty region can never be dereferenced, so
246 if loan_region == ty::ReEmpty {
250 // Check that the lifetime of the borrow does not exceed
251 // the lifetime of the data being borrowed.
252 if lifetime::guarantee_lifetime(self.bccx, self.item_ub,
253 borrow_span, cause, cmt.clone(), loan_region,
255 return; // reported an error, no sense in reporting more.
258 // Check that we don't allow mutable borrows of non-mutable data.
259 if check_mutability(self.bccx, borrow_span, cause,
260 cmt.clone(), req_kind).is_err() {
261 return; // reported an error, no sense in reporting more.
264 // Check that we don't allow mutable borrows of aliasable data.
265 if check_aliasability(self.bccx, borrow_span, cause,
266 cmt.clone(), req_kind).is_err() {
267 return; // reported an error, no sense in reporting more.
270 // Compute the restrictions that are required to enforce the
272 let restr = restrictions::compute_restrictions(
273 self.bccx, borrow_span, cause,
274 cmt.clone(), loan_region);
276 debug!("guarantee_valid(): restrictions={:?}", restr);
278 // Create the loan record (if needed).
279 let loan = match restr {
280 restrictions::Safe => {
281 // No restrictions---no loan record necessary
285 restrictions::SafeIf(loan_path, restricted_paths) => {
286 let loan_scope = match loan_region {
287 ty::ReScope(scope) => scope,
289 ty::ReFree(ref fr) => fr.scope,
292 // If we get here, an error must have been
294 // `lifetime::guarantee_lifetime()`, because
295 // the only legal ways to have a borrow with a
296 // static lifetime should not require
297 // restrictions. To avoid reporting derived
298 // errors, we just return here without adding
304 ty::ReLateBound(..) |
305 ty::ReEarlyBound(..) |
307 self.tcx().sess.span_bug(
309 &format!("invalid borrow lifetime: {:?}",
313 debug!("loan_scope = {:?}", loan_scope);
315 let borrow_scope = region::CodeExtent::from_node_id(borrow_id);
316 let gen_scope = self.compute_gen_scope(borrow_scope, loan_scope);
317 debug!("gen_scope = {:?}", gen_scope);
319 let kill_scope = self.compute_kill_scope(loan_scope, &*loan_path);
320 debug!("kill_scope = {:?}", kill_scope);
322 if req_kind == ty::MutBorrow {
323 self.mark_loan_path_as_mutated(&*loan_path);
327 index: self.all_loans.len(),
328 loan_path: loan_path,
330 gen_scope: gen_scope,
331 kill_scope: kill_scope,
333 restricted_paths: restricted_paths,
339 debug!("guarantee_valid(borrow_id={}), loan={}",
340 borrow_id, loan.repr(self.tcx()));
342 // let loan_path = loan.loan_path;
343 // let loan_gen_scope = loan.gen_scope;
344 // let loan_kill_scope = loan.kill_scope;
345 self.all_loans.push(loan);
347 // if loan_gen_scope != borrow_id {
348 // FIXME(#6268) Nested method calls
350 // Typically, the scope of the loan includes the point at
351 // which the loan is originated. This
352 // This is a subtle case. See the test case
353 // <compile-fail/borrowck-bad-nested-calls-free.rs>
354 // to see what we are guarding against.
356 //let restr = restrictions::compute_restrictions(
357 // self.bccx, borrow_span, cmt, RESTR_EMPTY);
359 // let all_loans = &mut *self.all_loans; // FIXME(#5074)
361 // index: all_loans.len(),
362 // loan_path: loan_path,
364 // mutbl: ConstMutability,
365 // gen_scope: borrow_id,
366 // kill_scope: kill_scope,
367 // span: borrow_span,
368 // restrictions: restrictions
372 fn check_mutability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
374 cause: euv::LoanCause,
376 req_kind: ty::BorrowKind)
378 //! Implements the M-* rules in doc.rs.
381 ty::UniqueImmBorrow | ty::ImmBorrow => {
383 // I am intentionally leaving this here to help
384 // refactoring if, in the future, we should add new
385 // kinds of mutability.
386 mc::McImmutable | mc::McDeclared | mc::McInherited => {
387 // both imm and mut data can be lent as imm;
388 // for mutable data, this is a freeze
395 // Only mutable data can be lent as mutable.
396 if !cmt.mutbl.is_mutable() {
397 Err(bccx.report(BckError { span: borrow_span,
409 pub fn mark_loan_path_as_mutated(&self, loan_path: &LoanPath) {
410 //! For mutable loans of content whose mutability derives
411 //! from a local variable, mark the mutability decl as necessary.
413 match loan_path.kind {
415 LpUpvar(ty::UpvarId{ var_id: local_id, closure_expr_id: _ }) => {
416 self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
418 LpDowncast(ref base, _) |
419 LpExtend(ref base, mc::McInherited, _) |
420 LpExtend(ref base, mc::McDeclared, _) => {
421 self.mark_loan_path_as_mutated(&**base);
423 LpExtend(_, mc::McImmutable, _) => {
429 pub fn compute_gen_scope(&self,
430 borrow_scope: region::CodeExtent,
431 loan_scope: region::CodeExtent)
432 -> region::CodeExtent {
433 //! Determine when to introduce the loan. Typically the loan
434 //! is introduced at the point of the borrow, but in some cases,
435 //! notably method arguments, the loan may be introduced only
436 //! later, once it comes into scope.
438 if self.bccx.tcx.region_maps.is_subscope_of(borrow_scope, loan_scope) {
445 pub fn compute_kill_scope(&self, loan_scope: region::CodeExtent, lp: &LoanPath<'tcx>)
446 -> region::CodeExtent {
447 //! Determine when the loan restrictions go out of scope.
448 //! This is either when the lifetime expires or when the
449 //! local variable which roots the loan-path goes out of scope,
450 //! whichever happens faster.
452 //! It may seem surprising that we might have a loan region
453 //! larger than the variable which roots the loan-path; this can
454 //! come about when variables of `&mut` type are re-borrowed,
455 //! as in this example:
457 //! fn counter<'a>(v: &'a mut Foo) -> &'a mut uint {
461 //! In this case, the reference (`'a`) outlives the
462 //! variable `v` that hosts it. Note that this doesn't come up
463 //! with immutable `&` pointers, because borrows of such pointers
464 //! do not require restrictions and hence do not cause a loan.
466 let lexical_scope = lp.kill_scope(self.bccx.tcx);
467 let rm = &self.bccx.tcx.region_maps;
468 if rm.is_subscope_of(lexical_scope, loan_scope) {
471 assert!(self.bccx.tcx.region_maps.is_subscope_of(loan_scope, lexical_scope));
476 pub fn report_potential_errors(&self) {
477 self.move_error_collector.report_potential_errors(self.bccx);
481 /// Context used while gathering loans on static initializers
483 /// This visitor walks static initializer's expressions and makes
484 /// sure the loans being taken are sound.
485 struct StaticInitializerCtxt<'a, 'tcx: 'a> {
486 bccx: &'a BorrowckCtxt<'a, 'tcx>,
489 impl<'a, 'tcx, 'v> Visitor<'v> for StaticInitializerCtxt<'a, 'tcx> {
490 fn visit_expr(&mut self, ex: &Expr) {
491 if let ast::ExprAddrOf(mutbl, ref base) = ex.node {
492 let param_env = ty::empty_parameter_environment(self.bccx.tcx);
493 let mc = mc::MemCategorizationContext::new(¶m_env);
494 let base_cmt = mc.cat_expr(&**base).unwrap();
495 let borrow_kind = ty::BorrowKind::from_mutbl(mutbl);
496 // Check that we don't allow borrows of unsafe static items.
497 if check_aliasability(self.bccx, ex.span, euv::AddrOf,
498 base_cmt, borrow_kind).is_err() {
499 return; // reported an error, no sense in reporting more.
503 visit::walk_expr(self, ex);
507 pub fn gather_loans_in_static_initializer(bccx: &mut BorrowckCtxt, expr: &ast::Expr) {
509 debug!("gather_loans_in_static_initializer(expr={})", expr.repr(bccx.tcx));
511 let mut sicx = StaticInitializerCtxt {
515 sicx.visit_expr(expr);