]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/gather_loans/mod.rs
rustc: use DefId instead of CodeExtent for FreeRegion's scope.
[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::infer::InferCtxt;
22 use rustc::middle::expr_use_visitor as euv;
23 use rustc::middle::mem_categorization as mc;
24 use rustc::middle::mem_categorization::Categorization;
25 use rustc::middle::region;
26 use rustc::ty::{self, TyCtxt};
27
28 use syntax::ast;
29 use syntax_pos::Span;
30 use rustc::hir;
31
32 use self::restrictions::RestrictionResult;
33
34 mod lifetime;
35 mod restrictions;
36 mod gather_moves;
37 mod move_error;
38
39 pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
40                                     body: hir::BodyId)
41                                     -> (Vec<Loan<'tcx>>,
42                                         move_data::MoveData<'tcx>) {
43     let infcx = bccx.tcx.borrowck_fake_infer_ctxt(body);
44     let mut glcx = GatherLoanCtxt {
45         bccx: bccx,
46         infcx: &infcx,
47         all_loans: Vec::new(),
48         item_ub: bccx.tcx.node_extent(body.node_id),
49         move_data: MoveData::new(),
50         move_error_collector: move_error::MoveErrorCollector::new(),
51     };
52
53     let body = glcx.bccx.tcx.hir.body(body);
54     euv::ExprUseVisitor::new(&mut glcx, &bccx.region_maps, &infcx).consume_body(body);
55
56     glcx.report_potential_errors();
57     let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
58     (all_loans, move_data)
59 }
60
61 struct GatherLoanCtxt<'a, 'tcx: 'a> {
62     bccx: &'a BorrowckCtxt<'a, 'tcx>,
63     infcx: &'a InferCtxt<'a, 'tcx, 'tcx>,
64     move_data: move_data::MoveData<'tcx>,
65     move_error_collector: move_error::MoveErrorCollector<'tcx>,
66     all_loans: Vec<Loan<'tcx>>,
67     /// `item_ub` is used as an upper-bound on the lifetime whenever we
68     /// ask for the scope of an expression categorized as an upvar.
69     item_ub: region::CodeExtent<'tcx>,
70 }
71
72 impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
73     fn consume(&mut self,
74                consume_id: ast::NodeId,
75                _consume_span: Span,
76                cmt: mc::cmt<'tcx>,
77                mode: euv::ConsumeMode) {
78         debug!("consume(consume_id={}, cmt={:?}, mode={:?})",
79                consume_id, cmt, mode);
80
81         match mode {
82             euv::Move(move_reason) => {
83                 gather_moves::gather_move_from_expr(
84                     self.bccx, &self.move_data, &mut self.move_error_collector,
85                     consume_id, cmt, move_reason);
86             }
87             euv::Copy => { }
88         }
89     }
90
91     fn matched_pat(&mut self,
92                    matched_pat: &hir::Pat,
93                    cmt: mc::cmt<'tcx>,
94                    mode: euv::MatchMode) {
95         debug!("matched_pat(matched_pat={:?}, cmt={:?}, mode={:?})",
96                matched_pat,
97                cmt,
98                mode);
99
100         if let Categorization::Downcast(..) = cmt.cat {
101             gather_moves::gather_match_variant(
102                 self.bccx, &self.move_data, &mut self.move_error_collector,
103                 matched_pat, cmt, mode);
104         }
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
139         self.guarantee_valid(borrow_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               mode: euv::MutateMode)
152     {
153         self.guarantee_assignment_valid(assignment_id,
154                                         assignment_span,
155                                         assignee_cmt,
156                                         mode);
157     }
158
159     fn decl_without_init(&mut self, id: ast::NodeId, _span: Span) {
160         let ty = self.infcx.tables.borrow().node_id_to_type(id);
161         gather_moves::gather_decl(self.bccx, &self.move_data, id, ty);
162     }
163 }
164
165 /// Implements the A-* rules in README.md.
166 fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
167                                 borrow_span: Span,
168                                 loan_cause: AliasableViolationKind,
169                                 cmt: mc::cmt<'tcx>,
170                                 req_kind: ty::BorrowKind)
171                                 -> Result<(),()> {
172
173     let aliasability = cmt.freely_aliasable();
174     debug!("check_aliasability aliasability={:?} req_kind={:?}",
175            aliasability, req_kind);
176
177     match (aliasability, req_kind) {
178         (mc::Aliasability::NonAliasable, _) => {
179             /* Uniquely accessible path -- OK for `&` and `&mut` */
180             Ok(())
181         }
182         (mc::Aliasability::FreelyAliasable(mc::AliasableStatic), ty::ImmBorrow) => {
183             // Borrow of an immutable static item.
184             Ok(())
185         }
186         (mc::Aliasability::FreelyAliasable(mc::AliasableStaticMut), _) => {
187             // Even touching a static mut is considered unsafe. We assume the
188             // user knows what they're doing in these cases.
189             Ok(())
190         }
191         (mc::Aliasability::FreelyAliasable(alias_cause), ty::UniqueImmBorrow) |
192         (mc::Aliasability::FreelyAliasable(alias_cause), ty::MutBorrow) => {
193             bccx.report_aliasability_violation(
194                         borrow_span,
195                         loan_cause,
196                         alias_cause,
197                         cmt);
198             Err(())
199         }
200         (..) => {
201             Ok(())
202         }
203     }
204 }
205
206 /// Implements the M-* rules in README.md.
207 fn check_mutability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
208                               borrow_span: Span,
209                               cause: AliasableViolationKind,
210                               cmt: mc::cmt<'tcx>,
211                               req_kind: ty::BorrowKind)
212                               -> Result<(),()> {
213     debug!("check_mutability(cause={:?} cmt={:?} req_kind={:?}",
214            cause, cmt, req_kind);
215     match req_kind {
216         ty::UniqueImmBorrow | ty::ImmBorrow => {
217             match cmt.mutbl {
218                 // I am intentionally leaving this here to help
219                 // refactoring if, in the future, we should add new
220                 // kinds of mutability.
221                 mc::McImmutable | mc::McDeclared | mc::McInherited => {
222                     // both imm and mut data can be lent as imm;
223                     // for mutable data, this is a freeze
224                     Ok(())
225                 }
226             }
227         }
228
229         ty::MutBorrow => {
230             // Only mutable data can be lent as mutable.
231             if !cmt.mutbl.is_mutable() {
232                 Err(bccx.report(BckError { span: borrow_span,
233                                            cause: cause,
234                                            cmt: cmt,
235                                            code: err_mutbl }))
236             } else {
237                 Ok(())
238             }
239         }
240     }
241 }
242
243 impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
244     pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { self.bccx.tcx }
245
246     /// Guarantees that `cmt` is assignable, or reports an error.
247     fn guarantee_assignment_valid(&mut self,
248                                   assignment_id: ast::NodeId,
249                                   assignment_span: Span,
250                                   cmt: mc::cmt<'tcx>,
251                                   mode: euv::MutateMode) {
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.clone(), 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.clone(), 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                                                 assignment_id, assignment_span,
284                                                 lp, cmt.id, mode);
285             }
286             None => {
287                 // This can occur with e.g. `*foo() = 5`.  In such
288                 // cases, there is no need to check for conflicts
289                 // with moves etc, just ignore.
290             }
291         }
292     }
293
294     /// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or
295     /// reports an error.  This may entail taking out loans, which will be added to the
296     /// `req_loan_map`.
297     fn guarantee_valid(&mut self,
298                        borrow_id: ast::NodeId,
299                        borrow_span: Span,
300                        cmt: mc::cmt<'tcx>,
301                        req_kind: ty::BorrowKind,
302                        loan_region: ty::Region<'tcx>,
303                        cause: euv::LoanCause) {
304         debug!("guarantee_valid(borrow_id={}, cmt={:?}, \
305                 req_mutbl={:?}, loan_region={:?})",
306                borrow_id,
307                cmt,
308                req_kind,
309                loan_region);
310
311         // a loan for the empty region can never be dereferenced, so
312         // it is always safe
313         if *loan_region == ty::ReEmpty {
314             return;
315         }
316
317         // Check that the lifetime of the borrow does not exceed
318         // the lifetime of the data being borrowed.
319         if lifetime::guarantee_lifetime(self.bccx, self.item_ub,
320                                         borrow_span, cause, cmt.clone(), loan_region,
321                                         req_kind).is_err() {
322             return; // reported an error, no sense in reporting more.
323         }
324
325         // Check that we don't allow mutable borrows of non-mutable data.
326         if check_mutability(self.bccx, borrow_span, BorrowViolation(cause),
327                             cmt.clone(), req_kind).is_err() {
328             return; // reported an error, no sense in reporting more.
329         }
330
331         // Check that we don't allow mutable borrows of aliasable data.
332         if check_aliasability(self.bccx, borrow_span, BorrowViolation(cause),
333                               cmt.clone(), req_kind).is_err() {
334             return; // reported an error, no sense in reporting more.
335         }
336
337         // Compute the restrictions that are required to enforce the
338         // loan is safe.
339         let restr = restrictions::compute_restrictions(
340             self.bccx, borrow_span, cause,
341             cmt.clone(), 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::ReFree(ref fr) => {
357                         self.bccx.region_maps.free_extent(self.tcx(), fr)
358                     }
359
360                     ty::ReStatic => self.item_ub,
361
362                     ty::ReEmpty |
363                     ty::ReLateBound(..) |
364                     ty::ReEarlyBound(..) |
365                     ty::ReVar(..) |
366                     ty::ReSkolemized(..) |
367                     ty::ReErased => {
368                         span_bug!(
369                             cmt.span,
370                             "invalid borrow lifetime: {:?}",
371                             loan_region);
372                     }
373                 };
374                 debug!("loan_scope = {:?}", loan_scope);
375
376                 let borrow_scope = self.tcx().node_extent(borrow_id);
377                 let gen_scope = self.compute_gen_scope(borrow_scope, loan_scope);
378                 debug!("gen_scope = {:?}", gen_scope);
379
380                 let kill_scope = self.compute_kill_scope(loan_scope, &loan_path);
381                 debug!("kill_scope = {:?}", kill_scope);
382
383                 if req_kind == ty::MutBorrow {
384                     self.mark_loan_path_as_mutated(&loan_path);
385                 }
386
387                 Loan {
388                     index: self.all_loans.len(),
389                     loan_path: loan_path,
390                     kind: req_kind,
391                     gen_scope: gen_scope,
392                     kill_scope: kill_scope,
393                     span: borrow_span,
394                     restricted_paths: restricted_paths,
395                     cause: cause,
396                 }
397             }
398         };
399
400         debug!("guarantee_valid(borrow_id={}), loan={:?}",
401                borrow_id, loan);
402
403         // let loan_path = loan.loan_path;
404         // let loan_gen_scope = loan.gen_scope;
405         // let loan_kill_scope = loan.kill_scope;
406         self.all_loans.push(loan);
407
408         // if loan_gen_scope != borrow_id {
409             // FIXME(#6268) Nested method calls
410             //
411             // Typically, the scope of the loan includes the point at
412             // which the loan is originated. This
413             // This is a subtle case. See the test case
414             // <compile-fail/borrowck-bad-nested-calls-free.rs>
415             // to see what we are guarding against.
416
417             //let restr = restrictions::compute_restrictions(
418             //    self.bccx, borrow_span, cmt, RESTR_EMPTY);
419             //let loan = {
420             //    let all_loans = &mut *self.all_loans; // FIXME(#5074)
421             //    Loan {
422             //        index: all_loans.len(),
423             //        loan_path: loan_path,
424             //        cmt: cmt,
425             //        mutbl: ConstMutability,
426             //        gen_scope: borrow_id,
427             //        kill_scope: kill_scope,
428             //        span: borrow_span,
429             //        restrictions: restrictions
430             //    }
431         // }
432     }
433
434     pub fn mark_loan_path_as_mutated(&self, loan_path: &LoanPath) {
435         //! For mutable loans of content whose mutability derives
436         //! from a local variable, mark the mutability decl as necessary.
437
438         match loan_path.kind {
439             LpVar(local_id) |
440             LpUpvar(ty::UpvarId{ var_id: local_id, closure_expr_id: _ }) => {
441                 self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
442             }
443             LpDowncast(ref base, _) |
444             LpExtend(ref base, mc::McInherited, _) |
445             LpExtend(ref base, mc::McDeclared, _) => {
446                 self.mark_loan_path_as_mutated(&base);
447             }
448             LpExtend(_, mc::McImmutable, _) => {
449                 // Nothing to do.
450             }
451         }
452     }
453
454     pub fn compute_gen_scope(&self,
455                              borrow_scope: region::CodeExtent<'tcx>,
456                              loan_scope: region::CodeExtent<'tcx>)
457                              -> region::CodeExtent<'tcx> {
458         //! Determine when to introduce the loan. Typically the loan
459         //! is introduced at the point of the borrow, but in some cases,
460         //! notably method arguments, the loan may be introduced only
461         //! later, once it comes into scope.
462
463         if self.bccx.region_maps.is_subscope_of(borrow_scope, loan_scope) {
464             borrow_scope
465         } else {
466             loan_scope
467         }
468     }
469
470     pub fn compute_kill_scope(&self, loan_scope: region::CodeExtent<'tcx>, lp: &LoanPath<'tcx>)
471                               -> region::CodeExtent<'tcx> {
472         //! Determine when the loan restrictions go out of scope.
473         //! This is either when the lifetime expires or when the
474         //! local variable which roots the loan-path goes out of scope,
475         //! whichever happens faster.
476         //!
477         //! It may seem surprising that we might have a loan region
478         //! larger than the variable which roots the loan-path; this can
479         //! come about when variables of `&mut` type are re-borrowed,
480         //! as in this example:
481         //!
482         //!     struct Foo { counter: u32 }
483         //!
484         //!     fn counter<'a>(v: &'a mut Foo) -> &'a mut u32 {
485         //!         &mut v.counter
486         //!     }
487         //!
488         //! In this case, the reference (`'a`) outlives the
489         //! variable `v` that hosts it. Note that this doesn't come up
490         //! with immutable `&` pointers, because borrows of such pointers
491         //! do not require restrictions and hence do not cause a loan.
492
493         let lexical_scope = lp.kill_scope(self.bccx);
494         if self.bccx.region_maps.is_subscope_of(lexical_scope, loan_scope) {
495             lexical_scope
496         } else {
497             assert!(self.bccx.region_maps.is_subscope_of(loan_scope, lexical_scope));
498             loan_scope
499         }
500     }
501
502     pub fn report_potential_errors(&self) {
503         self.move_error_collector.report_potential_errors(self.bccx);
504     }
505 }