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
;
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
, 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={:?})",
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={:?})",
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
, 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 let opt_lp
= opt_loan_path(&assignee_cmt
);
155 debug
!("mutate(assignment_id={}, assignee_cmt={:?}) opt_lp={:?}",
156 assignment_id
, assignee_cmt
, opt_lp
);
160 gather_moves
::gather_assignment(self.bccx
, &self.move_data
,
161 assignment_id
, assignment_span
,
162 lp
, assignee_cmt
.id
, mode
);
165 // This can occur with e.g. `*foo() = 5`. In such
166 // cases, there is no need to check for conflicts
167 // with moves etc, just ignore.
172 fn decl_without_init(&mut self, id
: ast
::NodeId
, span
: Span
) {
173 gather_moves
::gather_decl(self.bccx
, &self.move_data
, id
, span
, id
);
177 /// Implements the A-* rules in README.md.
178 fn check_aliasability
<'a
, 'tcx
>(bccx
: &BorrowckCtxt
<'a
, 'tcx
>,
180 loan_cause
: euv
::LoanCause
,
182 req_kind
: ty
::BorrowKind
)
185 let aliasability
= cmt
.freely_aliasable(bccx
.tcx
);
186 debug
!("check_aliasability aliasability={:?} req_kind={:?}",
187 aliasability
, req_kind
);
189 match (aliasability
, req_kind
) {
190 (mc
::Aliasability
::NonAliasable
, _
) => {
191 /* Uniquely accessible path -- OK for `&` and `&mut` */
194 (mc
::Aliasability
::FreelyAliasable(mc
::AliasableStatic
), ty
::ImmBorrow
) => {
195 // Borrow of an immutable static item.
198 (mc
::Aliasability
::FreelyAliasable(mc
::AliasableStaticMut
), _
) => {
199 // Even touching a static mut is considered unsafe. We assume the
200 // user knows what they're doing in these cases.
203 (mc
::Aliasability
::ImmutableUnique(_
), ty
::MutBorrow
) => {
204 bccx
.report_aliasability_violation(
206 BorrowViolation(loan_cause
),
207 mc
::AliasableReason
::UnaliasableImmutable
);
210 (mc
::Aliasability
::FreelyAliasable(alias_cause
), ty
::UniqueImmBorrow
) |
211 (mc
::Aliasability
::FreelyAliasable(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={:?})",
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
.to_code_extent(),
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={:?}",
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 README.md.
379 debug
!("check_mutability(cause={:?} cmt={:?} req_kind={:?}",
380 cause
, cmt
, req_kind
);
382 ty
::UniqueImmBorrow
| ty
::ImmBorrow
=> {
384 // I am intentionally leaving this here to help
385 // refactoring if, in the future, we should add new
386 // kinds of mutability.
387 mc
::McImmutable
| mc
::McDeclared
| mc
::McInherited
=> {
388 // both imm and mut data can be lent as imm;
389 // for mutable data, this is a freeze
396 // Only mutable data can be lent as mutable.
397 if !cmt
.mutbl
.is_mutable() {
398 Err(bccx
.report(BckError
{ span
: borrow_span
,
410 pub fn mark_loan_path_as_mutated(&self, loan_path
: &LoanPath
) {
411 //! For mutable loans of content whose mutability derives
412 //! from a local variable, mark the mutability decl as necessary.
414 match loan_path
.kind
{
416 LpUpvar(ty
::UpvarId{ var_id: local_id, closure_expr_id: _ }
) => {
417 self.tcx().used_mut_nodes
.borrow_mut().insert(local_id
);
419 LpDowncast(ref base
, _
) |
420 LpExtend(ref base
, mc
::McInherited
, _
) |
421 LpExtend(ref base
, mc
::McDeclared
, _
) => {
422 self.mark_loan_path_as_mutated(&**base
);
424 LpExtend(_
, mc
::McImmutable
, _
) => {
430 pub fn compute_gen_scope(&self,
431 borrow_scope
: region
::CodeExtent
,
432 loan_scope
: region
::CodeExtent
)
433 -> region
::CodeExtent
{
434 //! Determine when to introduce the loan. Typically the loan
435 //! is introduced at the point of the borrow, but in some cases,
436 //! notably method arguments, the loan may be introduced only
437 //! later, once it comes into scope.
439 if self.bccx
.tcx
.region_maps
.is_subscope_of(borrow_scope
, loan_scope
) {
446 pub fn compute_kill_scope(&self, loan_scope
: region
::CodeExtent
, lp
: &LoanPath
<'tcx
>)
447 -> region
::CodeExtent
{
448 //! Determine when the loan restrictions go out of scope.
449 //! This is either when the lifetime expires or when the
450 //! local variable which roots the loan-path goes out of scope,
451 //! whichever happens faster.
453 //! It may seem surprising that we might have a loan region
454 //! larger than the variable which roots the loan-path; this can
455 //! come about when variables of `&mut` type are re-borrowed,
456 //! as in this example:
458 //! fn counter<'a>(v: &'a mut Foo) -> &'a mut uint {
462 //! In this case, the reference (`'a`) outlives the
463 //! variable `v` that hosts it. Note that this doesn't come up
464 //! with immutable `&` pointers, because borrows of such pointers
465 //! do not require restrictions and hence do not cause a loan.
467 let lexical_scope
= lp
.kill_scope(self.bccx
.tcx
);
468 let rm
= &self.bccx
.tcx
.region_maps
;
469 if rm
.is_subscope_of(lexical_scope
, loan_scope
) {
472 assert
!(self.bccx
.tcx
.region_maps
.is_subscope_of(loan_scope
, lexical_scope
));
477 pub fn report_potential_errors(&self) {
478 self.move_error_collector
.report_potential_errors(self.bccx
);
482 /// Context used while gathering loans on static initializers
484 /// This visitor walks static initializer's expressions and makes
485 /// sure the loans being taken are sound.
486 struct StaticInitializerCtxt
<'a
, 'tcx
: 'a
> {
487 bccx
: &'a BorrowckCtxt
<'a
, 'tcx
>,
490 impl<'a
, 'tcx
, 'v
> Visitor
<'v
> for StaticInitializerCtxt
<'a
, 'tcx
> {
491 fn visit_expr(&mut self, ex
: &Expr
) {
492 if let ast
::ExprAddrOf(mutbl
, ref base
) = ex
.node
{
493 let param_env
= ty
::empty_parameter_environment(self.bccx
.tcx
);
494 let mc
= mc
::MemCategorizationContext
::new(¶m_env
);
495 let base_cmt
= mc
.cat_expr(&**base
).unwrap();
496 let borrow_kind
= ty
::BorrowKind
::from_mutbl(mutbl
);
497 // Check that we don't allow borrows of unsafe static items.
498 if check_aliasability(self.bccx
, ex
.span
, euv
::AddrOf
,
499 base_cmt
, borrow_kind
).is_err() {
500 return; // reported an error, no sense in reporting more.
504 visit
::walk_expr(self, ex
);
508 pub fn gather_loans_in_static_initializer(bccx
: &mut BorrowckCtxt
, expr
: &ast
::Expr
) {
510 debug
!("gather_loans_in_static_initializer(expr={:?})", expr
);
512 let mut sicx
= StaticInitializerCtxt
{
516 sicx
.visit_expr(expr
);