]>
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; |
54a0048b | 37 | use rustc::ty::{self, Ty, 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; |
1a4d82fc | 44 | use syntax::codemap::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 { | |
7453a54e | 90 | gather_loans::gather_loans_in_static_initializer(self, &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 { |
7453a54e | 97 | gather_loans::gather_loans_in_static_initializer(self, &expr); |
d9579d0f | 98 | } |
92a42be0 | 99 | intravisit::walk_impl_item(self, ii); |
d9579d0f | 100 | } |
1a4d82fc JJ |
101 | } |
102 | ||
54a0048b | 103 | pub fn check_crate<'tcx>(tcx: &TyCtxt<'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) => { | |
7453a54e | 145 | gather_loans::gather_loans_in_static_initializer(this, &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>( | |
54a0048b SL |
247 | tcx: &'a TyCtxt<'tcx>, |
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> { | |
54a0048b | 281 | tcx: &'a TyCtxt<'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, | |
54a0048b | 415 | tcx: &TyCtxt) -> ast::NodeId { |
1a4d82fc | 416 | match tcx.map.get(closure_id) { |
e9174d1e SL |
417 | hir_map::NodeExpr(expr) => match expr.node { |
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 | ||
429 | impl<'tcx> LoanPath<'tcx> { | |
54a0048b | 430 | pub fn kill_scope(&self, tcx: &TyCtxt<'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. | |
9cc50fc6 | 623 | let mut db = self.struct_span_err( |
1a4d82fc | 624 | err.span, |
c34b1796 | 625 | &self.bckerr_to_string(&err)); |
9cc50fc6 SL |
626 | self.note_and_explain_bckerr(&mut db, err); |
627 | db.emit(); | |
1a4d82fc JJ |
628 | } |
629 | ||
630 | pub fn report_use_of_moved_value<'b>(&self, | |
631 | use_span: Span, | |
632 | use_kind: MovedValueUseKind, | |
633 | lp: &LoanPath<'tcx>, | |
634 | the_move: &move_data::Move, | |
635 | moved_lp: &LoanPath<'tcx>, | |
636 | param_env: &ty::ParameterEnvironment<'b,'tcx>) { | |
637 | let verb = match use_kind { | |
638 | MovedInUse => "use", | |
639 | MovedInCapture => "capture", | |
640 | }; | |
641 | ||
9cc50fc6 | 642 | let (ol, moved_lp_msg, mut err) = match the_move.kind { |
1a4d82fc | 643 | move_data::Declared => { |
9cc50fc6 | 644 | let err = struct_span_err!( |
62682a34 SL |
645 | self.tcx.sess, use_span, E0381, |
646 | "{} of possibly uninitialized variable: `{}`", | |
647 | verb, | |
648 | self.loan_path_to_string(lp)); | |
649 | ||
1a4d82fc | 650 | (self.loan_path_to_string(moved_lp), |
9cc50fc6 SL |
651 | String::new(), |
652 | err) | |
1a4d82fc JJ |
653 | } |
654 | _ => { | |
655 | // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would | |
656 | // normally generate a rather confusing message: | |
657 | // | |
658 | // error: use of moved value: `x.b` | |
659 | // note: `x.a` moved here... | |
660 | // | |
661 | // What we want to do instead is get the 'common ancestor' of the two moves and | |
662 | // use that for most of the message instead, giving is something like this: | |
663 | // | |
664 | // error: use of moved value: `x` | |
665 | // note: `x` moved here (through moving `x.a`)... | |
666 | ||
667 | let common = moved_lp.common(lp); | |
668 | let has_common = common.is_some(); | |
669 | let has_fork = moved_lp.has_fork(lp); | |
670 | let (nl, ol, moved_lp_msg) = | |
671 | if has_fork && has_common { | |
672 | let nl = self.loan_path_to_string(&common.unwrap()); | |
673 | let ol = nl.clone(); | |
674 | let moved_lp_msg = format!(" (through moving `{}`)", | |
675 | self.loan_path_to_string(moved_lp)); | |
676 | (nl, ol, moved_lp_msg) | |
677 | } else { | |
678 | (self.loan_path_to_string(lp), | |
679 | self.loan_path_to_string(moved_lp), | |
680 | String::new()) | |
681 | }; | |
682 | ||
683 | let partial = moved_lp.depth() > lp.depth(); | |
684 | let msg = if !has_fork && partial { "partially " } | |
685 | else if has_fork && !has_common { "collaterally "} | |
686 | else { "" }; | |
9cc50fc6 | 687 | let err = struct_span_err!( |
62682a34 SL |
688 | self.tcx.sess, use_span, E0382, |
689 | "{} of {}moved value: `{}`", | |
690 | verb, msg, nl); | |
9cc50fc6 | 691 | (ol, moved_lp_msg, err) |
1a4d82fc JJ |
692 | } |
693 | }; | |
694 | ||
695 | match the_move.kind { | |
696 | move_data::Declared => {} | |
697 | ||
698 | move_data::MoveExpr => { | |
699 | let (expr_ty, expr_span) = match self.tcx | |
700 | .map | |
701 | .find(the_move.id) { | |
e9174d1e | 702 | Some(hir_map::NodeExpr(expr)) => { |
7453a54e | 703 | (self.tcx.expr_ty_adjusted(&expr), expr.span) |
1a4d82fc JJ |
704 | } |
705 | r => { | |
54a0048b SL |
706 | bug!("MoveExpr({}) maps to {:?}, not Expr", |
707 | the_move.id, | |
708 | r) | |
1a4d82fc JJ |
709 | } |
710 | }; | |
711 | let (suggestion, _) = | |
712 | move_suggestion(param_env, expr_span, expr_ty, ("moved by default", "")); | |
c34b1796 AL |
713 | // If the two spans are the same, it's because the expression will be evaluated |
714 | // multiple times. Avoid printing the same span and adjust the wording so it makes | |
715 | // more sense that it's from multiple evalutations. | |
716 | if expr_span == use_span { | |
9cc50fc6 | 717 | err.note( |
c34b1796 AL |
718 | &format!("`{}` was previously moved here{} because it has type `{}`, \ |
719 | which is {}", | |
720 | ol, | |
721 | moved_lp_msg, | |
62682a34 | 722 | expr_ty, |
c34b1796 AL |
723 | suggestion)); |
724 | } else { | |
9cc50fc6 | 725 | err.span_note( |
c34b1796 AL |
726 | expr_span, |
727 | &format!("`{}` moved here{} because it has type `{}`, which is {}", | |
728 | ol, | |
729 | moved_lp_msg, | |
62682a34 | 730 | expr_ty, |
c34b1796 AL |
731 | suggestion)); |
732 | } | |
1a4d82fc JJ |
733 | } |
734 | ||
735 | move_data::MovePat => { | |
c1a9b12d | 736 | let pat_ty = self.tcx.node_id_to_type(the_move.id); |
1a4d82fc | 737 | let span = self.tcx.map.span(the_move.id); |
9cc50fc6 | 738 | err.span_note(span, |
1a4d82fc JJ |
739 | &format!("`{}` moved here{} because it has type `{}`, \ |
740 | which is moved by default", | |
741 | ol, | |
742 | moved_lp_msg, | |
62682a34 | 743 | pat_ty)); |
c1a9b12d SL |
744 | match self.tcx.sess.codemap().span_to_snippet(span) { |
745 | Ok(string) => { | |
9cc50fc6 | 746 | err.span_suggestion( |
c1a9b12d SL |
747 | span, |
748 | &format!("if you would like to borrow the value instead, \ | |
749 | use a `ref` binding as shown:"), | |
750 | format!("ref {}", string)); | |
751 | }, | |
752 | Err(_) => { | |
9cc50fc6 | 753 | err.fileline_help(span, |
c1a9b12d SL |
754 | "use `ref` to override"); |
755 | }, | |
756 | } | |
1a4d82fc JJ |
757 | } |
758 | ||
759 | move_data::Captured => { | |
760 | let (expr_ty, expr_span) = match self.tcx | |
761 | .map | |
762 | .find(the_move.id) { | |
e9174d1e | 763 | Some(hir_map::NodeExpr(expr)) => { |
7453a54e | 764 | (self.tcx.expr_ty_adjusted(&expr), expr.span) |
1a4d82fc JJ |
765 | } |
766 | r => { | |
54a0048b SL |
767 | bug!("Captured({}) maps to {:?}, not Expr", |
768 | the_move.id, | |
769 | r) | |
1a4d82fc JJ |
770 | } |
771 | }; | |
772 | let (suggestion, help) = | |
773 | move_suggestion(param_env, | |
774 | expr_span, | |
775 | expr_ty, | |
776 | ("moved by default", | |
777 | "make a copy and capture that instead to override")); | |
9cc50fc6 | 778 | err.span_note( |
1a4d82fc JJ |
779 | expr_span, |
780 | &format!("`{}` moved into closure environment here{} because it \ | |
781 | has type `{}`, which is {}", | |
782 | ol, | |
783 | moved_lp_msg, | |
c1a9b12d | 784 | moved_lp.ty, |
c34b1796 | 785 | suggestion)); |
9cc50fc6 | 786 | err.fileline_help(expr_span, help); |
1a4d82fc JJ |
787 | } |
788 | } | |
9cc50fc6 | 789 | err.emit(); |
1a4d82fc JJ |
790 | |
791 | fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>, | |
792 | span: Span, | |
793 | ty: Ty<'tcx>, | |
794 | default_msgs: (&'static str, &'static str)) | |
795 | -> (&'static str, &'static str) { | |
796 | match ty.sty { | |
797 | _ => { | |
c1a9b12d | 798 | if ty.moves_by_default(param_env, span) { |
1a4d82fc JJ |
799 | ("non-copyable", |
800 | "perhaps you meant to use `clone()`?") | |
801 | } else { | |
802 | default_msgs | |
803 | } | |
804 | } | |
805 | } | |
806 | } | |
807 | } | |
808 | ||
85aaf69f SL |
809 | pub fn report_partial_reinitialization_of_uninitialized_structure( |
810 | &self, | |
811 | span: Span, | |
812 | lp: &LoanPath<'tcx>) { | |
62682a34 SL |
813 | span_err!( |
814 | self.tcx.sess, span, E0383, | |
815 | "partial reinitialization of uninitialized structure `{}`", | |
816 | self.loan_path_to_string(lp)); | |
85aaf69f SL |
817 | } |
818 | ||
1a4d82fc JJ |
819 | pub fn report_reassigned_immutable_variable(&self, |
820 | span: Span, | |
821 | lp: &LoanPath<'tcx>, | |
822 | assign: | |
823 | &move_data::Assignment) { | |
9cc50fc6 | 824 | struct_span_err!( |
62682a34 SL |
825 | self.tcx.sess, span, E0384, |
826 | "re-assignment of immutable variable `{}`", | |
9cc50fc6 SL |
827 | self.loan_path_to_string(lp)) |
828 | .span_note(assign.span, "prior assignment occurs here") | |
829 | .emit(); | |
1a4d82fc JJ |
830 | } |
831 | ||
832 | pub fn span_err(&self, s: Span, m: &str) { | |
833 | self.tcx.sess.span_err(s, m); | |
834 | } | |
835 | ||
9cc50fc6 SL |
836 | pub fn struct_span_err(&self, s: Span, m: &str) -> DiagnosticBuilder<'a> { |
837 | self.tcx.sess.struct_span_err(s, m) | |
1a4d82fc JJ |
838 | } |
839 | ||
9cc50fc6 SL |
840 | pub fn struct_span_err_with_code(&self, |
841 | s: Span, | |
842 | msg: &str, | |
843 | code: &str) | |
844 | -> DiagnosticBuilder<'a> { | |
845 | self.tcx.sess.struct_span_err_with_code(s, msg, code) | |
1a4d82fc JJ |
846 | } |
847 | ||
9cc50fc6 SL |
848 | pub fn span_err_with_code(&self, s: Span, msg: &str, code: &str) { |
849 | self.tcx.sess.span_err_with_code(s, msg, code); | |
1a4d82fc JJ |
850 | } |
851 | ||
1a4d82fc JJ |
852 | pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String { |
853 | match err.code { | |
854 | err_mutbl => { | |
855 | let descr = match err.cmt.note { | |
856 | mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => { | |
7453a54e | 857 | self.cmt_to_string(&err.cmt) |
1a4d82fc JJ |
858 | } |
859 | _ => match opt_loan_path(&err.cmt) { | |
860 | None => { | |
861 | format!("{} {}", | |
862 | err.cmt.mutbl.to_user_str(), | |
7453a54e | 863 | self.cmt_to_string(&err.cmt)) |
1a4d82fc JJ |
864 | } |
865 | Some(lp) => { | |
866 | format!("{} {} `{}`", | |
867 | err.cmt.mutbl.to_user_str(), | |
7453a54e SL |
868 | self.cmt_to_string(&err.cmt), |
869 | self.loan_path_to_string(&lp)) | |
1a4d82fc JJ |
870 | } |
871 | } | |
872 | }; | |
873 | ||
874 | match err.cause { | |
c1a9b12d SL |
875 | MutabilityViolation => { |
876 | format!("cannot assign to {}", descr) | |
877 | } | |
878 | BorrowViolation(euv::ClosureCapture(_)) => { | |
1a4d82fc JJ |
879 | format!("closure cannot assign to {}", descr) |
880 | } | |
c1a9b12d SL |
881 | BorrowViolation(euv::OverloadedOperator) | |
882 | BorrowViolation(euv::AddrOf) | | |
883 | BorrowViolation(euv::RefBinding) | | |
884 | BorrowViolation(euv::AutoRef) | | |
885 | BorrowViolation(euv::AutoUnsafe) | | |
886 | BorrowViolation(euv::ForLoop) | | |
887 | BorrowViolation(euv::MatchDiscriminant) => { | |
1a4d82fc JJ |
888 | format!("cannot borrow {} as mutable", descr) |
889 | } | |
c1a9b12d | 890 | BorrowViolation(euv::ClosureInvocation) => { |
54a0048b | 891 | span_bug!(err.span, |
1a4d82fc JJ |
892 | "err_mutbl with a closure invocation"); |
893 | } | |
894 | } | |
895 | } | |
896 | err_out_of_scope(..) => { | |
897 | let msg = match opt_loan_path(&err.cmt) { | |
898 | None => "borrowed value".to_string(), | |
899 | Some(lp) => { | |
7453a54e | 900 | format!("`{}`", self.loan_path_to_string(&lp)) |
1a4d82fc JJ |
901 | } |
902 | }; | |
903 | format!("{} does not live long enough", msg) | |
904 | } | |
905 | err_borrowed_pointer_too_short(..) => { | |
9346a6ac | 906 | let descr = self.cmt_to_path_or_string(&err.cmt); |
1a4d82fc | 907 | format!("lifetime of {} is too short to guarantee \ |
9346a6ac AL |
908 | its contents can be safely reborrowed", |
909 | descr) | |
1a4d82fc JJ |
910 | } |
911 | } | |
912 | } | |
913 | ||
914 | pub fn report_aliasability_violation(&self, | |
915 | span: Span, | |
916 | kind: AliasableViolationKind, | |
917 | cause: mc::AliasableReason) { | |
918 | let mut is_closure = false; | |
919 | let prefix = match kind { | |
920 | MutabilityViolation => { | |
921 | "cannot assign to data" | |
922 | } | |
85aaf69f | 923 | BorrowViolation(euv::ClosureCapture(_)) | |
1a4d82fc JJ |
924 | BorrowViolation(euv::OverloadedOperator) | |
925 | BorrowViolation(euv::AddrOf) | | |
926 | BorrowViolation(euv::AutoRef) | | |
9346a6ac | 927 | BorrowViolation(euv::AutoUnsafe) | |
1a4d82fc JJ |
928 | BorrowViolation(euv::RefBinding) | |
929 | BorrowViolation(euv::MatchDiscriminant) => { | |
930 | "cannot borrow data mutably" | |
931 | } | |
932 | ||
933 | BorrowViolation(euv::ClosureInvocation) => { | |
934 | is_closure = true; | |
935 | "closure invocation" | |
936 | } | |
937 | ||
938 | BorrowViolation(euv::ForLoop) => { | |
939 | "`for` loop" | |
940 | } | |
941 | }; | |
942 | ||
9cc50fc6 | 943 | let mut err = match cause { |
1a4d82fc | 944 | mc::AliasableOther => { |
9cc50fc6 | 945 | struct_span_err!( |
62682a34 | 946 | self.tcx.sess, span, E0385, |
9cc50fc6 | 947 | "{} in an aliasable location", prefix) |
c34b1796 AL |
948 | } |
949 | mc::AliasableReason::UnaliasableImmutable => { | |
9cc50fc6 | 950 | struct_span_err!( |
62682a34 | 951 | self.tcx.sess, span, E0386, |
9cc50fc6 | 952 | "{} in an immutable container", prefix) |
1a4d82fc JJ |
953 | } |
954 | mc::AliasableClosure(id) => { | |
9cc50fc6 | 955 | let mut err = struct_span_err!( |
62682a34 SL |
956 | self.tcx.sess, span, E0387, |
957 | "{} in a captured outer variable in an `Fn` closure", prefix); | |
85aaf69f SL |
958 | if let BorrowViolation(euv::ClosureCapture(_)) = kind { |
959 | // The aliasability violation with closure captures can | |
960 | // happen for nested closures, so we know the enclosing | |
961 | // closure incorrectly accepts an `Fn` while it needs to | |
962 | // be `FnMut`. | |
9cc50fc6 | 963 | span_help!(&mut err, self.tcx.map.span(id), |
85aaf69f SL |
964 | "consider changing this to accept closures that implement `FnMut`"); |
965 | } else { | |
9cc50fc6 | 966 | span_help!(&mut err, self.tcx.map.span(id), |
1a4d82fc | 967 | "consider changing this closure to take self by mutable reference"); |
85aaf69f | 968 | } |
9cc50fc6 | 969 | err |
1a4d82fc | 970 | } |
92a42be0 SL |
971 | mc::AliasableStatic | |
972 | mc::AliasableStaticMut => { | |
9cc50fc6 | 973 | struct_span_err!( |
62682a34 | 974 | self.tcx.sess, span, E0388, |
9cc50fc6 | 975 | "{} in a static location", prefix) |
1a4d82fc JJ |
976 | } |
977 | mc::AliasableBorrowed => { | |
9cc50fc6 | 978 | struct_span_err!( |
62682a34 | 979 | self.tcx.sess, span, E0389, |
9cc50fc6 | 980 | "{} in a `&` reference", prefix) |
1a4d82fc | 981 | } |
9cc50fc6 | 982 | }; |
1a4d82fc JJ |
983 | |
984 | if is_closure { | |
9cc50fc6 SL |
985 | err.fileline_help(span, |
986 | "closures behind references must be called via `&mut`"); | |
1a4d82fc | 987 | } |
9cc50fc6 | 988 | err.emit(); |
1a4d82fc JJ |
989 | } |
990 | ||
9346a6ac AL |
991 | fn report_out_of_scope_escaping_closure_capture(&self, |
992 | err: &BckError<'tcx>, | |
993 | capture_span: Span) | |
994 | { | |
995 | let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt); | |
996 | ||
9346a6ac AL |
997 | let suggestion = |
998 | match self.tcx.sess.codemap().span_to_snippet(err.span) { | |
999 | Ok(string) => format!("move {}", string), | |
1000 | Err(_) => format!("move |<args>| <body>") | |
1001 | }; | |
1002 | ||
9cc50fc6 SL |
1003 | struct_span_err!(self.tcx.sess, err.span, E0373, |
1004 | "closure may outlive the current function, \ | |
1005 | but it borrows {}, \ | |
1006 | which is owned by the current function", | |
1007 | cmt_path_or_string) | |
1008 | .span_note(capture_span, | |
1009 | &format!("{} is borrowed here", | |
1010 | cmt_path_or_string)) | |
1011 | .span_suggestion(err.span, | |
1012 | &format!("to force the closure to take ownership of {} \ | |
1013 | (and any other referenced variables), \ | |
1014 | use the `move` keyword, as shown:", | |
7453a54e | 1015 | cmt_path_or_string), |
9cc50fc6 SL |
1016 | suggestion) |
1017 | .emit(); | |
9346a6ac AL |
1018 | } |
1019 | ||
9cc50fc6 | 1020 | pub fn note_and_explain_bckerr(&self, db: &mut DiagnosticBuilder, err: BckError<'tcx>) { |
1a4d82fc JJ |
1021 | let code = err.code; |
1022 | match code { | |
92a42be0 | 1023 | err_mutbl => { |
1a4d82fc JJ |
1024 | match err.cmt.note { |
1025 | mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => { | |
1026 | // If this is an `Fn` closure, it simply can't mutate upvars. | |
1027 | // If it's an `FnMut` closure, the original variable was declared immutable. | |
1028 | // We need to determine which is the case here. | |
1029 | let kind = match err.cmt.upvar().unwrap().cat { | |
92a42be0 | 1030 | Categorization::Upvar(mc::Upvar { kind, .. }) => kind, |
54a0048b | 1031 | _ => bug!() |
1a4d82fc | 1032 | }; |
54a0048b | 1033 | if kind == ty::ClosureKind::Fn { |
9cc50fc6 | 1034 | db.span_help( |
1a4d82fc JJ |
1035 | self.tcx.map.span(upvar_id.closure_expr_id), |
1036 | "consider changing this closure to take \ | |
1037 | self by mutable reference"); | |
1038 | } | |
1039 | } | |
92a42be0 SL |
1040 | _ => { |
1041 | if let Categorization::Local(local_id) = err.cmt.cat { | |
1042 | let span = self.tcx.map.span(local_id); | |
1043 | if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) { | |
9cc50fc6 | 1044 | db.span_suggestion( |
92a42be0 SL |
1045 | span, |
1046 | &format!("to make the {} mutable, use `mut` as shown:", | |
1047 | self.cmt_to_string(&err.cmt)), | |
1048 | format!("mut {}", snippet)); | |
1049 | } | |
1050 | } | |
1051 | } | |
1a4d82fc JJ |
1052 | } |
1053 | } | |
1054 | ||
1055 | err_out_of_scope(super_scope, sub_scope) => { | |
c1a9b12d | 1056 | self.tcx.note_and_explain_region( |
9cc50fc6 | 1057 | db, |
1a4d82fc JJ |
1058 | "reference must be valid for ", |
1059 | sub_scope, | |
1060 | "..."); | |
c1a9b12d | 1061 | self.tcx.note_and_explain_region( |
9cc50fc6 | 1062 | db, |
1a4d82fc JJ |
1063 | "...but borrowed value is only valid for ", |
1064 | super_scope, | |
1065 | ""); | |
62682a34 | 1066 | if let Some(span) = statement_scope_span(self.tcx, super_scope) { |
9cc50fc6 SL |
1067 | db.span_help(span, |
1068 | "consider using a `let` binding to increase its lifetime"); | |
1a4d82fc JJ |
1069 | } |
1070 | } | |
1071 | ||
1072 | err_borrowed_pointer_too_short(loan_scope, ptr_scope) => { | |
1073 | let descr = match opt_loan_path(&err.cmt) { | |
1074 | Some(lp) => { | |
7453a54e | 1075 | format!("`{}`", self.loan_path_to_string(&lp)) |
1a4d82fc | 1076 | } |
7453a54e | 1077 | None => self.cmt_to_string(&err.cmt), |
1a4d82fc | 1078 | }; |
c1a9b12d | 1079 | self.tcx.note_and_explain_region( |
9cc50fc6 | 1080 | db, |
1a4d82fc | 1081 | &format!("{} would have to be valid for ", |
c34b1796 | 1082 | descr), |
1a4d82fc JJ |
1083 | loan_scope, |
1084 | "..."); | |
c1a9b12d | 1085 | self.tcx.note_and_explain_region( |
9cc50fc6 | 1086 | db, |
c34b1796 | 1087 | &format!("...but {} is only valid for ", descr), |
1a4d82fc JJ |
1088 | ptr_scope, |
1089 | ""); | |
1090 | } | |
1091 | } | |
1092 | } | |
1093 | ||
1094 | pub fn append_loan_path_to_string(&self, | |
1095 | loan_path: &LoanPath<'tcx>, | |
1096 | out: &mut String) { | |
1097 | match loan_path.kind { | |
1098 | LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) | | |
1099 | LpVar(id) => { | |
c1a9b12d | 1100 | out.push_str(&self.tcx.local_var_name_str(id)); |
1a4d82fc JJ |
1101 | } |
1102 | ||
1103 | LpDowncast(ref lp_base, variant_def_id) => { | |
1104 | out.push('('); | |
7453a54e | 1105 | self.append_loan_path_to_string(&lp_base, out); |
1a4d82fc | 1106 | out.push_str(DOWNCAST_PRINTED_OPERATOR); |
c1a9b12d | 1107 | out.push_str(&self.tcx.item_path_str(variant_def_id)); |
1a4d82fc JJ |
1108 | out.push(')'); |
1109 | } | |
1110 | ||
1111 | ||
9cc50fc6 | 1112 | LpExtend(ref lp_base, _, LpInterior(_, InteriorField(fname))) => { |
7453a54e | 1113 | self.append_autoderefd_loan_path_to_string(&lp_base, out); |
1a4d82fc JJ |
1114 | match fname { |
1115 | mc::NamedField(fname) => { | |
1116 | out.push('.'); | |
c1a9b12d | 1117 | out.push_str(&fname.as_str()); |
1a4d82fc JJ |
1118 | } |
1119 | mc::PositionalField(idx) => { | |
1120 | out.push('.'); | |
c34b1796 | 1121 | out.push_str(&idx.to_string()); |
1a4d82fc JJ |
1122 | } |
1123 | } | |
1124 | } | |
1125 | ||
9cc50fc6 | 1126 | LpExtend(ref lp_base, _, LpInterior(_, InteriorElement(..))) => { |
7453a54e | 1127 | self.append_autoderefd_loan_path_to_string(&lp_base, out); |
1a4d82fc JJ |
1128 | out.push_str("[..]"); |
1129 | } | |
1130 | ||
1131 | LpExtend(ref lp_base, _, LpDeref(_)) => { | |
1132 | out.push('*'); | |
7453a54e | 1133 | self.append_loan_path_to_string(&lp_base, out); |
1a4d82fc JJ |
1134 | } |
1135 | } | |
1136 | } | |
1137 | ||
1138 | pub fn append_autoderefd_loan_path_to_string(&self, | |
1139 | loan_path: &LoanPath<'tcx>, | |
1140 | out: &mut String) { | |
1141 | match loan_path.kind { | |
1142 | LpExtend(ref lp_base, _, LpDeref(_)) => { | |
1143 | // For a path like `(*x).f` or `(*x)[3]`, autoderef | |
1144 | // rules would normally allow users to omit the `*x`. | |
1145 | // So just serialize such paths to `x.f` or x[3]` respectively. | |
7453a54e | 1146 | self.append_autoderefd_loan_path_to_string(&lp_base, out) |
1a4d82fc JJ |
1147 | } |
1148 | ||
1149 | LpDowncast(ref lp_base, variant_def_id) => { | |
1150 | out.push('('); | |
7453a54e | 1151 | self.append_autoderefd_loan_path_to_string(&lp_base, out); |
1a4d82fc | 1152 | out.push(':'); |
c1a9b12d | 1153 | out.push_str(&self.tcx.item_path_str(variant_def_id)); |
1a4d82fc JJ |
1154 | out.push(')'); |
1155 | } | |
1156 | ||
1157 | LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => { | |
1158 | self.append_loan_path_to_string(loan_path, out) | |
1159 | } | |
1160 | } | |
1161 | } | |
1162 | ||
1163 | pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String { | |
1164 | let mut result = String::new(); | |
1165 | self.append_loan_path_to_string(loan_path, &mut result); | |
1166 | result | |
1167 | } | |
1168 | ||
1169 | pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String { | |
1170 | cmt.descriptive_string(self.tcx) | |
1171 | } | |
9346a6ac AL |
1172 | |
1173 | pub fn cmt_to_path_or_string(&self, cmt: &mc::cmt<'tcx>) -> String { | |
1174 | match opt_loan_path(cmt) { | |
1175 | Some(lp) => format!("`{}`", self.loan_path_to_string(&lp)), | |
1176 | None => self.cmt_to_string(cmt), | |
1177 | } | |
1178 | } | |
1a4d82fc JJ |
1179 | } |
1180 | ||
54a0048b | 1181 | fn statement_scope_span(tcx: &TyCtxt, region: ty::Region) -> Option<Span> { |
62682a34 SL |
1182 | match region { |
1183 | ty::ReScope(scope) => { | |
e9174d1e SL |
1184 | match tcx.map.find(scope.node_id(&tcx.region_maps)) { |
1185 | Some(hir_map::NodeStmt(stmt)) => Some(stmt.span), | |
62682a34 SL |
1186 | _ => None |
1187 | } | |
1188 | } | |
1189 | _ => None | |
1190 | } | |
1a4d82fc JJ |
1191 | } |
1192 | ||
1193 | impl BitwiseOperator for LoanDataFlowOperator { | |
1194 | #[inline] | |
c34b1796 | 1195 | fn join(&self, succ: usize, pred: usize) -> usize { |
1a4d82fc JJ |
1196 | succ | pred // loans from both preds are in scope |
1197 | } | |
1198 | } | |
1199 | ||
1200 | impl DataFlowOperator for LoanDataFlowOperator { | |
1201 | #[inline] | |
1202 | fn initial_value(&self) -> bool { | |
1203 | false // no loans in scope by default | |
1204 | } | |
1205 | } | |
1206 | ||
62682a34 SL |
1207 | impl<'tcx> fmt::Debug for InteriorKind { |
1208 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
85aaf69f | 1209 | match *self { |
62682a34 SL |
1210 | InteriorField(mc::NamedField(fld)) => write!(f, "{}", fld), |
1211 | InteriorField(mc::PositionalField(i)) => write!(f, "#{}", i), | |
1212 | InteriorElement(..) => write!(f, "[]"), | |
85aaf69f SL |
1213 | } |
1214 | } | |
1215 | } | |
1216 | ||
62682a34 SL |
1217 | impl<'tcx> fmt::Debug for Loan<'tcx> { |
1218 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1219 | write!(f, "Loan_{}({:?}, {:?}, {:?}-{:?}, {:?})", | |
1220 | self.index, | |
1221 | self.loan_path, | |
1222 | self.kind, | |
1223 | self.gen_scope, | |
1224 | self.kill_scope, | |
1225 | self.restricted_paths) | |
1a4d82fc JJ |
1226 | } |
1227 | } | |
1228 | ||
62682a34 SL |
1229 | impl<'tcx> fmt::Debug for LoanPath<'tcx> { |
1230 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1a4d82fc JJ |
1231 | match self.kind { |
1232 | LpVar(id) => { | |
62682a34 | 1233 | write!(f, "$({})", ty::tls::with(|tcx| tcx.map.node_to_string(id))) |
1a4d82fc JJ |
1234 | } |
1235 | ||
1236 | LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => { | |
62682a34 SL |
1237 | let s = ty::tls::with(|tcx| tcx.map.node_to_string(var_id)); |
1238 | write!(f, "$({} captured by id={})", s, closure_expr_id) | |
1a4d82fc JJ |
1239 | } |
1240 | ||
1241 | LpDowncast(ref lp, variant_def_id) => { | |
e9174d1e | 1242 | let variant_str = if variant_def_id.is_local() { |
c1a9b12d | 1243 | ty::tls::with(|tcx| tcx.item_path_str(variant_def_id)) |
1a4d82fc | 1244 | } else { |
62682a34 | 1245 | format!("{:?}", variant_def_id) |
1a4d82fc | 1246 | }; |
62682a34 | 1247 | write!(f, "({:?}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str) |
1a4d82fc JJ |
1248 | } |
1249 | ||
1250 | LpExtend(ref lp, _, LpDeref(_)) => { | |
62682a34 | 1251 | write!(f, "{:?}.*", lp) |
1a4d82fc JJ |
1252 | } |
1253 | ||
9cc50fc6 | 1254 | LpExtend(ref lp, _, LpInterior(_, ref interior)) => { |
62682a34 | 1255 | write!(f, "{:?}.{:?}", lp, interior) |
1a4d82fc JJ |
1256 | } |
1257 | } | |
1258 | } | |
1259 | } | |
1260 | ||
62682a34 SL |
1261 | impl<'tcx> fmt::Display for LoanPath<'tcx> { |
1262 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1a4d82fc JJ |
1263 | match self.kind { |
1264 | LpVar(id) => { | |
62682a34 | 1265 | write!(f, "$({})", ty::tls::with(|tcx| tcx.map.node_to_user_string(id))) |
1a4d82fc JJ |
1266 | } |
1267 | ||
1268 | LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => { | |
62682a34 SL |
1269 | let s = ty::tls::with(|tcx| tcx.map.node_to_user_string(var_id)); |
1270 | write!(f, "$({} captured by closure)", s) | |
1a4d82fc JJ |
1271 | } |
1272 | ||
1273 | LpDowncast(ref lp, variant_def_id) => { | |
e9174d1e | 1274 | let variant_str = if variant_def_id.is_local() { |
c1a9b12d | 1275 | ty::tls::with(|tcx| tcx.item_path_str(variant_def_id)) |
1a4d82fc | 1276 | } else { |
62682a34 | 1277 | format!("{:?}", variant_def_id) |
1a4d82fc | 1278 | }; |
62682a34 | 1279 | write!(f, "({}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str) |
1a4d82fc JJ |
1280 | } |
1281 | ||
1282 | LpExtend(ref lp, _, LpDeref(_)) => { | |
62682a34 | 1283 | write!(f, "{}.*", lp) |
1a4d82fc JJ |
1284 | } |
1285 | ||
9cc50fc6 | 1286 | LpExtend(ref lp, _, LpInterior(_, ref interior)) => { |
62682a34 | 1287 | write!(f, "{}.{:?}", lp, interior) |
1a4d82fc JJ |
1288 | } |
1289 | } | |
1290 | } | |
1291 | } |