]>
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 | ||
abe05a73 | 23 | use rustc::hir::HirId; |
54a0048b | 24 | use rustc::hir::map as hir_map; |
32a655c1 | 25 | use rustc::hir::map::blocks::FnLikeNode; |
54a0048b | 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; |
abe05a73 XL |
31 | use rustc::middle::borrowck::BorrowCheckResult; |
32 | use rustc::hir::def_id::{DefId, LocalDefId}; | |
1a4d82fc | 33 | use rustc::middle::expr_use_visitor as euv; |
62682a34 | 34 | use rustc::middle::mem_categorization as mc; |
92a42be0 | 35 | use rustc::middle::mem_categorization::Categorization; |
cc61c64b | 36 | use rustc::middle::mem_categorization::ImmutabilityBlame; |
ea8adc8c | 37 | use rustc::middle::region; |
7cac9316 | 38 | use rustc::middle::free_region::RegionRelations; |
ea8adc8c | 39 | use rustc::ty::{self, Ty, TyCtxt}; |
cc61c64b | 40 | use rustc::ty::maps::Providers; |
3b2f2976 | 41 | use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin}; |
abe05a73 | 42 | use rustc::util::nodemap::FxHashSet; |
3b2f2976 | 43 | |
abe05a73 | 44 | use std::cell::RefCell; |
62682a34 | 45 | use std::fmt; |
1a4d82fc | 46 | use std::rc::Rc; |
9e0c209e | 47 | use std::hash::{Hash, Hasher}; |
9cc50fc6 | 48 | use syntax::ast; |
7cac9316 | 49 | use syntax_pos::{MultiSpan, Span}; |
abe05a73 | 50 | use errors::{DiagnosticBuilder, DiagnosticId}; |
e9174d1e | 51 | |
54a0048b | 52 | use rustc::hir; |
8bb4bdeb | 53 | use rustc::hir::intravisit::{self, Visitor}; |
54a0048b | 54 | |
1a4d82fc JJ |
55 | pub mod check_loans; |
56 | ||
57 | pub mod gather_loans; | |
58 | ||
59 | pub mod move_data; | |
60 | ||
abe05a73 XL |
61 | mod unused; |
62 | ||
1a4d82fc JJ |
63 | #[derive(Clone, Copy)] |
64 | pub struct LoanDataFlowOperator; | |
65 | ||
66 | pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>; | |
67 | ||
c30ab7b3 | 68 | pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { |
7cac9316 XL |
69 | for body_owner_def_id in tcx.body_owners() { |
70 | tcx.borrowck(body_owner_def_id); | |
71 | } | |
cc61c64b XL |
72 | } |
73 | ||
74 | pub fn provide(providers: &mut Providers) { | |
75 | *providers = Providers { | |
76 | borrowck, | |
77 | ..*providers | |
78 | }; | |
1a4d82fc JJ |
79 | } |
80 | ||
81 | /// Collection of conclusions determined via borrow checker analyses. | |
82 | pub struct AnalysisData<'a, 'tcx: 'a> { | |
83 | pub all_loans: Vec<Loan<'tcx>>, | |
84 | pub loans: DataFlowContext<'a, 'tcx, LoanDataFlowOperator>, | |
85 | pub move_data: move_data::FlowedMoveData<'a, 'tcx>, | |
86 | } | |
87 | ||
abe05a73 XL |
88 | fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) |
89 | -> Rc<BorrowCheckResult> | |
90 | { | |
cc61c64b | 91 | debug!("borrowck(body_owner_def_id={:?})", owner_def_id); |
54a0048b | 92 | |
cc61c64b | 93 | let owner_id = tcx.hir.as_local_node_id(owner_def_id).unwrap(); |
7cac9316 XL |
94 | |
95 | match tcx.hir.get(owner_id) { | |
96 | hir_map::NodeStructCtor(_) | | |
97 | hir_map::NodeVariant(_) => { | |
98 | // We get invoked with anything that has MIR, but some of | |
99 | // those things (notably the synthesized constructors from | |
100 | // tuple structs/variants) do not have an associated body | |
101 | // and do not need borrowchecking. | |
abe05a73 XL |
102 | return Rc::new(BorrowCheckResult { |
103 | used_mut_nodes: FxHashSet(), | |
104 | }) | |
7cac9316 XL |
105 | } |
106 | _ => { } | |
107 | } | |
108 | ||
cc61c64b | 109 | let body_id = tcx.hir.body_owned_by(owner_id); |
7cac9316 | 110 | let tables = tcx.typeck_tables_of(owner_def_id); |
ea8adc8c XL |
111 | let region_scope_tree = tcx.region_scope_tree(owner_def_id); |
112 | let body = tcx.hir.body(body_id); | |
abe05a73 XL |
113 | let mut bccx = BorrowckCtxt { |
114 | tcx, | |
115 | tables, | |
116 | region_scope_tree, | |
117 | owner_def_id, | |
118 | body, | |
119 | used_mut_nodes: RefCell::new(FxHashSet()), | |
120 | }; | |
8bb4bdeb | 121 | |
041b39d2 XL |
122 | // Eventually, borrowck will always read the MIR, but at the |
123 | // moment we do not. So, for now, we always force MIR to be | |
124 | // constructed for a given fn, since this may result in errors | |
125 | // being reported and we want that to happen. | |
126 | // | |
127 | // Note that `mir_validated` is a "stealable" result; the | |
128 | // thief, `optimized_mir()`, forces borrowck, so we know that | |
129 | // is not yet stolen. | |
130 | tcx.mir_validated(owner_def_id).borrow(); | |
54a0048b | 131 | |
3b2f2976 XL |
132 | // option dance because you can't capture an uninitialized variable |
133 | // by mut-ref. | |
134 | let mut cfg = None; | |
135 | if let Some(AnalysisData { all_loans, | |
136 | loans: loan_dfcx, | |
137 | move_data: flowed_moves }) = | |
abe05a73 | 138 | build_borrowck_dataflow_data(&mut bccx, false, body_id, |
3b2f2976 XL |
139 | |bccx| { |
140 | cfg = Some(cfg::CFG::new(bccx.tcx, &body)); | |
141 | cfg.as_mut().unwrap() | |
142 | }) | |
143 | { | |
abe05a73 | 144 | check_loans::check_loans(&mut bccx, &loan_dfcx, &flowed_moves, &all_loans, body); |
3b2f2976 | 145 | } |
abe05a73 XL |
146 | unused::check(&mut bccx, body); |
147 | ||
148 | Rc::new(BorrowCheckResult { | |
149 | used_mut_nodes: bccx.used_mut_nodes.into_inner(), | |
150 | }) | |
1a4d82fc JJ |
151 | } |
152 | ||
3b2f2976 XL |
153 | fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tcx>, |
154 | force_analysis: bool, | |
155 | body_id: hir::BodyId, | |
156 | get_cfg: F) | |
157 | -> Option<AnalysisData<'a, 'tcx>> | |
158 | where F: FnOnce(&mut BorrowckCtxt<'a, 'tcx>) -> &'c cfg::CFG | |
bd371182 | 159 | { |
1a4d82fc | 160 | // Check the body of fn items. |
e9174d1e | 161 | let tcx = this.tcx; |
32a655c1 SL |
162 | let id_range = { |
163 | let mut visitor = intravisit::IdRangeComputingVisitor::new(&tcx.hir); | |
ea8adc8c | 164 | visitor.visit_body(this.body); |
32a655c1 SL |
165 | visitor.result() |
166 | }; | |
1a4d82fc | 167 | let (all_loans, move_data) = |
32a655c1 | 168 | gather_loans::gather_loans_in_fn(this, body_id); |
1a4d82fc | 169 | |
3b2f2976 XL |
170 | if !force_analysis && move_data.is_empty() && all_loans.is_empty() { |
171 | // large arrays of data inserted as constants can take a lot of | |
172 | // time and memory to borrow-check - see issue #36799. However, | |
173 | // they don't have lvalues, so no borrow-check is actually needed. | |
174 | // Recognize that case and skip borrow-checking. | |
175 | debug!("skipping loan propagation for {:?} because of no loans", body_id); | |
176 | return None; | |
177 | } else { | |
178 | debug!("propagating loans in {:?}", body_id); | |
179 | } | |
180 | ||
181 | let cfg = get_cfg(this); | |
1a4d82fc JJ |
182 | let mut loan_dfcx = |
183 | DataFlowContext::new(this.tcx, | |
184 | "borrowck", | |
ea8adc8c | 185 | Some(this.body), |
1a4d82fc JJ |
186 | cfg, |
187 | LoanDataFlowOperator, | |
188 | id_range, | |
189 | all_loans.len()); | |
190 | for (loan_idx, loan) in all_loans.iter().enumerate() { | |
ea8adc8c | 191 | loan_dfcx.add_gen(loan.gen_scope.item_local_id(), loan_idx); |
e9174d1e | 192 | loan_dfcx.add_kill(KillFrom::ScopeEnd, |
ea8adc8c XL |
193 | loan.kill_scope.item_local_id(), |
194 | loan_idx); | |
1a4d82fc JJ |
195 | } |
196 | loan_dfcx.add_kills_from_flow_exits(cfg); | |
ea8adc8c | 197 | loan_dfcx.propagate(cfg, this.body); |
1a4d82fc JJ |
198 | |
199 | let flowed_moves = move_data::FlowedMoveData::new(move_data, | |
7cac9316 | 200 | this, |
1a4d82fc JJ |
201 | cfg, |
202 | id_range, | |
ea8adc8c | 203 | this.body); |
1a4d82fc | 204 | |
3b2f2976 XL |
205 | Some(AnalysisData { all_loans, |
206 | loans: loan_dfcx, | |
207 | move_data:flowed_moves }) | |
1a4d82fc JJ |
208 | } |
209 | ||
1a4d82fc JJ |
210 | /// Accessor for introspective clients inspecting `AnalysisData` and |
211 | /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer. | |
212 | pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>( | |
a7813a04 | 213 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
8bb4bdeb | 214 | body_id: hir::BodyId, |
92a42be0 | 215 | cfg: &cfg::CFG) |
bd371182 AL |
216 | -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>) |
217 | { | |
8bb4bdeb XL |
218 | let owner_id = tcx.hir.body_owner(body_id); |
219 | let owner_def_id = tcx.hir.local_def_id(owner_id); | |
7cac9316 | 220 | let tables = tcx.typeck_tables_of(owner_def_id); |
ea8adc8c XL |
221 | let region_scope_tree = tcx.region_scope_tree(owner_def_id); |
222 | let body = tcx.hir.body(body_id); | |
abe05a73 XL |
223 | let mut bccx = BorrowckCtxt { |
224 | tcx, | |
225 | tables, | |
226 | region_scope_tree, | |
227 | owner_def_id, | |
228 | body, | |
229 | used_mut_nodes: RefCell::new(FxHashSet()), | |
230 | }; | |
1a4d82fc | 231 | |
3b2f2976 XL |
232 | let dataflow_data = build_borrowck_dataflow_data(&mut bccx, true, body_id, |_| cfg); |
233 | (bccx, dataflow_data.unwrap()) | |
1a4d82fc JJ |
234 | } |
235 | ||
236 | // ---------------------------------------------------------------------- | |
237 | // Type definitions | |
238 | ||
239 | pub struct BorrowckCtxt<'a, 'tcx: 'a> { | |
a7813a04 | 240 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
1a4d82fc | 241 | |
8bb4bdeb XL |
242 | // tables for the current thing we are checking; set to |
243 | // Some in `borrowck_fn` and cleared later | |
244 | tables: &'a ty::TypeckTables<'tcx>, | |
7cac9316 | 245 | |
ea8adc8c | 246 | region_scope_tree: Rc<region::ScopeTree>, |
7cac9316 XL |
247 | |
248 | owner_def_id: DefId, | |
ea8adc8c XL |
249 | |
250 | body: &'tcx hir::Body, | |
abe05a73 XL |
251 | |
252 | used_mut_nodes: RefCell<FxHashSet<HirId>>, | |
1a4d82fc JJ |
253 | } |
254 | ||
3b2f2976 XL |
255 | impl<'b, 'tcx: 'b> BorrowckErrors for BorrowckCtxt<'b, 'tcx> { |
256 | fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self, | |
257 | sp: S, | |
258 | msg: &str, | |
abe05a73 | 259 | code: DiagnosticId) |
3b2f2976 XL |
260 | -> DiagnosticBuilder<'a> |
261 | { | |
262 | self.tcx.sess.struct_span_err_with_code(sp, msg, code) | |
263 | } | |
264 | ||
265 | fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self, | |
266 | sp: S, | |
267 | msg: &str) | |
268 | -> DiagnosticBuilder<'a> | |
269 | { | |
270 | self.tcx.sess.struct_span_err(sp, msg) | |
271 | } | |
272 | } | |
273 | ||
1a4d82fc JJ |
274 | /////////////////////////////////////////////////////////////////////////// |
275 | // Loans and loan paths | |
276 | ||
277 | /// Record of a loan that was issued. | |
278 | pub struct Loan<'tcx> { | |
c34b1796 | 279 | index: usize, |
1a4d82fc JJ |
280 | loan_path: Rc<LoanPath<'tcx>>, |
281 | kind: ty::BorrowKind, | |
282 | restricted_paths: Vec<Rc<LoanPath<'tcx>>>, | |
283 | ||
284 | /// gen_scope indicates where loan is introduced. Typically the | |
285 | /// loan is introduced at the point of the borrow, but in some | |
286 | /// cases, notably method arguments, the loan may be introduced | |
287 | /// only later, once it comes into scope. See also | |
288 | /// `GatherLoanCtxt::compute_gen_scope`. | |
ea8adc8c | 289 | gen_scope: region::Scope, |
1a4d82fc JJ |
290 | |
291 | /// kill_scope indicates when the loan goes out of scope. This is | |
292 | /// either when the lifetime expires or when the local variable | |
293 | /// which roots the loan-path goes out of scope, whichever happens | |
294 | /// faster. See also `GatherLoanCtxt::compute_kill_scope`. | |
ea8adc8c | 295 | kill_scope: region::Scope, |
1a4d82fc JJ |
296 | span: Span, |
297 | cause: euv::LoanCause, | |
298 | } | |
299 | ||
300 | impl<'tcx> Loan<'tcx> { | |
301 | pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> { | |
302 | self.loan_path.clone() | |
303 | } | |
304 | } | |
305 | ||
9e0c209e | 306 | #[derive(Eq)] |
1a4d82fc JJ |
307 | pub struct LoanPath<'tcx> { |
308 | kind: LoanPathKind<'tcx>, | |
ea8adc8c | 309 | ty: Ty<'tcx>, |
1a4d82fc JJ |
310 | } |
311 | ||
312 | impl<'tcx> PartialEq for LoanPath<'tcx> { | |
313 | fn eq(&self, that: &LoanPath<'tcx>) -> bool { | |
9e0c209e SL |
314 | self.kind == that.kind |
315 | } | |
316 | } | |
317 | ||
318 | impl<'tcx> Hash for LoanPath<'tcx> { | |
319 | fn hash<H: Hasher>(&self, state: &mut H) { | |
320 | self.kind.hash(state); | |
1a4d82fc JJ |
321 | } |
322 | } | |
323 | ||
85aaf69f | 324 | #[derive(PartialEq, Eq, Hash, Debug)] |
1a4d82fc | 325 | pub enum LoanPathKind<'tcx> { |
c34b1796 | 326 | LpVar(ast::NodeId), // `x` in README.md |
1a4d82fc | 327 | LpUpvar(ty::UpvarId), // `x` captured by-value into closure |
e9174d1e | 328 | LpDowncast(Rc<LoanPath<'tcx>>, DefId), // `x` downcast to particular enum variant |
9e0c209e | 329 | LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem<'tcx>) |
1a4d82fc JJ |
330 | } |
331 | ||
332 | impl<'tcx> LoanPath<'tcx> { | |
ea8adc8c | 333 | fn new(kind: LoanPathKind<'tcx>, ty: Ty<'tcx>) -> LoanPath<'tcx> { |
1a4d82fc JJ |
334 | LoanPath { kind: kind, ty: ty } |
335 | } | |
336 | ||
ea8adc8c | 337 | fn to_type(&self) -> Ty<'tcx> { self.ty } |
1a4d82fc JJ |
338 | } |
339 | ||
340 | // FIXME (pnkfelix): See discussion here | |
341 | // https://github.com/pnkfelix/rust/commit/ | |
342 | // b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003 | |
c34b1796 | 343 | const DOWNCAST_PRINTED_OPERATOR: &'static str = " as "; |
1a4d82fc | 344 | |
85aaf69f SL |
345 | // A local, "cleaned" version of `mc::InteriorKind` that drops |
346 | // information that is not relevant to loan-path analysis. (In | |
b039eaaf | 347 | // particular, the distinction between how precisely an array-element |
85aaf69f | 348 | // is tracked is irrelevant here.) |
62682a34 | 349 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] |
85aaf69f SL |
350 | pub enum InteriorKind { |
351 | InteriorField(mc::FieldName), | |
7cac9316 | 352 | InteriorElement, |
85aaf69f SL |
353 | } |
354 | ||
355 | trait ToInteriorKind { fn cleaned(self) -> InteriorKind; } | |
356 | impl ToInteriorKind for mc::InteriorKind { | |
357 | fn cleaned(self) -> InteriorKind { | |
358 | match self { | |
359 | mc::InteriorField(name) => InteriorField(name), | |
7cac9316 | 360 | mc::InteriorElement(_) => InteriorElement, |
85aaf69f SL |
361 | } |
362 | } | |
363 | } | |
364 | ||
9cc50fc6 SL |
365 | // This can be: |
366 | // - a pointer dereference (`*LV` in README.md) | |
367 | // - a field reference, with an optional definition of the containing | |
368 | // enum variant (`LV.f` in README.md) | |
369 | // `DefId` is present when the field is part of struct that is in | |
370 | // a variant of an enum. For instance in: | |
371 | // `enum E { X { foo: u32 }, Y { foo: u32 }}` | |
372 | // each `foo` is qualified by the definitition id of the variant (`X` or `Y`). | |
c34b1796 | 373 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] |
9e0c209e SL |
374 | pub enum LoanPathElem<'tcx> { |
375 | LpDeref(mc::PointerKind<'tcx>), | |
9cc50fc6 | 376 | LpInterior(Option<DefId>, InteriorKind), |
1a4d82fc JJ |
377 | } |
378 | ||
abe05a73 | 379 | fn closure_to_block(closure_id: LocalDefId, |
3b2f2976 | 380 | tcx: TyCtxt) -> ast::NodeId { |
abe05a73 | 381 | let closure_id = tcx.hir.local_def_id_to_node_id(closure_id); |
32a655c1 | 382 | match tcx.hir.get(closure_id) { |
e9174d1e | 383 | hir_map::NodeExpr(expr) => match expr.node { |
ea8adc8c | 384 | hir::ExprClosure(.., body_id, _, _) => { |
32a655c1 | 385 | body_id.node_id |
1a4d82fc JJ |
386 | } |
387 | _ => { | |
54a0048b | 388 | bug!("encountered non-closure id: {}", closure_id) |
1a4d82fc JJ |
389 | } |
390 | }, | |
54a0048b | 391 | _ => bug!("encountered non-expr id: {}", closure_id) |
1a4d82fc JJ |
392 | } |
393 | } | |
394 | ||
a7813a04 | 395 | impl<'a, 'tcx> LoanPath<'tcx> { |
ea8adc8c | 396 | pub fn kill_scope(&self, bccx: &BorrowckCtxt<'a, 'tcx>) -> region::Scope { |
1a4d82fc | 397 | match self.kind { |
ea8adc8c XL |
398 | LpVar(local_id) => { |
399 | let hir_id = bccx.tcx.hir.node_to_hir_id(local_id); | |
400 | bccx.region_scope_tree.var_scope(hir_id.local_id) | |
401 | } | |
1a4d82fc | 402 | LpUpvar(upvar_id) => { |
7cac9316 | 403 | let block_id = closure_to_block(upvar_id.closure_expr_id, bccx.tcx); |
ea8adc8c XL |
404 | let hir_id = bccx.tcx.hir.node_to_hir_id(block_id); |
405 | region::Scope::Node(hir_id.local_id) | |
1a4d82fc JJ |
406 | } |
407 | LpDowncast(ref base, _) | | |
7cac9316 | 408 | LpExtend(ref base, ..) => base.kill_scope(bccx), |
1a4d82fc JJ |
409 | } |
410 | } | |
411 | ||
412 | fn has_fork(&self, other: &LoanPath<'tcx>) -> bool { | |
413 | match (&self.kind, &other.kind) { | |
9cc50fc6 SL |
414 | (&LpExtend(ref base, _, LpInterior(opt_variant_id, id)), |
415 | &LpExtend(ref base2, _, LpInterior(opt_variant_id2, id2))) => | |
416 | if id == id2 && opt_variant_id == opt_variant_id2 { | |
7453a54e | 417 | base.has_fork(&base2) |
1a4d82fc JJ |
418 | } else { |
419 | true | |
420 | }, | |
421 | (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other), | |
7453a54e | 422 | (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&base), |
1a4d82fc JJ |
423 | _ => false, |
424 | } | |
425 | } | |
426 | ||
c34b1796 | 427 | fn depth(&self) -> usize { |
1a4d82fc JJ |
428 | match self.kind { |
429 | LpExtend(ref base, _, LpDeref(_)) => base.depth(), | |
9e0c209e | 430 | LpExtend(ref base, _, LpInterior(..)) => base.depth() + 1, |
1a4d82fc JJ |
431 | _ => 0, |
432 | } | |
433 | } | |
434 | ||
435 | fn common(&self, other: &LoanPath<'tcx>) -> Option<LoanPath<'tcx>> { | |
436 | match (&self.kind, &other.kind) { | |
9cc50fc6 SL |
437 | (&LpExtend(ref base, a, LpInterior(opt_variant_id, id)), |
438 | &LpExtend(ref base2, _, LpInterior(opt_variant_id2, id2))) => { | |
439 | if id == id2 && opt_variant_id == opt_variant_id2 { | |
7453a54e | 440 | base.common(&base2).map(|x| { |
1a4d82fc JJ |
441 | let xd = x.depth(); |
442 | if base.depth() == xd && base2.depth() == xd { | |
1a4d82fc | 443 | LoanPath { |
9cc50fc6 | 444 | kind: LpExtend(Rc::new(x), a, LpInterior(opt_variant_id, id)), |
1a4d82fc JJ |
445 | ty: self.ty, |
446 | } | |
447 | } else { | |
448 | x | |
449 | } | |
450 | }) | |
451 | } else { | |
7453a54e | 452 | base.common(&base2) |
1a4d82fc JJ |
453 | } |
454 | } | |
455 | (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other), | |
7453a54e | 456 | (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&other), |
1a4d82fc JJ |
457 | (&LpVar(id), &LpVar(id2)) => { |
458 | if id == id2 { | |
1a4d82fc JJ |
459 | Some(LoanPath { kind: LpVar(id), ty: self.ty }) |
460 | } else { | |
461 | None | |
462 | } | |
463 | } | |
464 | (&LpUpvar(id), &LpUpvar(id2)) => { | |
465 | if id == id2 { | |
1a4d82fc JJ |
466 | Some(LoanPath { kind: LpUpvar(id), ty: self.ty }) |
467 | } else { | |
468 | None | |
469 | } | |
470 | } | |
471 | _ => None, | |
472 | } | |
473 | } | |
474 | } | |
475 | ||
476 | pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> { | |
477 | //! Computes the `LoanPath` (if any) for a `cmt`. | |
478 | //! Note that this logic is somewhat duplicated in | |
479 | //! the method `compute()` found in `gather_loans::restrictions`, | |
480 | //! which allows it to share common loan path pieces as it | |
481 | //! traverses the CMT. | |
482 | ||
85aaf69f | 483 | let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty)); |
1a4d82fc JJ |
484 | |
485 | match cmt.cat { | |
92a42be0 SL |
486 | Categorization::Rvalue(..) | |
487 | Categorization::StaticItem => { | |
1a4d82fc JJ |
488 | None |
489 | } | |
490 | ||
92a42be0 | 491 | Categorization::Local(id) => { |
1a4d82fc JJ |
492 | Some(new_lp(LpVar(id))) |
493 | } | |
494 | ||
92a42be0 | 495 | Categorization::Upvar(mc::Upvar { id, .. }) => { |
1a4d82fc JJ |
496 | Some(new_lp(LpUpvar(id))) |
497 | } | |
498 | ||
7cac9316 | 499 | Categorization::Deref(ref cmt_base, pk) => { |
1a4d82fc JJ |
500 | opt_loan_path(cmt_base).map(|lp| { |
501 | new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk))) | |
502 | }) | |
503 | } | |
504 | ||
92a42be0 | 505 | Categorization::Interior(ref cmt_base, ik) => { |
1a4d82fc | 506 | opt_loan_path(cmt_base).map(|lp| { |
9cc50fc6 SL |
507 | let opt_variant_id = match cmt_base.cat { |
508 | Categorization::Downcast(_, did) => Some(did), | |
509 | _ => None | |
510 | }; | |
511 | new_lp(LpExtend(lp, cmt.mutbl, LpInterior(opt_variant_id, ik.cleaned()))) | |
1a4d82fc JJ |
512 | }) |
513 | } | |
514 | ||
92a42be0 | 515 | Categorization::Downcast(ref cmt_base, variant_def_id) => |
1a4d82fc JJ |
516 | opt_loan_path(cmt_base) |
517 | .map(|lp| { | |
518 | new_lp(LpDowncast(lp, variant_def_id)) | |
519 | }), | |
520 | ||
521 | } | |
522 | } | |
523 | ||
524 | /////////////////////////////////////////////////////////////////////////// | |
525 | // Errors | |
526 | ||
527 | // Errors that can occur | |
32a655c1 | 528 | #[derive(Debug, PartialEq)] |
9e0c209e | 529 | pub enum bckerr_code<'tcx> { |
1a4d82fc | 530 | err_mutbl, |
9e0c209e | 531 | /// superscope, subscope, loan cause |
7cac9316 XL |
532 | err_out_of_scope(ty::Region<'tcx>, ty::Region<'tcx>, euv::LoanCause), |
533 | err_borrowed_pointer_too_short(ty::Region<'tcx>, ty::Region<'tcx>), // loan, ptr | |
1a4d82fc JJ |
534 | } |
535 | ||
536 | // Combination of an error code and the categorization of the expression | |
537 | // that caused it | |
32a655c1 | 538 | #[derive(Debug, PartialEq)] |
1a4d82fc JJ |
539 | pub struct BckError<'tcx> { |
540 | span: Span, | |
c1a9b12d | 541 | cause: AliasableViolationKind, |
1a4d82fc | 542 | cmt: mc::cmt<'tcx>, |
9e0c209e | 543 | code: bckerr_code<'tcx> |
1a4d82fc JJ |
544 | } |
545 | ||
c1a9b12d | 546 | #[derive(Copy, Clone, Debug, PartialEq)] |
1a4d82fc JJ |
547 | pub enum AliasableViolationKind { |
548 | MutabilityViolation, | |
549 | BorrowViolation(euv::LoanCause) | |
550 | } | |
551 | ||
c34b1796 | 552 | #[derive(Copy, Clone, Debug)] |
1a4d82fc JJ |
553 | pub enum MovedValueUseKind { |
554 | MovedInUse, | |
555 | MovedInCapture, | |
556 | } | |
557 | ||
558 | /////////////////////////////////////////////////////////////////////////// | |
559 | // Misc | |
560 | ||
561 | impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { | |
8bb4bdeb | 562 | pub fn is_subregion_of(&self, |
7cac9316 XL |
563 | r_sub: ty::Region<'tcx>, |
564 | r_sup: ty::Region<'tcx>) | |
bd371182 AL |
565 | -> bool |
566 | { | |
7cac9316 XL |
567 | let region_rels = RegionRelations::new(self.tcx, |
568 | self.owner_def_id, | |
ea8adc8c | 569 | &self.region_scope_tree, |
7cac9316 XL |
570 | &self.tables.free_region_map); |
571 | region_rels.is_subregion_of(r_sub, r_sup) | |
1a4d82fc JJ |
572 | } |
573 | ||
574 | pub fn report(&self, err: BckError<'tcx>) { | |
9346a6ac AL |
575 | // Catch and handle some particular cases. |
576 | match (&err.code, &err.cause) { | |
9e0c209e | 577 | (&err_out_of_scope(&ty::ReScope(_), &ty::ReStatic, _), |
c1a9b12d | 578 | &BorrowViolation(euv::ClosureCapture(span))) | |
7cac9316 XL |
579 | (&err_out_of_scope(&ty::ReScope(_), &ty::ReEarlyBound(..), _), |
580 | &BorrowViolation(euv::ClosureCapture(span))) | | |
9e0c209e | 581 | (&err_out_of_scope(&ty::ReScope(_), &ty::ReFree(..), _), |
c1a9b12d | 582 | &BorrowViolation(euv::ClosureCapture(span))) => { |
9346a6ac AL |
583 | return self.report_out_of_scope_escaping_closure_capture(&err, span); |
584 | } | |
585 | _ => { } | |
586 | } | |
587 | ||
ea8adc8c | 588 | self.report_bckerr(&err); |
1a4d82fc JJ |
589 | } |
590 | ||
a7813a04 XL |
591 | pub fn report_use_of_moved_value(&self, |
592 | use_span: Span, | |
593 | use_kind: MovedValueUseKind, | |
594 | lp: &LoanPath<'tcx>, | |
595 | the_move: &move_data::Move, | |
596 | moved_lp: &LoanPath<'tcx>, | |
7cac9316 | 597 | _param_env: ty::ParamEnv<'tcx>) { |
a7813a04 XL |
598 | let (verb, verb_participle) = match use_kind { |
599 | MovedInUse => ("use", "used"), | |
600 | MovedInCapture => ("capture", "captured"), | |
1a4d82fc JJ |
601 | }; |
602 | ||
7cac9316 | 603 | let (_ol, _moved_lp_msg, mut err, need_note) = match the_move.kind { |
1a4d82fc | 604 | move_data::Declared => { |
a7813a04 XL |
605 | // If this is an uninitialized variable, just emit a simple warning |
606 | // and return. | |
3b2f2976 XL |
607 | self.cannot_act_on_uninitialized_variable(use_span, |
608 | verb, | |
609 | &self.loan_path_to_string(lp), | |
610 | Origin::Ast) | |
611 | .span_label(use_span, format!("use of possibly uninitialized `{}`", | |
612 | self.loan_path_to_string(lp))) | |
613 | .emit(); | |
a7813a04 | 614 | return; |
1a4d82fc JJ |
615 | } |
616 | _ => { | |
617 | // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would | |
618 | // normally generate a rather confusing message: | |
619 | // | |
620 | // error: use of moved value: `x.b` | |
621 | // note: `x.a` moved here... | |
622 | // | |
623 | // What we want to do instead is get the 'common ancestor' of the two moves and | |
624 | // use that for most of the message instead, giving is something like this: | |
625 | // | |
626 | // error: use of moved value: `x` | |
627 | // note: `x` moved here (through moving `x.a`)... | |
628 | ||
629 | let common = moved_lp.common(lp); | |
630 | let has_common = common.is_some(); | |
631 | let has_fork = moved_lp.has_fork(lp); | |
632 | let (nl, ol, moved_lp_msg) = | |
633 | if has_fork && has_common { | |
634 | let nl = self.loan_path_to_string(&common.unwrap()); | |
635 | let ol = nl.clone(); | |
636 | let moved_lp_msg = format!(" (through moving `{}`)", | |
637 | self.loan_path_to_string(moved_lp)); | |
638 | (nl, ol, moved_lp_msg) | |
639 | } else { | |
640 | (self.loan_path_to_string(lp), | |
641 | self.loan_path_to_string(moved_lp), | |
642 | String::new()) | |
643 | }; | |
644 | ||
645 | let partial = moved_lp.depth() > lp.depth(); | |
646 | let msg = if !has_fork && partial { "partially " } | |
647 | else if has_fork && !has_common { "collaterally "} | |
abe05a73 XL |
648 | else { "" }; |
649 | let mut err = self.cannot_act_on_moved_value(use_span, | |
650 | verb, | |
651 | msg, | |
652 | &format!("{}", nl), | |
653 | Origin::Ast); | |
7cac9316 XL |
654 | let need_note = match lp.ty.sty { |
655 | ty::TypeVariants::TyClosure(id, _) => { | |
656 | let node_id = self.tcx.hir.as_local_node_id(id).unwrap(); | |
3b2f2976 | 657 | let hir_id = self.tcx.hir.node_to_hir_id(node_id); |
7cac9316 | 658 | if let Some(&(ty::ClosureKind::FnOnce, Some((span, name)))) = |
3b2f2976 | 659 | self.tables.closure_kinds().get(hir_id) |
7cac9316 XL |
660 | { |
661 | err.span_note(span, &format!( | |
662 | "closure cannot be invoked more than once because \ | |
663 | it moves the variable `{}` out of its environment", | |
664 | name | |
665 | )); | |
666 | false | |
667 | } else { | |
668 | true | |
669 | } | |
670 | } | |
671 | _ => true, | |
672 | }; | |
673 | (ol, moved_lp_msg, err, need_note) | |
674 | } | |
a7813a04 XL |
675 | }; |
676 | ||
677 | // Get type of value and span where it was previously | |
678 | // moved. | |
ea8adc8c XL |
679 | let node_id = self.tcx.hir.hir_to_node_id(hir::HirId { |
680 | owner: self.body.value.hir_id.owner, | |
681 | local_id: the_move.id | |
682 | }); | |
a7813a04 XL |
683 | let (move_span, move_note) = match the_move.kind { |
684 | move_data::Declared => { | |
685 | unreachable!(); | |
1a4d82fc | 686 | } |
a7813a04 XL |
687 | |
688 | move_data::MoveExpr | | |
ea8adc8c | 689 | move_data::MovePat => (self.tcx.hir.span(node_id), ""), |
a7813a04 XL |
690 | |
691 | move_data::Captured => | |
ea8adc8c XL |
692 | (match self.tcx.hir.expect_expr(node_id).node { |
693 | hir::ExprClosure(.., fn_decl_span, _) => fn_decl_span, | |
694 | ref r => bug!("Captured({:?}) maps to non-closure: {:?}", | |
a7813a04 XL |
695 | the_move.id, r), |
696 | }, " (into closure)"), | |
1a4d82fc JJ |
697 | }; |
698 | ||
a7813a04 XL |
699 | // Annotate the use and the move in the span. Watch out for |
700 | // the case where the use and the move are the same. This | |
701 | // means the use is in a loop. | |
702 | err = if use_span == move_span { | |
703 | err.span_label( | |
704 | use_span, | |
7cac9316 | 705 | format!("value moved{} here in previous iteration of loop", |
a7813a04 XL |
706 | move_note)); |
707 | err | |
708 | } else { | |
7cac9316 XL |
709 | err.span_label(use_span, format!("value {} here after move", verb_participle)) |
710 | .span_label(move_span, format!("value moved{} here", move_note)); | |
a7813a04 XL |
711 | err |
712 | }; | |
1a4d82fc | 713 | |
7cac9316 XL |
714 | if need_note { |
715 | err.note(&format!("move occurs because `{}` has type `{}`, \ | |
716 | which does not implement the `Copy` trait", | |
717 | self.loan_path_to_string(moved_lp), | |
718 | moved_lp.ty)); | |
719 | } | |
1a4d82fc | 720 | |
a7813a04 XL |
721 | // Note: we used to suggest adding a `ref binding` or calling |
722 | // `clone` but those suggestions have been removed because | |
723 | // they are often not what you actually want to do, and were | |
724 | // not considered particularly helpful. | |
1a4d82fc | 725 | |
9cc50fc6 | 726 | err.emit(); |
1a4d82fc JJ |
727 | } |
728 | ||
85aaf69f SL |
729 | pub fn report_partial_reinitialization_of_uninitialized_structure( |
730 | &self, | |
731 | span: Span, | |
732 | lp: &LoanPath<'tcx>) { | |
abe05a73 XL |
733 | self.cannot_partially_reinit_an_uninit_struct(span, |
734 | &self.loan_path_to_string(lp), | |
735 | Origin::Ast) | |
736 | .emit(); | |
85aaf69f SL |
737 | } |
738 | ||
1a4d82fc JJ |
739 | pub fn report_reassigned_immutable_variable(&self, |
740 | span: Span, | |
741 | lp: &LoanPath<'tcx>, | |
742 | assign: | |
743 | &move_data::Assignment) { | |
3b2f2976 XL |
744 | let mut err = self.cannot_reassign_immutable(span, |
745 | &self.loan_path_to_string(lp), | |
746 | Origin::Ast); | |
abe05a73 | 747 | err.span_label(span, "cannot assign twice to immutable variable"); |
5bcae85e | 748 | if span != assign.span { |
7cac9316 | 749 | err.span_label(assign.span, format!("first assignment to `{}`", |
5bcae85e SL |
750 | self.loan_path_to_string(lp))); |
751 | } | |
752 | err.emit(); | |
1a4d82fc JJ |
753 | } |
754 | ||
a7813a04 XL |
755 | pub fn struct_span_err_with_code<S: Into<MultiSpan>>(&self, |
756 | s: S, | |
757 | msg: &str, | |
abe05a73 | 758 | code: DiagnosticId) |
a7813a04 | 759 | -> DiagnosticBuilder<'a> { |
9cc50fc6 | 760 | self.tcx.sess.struct_span_err_with_code(s, msg, code) |
1a4d82fc JJ |
761 | } |
762 | ||
abe05a73 XL |
763 | pub fn span_err_with_code<S: Into<MultiSpan>>( |
764 | &self, | |
765 | s: S, | |
766 | msg: &str, | |
767 | code: DiagnosticId, | |
768 | ) { | |
ea8adc8c XL |
769 | self.tcx.sess.span_err_with_code(s, msg, code); |
770 | } | |
771 | ||
772 | fn report_bckerr(&self, err: &BckError<'tcx>) { | |
773 | let error_span = err.span.clone(); | |
32a655c1 | 774 | |
7cac9316 | 775 | match err.code { |
1a4d82fc JJ |
776 | err_mutbl => { |
777 | let descr = match err.cmt.note { | |
778 | mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => { | |
7453a54e | 779 | self.cmt_to_string(&err.cmt) |
1a4d82fc JJ |
780 | } |
781 | _ => match opt_loan_path(&err.cmt) { | |
782 | None => { | |
783 | format!("{} {}", | |
784 | err.cmt.mutbl.to_user_str(), | |
7453a54e | 785 | self.cmt_to_string(&err.cmt)) |
32a655c1 | 786 | |
1a4d82fc JJ |
787 | } |
788 | Some(lp) => { | |
789 | format!("{} {} `{}`", | |
790 | err.cmt.mutbl.to_user_str(), | |
7453a54e SL |
791 | self.cmt_to_string(&err.cmt), |
792 | self.loan_path_to_string(&lp)) | |
1a4d82fc JJ |
793 | } |
794 | } | |
795 | }; | |
796 | ||
ea8adc8c | 797 | let mut db = match err.cause { |
c1a9b12d | 798 | MutabilityViolation => { |
abe05a73 XL |
799 | let mut db = self.cannot_assign(error_span, &descr, Origin::Ast); |
800 | if let mc::NoteClosureEnv(upvar_id) = err.cmt.note { | |
801 | let node_id = self.tcx.hir.hir_to_node_id(upvar_id.var_id); | |
802 | let sp = self.tcx.hir.span(node_id); | |
803 | match self.tcx.sess.codemap().span_to_snippet(sp) { | |
804 | Ok(snippet) => { | |
805 | let msg = &format!("consider making `{}` mutable", snippet); | |
806 | db.span_suggestion(sp, msg, format!("mut {}", snippet)); | |
807 | } | |
808 | _ => { | |
809 | db.span_help(sp, "consider making this binding mutable"); | |
810 | } | |
811 | } | |
812 | } | |
813 | db | |
c1a9b12d SL |
814 | } |
815 | BorrowViolation(euv::ClosureCapture(_)) => { | |
abe05a73 | 816 | self.closure_cannot_assign_to_borrowed(error_span, &descr, Origin::Ast) |
1a4d82fc | 817 | } |
c1a9b12d SL |
818 | BorrowViolation(euv::OverloadedOperator) | |
819 | BorrowViolation(euv::AddrOf) | | |
820 | BorrowViolation(euv::RefBinding) | | |
821 | BorrowViolation(euv::AutoRef) | | |
822 | BorrowViolation(euv::AutoUnsafe) | | |
823 | BorrowViolation(euv::ForLoop) | | |
824 | BorrowViolation(euv::MatchDiscriminant) => { | |
abe05a73 | 825 | self.cannot_borrow_path_as_mutable(error_span, &descr, Origin::Ast) |
1a4d82fc | 826 | } |
c1a9b12d | 827 | BorrowViolation(euv::ClosureInvocation) => { |
54a0048b | 828 | span_bug!(err.span, |
1a4d82fc JJ |
829 | "err_mutbl with a closure invocation"); |
830 | } | |
ea8adc8c XL |
831 | }; |
832 | ||
833 | self.note_and_explain_mutbl_error(&mut db, &err, &error_span); | |
834 | self.note_immutability_blame(&mut db, err.cmt.immutability_blame()); | |
835 | db.emit(); | |
1a4d82fc | 836 | } |
ea8adc8c | 837 | err_out_of_scope(super_scope, sub_scope, cause) => { |
1a4d82fc JJ |
838 | let msg = match opt_loan_path(&err.cmt) { |
839 | None => "borrowed value".to_string(), | |
840 | Some(lp) => { | |
7453a54e | 841 | format!("`{}`", self.loan_path_to_string(&lp)) |
1a4d82fc JJ |
842 | } |
843 | }; | |
ea8adc8c XL |
844 | |
845 | // When you have a borrow that lives across a yield, | |
846 | // that reference winds up captured in the generator | |
847 | // type. Regionck then constraints it to live as long | |
848 | // as the generator itself. If that borrow is borrowing | |
849 | // data owned by the generator, this winds up resulting in | |
850 | // an `err_out_of_scope` error: | |
851 | // | |
852 | // ``` | |
853 | // { | |
854 | // let g = || { | |
855 | // let a = &3; // this borrow is forced to ... -+ | |
856 | // yield (); // | | |
857 | // println!("{}", a); // | | |
858 | // }; // | | |
859 | // } <----------------------... live until here --------+ | |
860 | // ``` | |
861 | // | |
862 | // To detect this case, we look for cases where the | |
863 | // `super_scope` (lifetime of the value) is within the | |
864 | // body, but the `sub_scope` is not. | |
865 | debug!("err_out_of_scope: self.body.is_generator = {:?}", | |
866 | self.body.is_generator); | |
867 | let maybe_borrow_across_yield = if self.body.is_generator { | |
868 | let body_scope = region::Scope::Node(self.body.value.hir_id.local_id); | |
869 | debug!("err_out_of_scope: body_scope = {:?}", body_scope); | |
870 | debug!("err_out_of_scope: super_scope = {:?}", super_scope); | |
871 | debug!("err_out_of_scope: sub_scope = {:?}", sub_scope); | |
872 | match (super_scope, sub_scope) { | |
873 | (&ty::RegionKind::ReScope(value_scope), | |
874 | &ty::RegionKind::ReScope(loan_scope)) => { | |
875 | if { | |
876 | // value_scope <= body_scope && | |
877 | self.region_scope_tree.is_subscope_of(value_scope, body_scope) && | |
878 | // body_scope <= loan_scope | |
879 | self.region_scope_tree.is_subscope_of(body_scope, loan_scope) | |
880 | } { | |
881 | // We now know that this is a case | |
882 | // that fits the bill described above: | |
883 | // a borrow of something whose scope | |
884 | // is within the generator, but the | |
885 | // borrow is for a scope outside the | |
886 | // generator. | |
887 | // | |
888 | // Now look within the scope of the of | |
889 | // the value being borrowed (in the | |
890 | // example above, that would be the | |
891 | // block remainder that starts with | |
892 | // `let a`) for a yield. We can cite | |
893 | // that for the user. | |
894 | self.region_scope_tree.yield_in_scope(value_scope) | |
895 | } else { | |
896 | None | |
897 | } | |
898 | } | |
899 | _ => None, | |
900 | } | |
901 | } else { | |
902 | None | |
903 | }; | |
904 | ||
905 | if let Some((yield_span, _)) = maybe_borrow_across_yield { | |
906 | debug!("err_out_of_scope: opt_yield_span = {:?}", yield_span); | |
abe05a73 | 907 | self.cannot_borrow_across_generator_yield(error_span, yield_span, Origin::Ast) |
ea8adc8c XL |
908 | .emit(); |
909 | return; | |
910 | } | |
911 | ||
abe05a73 | 912 | let mut db = self.path_does_not_live_long_enough(error_span, &msg, Origin::Ast); |
ea8adc8c XL |
913 | let (value_kind, value_msg) = match err.cmt.cat { |
914 | mc::Categorization::Rvalue(..) => | |
915 | ("temporary value", "temporary value created here"), | |
916 | _ => | |
917 | ("borrowed value", "borrow occurs here") | |
918 | }; | |
919 | ||
920 | let is_closure = match cause { | |
921 | euv::ClosureCapture(s) => { | |
922 | // The primary span starts out as the closure creation point. | |
923 | // Change the primary span here to highlight the use of the variable | |
924 | // in the closure, because it seems more natural. Highlight | |
925 | // closure creation point as a secondary span. | |
926 | match db.span.primary_span() { | |
927 | Some(primary) => { | |
928 | db.span = MultiSpan::from_span(s); | |
929 | db.span_label(primary, "capture occurs here"); | |
930 | db.span_label(s, "does not live long enough"); | |
931 | true | |
932 | } | |
933 | None => false | |
934 | } | |
935 | } | |
936 | _ => { | |
937 | db.span_label(error_span, "does not live long enough"); | |
938 | false | |
939 | } | |
940 | }; | |
941 | ||
942 | let sub_span = self.region_end_span(sub_scope); | |
943 | let super_span = self.region_end_span(super_scope); | |
944 | ||
945 | match (sub_span, super_span) { | |
946 | (Some(s1), Some(s2)) if s1 == s2 => { | |
947 | if !is_closure { | |
948 | db.span = MultiSpan::from_span(s1); | |
949 | db.span_label(error_span, value_msg); | |
950 | let msg = match opt_loan_path(&err.cmt) { | |
951 | None => value_kind.to_string(), | |
952 | Some(lp) => { | |
953 | format!("`{}`", self.loan_path_to_string(&lp)) | |
954 | } | |
955 | }; | |
956 | db.span_label(s1, | |
957 | format!("{} dropped here while still borrowed", msg)); | |
958 | } else { | |
959 | db.span_label(s1, format!("{} dropped before borrower", value_kind)); | |
960 | } | |
961 | db.note("values in a scope are dropped in the opposite order \ | |
962 | they are created"); | |
963 | } | |
964 | (Some(s1), Some(s2)) if !is_closure => { | |
965 | db.span = MultiSpan::from_span(s2); | |
966 | db.span_label(error_span, value_msg); | |
967 | let msg = match opt_loan_path(&err.cmt) { | |
968 | None => value_kind.to_string(), | |
969 | Some(lp) => { | |
970 | format!("`{}`", self.loan_path_to_string(&lp)) | |
971 | } | |
972 | }; | |
973 | db.span_label(s2, format!("{} dropped here while still borrowed", msg)); | |
974 | db.span_label(s1, format!("{} needs to live until here", value_kind)); | |
975 | } | |
976 | _ => { | |
977 | match sub_span { | |
978 | Some(s) => { | |
979 | db.span_label(s, format!("{} needs to live until here", | |
980 | value_kind)); | |
981 | } | |
982 | None => { | |
983 | self.tcx.note_and_explain_region( | |
984 | &self.region_scope_tree, | |
985 | &mut db, | |
986 | "borrowed value must be valid for ", | |
987 | sub_scope, | |
988 | "..."); | |
989 | } | |
990 | } | |
991 | match super_span { | |
992 | Some(s) => { | |
993 | db.span_label(s, format!("{} only lives until here", value_kind)); | |
994 | } | |
995 | None => { | |
996 | self.tcx.note_and_explain_region( | |
997 | &self.region_scope_tree, | |
998 | &mut db, | |
999 | "...but borrowed value is only valid for ", | |
1000 | super_scope, | |
1001 | ""); | |
1002 | } | |
1003 | } | |
1004 | } | |
1005 | } | |
1006 | ||
1007 | if let ty::ReScope(scope) = *super_scope { | |
1008 | let node_id = scope.node_id(self.tcx, &self.region_scope_tree); | |
1009 | match self.tcx.hir.find(node_id) { | |
1010 | Some(hir_map::NodeStmt(_)) => { | |
1011 | db.note("consider using a `let` binding to increase its lifetime"); | |
1012 | } | |
1013 | _ => {} | |
1014 | } | |
1015 | } | |
1016 | ||
1017 | db.emit(); | |
1a4d82fc | 1018 | } |
ea8adc8c | 1019 | err_borrowed_pointer_too_short(loan_scope, ptr_scope) => { |
9346a6ac | 1020 | let descr = self.cmt_to_path_or_string(&err.cmt); |
abe05a73 | 1021 | let mut db = self.lifetime_too_short_for_reborrow(error_span, &descr, Origin::Ast); |
ea8adc8c XL |
1022 | let descr = match opt_loan_path(&err.cmt) { |
1023 | Some(lp) => { | |
1024 | format!("`{}`", self.loan_path_to_string(&lp)) | |
1025 | } | |
1026 | None => self.cmt_to_string(&err.cmt), | |
1027 | }; | |
1028 | self.tcx.note_and_explain_region( | |
1029 | &self.region_scope_tree, | |
1030 | &mut db, | |
1031 | &format!("{} would have to be valid for ", | |
1032 | descr), | |
1033 | loan_scope, | |
1034 | "..."); | |
1035 | self.tcx.note_and_explain_region( | |
1036 | &self.region_scope_tree, | |
1037 | &mut db, | |
1038 | &format!("...but {} is only valid for ", descr), | |
1039 | ptr_scope, | |
1040 | ""); | |
1041 | ||
1042 | db.emit(); | |
1a4d82fc | 1043 | } |
7cac9316 | 1044 | } |
1a4d82fc JJ |
1045 | } |
1046 | ||
1047 | pub fn report_aliasability_violation(&self, | |
1048 | span: Span, | |
1049 | kind: AliasableViolationKind, | |
32a655c1 SL |
1050 | cause: mc::AliasableReason, |
1051 | cmt: mc::cmt<'tcx>) { | |
1a4d82fc JJ |
1052 | let mut is_closure = false; |
1053 | let prefix = match kind { | |
1054 | MutabilityViolation => { | |
1055 | "cannot assign to data" | |
1056 | } | |
85aaf69f | 1057 | BorrowViolation(euv::ClosureCapture(_)) | |
1a4d82fc JJ |
1058 | BorrowViolation(euv::OverloadedOperator) | |
1059 | BorrowViolation(euv::AddrOf) | | |
1060 | BorrowViolation(euv::AutoRef) | | |
9346a6ac | 1061 | BorrowViolation(euv::AutoUnsafe) | |
1a4d82fc JJ |
1062 | BorrowViolation(euv::RefBinding) | |
1063 | BorrowViolation(euv::MatchDiscriminant) => { | |
1064 | "cannot borrow data mutably" | |
1065 | } | |
1066 | ||
1067 | BorrowViolation(euv::ClosureInvocation) => { | |
1068 | is_closure = true; | |
1069 | "closure invocation" | |
1070 | } | |
1071 | ||
1072 | BorrowViolation(euv::ForLoop) => { | |
1073 | "`for` loop" | |
1074 | } | |
1075 | }; | |
1076 | ||
cc61c64b XL |
1077 | match cause { |
1078 | mc::AliasableStatic | | |
1079 | mc::AliasableStaticMut => { | |
1080 | // This path cannot occur. It happens when we have an | |
1081 | // `&mut` or assignment to a static. But in the case | |
1082 | // of `static X`, we get a mutability violation first, | |
1083 | // and never get here. In the case of `static mut X`, | |
1084 | // that is unsafe and hence the aliasability error is | |
1085 | // ignored. | |
1086 | span_bug!(span, "aliasability violation for static `{}`", prefix) | |
1a4d82fc | 1087 | } |
cc61c64b XL |
1088 | mc::AliasableBorrowed => {} |
1089 | }; | |
1090 | let blame = cmt.immutability_blame(); | |
1091 | let mut err = match blame { | |
1092 | Some(ImmutabilityBlame::ClosureEnv(id)) => { | |
cc61c64b | 1093 | // FIXME: the distinction between these 2 messages looks wrong. |
abe05a73 | 1094 | let help_msg = if let BorrowViolation(euv::ClosureCapture(_)) = kind { |
85aaf69f SL |
1095 | // The aliasability violation with closure captures can |
1096 | // happen for nested closures, so we know the enclosing | |
1097 | // closure incorrectly accepts an `Fn` while it needs to | |
1098 | // be `FnMut`. | |
cc61c64b XL |
1099 | "consider changing this to accept closures that implement `FnMut`" |
1100 | ||
85aaf69f | 1101 | } else { |
cc61c64b XL |
1102 | "consider changing this closure to take self by mutable reference" |
1103 | }; | |
abe05a73 XL |
1104 | let node_id = self.tcx.hir.local_def_id_to_node_id(id); |
1105 | let help_span = self.tcx.hir.span(node_id); | |
1106 | self.cannot_act_on_capture_in_sharable_fn(span, | |
1107 | prefix, | |
1108 | (help_span, help_msg), | |
1109 | Origin::Ast) | |
1a4d82fc | 1110 | } |
cc61c64b | 1111 | _ => { |
abe05a73 XL |
1112 | self.cannot_assign_into_immutable_reference(span, prefix, |
1113 | Origin::Ast) | |
1a4d82fc | 1114 | } |
9cc50fc6 | 1115 | }; |
cc61c64b | 1116 | self.note_immutability_blame(&mut err, blame); |
1a4d82fc JJ |
1117 | |
1118 | if is_closure { | |
a7813a04 | 1119 | err.help("closures behind references must be called via `&mut`"); |
1a4d82fc | 1120 | } |
9cc50fc6 | 1121 | err.emit(); |
1a4d82fc JJ |
1122 | } |
1123 | ||
32a655c1 | 1124 | /// Given a type, if it is an immutable reference, return a suggestion to make it mutable |
cc61c64b | 1125 | fn suggest_mut_for_immutable(&self, pty: &hir::Ty, is_implicit_self: bool) -> Option<String> { |
32a655c1 | 1126 | // Check wether the argument is an immutable reference |
cc61c64b | 1127 | debug!("suggest_mut_for_immutable({:?}, {:?})", pty, is_implicit_self); |
32a655c1 SL |
1128 | if let hir::TyRptr(lifetime, hir::MutTy { |
1129 | mutbl: hir::Mutability::MutImmutable, | |
1130 | ref ty | |
1131 | }) = pty.node { | |
1132 | // Account for existing lifetimes when generating the message | |
cc61c64b XL |
1133 | let pointee_snippet = match self.tcx.sess.codemap().span_to_snippet(ty.span) { |
1134 | Ok(snippet) => snippet, | |
1135 | _ => return None | |
1136 | }; | |
1137 | ||
1138 | let lifetime_snippet = if !lifetime.is_elided() { | |
1139 | format!("{} ", match self.tcx.sess.codemap().span_to_snippet(lifetime.span) { | |
1140 | Ok(lifetime_snippet) => lifetime_snippet, | |
1141 | _ => return None | |
1142 | }) | |
32a655c1 | 1143 | } else { |
cc61c64b XL |
1144 | String::new() |
1145 | }; | |
1146 | Some(format!("use `&{}mut {}` here to make mutable", | |
1147 | lifetime_snippet, | |
1148 | if is_implicit_self { "self" } else { &*pointee_snippet })) | |
1149 | } else { | |
1150 | None | |
1151 | } | |
1152 | } | |
1153 | ||
3b2f2976 | 1154 | fn local_binding_mode(&self, node_id: ast::NodeId) -> ty::BindingMode { |
cc61c64b | 1155 | let pat = match self.tcx.hir.get(node_id) { |
3b2f2976 | 1156 | hir_map::Node::NodeBinding(pat) => pat, |
cc61c64b XL |
1157 | node => bug!("bad node for local: {:?}", node) |
1158 | }; | |
1159 | ||
1160 | match pat.node { | |
3b2f2976 XL |
1161 | hir::PatKind::Binding(..) => { |
1162 | *self.tables | |
1163 | .pat_binding_modes() | |
1164 | .get(pat.hir_id) | |
1165 | .expect("missing binding mode") | |
1166 | } | |
cc61c64b | 1167 | _ => bug!("local is not a binding: {:?}", pat) |
32a655c1 | 1168 | } |
32a655c1 SL |
1169 | } |
1170 | ||
cc61c64b XL |
1171 | fn local_ty(&self, node_id: ast::NodeId) -> (Option<&hir::Ty>, bool) { |
1172 | let parent = self.tcx.hir.get_parent_node(node_id); | |
32a655c1 SL |
1173 | let parent_node = self.tcx.hir.get(parent); |
1174 | ||
1175 | // The parent node is like a fn | |
1176 | if let Some(fn_like) = FnLikeNode::from_node(parent_node) { | |
1177 | // `nid`'s parent's `Body` | |
1178 | let fn_body = self.tcx.hir.body(fn_like.body()); | |
cc61c64b XL |
1179 | // Get the position of `node_id` in the arguments list |
1180 | let arg_pos = fn_body.arguments.iter().position(|arg| arg.pat.id == node_id); | |
32a655c1 SL |
1181 | if let Some(i) = arg_pos { |
1182 | // The argument's `Ty` | |
cc61c64b XL |
1183 | (Some(&fn_like.decl().inputs[i]), |
1184 | i == 0 && fn_like.decl().has_implicit_self) | |
1185 | } else { | |
1186 | (None, false) | |
1187 | } | |
1188 | } else { | |
1189 | (None, false) | |
1190 | } | |
1191 | } | |
1192 | ||
1193 | fn note_immutability_blame(&self, | |
1194 | db: &mut DiagnosticBuilder, | |
1195 | blame: Option<ImmutabilityBlame>) { | |
1196 | match blame { | |
1197 | None => {} | |
1198 | Some(ImmutabilityBlame::ClosureEnv(_)) => {} | |
1199 | Some(ImmutabilityBlame::ImmLocal(node_id)) => { | |
1200 | let let_span = self.tcx.hir.span(node_id); | |
3b2f2976 | 1201 | if let ty::BindByValue(..) = self.local_binding_mode(node_id) { |
cc61c64b XL |
1202 | if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(let_span) { |
1203 | let (_, is_implicit_self) = self.local_ty(node_id); | |
1204 | if is_implicit_self && snippet != "self" { | |
1205 | // avoid suggesting `mut &self`. | |
1206 | return | |
1207 | } | |
1208 | db.span_label( | |
1209 | let_span, | |
7cac9316 | 1210 | format!("consider changing this to `mut {}`", snippet) |
cc61c64b XL |
1211 | ); |
1212 | } | |
1213 | } | |
1214 | } | |
1215 | Some(ImmutabilityBlame::LocalDeref(node_id)) => { | |
1216 | let let_span = self.tcx.hir.span(node_id); | |
1217 | match self.local_binding_mode(node_id) { | |
3b2f2976 | 1218 | ty::BindByReference(..) => { |
cc61c64b XL |
1219 | let snippet = self.tcx.sess.codemap().span_to_snippet(let_span); |
1220 | if let Ok(snippet) = snippet { | |
1221 | db.span_label( | |
1222 | let_span, | |
7cac9316 | 1223 | format!("consider changing this to `{}`", |
cc61c64b XL |
1224 | snippet.replace("ref ", "ref mut ")) |
1225 | ); | |
1226 | } | |
1227 | } | |
3b2f2976 | 1228 | ty::BindByValue(..) => { |
cc61c64b XL |
1229 | if let (Some(local_ty), is_implicit_self) = self.local_ty(node_id) { |
1230 | if let Some(msg) = | |
1231 | self.suggest_mut_for_immutable(local_ty, is_implicit_self) { | |
7cac9316 | 1232 | db.span_label(local_ty.span, msg); |
cc61c64b XL |
1233 | } |
1234 | } | |
1235 | } | |
1236 | } | |
1237 | } | |
1238 | Some(ImmutabilityBlame::AdtFieldDeref(_, field)) => { | |
1239 | let node_id = match self.tcx.hir.as_local_node_id(field.did) { | |
1240 | Some(node_id) => node_id, | |
1241 | None => return | |
1242 | }; | |
1243 | ||
1244 | if let hir_map::Node::NodeField(ref field) = self.tcx.hir.get(node_id) { | |
1245 | if let Some(msg) = self.suggest_mut_for_immutable(&field.ty, false) { | |
7cac9316 | 1246 | db.span_label(field.ty.span, msg); |
cc61c64b | 1247 | } |
32a655c1 SL |
1248 | } |
1249 | } | |
1250 | } | |
1251 | } | |
1252 | ||
9346a6ac AL |
1253 | fn report_out_of_scope_escaping_closure_capture(&self, |
1254 | err: &BckError<'tcx>, | |
1255 | capture_span: Span) | |
1256 | { | |
1257 | let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt); | |
1258 | ||
9346a6ac AL |
1259 | let suggestion = |
1260 | match self.tcx.sess.codemap().span_to_snippet(err.span) { | |
1261 | Ok(string) => format!("move {}", string), | |
1262 | Err(_) => format!("move |<args>| <body>") | |
1263 | }; | |
1264 | ||
abe05a73 XL |
1265 | self.cannot_capture_in_long_lived_closure(err.span, |
1266 | &cmt_path_or_string, | |
1267 | capture_span, | |
1268 | Origin::Ast) | |
9cc50fc6 SL |
1269 | .span_suggestion(err.span, |
1270 | &format!("to force the closure to take ownership of {} \ | |
1271 | (and any other referenced variables), \ | |
041b39d2 | 1272 | use the `move` keyword", |
7453a54e | 1273 | cmt_path_or_string), |
9cc50fc6 SL |
1274 | suggestion) |
1275 | .emit(); | |
9346a6ac AL |
1276 | } |
1277 | ||
7cac9316 | 1278 | fn region_end_span(&self, region: ty::Region<'tcx>) -> Option<Span> { |
9e0c209e SL |
1279 | match *region { |
1280 | ty::ReScope(scope) => { | |
ea8adc8c | 1281 | Some(scope.span(self.tcx, &self.region_scope_tree).end_point()) |
9e0c209e SL |
1282 | } |
1283 | _ => None | |
1284 | } | |
1285 | } | |
1286 | ||
476ff2be SL |
1287 | fn note_and_explain_mutbl_error(&self, db: &mut DiagnosticBuilder, err: &BckError<'tcx>, |
1288 | error_span: &Span) { | |
1289 | match err.cmt.note { | |
1290 | mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => { | |
1291 | // If this is an `Fn` closure, it simply can't mutate upvars. | |
1292 | // If it's an `FnMut` closure, the original variable was declared immutable. | |
1293 | // We need to determine which is the case here. | |
1294 | let kind = match err.cmt.upvar().unwrap().cat { | |
1295 | Categorization::Upvar(mc::Upvar { kind, .. }) => kind, | |
1296 | _ => bug!() | |
1297 | }; | |
1298 | if kind == ty::ClosureKind::Fn { | |
3b2f2976 | 1299 | let closure_node_id = |
abe05a73 | 1300 | self.tcx.hir.local_def_id_to_node_id(upvar_id.closure_expr_id); |
3b2f2976 | 1301 | db.span_help(self.tcx.hir.span(closure_node_id), |
476ff2be | 1302 | "consider changing this closure to take \ |
7cac9316 | 1303 | self by mutable reference"); |
476ff2be SL |
1304 | } |
1305 | } | |
1306 | _ => { | |
32a655c1 | 1307 | if let Categorization::Deref(..) = err.cmt.cat { |
7cac9316 | 1308 | db.span_label(*error_span, "cannot borrow as mutable"); |
476ff2be | 1309 | } else if let Categorization::Local(local_id) = err.cmt.cat { |
32a655c1 | 1310 | let span = self.tcx.hir.span(local_id); |
476ff2be SL |
1311 | if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) { |
1312 | if snippet.starts_with("ref mut ") || snippet.starts_with("&mut ") { | |
7cac9316 XL |
1313 | db.span_label(*error_span, "cannot reborrow mutably"); |
1314 | db.span_label(*error_span, "try removing `&mut` here"); | |
476ff2be | 1315 | } else { |
7cac9316 | 1316 | db.span_label(*error_span, "cannot borrow mutably"); |
476ff2be SL |
1317 | } |
1318 | } else { | |
7cac9316 | 1319 | db.span_label(*error_span, "cannot borrow mutably"); |
476ff2be | 1320 | } |
cc61c64b XL |
1321 | } else if let Categorization::Interior(ref cmt, _) = err.cmt.cat { |
1322 | if let mc::MutabilityCategory::McImmutable = cmt.mutbl { | |
1323 | db.span_label(*error_span, | |
7cac9316 | 1324 | "cannot mutably borrow immutable field"); |
cc61c64b | 1325 | } |
476ff2be SL |
1326 | } |
1327 | } | |
1328 | } | |
1329 | } | |
1a4d82fc JJ |
1330 | pub fn append_loan_path_to_string(&self, |
1331 | loan_path: &LoanPath<'tcx>, | |
1332 | out: &mut String) { | |
1333 | match loan_path.kind { | |
3b2f2976 | 1334 | LpUpvar(ty::UpvarId { var_id: id, closure_expr_id: _ }) => { |
ea8adc8c | 1335 | out.push_str(&self.tcx.hir.name(self.tcx.hir.hir_to_node_id(id)).as_str()); |
3b2f2976 | 1336 | } |
1a4d82fc | 1337 | LpVar(id) => { |
ea8adc8c | 1338 | out.push_str(&self.tcx.hir.name(id).as_str()); |
1a4d82fc JJ |
1339 | } |
1340 | ||
1341 | LpDowncast(ref lp_base, variant_def_id) => { | |
1342 | out.push('('); | |
7453a54e | 1343 | self.append_loan_path_to_string(&lp_base, out); |
1a4d82fc | 1344 | out.push_str(DOWNCAST_PRINTED_OPERATOR); |
c1a9b12d | 1345 | out.push_str(&self.tcx.item_path_str(variant_def_id)); |
1a4d82fc JJ |
1346 | out.push(')'); |
1347 | } | |
1348 | ||
9cc50fc6 | 1349 | LpExtend(ref lp_base, _, LpInterior(_, InteriorField(fname))) => { |
7453a54e | 1350 | self.append_autoderefd_loan_path_to_string(&lp_base, out); |
1a4d82fc JJ |
1351 | match fname { |
1352 | mc::NamedField(fname) => { | |
1353 | out.push('.'); | |
c1a9b12d | 1354 | out.push_str(&fname.as_str()); |
1a4d82fc JJ |
1355 | } |
1356 | mc::PositionalField(idx) => { | |
1357 | out.push('.'); | |
c34b1796 | 1358 | out.push_str(&idx.to_string()); |
1a4d82fc JJ |
1359 | } |
1360 | } | |
1361 | } | |
1362 | ||
7cac9316 | 1363 | LpExtend(ref lp_base, _, LpInterior(_, InteriorElement)) => { |
7453a54e | 1364 | self.append_autoderefd_loan_path_to_string(&lp_base, out); |
1a4d82fc JJ |
1365 | out.push_str("[..]"); |
1366 | } | |
1367 | ||
1368 | LpExtend(ref lp_base, _, LpDeref(_)) => { | |
1369 | out.push('*'); | |
7453a54e | 1370 | self.append_loan_path_to_string(&lp_base, out); |
1a4d82fc JJ |
1371 | } |
1372 | } | |
1373 | } | |
1374 | ||
1375 | pub fn append_autoderefd_loan_path_to_string(&self, | |
1376 | loan_path: &LoanPath<'tcx>, | |
1377 | out: &mut String) { | |
1378 | match loan_path.kind { | |
1379 | LpExtend(ref lp_base, _, LpDeref(_)) => { | |
1380 | // For a path like `(*x).f` or `(*x)[3]`, autoderef | |
1381 | // rules would normally allow users to omit the `*x`. | |
1382 | // So just serialize such paths to `x.f` or x[3]` respectively. | |
7453a54e | 1383 | self.append_autoderefd_loan_path_to_string(&lp_base, out) |
1a4d82fc JJ |
1384 | } |
1385 | ||
1386 | LpDowncast(ref lp_base, variant_def_id) => { | |
1387 | out.push('('); | |
7453a54e | 1388 | self.append_autoderefd_loan_path_to_string(&lp_base, out); |
1a4d82fc | 1389 | out.push(':'); |
c1a9b12d | 1390 | out.push_str(&self.tcx.item_path_str(variant_def_id)); |
1a4d82fc JJ |
1391 | out.push(')'); |
1392 | } | |
1393 | ||
9e0c209e | 1394 | LpVar(..) | LpUpvar(..) | LpExtend(.., LpInterior(..)) => { |
1a4d82fc JJ |
1395 | self.append_loan_path_to_string(loan_path, out) |
1396 | } | |
1397 | } | |
1398 | } | |
1399 | ||
1400 | pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String { | |
1401 | let mut result = String::new(); | |
1402 | self.append_loan_path_to_string(loan_path, &mut result); | |
1403 | result | |
1404 | } | |
1405 | ||
1406 | pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String { | |
1407 | cmt.descriptive_string(self.tcx) | |
1408 | } | |
9346a6ac AL |
1409 | |
1410 | pub fn cmt_to_path_or_string(&self, cmt: &mc::cmt<'tcx>) -> String { | |
1411 | match opt_loan_path(cmt) { | |
1412 | Some(lp) => format!("`{}`", self.loan_path_to_string(&lp)), | |
1413 | None => self.cmt_to_string(cmt), | |
1414 | } | |
1415 | } | |
1a4d82fc JJ |
1416 | } |
1417 | ||
1a4d82fc JJ |
1418 | impl BitwiseOperator for LoanDataFlowOperator { |
1419 | #[inline] | |
c34b1796 | 1420 | fn join(&self, succ: usize, pred: usize) -> usize { |
1a4d82fc JJ |
1421 | succ | pred // loans from both preds are in scope |
1422 | } | |
1423 | } | |
1424 | ||
1425 | impl DataFlowOperator for LoanDataFlowOperator { | |
1426 | #[inline] | |
1427 | fn initial_value(&self) -> bool { | |
1428 | false // no loans in scope by default | |
1429 | } | |
1430 | } | |
1431 | ||
62682a34 SL |
1432 | impl<'tcx> fmt::Debug for InteriorKind { |
1433 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
85aaf69f | 1434 | match *self { |
62682a34 SL |
1435 | InteriorField(mc::NamedField(fld)) => write!(f, "{}", fld), |
1436 | InteriorField(mc::PositionalField(i)) => write!(f, "#{}", i), | |
7cac9316 | 1437 | InteriorElement => write!(f, "[]"), |
85aaf69f SL |
1438 | } |
1439 | } | |
1440 | } | |
1441 | ||
62682a34 SL |
1442 | impl<'tcx> fmt::Debug for Loan<'tcx> { |
1443 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1444 | write!(f, "Loan_{}({:?}, {:?}, {:?}-{:?}, {:?})", | |
1445 | self.index, | |
1446 | self.loan_path, | |
1447 | self.kind, | |
1448 | self.gen_scope, | |
1449 | self.kill_scope, | |
1450 | self.restricted_paths) | |
1a4d82fc JJ |
1451 | } |
1452 | } | |
1453 | ||
62682a34 SL |
1454 | impl<'tcx> fmt::Debug for LoanPath<'tcx> { |
1455 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1a4d82fc JJ |
1456 | match self.kind { |
1457 | LpVar(id) => { | |
32a655c1 | 1458 | write!(f, "$({})", ty::tls::with(|tcx| tcx.hir.node_to_string(id))) |
1a4d82fc JJ |
1459 | } |
1460 | ||
1461 | LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => { | |
3b2f2976 | 1462 | let s = ty::tls::with(|tcx| { |
ea8adc8c | 1463 | let var_node_id = tcx.hir.hir_to_node_id(var_id); |
3b2f2976 XL |
1464 | tcx.hir.node_to_string(var_node_id) |
1465 | }); | |
1466 | write!(f, "$({} captured by id={:?})", s, closure_expr_id) | |
1a4d82fc JJ |
1467 | } |
1468 | ||
1469 | LpDowncast(ref lp, variant_def_id) => { | |
e9174d1e | 1470 | let variant_str = if variant_def_id.is_local() { |
c1a9b12d | 1471 | ty::tls::with(|tcx| tcx.item_path_str(variant_def_id)) |
1a4d82fc | 1472 | } else { |
62682a34 | 1473 | format!("{:?}", variant_def_id) |
1a4d82fc | 1474 | }; |
62682a34 | 1475 | write!(f, "({:?}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str) |
1a4d82fc JJ |
1476 | } |
1477 | ||
1478 | LpExtend(ref lp, _, LpDeref(_)) => { | |
62682a34 | 1479 | write!(f, "{:?}.*", lp) |
1a4d82fc JJ |
1480 | } |
1481 | ||
9cc50fc6 | 1482 | LpExtend(ref lp, _, LpInterior(_, ref interior)) => { |
62682a34 | 1483 | write!(f, "{:?}.{:?}", lp, interior) |
1a4d82fc JJ |
1484 | } |
1485 | } | |
1486 | } | |
1487 | } | |
1488 | ||
62682a34 SL |
1489 | impl<'tcx> fmt::Display for LoanPath<'tcx> { |
1490 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1a4d82fc JJ |
1491 | match self.kind { |
1492 | LpVar(id) => { | |
32a655c1 | 1493 | write!(f, "$({})", ty::tls::with(|tcx| tcx.hir.node_to_user_string(id))) |
1a4d82fc JJ |
1494 | } |
1495 | ||
1496 | LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => { | |
3b2f2976 | 1497 | let s = ty::tls::with(|tcx| { |
ea8adc8c | 1498 | let var_node_id = tcx.hir.hir_to_node_id(var_id); |
3b2f2976 XL |
1499 | tcx.hir.node_to_string(var_node_id) |
1500 | }); | |
62682a34 | 1501 | write!(f, "$({} captured by closure)", s) |
1a4d82fc JJ |
1502 | } |
1503 | ||
1504 | LpDowncast(ref lp, variant_def_id) => { | |
e9174d1e | 1505 | let variant_str = if variant_def_id.is_local() { |
c1a9b12d | 1506 | ty::tls::with(|tcx| tcx.item_path_str(variant_def_id)) |
1a4d82fc | 1507 | } else { |
62682a34 | 1508 | format!("{:?}", variant_def_id) |
1a4d82fc | 1509 | }; |
62682a34 | 1510 | write!(f, "({}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str) |
1a4d82fc JJ |
1511 | } |
1512 | ||
1513 | LpExtend(ref lp, _, LpDeref(_)) => { | |
62682a34 | 1514 | write!(f, "{}.*", lp) |
1a4d82fc JJ |
1515 | } |
1516 | ||
9cc50fc6 | 1517 | LpExtend(ref lp, _, LpInterior(_, ref interior)) => { |
62682a34 | 1518 | write!(f, "{}.{:?}", lp, interior) |
1a4d82fc JJ |
1519 | } |
1520 | } | |
1521 | } | |
1522 | } |