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