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