]> git.proxmox.com Git - rustc.git/blob - src/librustc_ast_borrowck/borrowck/gather_loans/mod.rs
New upstream version 1.39.0+dfsg1
[rustc.git] / src / librustc_ast_borrowck / borrowck / gather_loans / mod.rs
1 // ----------------------------------------------------------------------
2 // Gathering loans
3 //
4 // The borrow check proceeds in two phases. In phase one, we gather the full
5 // set of loans that are required at any point. These are sorted according to
6 // their associated scopes. In phase two, checking loans, we will then make
7 // sure that all of these loans are honored.
8
9 use crate::borrowck::*;
10 use crate::borrowck::move_data::MoveData;
11 use rustc::middle::expr_use_visitor as euv;
12 use rustc::middle::mem_categorization as mc;
13 use rustc::middle::mem_categorization::Categorization;
14 use rustc::middle::region;
15 use rustc::ty::{self, TyCtxt};
16
17 use syntax_pos::Span;
18 use rustc::hir;
19 use log::debug;
20
21 use restrictions::RestrictionResult;
22
23 mod lifetime;
24 mod restrictions;
25 mod gather_moves;
26
27 pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
28 body: hir::BodyId)
29 -> (Vec<Loan<'tcx>>, move_data::MoveData<'tcx>) {
30 let def_id = bccx.tcx.hir().body_owner_def_id(body);
31 let param_env = bccx.tcx.param_env(def_id);
32 let mut glcx = GatherLoanCtxt {
33 bccx,
34 all_loans: Vec::new(),
35 item_ub: region::Scope {
36 id: bccx.tcx.hir().body(body).value.hir_id.local_id,
37 data: region::ScopeData::Node
38 },
39 move_data: MoveData::default(),
40 };
41
42 let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id);
43 euv::ExprUseVisitor::new(&mut glcx,
44 bccx.tcx,
45 def_id,
46 param_env,
47 &bccx.region_scope_tree,
48 bccx.tables,
49 Some(rvalue_promotable_map))
50 .consume_body(bccx.body);
51
52 let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
53 (all_loans, move_data)
54 }
55
56 struct GatherLoanCtxt<'a, 'tcx> {
57 bccx: &'a BorrowckCtxt<'a, 'tcx>,
58 move_data: move_data::MoveData<'tcx>,
59 all_loans: Vec<Loan<'tcx>>,
60 /// `item_ub` is used as an upper-bound on the lifetime whenever we
61 /// ask for the scope of an expression categorized as an upvar.
62 item_ub: region::Scope,
63 }
64
65 impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
66 fn consume(&mut self,
67 consume_id: hir::HirId,
68 _consume_span: Span,
69 cmt: &mc::cmt_<'tcx>,
70 mode: euv::ConsumeMode) {
71 debug!("consume(consume_id={}, cmt={:?}, mode={:?})",
72 consume_id, cmt, mode);
73
74 match mode {
75 euv::Move(_) => {
76 gather_moves::gather_move_from_expr(
77 self.bccx, &self.move_data,
78 consume_id.local_id, cmt);
79 }
80 euv::Copy => { }
81 }
82 }
83
84 fn matched_pat(&mut self,
85 matched_pat: &hir::Pat,
86 cmt: &mc::cmt_<'tcx>,
87 mode: euv::MatchMode) {
88 debug!("matched_pat(matched_pat={:?}, cmt={:?}, mode={:?})",
89 matched_pat,
90 cmt,
91 mode);
92 }
93
94 fn consume_pat(&mut self,
95 consume_pat: &hir::Pat,
96 cmt: &mc::cmt_<'tcx>,
97 mode: euv::ConsumeMode) {
98 debug!("consume_pat(consume_pat={:?}, cmt={:?}, mode={:?})",
99 consume_pat,
100 cmt,
101 mode);
102
103 match mode {
104 euv::Copy => { return; }
105 euv::Move(_) => { }
106 }
107
108 gather_moves::gather_move_from_pat(
109 self.bccx, &self.move_data,
110 consume_pat, cmt);
111 }
112
113 fn borrow(&mut self,
114 borrow_id: hir::HirId,
115 _: Span,
116 cmt: &mc::cmt_<'tcx>,
117 loan_region: ty::Region<'tcx>,
118 bk: ty::BorrowKind,
119 loan_cause: euv::LoanCause)
120 {
121 debug!("borrow(borrow_id={}, cmt={:?}, loan_region={:?}, \
122 bk={:?}, loan_cause={:?})",
123 borrow_id, cmt, loan_region,
124 bk, loan_cause);
125
126 self.guarantee_valid(borrow_id.local_id,
127 cmt,
128 bk,
129 loan_region);
130 }
131
132 fn mutate(&mut self,
133 assignment_id: hir::HirId,
134 assignment_span: Span,
135 assignee_cmt: &mc::cmt_<'tcx>,
136 _: euv::MutateMode)
137 {
138 self.guarantee_assignment_valid(assignment_id,
139 assignment_span,
140 assignee_cmt);
141 }
142
143 fn decl_without_init(&mut self, id: hir::HirId, _span: Span) {
144 let ty = self.bccx
145 .tables
146 .node_type(id);
147 gather_moves::gather_decl(self.bccx, &self.move_data, id, ty);
148 }
149
150 fn nested_body(&mut self, body_id: hir::BodyId) {
151 debug!("nested_body(body_id={:?})", body_id);
152 // rust-lang/rust#58776: MIR and AST borrow check disagree on where
153 // certain closure errors are reported. As such migrate borrowck has to
154 // operate at the level of items, rather than bodies. Check if the
155 // contained closure had any errors and set `signalled_any_error` if it
156 // has.
157 let bccx = self.bccx;
158 if bccx.tcx.migrate_borrowck() {
159 if let SignalledError::NoErrorsSeen = bccx.signalled_any_error.get() {
160 let closure_def_id = bccx.tcx.hir().body_owner_def_id(body_id);
161 debug!("checking closure: {:?}", closure_def_id);
162
163 bccx.signalled_any_error.set(bccx.tcx.borrowck(closure_def_id).signalled_any_error);
164 }
165 }
166 }
167 }
168
169 /// Implements the A-* rules in README.md.
170 fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
171 cmt: &mc::cmt_<'tcx>,
172 req_kind: ty::BorrowKind)
173 -> Result<(),()> {
174
175 let aliasability = cmt.freely_aliasable();
176 debug!("check_aliasability aliasability={:?} req_kind={:?}",
177 aliasability, req_kind);
178
179 match (aliasability, req_kind) {
180 (mc::Aliasability::NonAliasable, _) => {
181 /* Uniquely accessible path -- OK for `&` and `&mut` */
182 Ok(())
183 }
184 (mc::Aliasability::FreelyAliasable(mc::AliasableStatic), ty::ImmBorrow) => {
185 // Borrow of an immutable static item.
186 Ok(())
187 }
188 (mc::Aliasability::FreelyAliasable(mc::AliasableStaticMut), _) => {
189 // Even touching a static mut is considered unsafe. We assume the
190 // user knows what they're doing in these cases.
191 Ok(())
192 }
193 (mc::Aliasability::FreelyAliasable(_), ty::UniqueImmBorrow) |
194 (mc::Aliasability::FreelyAliasable(_), ty::MutBorrow) => {
195 bccx.signal_error();
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 cmt: &mc::cmt_<'tcx>,
207 req_kind: ty::BorrowKind)
208 -> Result<(),()> {
209 debug!("check_mutability(cmt={:?} req_kind={:?}", cmt, req_kind);
210 match req_kind {
211 ty::UniqueImmBorrow | ty::ImmBorrow => {
212 match cmt.mutbl {
213 // I am intentionally leaving this here to help
214 // refactoring if, in the future, we should add new
215 // kinds of mutability.
216 mc::McImmutable | mc::McDeclared | mc::McInherited => {
217 // both imm and mut data can be lent as imm;
218 // for mutable data, this is a freeze
219 Ok(())
220 }
221 }
222 }
223
224 ty::MutBorrow => {
225 // Only mutable data can be lent as mutable.
226 if !cmt.mutbl.is_mutable() {
227 Err(bccx.signal_error())
228 } else {
229 Ok(())
230 }
231 }
232 }
233 }
234
235 impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
236 pub fn tcx(&self) -> TyCtxt<'tcx> { self.bccx.tcx }
237
238 /// Guarantees that `cmt` is assignable, or reports an error.
239 fn guarantee_assignment_valid(&mut self,
240 assignment_id: hir::HirId,
241 assignment_span: Span,
242 cmt: &mc::cmt_<'tcx>) {
243
244 let opt_lp = opt_loan_path(cmt);
245 debug!("guarantee_assignment_valid(assignment_id={}, cmt={:?}) opt_lp={:?}",
246 assignment_id, cmt, opt_lp);
247
248 if let Categorization::Local(..) = cmt.cat {
249 // Only re-assignments to locals require it to be
250 // mutable - this is checked in check_loans.
251 } else {
252 // Check that we don't allow assignments to non-mutable data.
253 if check_mutability(self.bccx, cmt, ty::MutBorrow).is_err() {
254 return; // reported an error, no sense in reporting more.
255 }
256 }
257
258 // Check that we don't allow assignments to aliasable data
259 if check_aliasability(self.bccx, cmt, ty::MutBorrow).is_err() {
260 return; // reported an error, no sense in reporting more.
261 }
262
263 match opt_lp {
264 Some(lp) => {
265 gather_moves::gather_assignment(self.bccx, &self.move_data,
266 assignment_id.local_id,
267 assignment_span,
268 lp);
269 }
270 None => {
271 // This can occur with e.g., `*foo() = 5`. In such
272 // cases, there is no need to check for conflicts
273 // with moves etc, just ignore.
274 }
275 }
276 }
277
278 /// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or
279 /// reports an error. This may entail taking out loans, which will be added to the
280 /// `req_loan_map`.
281 fn guarantee_valid(&mut self,
282 borrow_id: hir::ItemLocalId,
283 cmt: &mc::cmt_<'tcx>,
284 req_kind: ty::BorrowKind,
285 loan_region: ty::Region<'tcx>) {
286 debug!("guarantee_valid(borrow_id={:?}, cmt={:?}, \
287 req_mutbl={:?}, loan_region={:?})",
288 borrow_id,
289 cmt,
290 req_kind,
291 loan_region);
292
293 // a loan for the empty region can never be dereferenced, so
294 // it is always safe
295 if *loan_region == ty::ReEmpty {
296 return;
297 }
298
299 // Check that the lifetime of the borrow does not exceed
300 // the lifetime of the data being borrowed.
301 if lifetime::guarantee_lifetime(self.bccx, self.item_ub, cmt, loan_region).is_err() {
302 return; // reported an error, no sense in reporting more.
303 }
304
305 // Check that we don't allow mutable borrows of non-mutable data.
306 if check_mutability(self.bccx, cmt, req_kind).is_err() {
307 return; // reported an error, no sense in reporting more.
308 }
309
310 // Check that we don't allow mutable borrows of aliasable data.
311 if check_aliasability(self.bccx, cmt, req_kind).is_err() {
312 return; // reported an error, no sense in reporting more.
313 }
314
315 // Compute the restrictions that are required to enforce the
316 // loan is safe.
317 let restr = restrictions::compute_restrictions(self.bccx, &cmt, loan_region);
318
319 debug!("guarantee_valid(): restrictions={:?}", restr);
320
321 // Create the loan record (if needed).
322 let loan = match restr {
323 RestrictionResult::Safe => {
324 // No restrictions---no loan record necessary
325 return;
326 }
327
328 RestrictionResult::SafeIf(loan_path, restricted_paths) => {
329 let loan_scope = match *loan_region {
330 ty::ReScope(scope) => scope,
331
332 ty::ReEarlyBound(ref br) => {
333 self.bccx.region_scope_tree.early_free_scope(self.tcx(), br)
334 }
335
336 ty::ReFree(ref fr) => {
337 self.bccx.region_scope_tree.free_scope(self.tcx(), fr)
338 }
339
340 ty::ReStatic => self.item_ub,
341
342 ty::ReEmpty |
343 ty::ReClosureBound(..) |
344 ty::ReLateBound(..) |
345 ty::ReVar(..) |
346 ty::RePlaceholder(..) |
347 ty::ReErased => {
348 span_bug!(
349 cmt.span,
350 "invalid borrow lifetime: {:?}",
351 loan_region);
352 }
353 };
354 debug!("loan_scope = {:?}", loan_scope);
355
356 let borrow_scope = region::Scope {
357 id: borrow_id,
358 data: region::ScopeData::Node
359 };
360 let gen_scope = self.compute_gen_scope(borrow_scope, loan_scope);
361 debug!("gen_scope = {:?}", gen_scope);
362
363 let kill_scope = self.compute_kill_scope(loan_scope, &loan_path);
364 debug!("kill_scope = {:?}", kill_scope);
365
366 Loan {
367 index: self.all_loans.len(),
368 loan_path,
369 kind: req_kind,
370 gen_scope,
371 kill_scope,
372 restricted_paths,
373 }
374 }
375 };
376
377 debug!("guarantee_valid(borrow_id={:?}), loan={:?}",
378 borrow_id, loan);
379
380 // let loan_path = loan.loan_path;
381 // let loan_gen_scope = loan.gen_scope;
382 // let loan_kill_scope = loan.kill_scope;
383 self.all_loans.push(loan);
384 }
385
386 pub fn compute_gen_scope(&self,
387 borrow_scope: region::Scope,
388 loan_scope: region::Scope)
389 -> region::Scope {
390 //! Determine when to introduce the loan. Typically the loan
391 //! is introduced at the point of the borrow, but in some cases,
392 //! notably method arguments, the loan may be introduced only
393 //! later, once it comes into scope.
394
395 if self.bccx.region_scope_tree.is_subscope_of(borrow_scope, loan_scope) {
396 borrow_scope
397 } else {
398 loan_scope
399 }
400 }
401
402 pub fn compute_kill_scope(&self, loan_scope: region::Scope, lp: &LoanPath<'tcx>)
403 -> region::Scope {
404 //! Determine when the loan restrictions go out of scope.
405 //! This is either when the lifetime expires or when the
406 //! local variable which roots the loan-path goes out of scope,
407 //! whichever happens faster.
408 //!
409 //! It may seem surprising that we might have a loan region
410 //! larger than the variable which roots the loan-path; this can
411 //! come about when variables of `&mut` type are re-borrowed,
412 //! as in this example:
413 //!
414 //! struct Foo { counter: u32 }
415 //!
416 //! fn counter<'a>(v: &'a mut Foo) -> &'a mut u32 {
417 //! &mut v.counter
418 //! }
419 //!
420 //! In this case, the reference (`'a`) outlives the
421 //! variable `v` that hosts it. Note that this doesn't come up
422 //! with immutable `&` pointers, because borrows of such pointers
423 //! do not require restrictions and hence do not cause a loan.
424
425 let lexical_scope = lp.kill_scope(self.bccx);
426 if self.bccx.region_scope_tree.is_subscope_of(lexical_scope, loan_scope) {
427 lexical_scope
428 } else {
429 assert!(self.bccx.region_scope_tree.is_subscope_of(loan_scope, lexical_scope));
430 loan_scope
431 }
432 }
433 }