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