1 // ----------------------------------------------------------------------
4 // The borrow check proceeds in two phases. In phase one, we gather the full
5 // set of loans that are required at any point. These are sorted according to
6 // their associated scopes. In phase two, checking loans, we will then make
7 // sure that all of these loans are honored.
9 use crate::borrowck
::*;
10 use crate::borrowck
::move_data
::MoveData
;
11 use rustc
::middle
::expr_use_visitor
as euv
;
12 use rustc
::middle
::mem_categorization
as mc
;
13 use rustc
::middle
::mem_categorization
::Categorization
;
14 use rustc
::middle
::region
;
15 use rustc
::ty
::{self, TyCtxt}
;
21 use restrictions
::RestrictionResult
;
27 pub fn gather_loans_in_fn
<'a
, 'tcx
>(bccx
: &BorrowckCtxt
<'a
, 'tcx
>,
29 -> (Vec
<Loan
<'tcx
>>, move_data
::MoveData
<'tcx
>) {
30 let def_id
= bccx
.tcx
.hir().body_owner_def_id(body
);
31 let param_env
= bccx
.tcx
.param_env(def_id
);
32 let mut glcx
= GatherLoanCtxt
{
34 all_loans
: Vec
::new(),
35 item_ub
: region
::Scope
{
36 id
: bccx
.tcx
.hir().body(body
).value
.hir_id
.local_id
,
37 data
: region
::ScopeData
::Node
39 move_data
: MoveData
::default(),
42 let rvalue_promotable_map
= bccx
.tcx
.rvalue_promotable_map(def_id
);
43 euv
::ExprUseVisitor
::new(&mut glcx
,
47 &bccx
.region_scope_tree
,
49 Some(rvalue_promotable_map
))
50 .consume_body(bccx
.body
);
52 let GatherLoanCtxt { all_loans, move_data, .. }
= glcx
;
53 (all_loans
, move_data
)
56 struct GatherLoanCtxt
<'a
, 'tcx
> {
57 bccx
: &'a BorrowckCtxt
<'a
, 'tcx
>,
58 move_data
: move_data
::MoveData
<'tcx
>,
59 all_loans
: Vec
<Loan
<'tcx
>>,
60 /// `item_ub` is used as an upper-bound on the lifetime whenever we
61 /// ask for the scope of an expression categorized as an upvar.
62 item_ub
: region
::Scope
,
65 impl<'a
, 'tcx
> euv
::Delegate
<'tcx
> for GatherLoanCtxt
<'a
, 'tcx
> {
67 consume_id
: hir
::HirId
,
70 mode
: euv
::ConsumeMode
) {
71 debug
!("consume(consume_id={}, cmt={:?}, mode={:?})",
72 consume_id
, cmt
, mode
);
76 gather_moves
::gather_move_from_expr(
77 self.bccx
, &self.move_data
,
78 consume_id
.local_id
, cmt
);
84 fn matched_pat(&mut self,
85 matched_pat
: &hir
::Pat
,
87 mode
: euv
::MatchMode
) {
88 debug
!("matched_pat(matched_pat={:?}, cmt={:?}, mode={:?})",
94 fn consume_pat(&mut self,
95 consume_pat
: &hir
::Pat
,
97 mode
: euv
::ConsumeMode
) {
98 debug
!("consume_pat(consume_pat={:?}, cmt={:?}, mode={:?})",
104 euv
::Copy
=> { return; }
108 gather_moves
::gather_move_from_pat(
109 self.bccx
, &self.move_data
,
114 borrow_id
: hir
::HirId
,
116 cmt
: &mc
::cmt_
<'tcx
>,
117 loan_region
: ty
::Region
<'tcx
>,
119 loan_cause
: euv
::LoanCause
)
121 debug
!("borrow(borrow_id={}, cmt={:?}, loan_region={:?}, \
122 bk={:?}, loan_cause={:?})",
123 borrow_id
, cmt
, loan_region
,
126 self.guarantee_valid(borrow_id
.local_id
,
133 assignment_id
: hir
::HirId
,
134 assignment_span
: Span
,
135 assignee_cmt
: &mc
::cmt_
<'tcx
>,
138 self.guarantee_assignment_valid(assignment_id
,
143 fn decl_without_init(&mut self, id
: hir
::HirId
, _span
: Span
) {
147 gather_moves
::gather_decl(self.bccx
, &self.move_data
, id
, ty
);
150 fn nested_body(&mut self, body_id
: hir
::BodyId
) {
151 debug
!("nested_body(body_id={:?})", body_id
);
152 // rust-lang/rust#58776: MIR and AST borrow check disagree on where
153 // certain closure errors are reported. As such migrate borrowck has to
154 // operate at the level of items, rather than bodies. Check if the
155 // contained closure had any errors and set `signalled_any_error` if it
157 let bccx
= self.bccx
;
158 if bccx
.tcx
.migrate_borrowck() {
159 if let SignalledError
::NoErrorsSeen
= bccx
.signalled_any_error
.get() {
160 let closure_def_id
= bccx
.tcx
.hir().body_owner_def_id(body_id
);
161 debug
!("checking closure: {:?}", closure_def_id
);
163 bccx
.signalled_any_error
.set(bccx
.tcx
.borrowck(closure_def_id
).signalled_any_error
);
169 /// Implements the A-* rules in README.md.
170 fn check_aliasability
<'a
, 'tcx
>(bccx
: &BorrowckCtxt
<'a
, 'tcx
>,
171 cmt
: &mc
::cmt_
<'tcx
>,
172 req_kind
: ty
::BorrowKind
)
175 let aliasability
= cmt
.freely_aliasable();
176 debug
!("check_aliasability aliasability={:?} req_kind={:?}",
177 aliasability
, req_kind
);
179 match (aliasability
, req_kind
) {
180 (mc
::Aliasability
::NonAliasable
, _
) => {
181 /* Uniquely accessible path -- OK for `&` and `&mut` */
184 (mc
::Aliasability
::FreelyAliasable(mc
::AliasableStatic
), ty
::ImmBorrow
) => {
185 // Borrow of an immutable static item.
188 (mc
::Aliasability
::FreelyAliasable(mc
::AliasableStaticMut
), _
) => {
189 // Even touching a static mut is considered unsafe. We assume the
190 // user knows what they're doing in these cases.
193 (mc
::Aliasability
::FreelyAliasable(_
), ty
::UniqueImmBorrow
) |
194 (mc
::Aliasability
::FreelyAliasable(_
), ty
::MutBorrow
) => {
204 /// Implements the M-* rules in README.md.
205 fn check_mutability
<'a
, 'tcx
>(bccx
: &BorrowckCtxt
<'a
, 'tcx
>,
206 cmt
: &mc
::cmt_
<'tcx
>,
207 req_kind
: ty
::BorrowKind
)
209 debug
!("check_mutability(cmt={:?} req_kind={:?}", cmt
, req_kind
);
211 ty
::UniqueImmBorrow
| ty
::ImmBorrow
=> {
213 // I am intentionally leaving this here to help
214 // refactoring if, in the future, we should add new
215 // kinds of mutability.
216 mc
::McImmutable
| mc
::McDeclared
| mc
::McInherited
=> {
217 // both imm and mut data can be lent as imm;
218 // for mutable data, this is a freeze
225 // Only mutable data can be lent as mutable.
226 if !cmt
.mutbl
.is_mutable() {
227 Err(bccx
.signal_error())
235 impl<'a
, 'tcx
> GatherLoanCtxt
<'a
, 'tcx
> {
236 pub fn tcx(&self) -> TyCtxt
<'tcx
> { self.bccx.tcx }
238 /// Guarantees that `cmt` is assignable, or reports an error.
239 fn guarantee_assignment_valid(&mut self,
240 assignment_id
: hir
::HirId
,
241 assignment_span
: Span
,
242 cmt
: &mc
::cmt_
<'tcx
>) {
244 let opt_lp
= opt_loan_path(cmt
);
245 debug
!("guarantee_assignment_valid(assignment_id={}, cmt={:?}) opt_lp={:?}",
246 assignment_id
, cmt
, opt_lp
);
248 if let Categorization
::Local(..) = cmt
.cat
{
249 // Only re-assignments to locals require it to be
250 // mutable - this is checked in check_loans.
252 // Check that we don't allow assignments to non-mutable data.
253 if check_mutability(self.bccx
, cmt
, ty
::MutBorrow
).is_err() {
254 return; // reported an error, no sense in reporting more.
258 // Check that we don't allow assignments to aliasable data
259 if check_aliasability(self.bccx
, cmt
, ty
::MutBorrow
).is_err() {
260 return; // reported an error, no sense in reporting more.
265 gather_moves
::gather_assignment(self.bccx
, &self.move_data
,
266 assignment_id
.local_id
,
271 // This can occur with e.g., `*foo() = 5`. In such
272 // cases, there is no need to check for conflicts
273 // with moves etc, just ignore.
278 /// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or
279 /// reports an error. This may entail taking out loans, which will be added to the
281 fn guarantee_valid(&mut self,
282 borrow_id
: hir
::ItemLocalId
,
283 cmt
: &mc
::cmt_
<'tcx
>,
284 req_kind
: ty
::BorrowKind
,
285 loan_region
: ty
::Region
<'tcx
>) {
286 debug
!("guarantee_valid(borrow_id={:?}, cmt={:?}, \
287 req_mutbl={:?}, loan_region={:?})",
293 // a loan for the empty region can never be dereferenced, so
295 if *loan_region
== ty
::ReEmpty
{
299 // Check that the lifetime of the borrow does not exceed
300 // the lifetime of the data being borrowed.
301 if lifetime
::guarantee_lifetime(self.bccx
, self.item_ub
, cmt
, loan_region
).is_err() {
302 return; // reported an error, no sense in reporting more.
305 // Check that we don't allow mutable borrows of non-mutable data.
306 if check_mutability(self.bccx
, cmt
, req_kind
).is_err() {
307 return; // reported an error, no sense in reporting more.
310 // Check that we don't allow mutable borrows of aliasable data.
311 if check_aliasability(self.bccx
, cmt
, req_kind
).is_err() {
312 return; // reported an error, no sense in reporting more.
315 // Compute the restrictions that are required to enforce the
317 let restr
= restrictions
::compute_restrictions(self.bccx
, &cmt
, loan_region
);
319 debug
!("guarantee_valid(): restrictions={:?}", restr
);
321 // Create the loan record (if needed).
322 let loan
= match restr
{
323 RestrictionResult
::Safe
=> {
324 // No restrictions---no loan record necessary
328 RestrictionResult
::SafeIf(loan_path
, restricted_paths
) => {
329 let loan_scope
= match *loan_region
{
330 ty
::ReScope(scope
) => scope
,
332 ty
::ReEarlyBound(ref br
) => {
333 self.bccx
.region_scope_tree
.early_free_scope(self.tcx(), br
)
336 ty
::ReFree(ref fr
) => {
337 self.bccx
.region_scope_tree
.free_scope(self.tcx(), fr
)
340 ty
::ReStatic
=> self.item_ub
,
343 ty
::ReClosureBound(..) |
344 ty
::ReLateBound(..) |
346 ty
::RePlaceholder(..) |
350 "invalid borrow lifetime: {:?}",
354 debug
!("loan_scope = {:?}", loan_scope
);
356 let borrow_scope
= region
::Scope
{
358 data
: region
::ScopeData
::Node
360 let gen_scope
= self.compute_gen_scope(borrow_scope
, loan_scope
);
361 debug
!("gen_scope = {:?}", gen_scope
);
363 let kill_scope
= self.compute_kill_scope(loan_scope
, &loan_path
);
364 debug
!("kill_scope = {:?}", kill_scope
);
367 index
: self.all_loans
.len(),
377 debug
!("guarantee_valid(borrow_id={:?}), loan={:?}",
380 // let loan_path = loan.loan_path;
381 // let loan_gen_scope = loan.gen_scope;
382 // let loan_kill_scope = loan.kill_scope;
383 self.all_loans
.push(loan
);
386 pub fn compute_gen_scope(&self,
387 borrow_scope
: region
::Scope
,
388 loan_scope
: region
::Scope
)
390 //! Determine when to introduce the loan. Typically the loan
391 //! is introduced at the point of the borrow, but in some cases,
392 //! notably method arguments, the loan may be introduced only
393 //! later, once it comes into scope.
395 if self.bccx
.region_scope_tree
.is_subscope_of(borrow_scope
, loan_scope
) {
402 pub fn compute_kill_scope(&self, loan_scope
: region
::Scope
, lp
: &LoanPath
<'tcx
>)
404 //! Determine when the loan restrictions go out of scope.
405 //! This is either when the lifetime expires or when the
406 //! local variable which roots the loan-path goes out of scope,
407 //! whichever happens faster.
409 //! It may seem surprising that we might have a loan region
410 //! larger than the variable which roots the loan-path; this can
411 //! come about when variables of `&mut` type are re-borrowed,
412 //! as in this example:
414 //! struct Foo { counter: u32 }
416 //! fn counter<'a>(v: &'a mut Foo) -> &'a mut u32 {
420 //! In this case, the reference (`'a`) outlives the
421 //! variable `v` that hosts it. Note that this doesn't come up
422 //! with immutable `&` pointers, because borrows of such pointers
423 //! do not require restrictions and hence do not cause a loan.
425 let lexical_scope
= lp
.kill_scope(self.bccx
);
426 if self.bccx
.region_scope_tree
.is_subscope_of(lexical_scope
, loan_scope
) {
429 assert
!(self.bccx
.region_scope_tree
.is_subscope_of(loan_scope
, lexical_scope
));