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