]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/gather_loans/mod.rs
1a9672aa6ce5e6562179764e2780077f90a8391e
[rust.git] / src / librustc_borrowck / borrowck / gather_loans / mod.rs
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.
4 //
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.
10
11 // ----------------------------------------------------------------------
12 // Gathering loans
13 //
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.
18
19 use borrowck::*;
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};
26
27 use syntax::ast;
28 use syntax_pos::Span;
29 use rustc::hir;
30
31 use self::restrictions::RestrictionResult;
32
33 mod lifetime;
34 mod restrictions;
35 mod gather_moves;
36 mod move_error;
37
38 pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
39                                     body: hir::BodyId)
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 {
44         bccx,
45         all_loans: Vec::new(),
46         item_ub: region::Scope {
47             id: bccx.tcx.hir().body(body).value.hir_id.local_id,
48             data: region::ScopeData::Node
49         },
50         move_data: MoveData::default(),
51         move_error_collector: move_error::MoveErrorCollector::new(),
52     };
53
54     let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id);
55     euv::ExprUseVisitor::new(&mut glcx,
56                              bccx.tcx,
57                              param_env,
58                              &bccx.region_scope_tree,
59                              bccx.tables,
60                              Some(rvalue_promotable_map))
61         .consume_body(bccx.body);
62
63     glcx.report_potential_errors();
64     let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
65     (all_loans, move_data)
66 }
67
68 struct GatherLoanCtxt<'a, 'tcx: 'a> {
69     bccx: &'a BorrowckCtxt<'a, 'tcx>,
70     move_data: move_data::MoveData<'tcx>,
71     move_error_collector: move_error::MoveErrorCollector<'tcx>,
72     all_loans: Vec<Loan<'tcx>>,
73     /// `item_ub` is used as an upper-bound on the lifetime whenever we
74     /// ask for the scope of an expression categorized as an upvar.
75     item_ub: region::Scope,
76 }
77
78 impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
79     fn consume(&mut self,
80                consume_id: ast::NodeId,
81                _consume_span: Span,
82                cmt: &mc::cmt_<'tcx>,
83                mode: euv::ConsumeMode) {
84         debug!("consume(consume_id={}, cmt={:?}, mode={:?})",
85                consume_id, cmt, mode);
86
87         match mode {
88             euv::Move(move_reason) => {
89                 gather_moves::gather_move_from_expr(
90                     self.bccx, &self.move_data, &mut self.move_error_collector,
91                     self.bccx.tcx.hir().node_to_hir_id(consume_id).local_id, cmt, move_reason);
92             }
93             euv::Copy => { }
94         }
95     }
96
97     fn matched_pat(&mut self,
98                    matched_pat: &hir::Pat,
99                    cmt: &mc::cmt_<'tcx>,
100                    mode: euv::MatchMode) {
101         debug!("matched_pat(matched_pat={:?}, cmt={:?}, mode={:?})",
102                matched_pat,
103                cmt,
104                mode);
105     }
106
107     fn consume_pat(&mut self,
108                    consume_pat: &hir::Pat,
109                    cmt: &mc::cmt_<'tcx>,
110                    mode: euv::ConsumeMode) {
111         debug!("consume_pat(consume_pat={:?}, cmt={:?}, mode={:?})",
112                consume_pat,
113                cmt,
114                mode);
115
116         match mode {
117             euv::Copy => { return; }
118             euv::Move(_) => { }
119         }
120
121         gather_moves::gather_move_from_pat(
122             self.bccx, &self.move_data, &mut self.move_error_collector,
123             consume_pat, cmt);
124     }
125
126     fn borrow(&mut self,
127               borrow_id: ast::NodeId,
128               borrow_span: Span,
129               cmt: &mc::cmt_<'tcx>,
130               loan_region: ty::Region<'tcx>,
131               bk: ty::BorrowKind,
132               loan_cause: euv::LoanCause)
133     {
134         debug!("borrow(borrow_id={}, cmt={:?}, loan_region={:?}, \
135                bk={:?}, loan_cause={:?})",
136                borrow_id, cmt, loan_region,
137                bk, loan_cause);
138         let hir_id = self.bccx.tcx.hir().node_to_hir_id(borrow_id);
139         self.guarantee_valid(hir_id.local_id,
140                              borrow_span,
141                              cmt,
142                              bk,
143                              loan_region,
144                              loan_cause);
145     }
146
147     fn mutate(&mut self,
148               assignment_id: ast::NodeId,
149               assignment_span: Span,
150               assignee_cmt: &mc::cmt_<'tcx>,
151               _: euv::MutateMode)
152     {
153         self.guarantee_assignment_valid(assignment_id,
154                                         assignment_span,
155                                         assignee_cmt);
156     }
157
158     fn decl_without_init(&mut self, id: ast::NodeId, _span: Span) {
159         let ty = self.bccx
160                      .tables
161                      .node_id_to_type(self.bccx.tcx.hir().node_to_hir_id(id));
162         gather_moves::gather_decl(self.bccx, &self.move_data, id, ty);
163     }
164 }
165
166 /// Implements the A-* rules in README.md.
167 fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
168                                 borrow_span: Span,
169                                 loan_cause: AliasableViolationKind,
170                                 cmt: &mc::cmt_<'tcx>,
171                                 req_kind: ty::BorrowKind)
172                                 -> Result<(),()> {
173
174     let aliasability = cmt.freely_aliasable();
175     debug!("check_aliasability aliasability={:?} req_kind={:?}",
176            aliasability, req_kind);
177
178     match (aliasability, req_kind) {
179         (mc::Aliasability::NonAliasable, _) => {
180             /* Uniquely accessible path -- OK for `&` and `&mut` */
181             Ok(())
182         }
183         (mc::Aliasability::FreelyAliasable(mc::AliasableStatic), ty::ImmBorrow) => {
184             // Borrow of an immutable static item.
185             Ok(())
186         }
187         (mc::Aliasability::FreelyAliasable(mc::AliasableStaticMut), _) => {
188             // Even touching a static mut is considered unsafe. We assume the
189             // user knows what they're doing in these cases.
190             Ok(())
191         }
192         (mc::Aliasability::FreelyAliasable(alias_cause), ty::UniqueImmBorrow) |
193         (mc::Aliasability::FreelyAliasable(alias_cause), ty::MutBorrow) => {
194             bccx.report_aliasability_violation(
195                         borrow_span,
196                         loan_cause,
197                         alias_cause,
198                         cmt);
199             Err(())
200         }
201         (..) => {
202             Ok(())
203         }
204     }
205 }
206
207 /// Implements the M-* rules in README.md.
208 fn check_mutability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
209                               borrow_span: Span,
210                               cause: AliasableViolationKind,
211                               cmt: &mc::cmt_<'tcx>,
212                               req_kind: ty::BorrowKind)
213                               -> Result<(),()> {
214     debug!("check_mutability(cause={:?} cmt={:?} req_kind={:?}",
215            cause, cmt, req_kind);
216     match req_kind {
217         ty::UniqueImmBorrow | ty::ImmBorrow => {
218             match cmt.mutbl {
219                 // I am intentionally leaving this here to help
220                 // refactoring if, in the future, we should add new
221                 // kinds of mutability.
222                 mc::McImmutable | mc::McDeclared | mc::McInherited => {
223                     // both imm and mut data can be lent as imm;
224                     // for mutable data, this is a freeze
225                     Ok(())
226                 }
227             }
228         }
229
230         ty::MutBorrow => {
231             // Only mutable data can be lent as mutable.
232             if !cmt.mutbl.is_mutable() {
233                 Err(bccx.report(BckError { span: borrow_span,
234                                            cause,
235                                            cmt,
236                                            code: err_mutbl }))
237             } else {
238                 Ok(())
239             }
240         }
241     }
242 }
243
244 impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
245     pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { self.bccx.tcx }
246
247     /// Guarantees that `cmt` is assignable, or reports an error.
248     fn guarantee_assignment_valid(&mut self,
249                                   assignment_id: ast::NodeId,
250                                   assignment_span: Span,
251                                   cmt: &mc::cmt_<'tcx>) {
252
253         let opt_lp = opt_loan_path(cmt);
254         debug!("guarantee_assignment_valid(assignment_id={}, cmt={:?}) opt_lp={:?}",
255                assignment_id, cmt, opt_lp);
256
257         if let Categorization::Local(..) = cmt.cat {
258             // Only re-assignments to locals require it to be
259             // mutable - this is checked in check_loans.
260         } else {
261             // Check that we don't allow assignments to non-mutable data.
262             if check_mutability(self.bccx, assignment_span, MutabilityViolation,
263                                 cmt, ty::MutBorrow).is_err() {
264                 return; // reported an error, no sense in reporting more.
265             }
266         }
267
268         // Check that we don't allow assignments to aliasable data
269         if check_aliasability(self.bccx, assignment_span, MutabilityViolation,
270                               cmt, ty::MutBorrow).is_err() {
271             return; // reported an error, no sense in reporting more.
272         }
273
274         match opt_lp {
275             Some(lp) => {
276                 if let Categorization::Local(..) = cmt.cat {
277                     // Only re-assignments to locals require it to be
278                     // mutable - this is checked in check_loans.
279                 } else {
280                     self.mark_loan_path_as_mutated(&lp);
281                 }
282                 gather_moves::gather_assignment(self.bccx, &self.move_data,
283                                                 self.bccx.tcx.hir().node_to_hir_id(assignment_id)
284                                                     .local_id,
285                                                 assignment_span,
286                                                 lp);
287             }
288             None => {
289                 // This can occur with e.g. `*foo() = 5`.  In such
290                 // cases, there is no need to check for conflicts
291                 // with moves etc, just ignore.
292             }
293         }
294     }
295
296     /// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or
297     /// reports an error.  This may entail taking out loans, which will be added to the
298     /// `req_loan_map`.
299     fn guarantee_valid(&mut self,
300                        borrow_id: hir::ItemLocalId,
301                        borrow_span: Span,
302                        cmt: &mc::cmt_<'tcx>,
303                        req_kind: ty::BorrowKind,
304                        loan_region: ty::Region<'tcx>,
305                        cause: euv::LoanCause) {
306         debug!("guarantee_valid(borrow_id={:?}, cmt={:?}, \
307                 req_mutbl={:?}, loan_region={:?})",
308                borrow_id,
309                cmt,
310                req_kind,
311                loan_region);
312
313         // a loan for the empty region can never be dereferenced, so
314         // it is always safe
315         if *loan_region == ty::ReEmpty {
316             return;
317         }
318
319         // Check that the lifetime of the borrow does not exceed
320         // the lifetime of the data being borrowed.
321         if lifetime::guarantee_lifetime(self.bccx, self.item_ub,
322                                         borrow_span, cause, cmt, loan_region).is_err() {
323             return; // reported an error, no sense in reporting more.
324         }
325
326         // Check that we don't allow mutable borrows of non-mutable data.
327         if check_mutability(self.bccx, borrow_span, BorrowViolation(cause),
328                             cmt, req_kind).is_err() {
329             return; // reported an error, no sense in reporting more.
330         }
331
332         // Check that we don't allow mutable borrows of aliasable data.
333         if check_aliasability(self.bccx, borrow_span, BorrowViolation(cause),
334                               cmt, req_kind).is_err() {
335             return; // reported an error, no sense in reporting more.
336         }
337
338         // Compute the restrictions that are required to enforce the
339         // loan is safe.
340         let restr = restrictions::compute_restrictions(
341             self.bccx, borrow_span, cause, &cmt, loan_region);
342
343         debug!("guarantee_valid(): restrictions={:?}", restr);
344
345         // Create the loan record (if needed).
346         let loan = match restr {
347             RestrictionResult::Safe => {
348                 // No restrictions---no loan record necessary
349                 return;
350             }
351
352             RestrictionResult::SafeIf(loan_path, restricted_paths) => {
353                 let loan_scope = match *loan_region {
354                     ty::ReScope(scope) => scope,
355
356                     ty::ReEarlyBound(ref br) => {
357                         self.bccx.region_scope_tree.early_free_scope(self.tcx(), br)
358                     }
359
360                     ty::ReFree(ref fr) => {
361                         self.bccx.region_scope_tree.free_scope(self.tcx(), fr)
362                     }
363
364                     ty::ReStatic => self.item_ub,
365
366                     ty::ReEmpty |
367                     ty::ReClosureBound(..) |
368                     ty::ReLateBound(..) |
369                     ty::ReVar(..) |
370                     ty::RePlaceholder(..) |
371                     ty::ReErased => {
372                         span_bug!(
373                             cmt.span,
374                             "invalid borrow lifetime: {:?}",
375                             loan_region);
376                     }
377                 };
378                 debug!("loan_scope = {:?}", loan_scope);
379
380                 let borrow_scope = region::Scope {
381                     id: borrow_id,
382                     data: region::ScopeData::Node
383                 };
384                 let gen_scope = self.compute_gen_scope(borrow_scope, loan_scope);
385                 debug!("gen_scope = {:?}", gen_scope);
386
387                 let kill_scope = self.compute_kill_scope(loan_scope, &loan_path);
388                 debug!("kill_scope = {:?}", kill_scope);
389
390                 if req_kind == ty::MutBorrow {
391                     self.mark_loan_path_as_mutated(&loan_path);
392                 }
393
394                 Loan {
395                     index: self.all_loans.len(),
396                     loan_path,
397                     kind: req_kind,
398                     gen_scope,
399                     kill_scope,
400                     span: borrow_span,
401                     restricted_paths,
402                     cause,
403                 }
404             }
405         };
406
407         debug!("guarantee_valid(borrow_id={:?}), loan={:?}",
408                borrow_id, loan);
409
410         // let loan_path = loan.loan_path;
411         // let loan_gen_scope = loan.gen_scope;
412         // let loan_kill_scope = loan.kill_scope;
413         self.all_loans.push(loan);
414
415         // if loan_gen_scope != borrow_id {
416             // FIXME(https://github.com/rust-lang/rfcs/issues/811) Nested method calls
417             //
418             // Typically, the scope of the loan includes the point at
419             // which the loan is originated. This
420             // This is a subtle case. See the test case
421             // <compile-fail/borrowck-bad-nested-calls-free.rs>
422             // to see what we are guarding against.
423
424             //let restr = restrictions::compute_restrictions(
425             //    self.bccx, borrow_span, cmt, RESTR_EMPTY);
426             //let loan = {
427             //    Loan {
428             //        index: self.all_loans.len(),
429             //        loan_path,
430             //        cmt,
431             //        mutbl: ConstMutability,
432             //        gen_scope: borrow_id,
433             //        kill_scope,
434             //        span: borrow_span,
435             //        restrictions,
436             //    }
437         // }
438     }
439
440     pub fn mark_loan_path_as_mutated(&self, loan_path: &LoanPath) {
441         //! For mutable loans of content whose mutability derives
442         //! from a local variable, mark the mutability decl as necessary.
443
444         let mut wrapped_path = Some(loan_path);
445         let mut through_borrow = false;
446
447         while let Some(current_path) = wrapped_path {
448             wrapped_path = match current_path.kind {
449                 LpVar(local_id) => {
450                     if !through_borrow {
451                         let hir_id = self.bccx.tcx.hir().node_to_hir_id(local_id);
452                         self.bccx.used_mut_nodes.borrow_mut().insert(hir_id);
453                     }
454                     None
455                 }
456                 LpUpvar(ty::UpvarId{ var_path: ty::UpvarPath { hir_id }, closure_expr_id: _ }) => {
457                     self.bccx.used_mut_nodes.borrow_mut().insert(hir_id);
458                     None
459                 }
460                 LpExtend(ref base, mc::McInherited, LpDeref(pointer_kind)) |
461                 LpExtend(ref base, mc::McDeclared, LpDeref(pointer_kind)) => {
462                     if pointer_kind != mc::Unique {
463                         through_borrow = true;
464                     }
465                     Some(base)
466                 }
467                 LpDowncast(ref base, _) |
468                 LpExtend(ref base, mc::McInherited, _) |
469                 LpExtend(ref base, mc::McDeclared, _) => {
470                     Some(base)
471                 }
472                 LpExtend(_, mc::McImmutable, _) => {
473                     // Nothing to do.
474                     None
475                 }
476             }
477         }
478
479     }
480
481     pub fn compute_gen_scope(&self,
482                              borrow_scope: region::Scope,
483                              loan_scope: region::Scope)
484                              -> region::Scope {
485         //! Determine when to introduce the loan. Typically the loan
486         //! is introduced at the point of the borrow, but in some cases,
487         //! notably method arguments, the loan may be introduced only
488         //! later, once it comes into scope.
489
490         if self.bccx.region_scope_tree.is_subscope_of(borrow_scope, loan_scope) {
491             borrow_scope
492         } else {
493             loan_scope
494         }
495     }
496
497     pub fn compute_kill_scope(&self, loan_scope: region::Scope, lp: &LoanPath<'tcx>)
498                               -> region::Scope {
499         //! Determine when the loan restrictions go out of scope.
500         //! This is either when the lifetime expires or when the
501         //! local variable which roots the loan-path goes out of scope,
502         //! whichever happens faster.
503         //!
504         //! It may seem surprising that we might have a loan region
505         //! larger than the variable which roots the loan-path; this can
506         //! come about when variables of `&mut` type are re-borrowed,
507         //! as in this example:
508         //!
509         //!     struct Foo { counter: u32 }
510         //!
511         //!     fn counter<'a>(v: &'a mut Foo) -> &'a mut u32 {
512         //!         &mut v.counter
513         //!     }
514         //!
515         //! In this case, the reference (`'a`) outlives the
516         //! variable `v` that hosts it. Note that this doesn't come up
517         //! with immutable `&` pointers, because borrows of such pointers
518         //! do not require restrictions and hence do not cause a loan.
519
520         let lexical_scope = lp.kill_scope(self.bccx);
521         if self.bccx.region_scope_tree.is_subscope_of(lexical_scope, loan_scope) {
522             lexical_scope
523         } else {
524             assert!(self.bccx.region_scope_tree.is_subscope_of(loan_scope, lexical_scope));
525             loan_scope
526         }
527     }
528
529     pub fn report_potential_errors(&self) {
530         self.move_error_collector.report_potential_errors(self.bccx);
531     }
532 }