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
>,
148 mode
: euv
::MutateMode
)
150 self.guarantee_assignment_valid(assignment_id
,
156 fn decl_without_init(&mut self, id
: ast
::NodeId
, _span
: Span
) {
159 .node_id_to_type(self.bccx
.tcx
.hir
.node_to_hir_id(id
));
160 gather_moves
::gather_decl(self.bccx
, &self.move_data
, id
, ty
);
164 /// Implements the A-* rules in README.md.
165 fn check_aliasability
<'a
, 'tcx
>(bccx
: &BorrowckCtxt
<'a
, 'tcx
>,
167 loan_cause
: AliasableViolationKind
,
168 cmt
: &mc
::cmt_
<'tcx
>,
169 req_kind
: ty
::BorrowKind
)
172 let aliasability
= cmt
.freely_aliasable();
173 debug
!("check_aliasability aliasability={:?} req_kind={:?}",
174 aliasability
, req_kind
);
176 match (aliasability
, req_kind
) {
177 (mc
::Aliasability
::NonAliasable
, _
) => {
178 /* Uniquely accessible path -- OK for `&` and `&mut` */
181 (mc
::Aliasability
::FreelyAliasable(mc
::AliasableStatic
), ty
::ImmBorrow
) => {
182 // Borrow of an immutable static item.
185 (mc
::Aliasability
::FreelyAliasable(mc
::AliasableStaticMut
), _
) => {
186 // Even touching a static mut is considered unsafe. We assume the
187 // user knows what they're doing in these cases.
190 (mc
::Aliasability
::FreelyAliasable(alias_cause
), ty
::UniqueImmBorrow
) |
191 (mc
::Aliasability
::FreelyAliasable(alias_cause
), ty
::MutBorrow
) => {
192 bccx
.report_aliasability_violation(
205 /// Implements the M-* rules in README.md.
206 fn check_mutability
<'a
, 'tcx
>(bccx
: &BorrowckCtxt
<'a
, 'tcx
>,
208 cause
: AliasableViolationKind
,
209 cmt
: &mc
::cmt_
<'tcx
>,
210 req_kind
: ty
::BorrowKind
)
212 debug
!("check_mutability(cause={:?} cmt={:?} req_kind={:?}",
213 cause
, cmt
, req_kind
);
215 ty
::UniqueImmBorrow
| ty
::ImmBorrow
=> {
217 // I am intentionally leaving this here to help
218 // refactoring if, in the future, we should add new
219 // kinds of mutability.
220 mc
::McImmutable
| mc
::McDeclared
| mc
::McInherited
=> {
221 // both imm and mut data can be lent as imm;
222 // for mutable data, this is a freeze
229 // Only mutable data can be lent as mutable.
230 if !cmt
.mutbl
.is_mutable() {
231 Err(bccx
.report(BckError
{ span
: borrow_span
,
242 impl<'a
, 'tcx
> GatherLoanCtxt
<'a
, 'tcx
> {
243 pub fn tcx(&self) -> TyCtxt
<'a
, 'tcx
, 'tcx
> { self.bccx.tcx }
245 /// Guarantees that `cmt` is assignable, or reports an error.
246 fn guarantee_assignment_valid(&mut self,
247 assignment_id
: ast
::NodeId
,
248 assignment_span
: Span
,
249 cmt
: &mc
::cmt_
<'tcx
>,
250 mode
: euv
::MutateMode
) {
252 let opt_lp
= opt_loan_path(cmt
);
253 debug
!("guarantee_assignment_valid(assignment_id={}, cmt={:?}) opt_lp={:?}",
254 assignment_id
, cmt
, opt_lp
);
256 if let Categorization
::Local(..) = cmt
.cat
{
257 // Only re-assignments to locals require it to be
258 // mutable - this is checked in check_loans.
260 // Check that we don't allow assignments to non-mutable data.
261 if check_mutability(self.bccx
, assignment_span
, MutabilityViolation
,
262 cmt
, ty
::MutBorrow
).is_err() {
263 return; // reported an error, no sense in reporting more.
267 // Check that we don't allow assignments to aliasable data
268 if check_aliasability(self.bccx
, assignment_span
, MutabilityViolation
,
269 cmt
, ty
::MutBorrow
).is_err() {
270 return; // reported an error, no sense in reporting more.
275 if let Categorization
::Local(..) = cmt
.cat
{
276 // Only re-assignments to locals require it to be
277 // mutable - this is checked in check_loans.
279 self.mark_loan_path_as_mutated(&lp
);
281 gather_moves
::gather_assignment(self.bccx
, &self.move_data
,
282 self.bccx
.tcx
.hir
.node_to_hir_id(assignment_id
)
286 self.bccx
.tcx
.hir
.node_to_hir_id(cmt
.id
).local_id
,
290 // This can occur with e.g. `*foo() = 5`. In such
291 // cases, there is no need to check for conflicts
292 // with moves etc, just ignore.
297 /// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or
298 /// reports an error. This may entail taking out loans, which will be added to the
300 fn guarantee_valid(&mut self,
301 borrow_id
: hir
::ItemLocalId
,
303 cmt
: &mc
::cmt_
<'tcx
>,
304 req_kind
: ty
::BorrowKind
,
305 loan_region
: ty
::Region
<'tcx
>,
306 cause
: euv
::LoanCause
) {
307 debug
!("guarantee_valid(borrow_id={:?}, cmt={:?}, \
308 req_mutbl={:?}, loan_region={:?})",
314 // a loan for the empty region can never be dereferenced, so
316 if *loan_region
== ty
::ReEmpty
{
320 // Check that the lifetime of the borrow does not exceed
321 // the lifetime of the data being borrowed.
322 if lifetime
::guarantee_lifetime(self.bccx
, self.item_ub
,
323 borrow_span
, cause
, cmt
, loan_region
, req_kind
).is_err() {
324 return; // reported an error, no sense in reporting more.
327 // Check that we don't allow mutable borrows of non-mutable data.
328 if check_mutability(self.bccx
, borrow_span
, BorrowViolation(cause
),
329 cmt
, req_kind
).is_err() {
330 return; // reported an error, no sense in reporting more.
333 // Check that we don't allow mutable borrows of aliasable data.
334 if check_aliasability(self.bccx
, borrow_span
, BorrowViolation(cause
),
335 cmt
, req_kind
).is_err() {
336 return; // reported an error, no sense in reporting more.
339 // Compute the restrictions that are required to enforce the
341 let restr
= restrictions
::compute_restrictions(
342 self.bccx
, borrow_span
, cause
, &cmt
, loan_region
);
344 debug
!("guarantee_valid(): restrictions={:?}", restr
);
346 // Create the loan record (if needed).
347 let loan
= match restr
{
348 RestrictionResult
::Safe
=> {
349 // No restrictions---no loan record necessary
353 RestrictionResult
::SafeIf(loan_path
, restricted_paths
) => {
354 let loan_scope
= match *loan_region
{
355 ty
::ReScope(scope
) => scope
,
357 ty
::ReEarlyBound(ref br
) => {
358 self.bccx
.region_scope_tree
.early_free_scope(self.tcx(), br
)
361 ty
::ReFree(ref fr
) => {
362 self.bccx
.region_scope_tree
.free_scope(self.tcx(), fr
)
365 ty
::ReStatic
=> self.item_ub
,
369 ty
::ReClosureBound(..) |
370 ty
::ReLateBound(..) |
372 ty
::ReSkolemized(..) |
376 "invalid borrow lifetime: {:?}",
380 debug
!("loan_scope = {:?}", loan_scope
);
382 let borrow_scope
= region
::Scope
::Node(borrow_id
);
383 let gen_scope
= self.compute_gen_scope(borrow_scope
, loan_scope
);
384 debug
!("gen_scope = {:?}", gen_scope
);
386 let kill_scope
= self.compute_kill_scope(loan_scope
, &loan_path
);
387 debug
!("kill_scope = {:?}", kill_scope
);
389 if req_kind
== ty
::MutBorrow
{
390 self.mark_loan_path_as_mutated(&loan_path
);
394 index
: self.all_loans
.len(),
406 debug
!("guarantee_valid(borrow_id={:?}), loan={:?}",
409 // let loan_path = loan.loan_path;
410 // let loan_gen_scope = loan.gen_scope;
411 // let loan_kill_scope = loan.kill_scope;
412 self.all_loans
.push(loan
);
414 // if loan_gen_scope != borrow_id {
415 // FIXME(https://github.com/rust-lang/rfcs/issues/811) Nested method calls
417 // Typically, the scope of the loan includes the point at
418 // which the loan is originated. This
419 // This is a subtle case. See the test case
420 // <compile-fail/borrowck-bad-nested-calls-free.rs>
421 // to see what we are guarding against.
423 //let restr = restrictions::compute_restrictions(
424 // self.bccx, borrow_span, cmt, RESTR_EMPTY);
427 // index: self.all_loans.len(),
430 // mutbl: ConstMutability,
431 // gen_scope: borrow_id,
433 // span: borrow_span,
439 pub fn mark_loan_path_as_mutated(&self, loan_path
: &LoanPath
) {
440 //! For mutable loans of content whose mutability derives
441 //! from a local variable, mark the mutability decl as necessary.
443 let mut wrapped_path
= Some(loan_path
);
444 let mut through_borrow
= false;
446 while let Some(current_path
) = wrapped_path
{
447 wrapped_path
= match current_path
.kind
{
450 let hir_id
= self.bccx
.tcx
.hir
.node_to_hir_id(local_id
);
451 self.bccx
.used_mut_nodes
.borrow_mut().insert(hir_id
);
455 LpUpvar(ty
::UpvarId{ var_id, closure_expr_id: _ }
) => {
456 self.bccx
.used_mut_nodes
.borrow_mut().insert(var_id
);
459 LpExtend(ref base
, mc
::McInherited
, LpDeref(pointer_kind
)) |
460 LpExtend(ref base
, mc
::McDeclared
, LpDeref(pointer_kind
)) => {
461 if pointer_kind
!= mc
::Unique
{
462 through_borrow
= true;
466 LpDowncast(ref base
, _
) |
467 LpExtend(ref base
, mc
::McInherited
, _
) |
468 LpExtend(ref base
, mc
::McDeclared
, _
) => {
471 LpExtend(_
, mc
::McImmutable
, _
) => {
480 pub fn compute_gen_scope(&self,
481 borrow_scope
: region
::Scope
,
482 loan_scope
: region
::Scope
)
484 //! Determine when to introduce the loan. Typically the loan
485 //! is introduced at the point of the borrow, but in some cases,
486 //! notably method arguments, the loan may be introduced only
487 //! later, once it comes into scope.
489 if self.bccx
.region_scope_tree
.is_subscope_of(borrow_scope
, loan_scope
) {
496 pub fn compute_kill_scope(&self, loan_scope
: region
::Scope
, lp
: &LoanPath
<'tcx
>)
498 //! Determine when the loan restrictions go out of scope.
499 //! This is either when the lifetime expires or when the
500 //! local variable which roots the loan-path goes out of scope,
501 //! whichever happens faster.
503 //! It may seem surprising that we might have a loan region
504 //! larger than the variable which roots the loan-path; this can
505 //! come about when variables of `&mut` type are re-borrowed,
506 //! as in this example:
508 //! struct Foo { counter: u32 }
510 //! fn counter<'a>(v: &'a mut Foo) -> &'a mut u32 {
514 //! In this case, the reference (`'a`) outlives the
515 //! variable `v` that hosts it. Note that this doesn't come up
516 //! with immutable `&` pointers, because borrows of such pointers
517 //! do not require restrictions and hence do not cause a loan.
519 let lexical_scope
= lp
.kill_scope(self.bccx
);
520 if self.bccx
.region_scope_tree
.is_subscope_of(lexical_scope
, loan_scope
) {
523 assert
!(self.bccx
.region_scope_tree
.is_subscope_of(loan_scope
, lexical_scope
));
528 pub fn report_potential_errors(&self) {
529 self.move_error_collector
.report_potential_errors(self.bccx
);