]>
Commit | Line | Data |
---|---|---|
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 | ||
85aaf69f | 11 | //! See The Book chapter on the borrow checker for more details. |
1a4d82fc JJ |
12 | |
13 | #![allow(non_camel_case_types)] | |
14 | ||
15 | pub use self::LoanPathKind::*; | |
16 | pub use self::LoanPathElem::*; | |
17 | pub use self::bckerr_code::*; | |
18 | pub use self::AliasableViolationKind::*; | |
19 | pub use self::MovedValueUseKind::*; | |
20 | ||
85aaf69f SL |
21 | use self::InteriorKind::*; |
22 | ||
e9174d1e | 23 | use rustc::front::map as hir_map; |
92a42be0 | 24 | use rustc::front::map::blocks::FnParts; |
1a4d82fc JJ |
25 | use rustc::middle::cfg; |
26 | use rustc::middle::dataflow::DataFlowContext; | |
27 | use rustc::middle::dataflow::BitwiseOperator; | |
28 | use rustc::middle::dataflow::DataFlowOperator; | |
9346a6ac | 29 | use rustc::middle::dataflow::KillFrom; |
e9174d1e | 30 | use rustc::middle::def_id::DefId; |
1a4d82fc | 31 | use rustc::middle::expr_use_visitor as euv; |
bd371182 | 32 | use rustc::middle::free_region::FreeRegionMap; |
62682a34 | 33 | use rustc::middle::mem_categorization as mc; |
92a42be0 | 34 | use rustc::middle::mem_categorization::Categorization; |
1a4d82fc JJ |
35 | use rustc::middle::region; |
36 | use rustc::middle::ty::{self, Ty}; | |
62682a34 SL |
37 | |
38 | use std::fmt; | |
bd371182 | 39 | use std::mem; |
1a4d82fc | 40 | use std::rc::Rc; |
e9174d1e | 41 | use syntax::ast::{self, NodeId}; |
1a4d82fc | 42 | use syntax::codemap::Span; |
e9174d1e SL |
43 | |
44 | use rustc_front::hir; | |
45 | use rustc_front::hir::{FnDecl, Block}; | |
92a42be0 SL |
46 | use rustc_front::intravisit; |
47 | use rustc_front::intravisit::{Visitor, FnKind}; | |
e9174d1e | 48 | use rustc_front::util as hir_util; |
1a4d82fc | 49 | |
1a4d82fc JJ |
50 | pub mod check_loans; |
51 | ||
52 | pub mod gather_loans; | |
53 | ||
54 | pub mod move_data; | |
55 | ||
56 | #[derive(Clone, Copy)] | |
57 | pub struct LoanDataFlowOperator; | |
58 | ||
59 | pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>; | |
60 | ||
61 | impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> { | |
62 | fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, | |
63 | b: &'v Block, s: Span, id: ast::NodeId) { | |
bd371182 | 64 | match fk { |
e9174d1e SL |
65 | FnKind::ItemFn(..) | |
66 | FnKind::Method(..) => { | |
bd371182 AL |
67 | let new_free_region_map = self.tcx.free_region_map(id); |
68 | let old_free_region_map = | |
69 | mem::replace(&mut self.free_region_map, new_free_region_map); | |
70 | borrowck_fn(self, fk, fd, b, s, id); | |
71 | self.free_region_map = old_free_region_map; | |
72 | } | |
73 | ||
e9174d1e | 74 | FnKind::Closure => { |
bd371182 AL |
75 | borrowck_fn(self, fk, fd, b, s, id); |
76 | } | |
77 | } | |
1a4d82fc JJ |
78 | } |
79 | ||
e9174d1e | 80 | fn visit_item(&mut self, item: &hir::Item) { |
1a4d82fc JJ |
81 | borrowck_item(self, item); |
82 | } | |
d9579d0f | 83 | |
e9174d1e SL |
84 | fn visit_trait_item(&mut self, ti: &hir::TraitItem) { |
85 | if let hir::ConstTraitItem(_, Some(ref expr)) = ti.node { | |
d9579d0f AL |
86 | gather_loans::gather_loans_in_static_initializer(self, &*expr); |
87 | } | |
92a42be0 | 88 | intravisit::walk_trait_item(self, ti); |
d9579d0f AL |
89 | } |
90 | ||
e9174d1e | 91 | fn visit_impl_item(&mut self, ii: &hir::ImplItem) { |
92a42be0 | 92 | if let hir::ImplItemKind::Const(_, ref expr) = ii.node { |
d9579d0f AL |
93 | gather_loans::gather_loans_in_static_initializer(self, &*expr); |
94 | } | |
92a42be0 | 95 | intravisit::walk_impl_item(self, ii); |
d9579d0f | 96 | } |
1a4d82fc JJ |
97 | } |
98 | ||
99 | pub fn check_crate(tcx: &ty::ctxt) { | |
100 | let mut bccx = BorrowckCtxt { | |
101 | tcx: tcx, | |
bd371182 | 102 | free_region_map: FreeRegionMap::new(), |
1a4d82fc JJ |
103 | stats: BorrowStats { |
104 | loaned_paths_same: 0, | |
105 | loaned_paths_imm: 0, | |
106 | stable_paths: 0, | |
107 | guaranteed_paths: 0 | |
108 | } | |
109 | }; | |
110 | ||
92a42be0 | 111 | tcx.map.krate().visit_all_items(&mut bccx); |
1a4d82fc JJ |
112 | |
113 | if tcx.sess.borrowck_stats() { | |
114 | println!("--- borrowck stats ---"); | |
115 | println!("paths requiring guarantees: {}", | |
116 | bccx.stats.guaranteed_paths); | |
117 | println!("paths requiring loans : {}", | |
118 | make_stat(&bccx, bccx.stats.loaned_paths_same)); | |
119 | println!("paths requiring imm loans : {}", | |
120 | make_stat(&bccx, bccx.stats.loaned_paths_imm)); | |
121 | println!("stable paths : {}", | |
122 | make_stat(&bccx, bccx.stats.stable_paths)); | |
123 | } | |
124 | ||
c34b1796 | 125 | fn make_stat(bccx: &BorrowckCtxt, stat: usize) -> String { |
1a4d82fc JJ |
126 | let total = bccx.stats.guaranteed_paths as f64; |
127 | let perc = if total == 0.0 { 0.0 } else { stat as f64 * 100.0 / total }; | |
128 | format!("{} ({:.0}%)", stat, perc) | |
129 | } | |
130 | } | |
131 | ||
e9174d1e | 132 | fn borrowck_item(this: &mut BorrowckCtxt, item: &hir::Item) { |
1a4d82fc JJ |
133 | // Gather loans for items. Note that we don't need |
134 | // to check loans for single expressions. The check | |
135 | // loan step is intended for things that have a data | |
136 | // flow dependent conditions. | |
137 | match item.node { | |
e9174d1e SL |
138 | hir::ItemStatic(_, _, ref ex) | |
139 | hir::ItemConst(_, ref ex) => { | |
1a4d82fc JJ |
140 | gather_loans::gather_loans_in_static_initializer(this, &**ex); |
141 | } | |
c34b1796 | 142 | _ => { } |
1a4d82fc | 143 | } |
c34b1796 | 144 | |
92a42be0 | 145 | intravisit::walk_item(this, item); |
1a4d82fc JJ |
146 | } |
147 | ||
148 | /// Collection of conclusions determined via borrow checker analyses. | |
149 | pub struct AnalysisData<'a, 'tcx: 'a> { | |
150 | pub all_loans: Vec<Loan<'tcx>>, | |
151 | pub loans: DataFlowContext<'a, 'tcx, LoanDataFlowOperator>, | |
152 | pub move_data: move_data::FlowedMoveData<'a, 'tcx>, | |
153 | } | |
154 | ||
155 | fn borrowck_fn(this: &mut BorrowckCtxt, | |
156 | fk: FnKind, | |
e9174d1e SL |
157 | decl: &hir::FnDecl, |
158 | body: &hir::Block, | |
1a4d82fc JJ |
159 | sp: Span, |
160 | id: ast::NodeId) { | |
161 | debug!("borrowck_fn(id={})", id); | |
162 | let cfg = cfg::CFG::new(this.tcx, body); | |
163 | let AnalysisData { all_loans, | |
164 | loans: loan_dfcx, | |
bd371182 | 165 | move_data: flowed_moves } = |
1a4d82fc JJ |
166 | build_borrowck_dataflow_data(this, fk, decl, &cfg, body, sp, id); |
167 | ||
168 | move_data::fragments::instrument_move_fragments(&flowed_moves.move_data, | |
bd371182 AL |
169 | this.tcx, |
170 | sp, | |
171 | id); | |
c1a9b12d SL |
172 | move_data::fragments::build_unfragmented_map(this, |
173 | &flowed_moves.move_data, | |
174 | id); | |
1a4d82fc JJ |
175 | |
176 | check_loans::check_loans(this, | |
177 | &loan_dfcx, | |
c1a9b12d | 178 | &flowed_moves, |
85aaf69f | 179 | &all_loans[..], |
1a4d82fc JJ |
180 | id, |
181 | decl, | |
182 | body); | |
183 | ||
92a42be0 | 184 | intravisit::walk_fn(this, fk, decl, body, sp); |
1a4d82fc JJ |
185 | } |
186 | ||
187 | fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>, | |
188 | fk: FnKind, | |
e9174d1e | 189 | decl: &hir::FnDecl, |
1a4d82fc | 190 | cfg: &cfg::CFG, |
e9174d1e | 191 | body: &hir::Block, |
1a4d82fc | 192 | sp: Span, |
bd371182 AL |
193 | id: ast::NodeId) |
194 | -> AnalysisData<'a, 'tcx> | |
195 | { | |
1a4d82fc | 196 | // Check the body of fn items. |
e9174d1e SL |
197 | let tcx = this.tcx; |
198 | let id_range = hir_util::compute_id_range_for_fn_body(fk, decl, body, sp, id); | |
1a4d82fc JJ |
199 | let (all_loans, move_data) = |
200 | gather_loans::gather_loans_in_fn(this, id, decl, body); | |
201 | ||
202 | let mut loan_dfcx = | |
203 | DataFlowContext::new(this.tcx, | |
204 | "borrowck", | |
205 | Some(decl), | |
206 | cfg, | |
207 | LoanDataFlowOperator, | |
208 | id_range, | |
209 | all_loans.len()); | |
210 | for (loan_idx, loan) in all_loans.iter().enumerate() { | |
e9174d1e SL |
211 | loan_dfcx.add_gen(loan.gen_scope.node_id(&tcx.region_maps), loan_idx); |
212 | loan_dfcx.add_kill(KillFrom::ScopeEnd, | |
213 | loan.kill_scope.node_id(&tcx.region_maps), loan_idx); | |
1a4d82fc JJ |
214 | } |
215 | loan_dfcx.add_kills_from_flow_exits(cfg); | |
216 | loan_dfcx.propagate(cfg, body); | |
217 | ||
218 | let flowed_moves = move_data::FlowedMoveData::new(move_data, | |
219 | this.tcx, | |
220 | cfg, | |
221 | id_range, | |
222 | decl, | |
223 | body); | |
224 | ||
225 | AnalysisData { all_loans: all_loans, | |
226 | loans: loan_dfcx, | |
227 | move_data:flowed_moves } | |
228 | } | |
229 | ||
1a4d82fc JJ |
230 | /// Accessor for introspective clients inspecting `AnalysisData` and |
231 | /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer. | |
232 | pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>( | |
233 | tcx: &'a ty::ctxt<'tcx>, | |
92a42be0 SL |
234 | fn_parts: FnParts<'a>, |
235 | cfg: &cfg::CFG) | |
bd371182 AL |
236 | -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>) |
237 | { | |
1a4d82fc JJ |
238 | |
239 | let mut bccx = BorrowckCtxt { | |
240 | tcx: tcx, | |
bd371182 | 241 | free_region_map: FreeRegionMap::new(), |
1a4d82fc JJ |
242 | stats: BorrowStats { |
243 | loaned_paths_same: 0, | |
244 | loaned_paths_imm: 0, | |
245 | stable_paths: 0, | |
246 | guaranteed_paths: 0 | |
247 | } | |
248 | }; | |
249 | ||
1a4d82fc | 250 | let dataflow_data = build_borrowck_dataflow_data(&mut bccx, |
92a42be0 SL |
251 | fn_parts.kind, |
252 | &*fn_parts.decl, | |
253 | cfg, | |
254 | &*fn_parts.body, | |
255 | fn_parts.span, | |
256 | fn_parts.id); | |
1a4d82fc JJ |
257 | |
258 | (bccx, dataflow_data) | |
259 | } | |
260 | ||
261 | // ---------------------------------------------------------------------- | |
262 | // Type definitions | |
263 | ||
264 | pub struct BorrowckCtxt<'a, 'tcx: 'a> { | |
265 | tcx: &'a ty::ctxt<'tcx>, | |
266 | ||
bd371182 AL |
267 | // Hacky. As we visit various fns, we have to load up the |
268 | // free-region map for each one. This map is computed by during | |
269 | // typeck for each fn item and stored -- closures just use the map | |
270 | // from the fn item that encloses them. Since we walk the fns in | |
271 | // order, we basically just overwrite this field as we enter a fn | |
272 | // item and restore it afterwards in a stack-like fashion. Then | |
273 | // the borrow checking code can assume that `free_region_map` is | |
274 | // always the correct map for the current fn. Feels like it'd be | |
275 | // better to just recompute this, rather than store it, but it's a | |
276 | // bit of a pain to factor that code out at the moment. | |
277 | free_region_map: FreeRegionMap, | |
278 | ||
1a4d82fc JJ |
279 | // Statistics: |
280 | stats: BorrowStats | |
281 | } | |
282 | ||
283 | struct BorrowStats { | |
c34b1796 AL |
284 | loaned_paths_same: usize, |
285 | loaned_paths_imm: usize, | |
286 | stable_paths: usize, | |
287 | guaranteed_paths: usize | |
1a4d82fc JJ |
288 | } |
289 | ||
290 | pub type BckResult<'tcx, T> = Result<T, BckError<'tcx>>; | |
291 | ||
292 | /////////////////////////////////////////////////////////////////////////// | |
293 | // Loans and loan paths | |
294 | ||
295 | /// Record of a loan that was issued. | |
296 | pub struct Loan<'tcx> { | |
c34b1796 | 297 | index: usize, |
1a4d82fc JJ |
298 | loan_path: Rc<LoanPath<'tcx>>, |
299 | kind: ty::BorrowKind, | |
300 | restricted_paths: Vec<Rc<LoanPath<'tcx>>>, | |
301 | ||
302 | /// gen_scope indicates where loan is introduced. Typically the | |
303 | /// loan is introduced at the point of the borrow, but in some | |
304 | /// cases, notably method arguments, the loan may be introduced | |
305 | /// only later, once it comes into scope. See also | |
306 | /// `GatherLoanCtxt::compute_gen_scope`. | |
307 | gen_scope: region::CodeExtent, | |
308 | ||
309 | /// kill_scope indicates when the loan goes out of scope. This is | |
310 | /// either when the lifetime expires or when the local variable | |
311 | /// which roots the loan-path goes out of scope, whichever happens | |
312 | /// faster. See also `GatherLoanCtxt::compute_kill_scope`. | |
313 | kill_scope: region::CodeExtent, | |
314 | span: Span, | |
315 | cause: euv::LoanCause, | |
316 | } | |
317 | ||
318 | impl<'tcx> Loan<'tcx> { | |
319 | pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> { | |
320 | self.loan_path.clone() | |
321 | } | |
322 | } | |
323 | ||
62682a34 | 324 | #[derive(Eq, Hash)] |
1a4d82fc JJ |
325 | pub struct LoanPath<'tcx> { |
326 | kind: LoanPathKind<'tcx>, | |
327 | ty: ty::Ty<'tcx>, | |
328 | } | |
329 | ||
330 | impl<'tcx> PartialEq for LoanPath<'tcx> { | |
331 | fn eq(&self, that: &LoanPath<'tcx>) -> bool { | |
332 | let r = self.kind == that.kind; | |
333 | debug_assert!(self.ty == that.ty || !r, | |
334 | "Somehow loan paths are equal though their tys are not."); | |
335 | r | |
336 | } | |
337 | } | |
338 | ||
85aaf69f | 339 | #[derive(PartialEq, Eq, Hash, Debug)] |
1a4d82fc | 340 | pub enum LoanPathKind<'tcx> { |
c34b1796 | 341 | LpVar(ast::NodeId), // `x` in README.md |
1a4d82fc | 342 | LpUpvar(ty::UpvarId), // `x` captured by-value into closure |
e9174d1e | 343 | LpDowncast(Rc<LoanPath<'tcx>>, DefId), // `x` downcast to particular enum variant |
1a4d82fc JJ |
344 | LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem) |
345 | } | |
346 | ||
347 | impl<'tcx> LoanPath<'tcx> { | |
348 | fn new(kind: LoanPathKind<'tcx>, ty: ty::Ty<'tcx>) -> LoanPath<'tcx> { | |
349 | LoanPath { kind: kind, ty: ty } | |
350 | } | |
351 | ||
352 | fn to_type(&self) -> ty::Ty<'tcx> { self.ty } | |
353 | } | |
354 | ||
355 | // FIXME (pnkfelix): See discussion here | |
356 | // https://github.com/pnkfelix/rust/commit/ | |
357 | // b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003 | |
c34b1796 | 358 | const DOWNCAST_PRINTED_OPERATOR: &'static str = " as "; |
1a4d82fc | 359 | |
85aaf69f SL |
360 | // A local, "cleaned" version of `mc::InteriorKind` that drops |
361 | // information that is not relevant to loan-path analysis. (In | |
b039eaaf | 362 | // particular, the distinction between how precisely an array-element |
85aaf69f | 363 | // is tracked is irrelevant here.) |
62682a34 | 364 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] |
85aaf69f SL |
365 | pub enum InteriorKind { |
366 | InteriorField(mc::FieldName), | |
367 | InteriorElement(mc::ElementKind), | |
368 | } | |
369 | ||
370 | trait ToInteriorKind { fn cleaned(self) -> InteriorKind; } | |
371 | impl ToInteriorKind for mc::InteriorKind { | |
372 | fn cleaned(self) -> InteriorKind { | |
373 | match self { | |
374 | mc::InteriorField(name) => InteriorField(name), | |
375 | mc::InteriorElement(_, elem_kind) => InteriorElement(elem_kind), | |
376 | } | |
377 | } | |
378 | } | |
379 | ||
c34b1796 | 380 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] |
1a4d82fc | 381 | pub enum LoanPathElem { |
c34b1796 AL |
382 | LpDeref(mc::PointerKind), // `*LV` in README.md |
383 | LpInterior(InteriorKind), // `LV.f` in README.md | |
1a4d82fc JJ |
384 | } |
385 | ||
386 | pub fn closure_to_block(closure_id: ast::NodeId, | |
387 | tcx: &ty::ctxt) -> ast::NodeId { | |
388 | match tcx.map.get(closure_id) { | |
e9174d1e SL |
389 | hir_map::NodeExpr(expr) => match expr.node { |
390 | hir::ExprClosure(_, _, ref block) => { | |
1a4d82fc JJ |
391 | block.id |
392 | } | |
393 | _ => { | |
394 | panic!("encountered non-closure id: {}", closure_id) | |
395 | } | |
396 | }, | |
397 | _ => panic!("encountered non-expr id: {}", closure_id) | |
398 | } | |
399 | } | |
400 | ||
401 | impl<'tcx> LoanPath<'tcx> { | |
402 | pub fn kill_scope(&self, tcx: &ty::ctxt<'tcx>) -> region::CodeExtent { | |
403 | match self.kind { | |
404 | LpVar(local_id) => tcx.region_maps.var_scope(local_id), | |
405 | LpUpvar(upvar_id) => { | |
406 | let block_id = closure_to_block(upvar_id.closure_expr_id, tcx); | |
e9174d1e | 407 | tcx.region_maps.node_extent(block_id) |
1a4d82fc JJ |
408 | } |
409 | LpDowncast(ref base, _) | | |
410 | LpExtend(ref base, _, _) => base.kill_scope(tcx), | |
411 | } | |
412 | } | |
413 | ||
414 | fn has_fork(&self, other: &LoanPath<'tcx>) -> bool { | |
415 | match (&self.kind, &other.kind) { | |
416 | (&LpExtend(ref base, _, LpInterior(id)), &LpExtend(ref base2, _, LpInterior(id2))) => | |
417 | if id == id2 { | |
418 | base.has_fork(&**base2) | |
419 | } else { | |
420 | true | |
421 | }, | |
422 | (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other), | |
423 | (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&**base), | |
424 | _ => false, | |
425 | } | |
426 | } | |
427 | ||
c34b1796 | 428 | fn depth(&self) -> usize { |
1a4d82fc JJ |
429 | match self.kind { |
430 | LpExtend(ref base, _, LpDeref(_)) => base.depth(), | |
431 | LpExtend(ref base, _, LpInterior(_)) => base.depth() + 1, | |
432 | _ => 0, | |
433 | } | |
434 | } | |
435 | ||
436 | fn common(&self, other: &LoanPath<'tcx>) -> Option<LoanPath<'tcx>> { | |
437 | match (&self.kind, &other.kind) { | |
438 | (&LpExtend(ref base, a, LpInterior(id)), | |
439 | &LpExtend(ref base2, _, LpInterior(id2))) => { | |
440 | if id == id2 { | |
441 | base.common(&**base2).map(|x| { | |
442 | let xd = x.depth(); | |
443 | if base.depth() == xd && base2.depth() == xd { | |
444 | assert_eq!(base.ty, base2.ty); | |
445 | assert_eq!(self.ty, other.ty); | |
446 | LoanPath { | |
447 | kind: LpExtend(Rc::new(x), a, LpInterior(id)), | |
448 | ty: self.ty, | |
449 | } | |
450 | } else { | |
451 | x | |
452 | } | |
453 | }) | |
454 | } else { | |
455 | base.common(&**base2) | |
456 | } | |
457 | } | |
458 | (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other), | |
459 | (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&**other), | |
460 | (&LpVar(id), &LpVar(id2)) => { | |
461 | if id == id2 { | |
462 | assert_eq!(self.ty, other.ty); | |
463 | Some(LoanPath { kind: LpVar(id), ty: self.ty }) | |
464 | } else { | |
465 | None | |
466 | } | |
467 | } | |
468 | (&LpUpvar(id), &LpUpvar(id2)) => { | |
469 | if id == id2 { | |
470 | assert_eq!(self.ty, other.ty); | |
471 | Some(LoanPath { kind: LpUpvar(id), ty: self.ty }) | |
472 | } else { | |
473 | None | |
474 | } | |
475 | } | |
476 | _ => None, | |
477 | } | |
478 | } | |
479 | } | |
480 | ||
481 | pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> { | |
482 | //! Computes the `LoanPath` (if any) for a `cmt`. | |
483 | //! Note that this logic is somewhat duplicated in | |
484 | //! the method `compute()` found in `gather_loans::restrictions`, | |
485 | //! which allows it to share common loan path pieces as it | |
486 | //! traverses the CMT. | |
487 | ||
85aaf69f | 488 | let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty)); |
1a4d82fc JJ |
489 | |
490 | match cmt.cat { | |
92a42be0 SL |
491 | Categorization::Rvalue(..) | |
492 | Categorization::StaticItem => { | |
1a4d82fc JJ |
493 | None |
494 | } | |
495 | ||
92a42be0 | 496 | Categorization::Local(id) => { |
1a4d82fc JJ |
497 | Some(new_lp(LpVar(id))) |
498 | } | |
499 | ||
92a42be0 | 500 | Categorization::Upvar(mc::Upvar { id, .. }) => { |
1a4d82fc JJ |
501 | Some(new_lp(LpUpvar(id))) |
502 | } | |
503 | ||
92a42be0 | 504 | Categorization::Deref(ref cmt_base, _, pk) => { |
1a4d82fc JJ |
505 | opt_loan_path(cmt_base).map(|lp| { |
506 | new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk))) | |
507 | }) | |
508 | } | |
509 | ||
92a42be0 | 510 | Categorization::Interior(ref cmt_base, ik) => { |
1a4d82fc | 511 | opt_loan_path(cmt_base).map(|lp| { |
85aaf69f | 512 | new_lp(LpExtend(lp, cmt.mutbl, LpInterior(ik.cleaned()))) |
1a4d82fc JJ |
513 | }) |
514 | } | |
515 | ||
92a42be0 | 516 | Categorization::Downcast(ref cmt_base, variant_def_id) => |
1a4d82fc JJ |
517 | opt_loan_path(cmt_base) |
518 | .map(|lp| { | |
519 | new_lp(LpDowncast(lp, variant_def_id)) | |
520 | }), | |
521 | ||
522 | } | |
523 | } | |
524 | ||
525 | /////////////////////////////////////////////////////////////////////////// | |
526 | // Errors | |
527 | ||
528 | // Errors that can occur | |
529 | #[derive(PartialEq)] | |
1a4d82fc JJ |
530 | pub enum bckerr_code { |
531 | err_mutbl, | |
532 | err_out_of_scope(ty::Region, ty::Region), // superscope, subscope | |
533 | err_borrowed_pointer_too_short(ty::Region, ty::Region), // loan, ptr | |
534 | } | |
535 | ||
536 | // Combination of an error code and the categorization of the expression | |
537 | // that caused it | |
538 | #[derive(PartialEq)] | |
539 | pub struct BckError<'tcx> { | |
540 | span: Span, | |
c1a9b12d | 541 | cause: AliasableViolationKind, |
1a4d82fc JJ |
542 | cmt: mc::cmt<'tcx>, |
543 | code: bckerr_code | |
544 | } | |
545 | ||
c1a9b12d | 546 | #[derive(Copy, Clone, Debug, PartialEq)] |
1a4d82fc JJ |
547 | pub enum AliasableViolationKind { |
548 | MutabilityViolation, | |
549 | BorrowViolation(euv::LoanCause) | |
550 | } | |
551 | ||
c34b1796 | 552 | #[derive(Copy, Clone, Debug)] |
1a4d82fc JJ |
553 | pub enum MovedValueUseKind { |
554 | MovedInUse, | |
555 | MovedInCapture, | |
556 | } | |
557 | ||
558 | /////////////////////////////////////////////////////////////////////////// | |
559 | // Misc | |
560 | ||
561 | impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { | |
562 | pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region) | |
bd371182 AL |
563 | -> bool |
564 | { | |
565 | self.free_region_map.is_subregion_of(self.tcx, r_sub, r_sup) | |
1a4d82fc JJ |
566 | } |
567 | ||
568 | pub fn report(&self, err: BckError<'tcx>) { | |
9346a6ac AL |
569 | // Catch and handle some particular cases. |
570 | match (&err.code, &err.cause) { | |
c1a9b12d SL |
571 | (&err_out_of_scope(ty::ReScope(_), ty::ReStatic), |
572 | &BorrowViolation(euv::ClosureCapture(span))) | | |
573 | (&err_out_of_scope(ty::ReScope(_), ty::ReFree(..)), | |
574 | &BorrowViolation(euv::ClosureCapture(span))) => { | |
9346a6ac AL |
575 | return self.report_out_of_scope_escaping_closure_capture(&err, span); |
576 | } | |
577 | _ => { } | |
578 | } | |
579 | ||
580 | // General fallback. | |
1a4d82fc JJ |
581 | self.span_err( |
582 | err.span, | |
c34b1796 | 583 | &self.bckerr_to_string(&err)); |
1a4d82fc JJ |
584 | self.note_and_explain_bckerr(err); |
585 | } | |
586 | ||
587 | pub fn report_use_of_moved_value<'b>(&self, | |
588 | use_span: Span, | |
589 | use_kind: MovedValueUseKind, | |
590 | lp: &LoanPath<'tcx>, | |
591 | the_move: &move_data::Move, | |
592 | moved_lp: &LoanPath<'tcx>, | |
593 | param_env: &ty::ParameterEnvironment<'b,'tcx>) { | |
594 | let verb = match use_kind { | |
595 | MovedInUse => "use", | |
596 | MovedInCapture => "capture", | |
597 | }; | |
598 | ||
599 | let (ol, moved_lp_msg) = match the_move.kind { | |
600 | move_data::Declared => { | |
62682a34 SL |
601 | span_err!( |
602 | self.tcx.sess, use_span, E0381, | |
603 | "{} of possibly uninitialized variable: `{}`", | |
604 | verb, | |
605 | self.loan_path_to_string(lp)); | |
606 | ||
1a4d82fc JJ |
607 | (self.loan_path_to_string(moved_lp), |
608 | String::new()) | |
609 | } | |
610 | _ => { | |
611 | // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would | |
612 | // normally generate a rather confusing message: | |
613 | // | |
614 | // error: use of moved value: `x.b` | |
615 | // note: `x.a` moved here... | |
616 | // | |
617 | // What we want to do instead is get the 'common ancestor' of the two moves and | |
618 | // use that for most of the message instead, giving is something like this: | |
619 | // | |
620 | // error: use of moved value: `x` | |
621 | // note: `x` moved here (through moving `x.a`)... | |
622 | ||
623 | let common = moved_lp.common(lp); | |
624 | let has_common = common.is_some(); | |
625 | let has_fork = moved_lp.has_fork(lp); | |
626 | let (nl, ol, moved_lp_msg) = | |
627 | if has_fork && has_common { | |
628 | let nl = self.loan_path_to_string(&common.unwrap()); | |
629 | let ol = nl.clone(); | |
630 | let moved_lp_msg = format!(" (through moving `{}`)", | |
631 | self.loan_path_to_string(moved_lp)); | |
632 | (nl, ol, moved_lp_msg) | |
633 | } else { | |
634 | (self.loan_path_to_string(lp), | |
635 | self.loan_path_to_string(moved_lp), | |
636 | String::new()) | |
637 | }; | |
638 | ||
639 | let partial = moved_lp.depth() > lp.depth(); | |
640 | let msg = if !has_fork && partial { "partially " } | |
641 | else if has_fork && !has_common { "collaterally "} | |
642 | else { "" }; | |
62682a34 SL |
643 | span_err!( |
644 | self.tcx.sess, use_span, E0382, | |
645 | "{} of {}moved value: `{}`", | |
646 | verb, msg, nl); | |
1a4d82fc JJ |
647 | (ol, moved_lp_msg) |
648 | } | |
649 | }; | |
650 | ||
651 | match the_move.kind { | |
652 | move_data::Declared => {} | |
653 | ||
654 | move_data::MoveExpr => { | |
655 | let (expr_ty, expr_span) = match self.tcx | |
656 | .map | |
657 | .find(the_move.id) { | |
e9174d1e | 658 | Some(hir_map::NodeExpr(expr)) => { |
c1a9b12d | 659 | (self.tcx.expr_ty_adjusted(&*expr), expr.span) |
1a4d82fc JJ |
660 | } |
661 | r => { | |
662 | self.tcx.sess.bug(&format!("MoveExpr({}) maps to \ | |
663 | {:?}, not Expr", | |
664 | the_move.id, | |
c34b1796 | 665 | r)) |
1a4d82fc JJ |
666 | } |
667 | }; | |
668 | let (suggestion, _) = | |
669 | move_suggestion(param_env, expr_span, expr_ty, ("moved by default", "")); | |
c34b1796 AL |
670 | // If the two spans are the same, it's because the expression will be evaluated |
671 | // multiple times. Avoid printing the same span and adjust the wording so it makes | |
672 | // more sense that it's from multiple evalutations. | |
673 | if expr_span == use_span { | |
674 | self.tcx.sess.note( | |
675 | &format!("`{}` was previously moved here{} because it has type `{}`, \ | |
676 | which is {}", | |
677 | ol, | |
678 | moved_lp_msg, | |
62682a34 | 679 | expr_ty, |
c34b1796 AL |
680 | suggestion)); |
681 | } else { | |
682 | self.tcx.sess.span_note( | |
683 | expr_span, | |
684 | &format!("`{}` moved here{} because it has type `{}`, which is {}", | |
685 | ol, | |
686 | moved_lp_msg, | |
62682a34 | 687 | expr_ty, |
c34b1796 AL |
688 | suggestion)); |
689 | } | |
1a4d82fc JJ |
690 | } |
691 | ||
692 | move_data::MovePat => { | |
c1a9b12d | 693 | let pat_ty = self.tcx.node_id_to_type(the_move.id); |
1a4d82fc JJ |
694 | let span = self.tcx.map.span(the_move.id); |
695 | self.tcx.sess.span_note(span, | |
696 | &format!("`{}` moved here{} because it has type `{}`, \ | |
697 | which is moved by default", | |
698 | ol, | |
699 | moved_lp_msg, | |
62682a34 | 700 | pat_ty)); |
c1a9b12d SL |
701 | match self.tcx.sess.codemap().span_to_snippet(span) { |
702 | Ok(string) => { | |
703 | self.tcx.sess.span_suggestion( | |
704 | span, | |
705 | &format!("if you would like to borrow the value instead, \ | |
706 | use a `ref` binding as shown:"), | |
707 | format!("ref {}", string)); | |
708 | }, | |
709 | Err(_) => { | |
710 | self.tcx.sess.fileline_help(span, | |
711 | "use `ref` to override"); | |
712 | }, | |
713 | } | |
1a4d82fc JJ |
714 | } |
715 | ||
716 | move_data::Captured => { | |
717 | let (expr_ty, expr_span) = match self.tcx | |
718 | .map | |
719 | .find(the_move.id) { | |
e9174d1e | 720 | Some(hir_map::NodeExpr(expr)) => { |
c1a9b12d | 721 | (self.tcx.expr_ty_adjusted(&*expr), expr.span) |
1a4d82fc JJ |
722 | } |
723 | r => { | |
724 | self.tcx.sess.bug(&format!("Captured({}) maps to \ | |
725 | {:?}, not Expr", | |
726 | the_move.id, | |
c34b1796 | 727 | r)) |
1a4d82fc JJ |
728 | } |
729 | }; | |
730 | let (suggestion, help) = | |
731 | move_suggestion(param_env, | |
732 | expr_span, | |
733 | expr_ty, | |
734 | ("moved by default", | |
735 | "make a copy and capture that instead to override")); | |
736 | self.tcx.sess.span_note( | |
737 | expr_span, | |
738 | &format!("`{}` moved into closure environment here{} because it \ | |
739 | has type `{}`, which is {}", | |
740 | ol, | |
741 | moved_lp_msg, | |
c1a9b12d | 742 | moved_lp.ty, |
c34b1796 AL |
743 | suggestion)); |
744 | self.tcx.sess.fileline_help(expr_span, help); | |
1a4d82fc JJ |
745 | } |
746 | } | |
747 | ||
748 | fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>, | |
749 | span: Span, | |
750 | ty: Ty<'tcx>, | |
751 | default_msgs: (&'static str, &'static str)) | |
752 | -> (&'static str, &'static str) { | |
753 | match ty.sty { | |
754 | _ => { | |
c1a9b12d | 755 | if ty.moves_by_default(param_env, span) { |
1a4d82fc JJ |
756 | ("non-copyable", |
757 | "perhaps you meant to use `clone()`?") | |
758 | } else { | |
759 | default_msgs | |
760 | } | |
761 | } | |
762 | } | |
763 | } | |
764 | } | |
765 | ||
85aaf69f SL |
766 | pub fn report_partial_reinitialization_of_uninitialized_structure( |
767 | &self, | |
768 | span: Span, | |
769 | lp: &LoanPath<'tcx>) { | |
62682a34 SL |
770 | span_err!( |
771 | self.tcx.sess, span, E0383, | |
772 | "partial reinitialization of uninitialized structure `{}`", | |
773 | self.loan_path_to_string(lp)); | |
85aaf69f SL |
774 | } |
775 | ||
1a4d82fc JJ |
776 | pub fn report_reassigned_immutable_variable(&self, |
777 | span: Span, | |
778 | lp: &LoanPath<'tcx>, | |
779 | assign: | |
780 | &move_data::Assignment) { | |
62682a34 SL |
781 | span_err!( |
782 | self.tcx.sess, span, E0384, | |
783 | "re-assignment of immutable variable `{}`", | |
784 | self.loan_path_to_string(lp)); | |
1a4d82fc JJ |
785 | self.tcx.sess.span_note(assign.span, "prior assignment occurs here"); |
786 | } | |
787 | ||
788 | pub fn span_err(&self, s: Span, m: &str) { | |
789 | self.tcx.sess.span_err(s, m); | |
790 | } | |
791 | ||
b039eaaf SL |
792 | pub fn span_err_with_code(&self, s: Span, msg: &str, code: &str) { |
793 | self.tcx.sess.span_err_with_code(s, msg, code); | |
794 | } | |
795 | ||
1a4d82fc JJ |
796 | pub fn span_bug(&self, s: Span, m: &str) { |
797 | self.tcx.sess.span_bug(s, m); | |
798 | } | |
799 | ||
800 | pub fn span_note(&self, s: Span, m: &str) { | |
801 | self.tcx.sess.span_note(s, m); | |
802 | } | |
803 | ||
804 | pub fn span_end_note(&self, s: Span, m: &str) { | |
805 | self.tcx.sess.span_end_note(s, m); | |
806 | } | |
807 | ||
c34b1796 AL |
808 | pub fn fileline_help(&self, s: Span, m: &str) { |
809 | self.tcx.sess.fileline_help(s, m); | |
810 | } | |
811 | ||
1a4d82fc JJ |
812 | pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String { |
813 | match err.code { | |
814 | err_mutbl => { | |
815 | let descr = match err.cmt.note { | |
816 | mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => { | |
817 | self.cmt_to_string(&*err.cmt) | |
818 | } | |
819 | _ => match opt_loan_path(&err.cmt) { | |
820 | None => { | |
821 | format!("{} {}", | |
822 | err.cmt.mutbl.to_user_str(), | |
823 | self.cmt_to_string(&*err.cmt)) | |
824 | } | |
825 | Some(lp) => { | |
826 | format!("{} {} `{}`", | |
827 | err.cmt.mutbl.to_user_str(), | |
828 | self.cmt_to_string(&*err.cmt), | |
829 | self.loan_path_to_string(&*lp)) | |
830 | } | |
831 | } | |
832 | }; | |
833 | ||
834 | match err.cause { | |
c1a9b12d SL |
835 | MutabilityViolation => { |
836 | format!("cannot assign to {}", descr) | |
837 | } | |
838 | BorrowViolation(euv::ClosureCapture(_)) => { | |
1a4d82fc JJ |
839 | format!("closure cannot assign to {}", descr) |
840 | } | |
c1a9b12d SL |
841 | BorrowViolation(euv::OverloadedOperator) | |
842 | BorrowViolation(euv::AddrOf) | | |
843 | BorrowViolation(euv::RefBinding) | | |
844 | BorrowViolation(euv::AutoRef) | | |
845 | BorrowViolation(euv::AutoUnsafe) | | |
846 | BorrowViolation(euv::ForLoop) | | |
847 | BorrowViolation(euv::MatchDiscriminant) => { | |
1a4d82fc JJ |
848 | format!("cannot borrow {} as mutable", descr) |
849 | } | |
c1a9b12d | 850 | BorrowViolation(euv::ClosureInvocation) => { |
1a4d82fc JJ |
851 | self.tcx.sess.span_bug(err.span, |
852 | "err_mutbl with a closure invocation"); | |
853 | } | |
854 | } | |
855 | } | |
856 | err_out_of_scope(..) => { | |
857 | let msg = match opt_loan_path(&err.cmt) { | |
858 | None => "borrowed value".to_string(), | |
859 | Some(lp) => { | |
860 | format!("`{}`", self.loan_path_to_string(&*lp)) | |
861 | } | |
862 | }; | |
863 | format!("{} does not live long enough", msg) | |
864 | } | |
865 | err_borrowed_pointer_too_short(..) => { | |
9346a6ac | 866 | let descr = self.cmt_to_path_or_string(&err.cmt); |
1a4d82fc | 867 | format!("lifetime of {} is too short to guarantee \ |
9346a6ac AL |
868 | its contents can be safely reborrowed", |
869 | descr) | |
1a4d82fc JJ |
870 | } |
871 | } | |
872 | } | |
873 | ||
874 | pub fn report_aliasability_violation(&self, | |
875 | span: Span, | |
876 | kind: AliasableViolationKind, | |
877 | cause: mc::AliasableReason) { | |
878 | let mut is_closure = false; | |
879 | let prefix = match kind { | |
880 | MutabilityViolation => { | |
881 | "cannot assign to data" | |
882 | } | |
85aaf69f | 883 | BorrowViolation(euv::ClosureCapture(_)) | |
1a4d82fc JJ |
884 | BorrowViolation(euv::OverloadedOperator) | |
885 | BorrowViolation(euv::AddrOf) | | |
886 | BorrowViolation(euv::AutoRef) | | |
9346a6ac | 887 | BorrowViolation(euv::AutoUnsafe) | |
1a4d82fc JJ |
888 | BorrowViolation(euv::RefBinding) | |
889 | BorrowViolation(euv::MatchDiscriminant) => { | |
890 | "cannot borrow data mutably" | |
891 | } | |
892 | ||
893 | BorrowViolation(euv::ClosureInvocation) => { | |
894 | is_closure = true; | |
895 | "closure invocation" | |
896 | } | |
897 | ||
898 | BorrowViolation(euv::ForLoop) => { | |
899 | "`for` loop" | |
900 | } | |
901 | }; | |
902 | ||
903 | match cause { | |
904 | mc::AliasableOther => { | |
62682a34 SL |
905 | span_err!( |
906 | self.tcx.sess, span, E0385, | |
907 | "{} in an aliasable location", prefix); | |
c34b1796 AL |
908 | } |
909 | mc::AliasableReason::UnaliasableImmutable => { | |
62682a34 SL |
910 | span_err!( |
911 | self.tcx.sess, span, E0386, | |
912 | "{} in an immutable container", prefix); | |
1a4d82fc JJ |
913 | } |
914 | mc::AliasableClosure(id) => { | |
62682a34 SL |
915 | span_err!( |
916 | self.tcx.sess, span, E0387, | |
917 | "{} in a captured outer variable in an `Fn` closure", prefix); | |
85aaf69f SL |
918 | if let BorrowViolation(euv::ClosureCapture(_)) = kind { |
919 | // The aliasability violation with closure captures can | |
920 | // happen for nested closures, so we know the enclosing | |
921 | // closure incorrectly accepts an `Fn` while it needs to | |
922 | // be `FnMut`. | |
923 | span_help!(self.tcx.sess, self.tcx.map.span(id), | |
924 | "consider changing this to accept closures that implement `FnMut`"); | |
925 | } else { | |
926 | span_help!(self.tcx.sess, self.tcx.map.span(id), | |
1a4d82fc | 927 | "consider changing this closure to take self by mutable reference"); |
85aaf69f | 928 | } |
1a4d82fc | 929 | } |
92a42be0 SL |
930 | mc::AliasableStatic | |
931 | mc::AliasableStaticMut => { | |
62682a34 SL |
932 | span_err!( |
933 | self.tcx.sess, span, E0388, | |
934 | "{} in a static location", prefix); | |
1a4d82fc JJ |
935 | } |
936 | mc::AliasableBorrowed => { | |
62682a34 SL |
937 | span_err!( |
938 | self.tcx.sess, span, E0389, | |
939 | "{} in a `&` reference", prefix); | |
1a4d82fc JJ |
940 | } |
941 | } | |
942 | ||
943 | if is_closure { | |
c34b1796 | 944 | self.tcx.sess.fileline_help( |
1a4d82fc JJ |
945 | span, |
946 | "closures behind references must be called via `&mut`"); | |
947 | } | |
948 | } | |
949 | ||
9346a6ac AL |
950 | fn report_out_of_scope_escaping_closure_capture(&self, |
951 | err: &BckError<'tcx>, | |
952 | capture_span: Span) | |
953 | { | |
954 | let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt); | |
955 | ||
956 | span_err!( | |
957 | self.tcx.sess, err.span, E0373, | |
958 | "closure may outlive the current function, \ | |
959 | but it borrows {}, \ | |
960 | which is owned by the current function", | |
961 | cmt_path_or_string); | |
962 | ||
963 | self.tcx.sess.span_note( | |
964 | capture_span, | |
965 | &format!("{} is borrowed here", | |
966 | cmt_path_or_string)); | |
967 | ||
968 | let suggestion = | |
969 | match self.tcx.sess.codemap().span_to_snippet(err.span) { | |
970 | Ok(string) => format!("move {}", string), | |
971 | Err(_) => format!("move |<args>| <body>") | |
972 | }; | |
973 | ||
974 | self.tcx.sess.span_suggestion( | |
975 | err.span, | |
976 | &format!("to force the closure to take ownership of {} \ | |
977 | (and any other referenced variables), \ | |
978 | use the `move` keyword, as shown:", | |
979 | cmt_path_or_string), | |
980 | suggestion); | |
981 | } | |
982 | ||
1a4d82fc JJ |
983 | pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) { |
984 | let code = err.code; | |
985 | match code { | |
92a42be0 | 986 | err_mutbl => { |
1a4d82fc JJ |
987 | match err.cmt.note { |
988 | mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => { | |
989 | // If this is an `Fn` closure, it simply can't mutate upvars. | |
990 | // If it's an `FnMut` closure, the original variable was declared immutable. | |
991 | // We need to determine which is the case here. | |
992 | let kind = match err.cmt.upvar().unwrap().cat { | |
92a42be0 | 993 | Categorization::Upvar(mc::Upvar { kind, .. }) => kind, |
1a4d82fc JJ |
994 | _ => unreachable!() |
995 | }; | |
85aaf69f | 996 | if kind == ty::FnClosureKind { |
1a4d82fc JJ |
997 | self.tcx.sess.span_help( |
998 | self.tcx.map.span(upvar_id.closure_expr_id), | |
999 | "consider changing this closure to take \ | |
1000 | self by mutable reference"); | |
1001 | } | |
1002 | } | |
92a42be0 SL |
1003 | _ => { |
1004 | if let Categorization::Local(local_id) = err.cmt.cat { | |
1005 | let span = self.tcx.map.span(local_id); | |
1006 | if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) { | |
1007 | self.tcx.sess.span_suggestion( | |
1008 | span, | |
1009 | &format!("to make the {} mutable, use `mut` as shown:", | |
1010 | self.cmt_to_string(&err.cmt)), | |
1011 | format!("mut {}", snippet)); | |
1012 | } | |
1013 | } | |
1014 | } | |
1a4d82fc JJ |
1015 | } |
1016 | } | |
1017 | ||
1018 | err_out_of_scope(super_scope, sub_scope) => { | |
c1a9b12d | 1019 | self.tcx.note_and_explain_region( |
1a4d82fc JJ |
1020 | "reference must be valid for ", |
1021 | sub_scope, | |
1022 | "..."); | |
c1a9b12d | 1023 | self.tcx.note_and_explain_region( |
1a4d82fc JJ |
1024 | "...but borrowed value is only valid for ", |
1025 | super_scope, | |
1026 | ""); | |
62682a34 SL |
1027 | if let Some(span) = statement_scope_span(self.tcx, super_scope) { |
1028 | self.tcx.sess.span_help(span, | |
1029 | "consider using a `let` binding to increase its lifetime"); | |
1a4d82fc JJ |
1030 | } |
1031 | } | |
1032 | ||
1033 | err_borrowed_pointer_too_short(loan_scope, ptr_scope) => { | |
1034 | let descr = match opt_loan_path(&err.cmt) { | |
1035 | Some(lp) => { | |
1036 | format!("`{}`", self.loan_path_to_string(&*lp)) | |
1037 | } | |
1038 | None => self.cmt_to_string(&*err.cmt), | |
1039 | }; | |
c1a9b12d | 1040 | self.tcx.note_and_explain_region( |
1a4d82fc | 1041 | &format!("{} would have to be valid for ", |
c34b1796 | 1042 | descr), |
1a4d82fc JJ |
1043 | loan_scope, |
1044 | "..."); | |
c1a9b12d | 1045 | self.tcx.note_and_explain_region( |
c34b1796 | 1046 | &format!("...but {} is only valid for ", descr), |
1a4d82fc JJ |
1047 | ptr_scope, |
1048 | ""); | |
1049 | } | |
1050 | } | |
1051 | } | |
1052 | ||
1053 | pub fn append_loan_path_to_string(&self, | |
1054 | loan_path: &LoanPath<'tcx>, | |
1055 | out: &mut String) { | |
1056 | match loan_path.kind { | |
1057 | LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) | | |
1058 | LpVar(id) => { | |
c1a9b12d | 1059 | out.push_str(&self.tcx.local_var_name_str(id)); |
1a4d82fc JJ |
1060 | } |
1061 | ||
1062 | LpDowncast(ref lp_base, variant_def_id) => { | |
1063 | out.push('('); | |
1064 | self.append_loan_path_to_string(&**lp_base, out); | |
1065 | out.push_str(DOWNCAST_PRINTED_OPERATOR); | |
c1a9b12d | 1066 | out.push_str(&self.tcx.item_path_str(variant_def_id)); |
1a4d82fc JJ |
1067 | out.push(')'); |
1068 | } | |
1069 | ||
1070 | ||
85aaf69f | 1071 | LpExtend(ref lp_base, _, LpInterior(InteriorField(fname))) => { |
1a4d82fc JJ |
1072 | self.append_autoderefd_loan_path_to_string(&**lp_base, out); |
1073 | match fname { | |
1074 | mc::NamedField(fname) => { | |
1075 | out.push('.'); | |
c1a9b12d | 1076 | out.push_str(&fname.as_str()); |
1a4d82fc JJ |
1077 | } |
1078 | mc::PositionalField(idx) => { | |
1079 | out.push('.'); | |
c34b1796 | 1080 | out.push_str(&idx.to_string()); |
1a4d82fc JJ |
1081 | } |
1082 | } | |
1083 | } | |
1084 | ||
85aaf69f | 1085 | LpExtend(ref lp_base, _, LpInterior(InteriorElement(..))) => { |
1a4d82fc JJ |
1086 | self.append_autoderefd_loan_path_to_string(&**lp_base, out); |
1087 | out.push_str("[..]"); | |
1088 | } | |
1089 | ||
1090 | LpExtend(ref lp_base, _, LpDeref(_)) => { | |
1091 | out.push('*'); | |
1092 | self.append_loan_path_to_string(&**lp_base, out); | |
1093 | } | |
1094 | } | |
1095 | } | |
1096 | ||
1097 | pub fn append_autoderefd_loan_path_to_string(&self, | |
1098 | loan_path: &LoanPath<'tcx>, | |
1099 | out: &mut String) { | |
1100 | match loan_path.kind { | |
1101 | LpExtend(ref lp_base, _, LpDeref(_)) => { | |
1102 | // For a path like `(*x).f` or `(*x)[3]`, autoderef | |
1103 | // rules would normally allow users to omit the `*x`. | |
1104 | // So just serialize such paths to `x.f` or x[3]` respectively. | |
1105 | self.append_autoderefd_loan_path_to_string(&**lp_base, out) | |
1106 | } | |
1107 | ||
1108 | LpDowncast(ref lp_base, variant_def_id) => { | |
1109 | out.push('('); | |
1110 | self.append_autoderefd_loan_path_to_string(&**lp_base, out); | |
1111 | out.push(':'); | |
c1a9b12d | 1112 | out.push_str(&self.tcx.item_path_str(variant_def_id)); |
1a4d82fc JJ |
1113 | out.push(')'); |
1114 | } | |
1115 | ||
1116 | LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => { | |
1117 | self.append_loan_path_to_string(loan_path, out) | |
1118 | } | |
1119 | } | |
1120 | } | |
1121 | ||
1122 | pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String { | |
1123 | let mut result = String::new(); | |
1124 | self.append_loan_path_to_string(loan_path, &mut result); | |
1125 | result | |
1126 | } | |
1127 | ||
1128 | pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String { | |
1129 | cmt.descriptive_string(self.tcx) | |
1130 | } | |
9346a6ac AL |
1131 | |
1132 | pub fn cmt_to_path_or_string(&self, cmt: &mc::cmt<'tcx>) -> String { | |
1133 | match opt_loan_path(cmt) { | |
1134 | Some(lp) => format!("`{}`", self.loan_path_to_string(&lp)), | |
1135 | None => self.cmt_to_string(cmt), | |
1136 | } | |
1137 | } | |
1a4d82fc JJ |
1138 | } |
1139 | ||
62682a34 SL |
1140 | fn statement_scope_span(tcx: &ty::ctxt, region: ty::Region) -> Option<Span> { |
1141 | match region { | |
1142 | ty::ReScope(scope) => { | |
e9174d1e SL |
1143 | match tcx.map.find(scope.node_id(&tcx.region_maps)) { |
1144 | Some(hir_map::NodeStmt(stmt)) => Some(stmt.span), | |
62682a34 SL |
1145 | _ => None |
1146 | } | |
1147 | } | |
1148 | _ => None | |
1149 | } | |
1a4d82fc JJ |
1150 | } |
1151 | ||
1152 | impl BitwiseOperator for LoanDataFlowOperator { | |
1153 | #[inline] | |
c34b1796 | 1154 | fn join(&self, succ: usize, pred: usize) -> usize { |
1a4d82fc JJ |
1155 | succ | pred // loans from both preds are in scope |
1156 | } | |
1157 | } | |
1158 | ||
1159 | impl DataFlowOperator for LoanDataFlowOperator { | |
1160 | #[inline] | |
1161 | fn initial_value(&self) -> bool { | |
1162 | false // no loans in scope by default | |
1163 | } | |
1164 | } | |
1165 | ||
62682a34 SL |
1166 | impl<'tcx> fmt::Debug for InteriorKind { |
1167 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
85aaf69f | 1168 | match *self { |
62682a34 SL |
1169 | InteriorField(mc::NamedField(fld)) => write!(f, "{}", fld), |
1170 | InteriorField(mc::PositionalField(i)) => write!(f, "#{}", i), | |
1171 | InteriorElement(..) => write!(f, "[]"), | |
85aaf69f SL |
1172 | } |
1173 | } | |
1174 | } | |
1175 | ||
62682a34 SL |
1176 | impl<'tcx> fmt::Debug for Loan<'tcx> { |
1177 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1178 | write!(f, "Loan_{}({:?}, {:?}, {:?}-{:?}, {:?})", | |
1179 | self.index, | |
1180 | self.loan_path, | |
1181 | self.kind, | |
1182 | self.gen_scope, | |
1183 | self.kill_scope, | |
1184 | self.restricted_paths) | |
1a4d82fc JJ |
1185 | } |
1186 | } | |
1187 | ||
62682a34 SL |
1188 | impl<'tcx> fmt::Debug for LoanPath<'tcx> { |
1189 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1a4d82fc JJ |
1190 | match self.kind { |
1191 | LpVar(id) => { | |
62682a34 | 1192 | write!(f, "$({})", ty::tls::with(|tcx| tcx.map.node_to_string(id))) |
1a4d82fc JJ |
1193 | } |
1194 | ||
1195 | LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => { | |
62682a34 SL |
1196 | let s = ty::tls::with(|tcx| tcx.map.node_to_string(var_id)); |
1197 | write!(f, "$({} captured by id={})", s, closure_expr_id) | |
1a4d82fc JJ |
1198 | } |
1199 | ||
1200 | LpDowncast(ref lp, variant_def_id) => { | |
e9174d1e | 1201 | let variant_str = if variant_def_id.is_local() { |
c1a9b12d | 1202 | ty::tls::with(|tcx| tcx.item_path_str(variant_def_id)) |
1a4d82fc | 1203 | } else { |
62682a34 | 1204 | format!("{:?}", variant_def_id) |
1a4d82fc | 1205 | }; |
62682a34 | 1206 | write!(f, "({:?}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str) |
1a4d82fc JJ |
1207 | } |
1208 | ||
1209 | LpExtend(ref lp, _, LpDeref(_)) => { | |
62682a34 | 1210 | write!(f, "{:?}.*", lp) |
1a4d82fc JJ |
1211 | } |
1212 | ||
1213 | LpExtend(ref lp, _, LpInterior(ref interior)) => { | |
62682a34 | 1214 | write!(f, "{:?}.{:?}", lp, interior) |
1a4d82fc JJ |
1215 | } |
1216 | } | |
1217 | } | |
1218 | } | |
1219 | ||
62682a34 SL |
1220 | impl<'tcx> fmt::Display for LoanPath<'tcx> { |
1221 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1a4d82fc JJ |
1222 | match self.kind { |
1223 | LpVar(id) => { | |
62682a34 | 1224 | write!(f, "$({})", ty::tls::with(|tcx| tcx.map.node_to_user_string(id))) |
1a4d82fc JJ |
1225 | } |
1226 | ||
1227 | LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => { | |
62682a34 SL |
1228 | let s = ty::tls::with(|tcx| tcx.map.node_to_user_string(var_id)); |
1229 | write!(f, "$({} captured by closure)", s) | |
1a4d82fc JJ |
1230 | } |
1231 | ||
1232 | LpDowncast(ref lp, variant_def_id) => { | |
e9174d1e | 1233 | let variant_str = if variant_def_id.is_local() { |
c1a9b12d | 1234 | ty::tls::with(|tcx| tcx.item_path_str(variant_def_id)) |
1a4d82fc | 1235 | } else { |
62682a34 | 1236 | format!("{:?}", variant_def_id) |
1a4d82fc | 1237 | }; |
62682a34 | 1238 | write!(f, "({}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str) |
1a4d82fc JJ |
1239 | } |
1240 | ||
1241 | LpExtend(ref lp, _, LpDeref(_)) => { | |
62682a34 | 1242 | write!(f, "{}.*", lp) |
1a4d82fc JJ |
1243 | } |
1244 | ||
1245 | LpExtend(ref lp, _, LpInterior(ref interior)) => { | |
62682a34 | 1246 | write!(f, "{}.{:?}", lp, interior) |
1a4d82fc JJ |
1247 | } |
1248 | } | |
1249 | } | |
1250 | } |