]> git.proxmox.com Git - rustc.git/blob - src/librustc_borrowck/borrowck/gather_loans/mod.rs
New upstream version 1.20.0+dfsg1
[rustc.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 }