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