mod.rs 40.9 KB
Newer Older
1
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 3 4 5 6 7 8 9 10
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

S
Steve Klabnik 已提交
11
//! See The Book chapter on the borrow checker for more details.
B
Brian Anderson 已提交
12

13
#![allow(non_camel_case_types)]
P
Patrick Walton 已提交
14

F
Felix S. Klock II 已提交
15
pub use self::LoanPathKind::*;
S
Steven Fackler 已提交
16 17 18 19 20
pub use self::LoanPathElem::*;
pub use self::bckerr_code::*;
pub use self::AliasableViolationKind::*;
pub use self::MovedValueUseKind::*;

21 22
use self::InteriorKind::*;

23 24 25 26 27 28 29
use rustc::middle::cfg;
use rustc::middle::dataflow::DataFlowContext;
use rustc::middle::dataflow::BitwiseOperator;
use rustc::middle::dataflow::DataFlowOperator;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization as mc;
use rustc::middle::region;
30
use rustc::middle::ty::{self, Ty};
31
use rustc::util::ppaux::{note_and_explain_region, Repr, UserString};
E
Eduard Burtescu 已提交
32
use std::rc::Rc;
33
use std::string::String;
34
use syntax::ast;
N
Niko Matsakis 已提交
35
use syntax::ast_map;
36
use syntax::ast_map::blocks::{FnLikeNode, FnParts};
37
use syntax::ast_util;
38
use syntax::codemap::Span;
39
use syntax::parse::token;
40
use syntax::visit;
41 42
use syntax::visit::{Visitor, FnKind};
use syntax::ast::{FnDecl, Block, NodeId};
B
Brian Anderson 已提交
43

44
pub mod check_loans;
N
Niko Matsakis 已提交
45

46
pub mod gather_loans;
N
Niko Matsakis 已提交
47

48 49
pub mod move_data;

50
#[derive(Clone, Copy)]
N
Niko Matsakis 已提交
51
pub struct LoanDataFlowOperator;
52

53
pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>;
B
Brian Anderson 已提交
54

55 56
impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
    fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
57 58
                b: &'v Block, s: Span, id: ast::NodeId) {
        borrowck_fn(self, fk, fd, b, s, id);
59
    }
F
Flavio Percoco 已提交
60

61
    fn visit_item(&mut self, item: &ast::Item) {
F
Flavio Percoco 已提交
62 63
        borrowck_item(self, item);
    }
64 65
}

66
pub fn check_crate(tcx: &ty::ctxt) {
67
    let mut bccx = BorrowckCtxt {
68
        tcx: tcx,
69 70 71 72 73
        stats: BorrowStats {
            loaned_paths_same: 0,
            loaned_paths_imm: 0,
            stable_paths: 0,
            guaranteed_paths: 0
F
Flavio Percoco 已提交
74
        }
75
    };
B
Brian Anderson 已提交
76

77
    visit::walk_crate(&mut bccx, tcx.map.krate());
B
Brian Anderson 已提交
78

F
Flavio Percoco 已提交
79 80 81
    if tcx.sess.borrowck_stats() {
        println!("--- borrowck stats ---");
        println!("paths requiring guarantees: {}",
82
                 bccx.stats.guaranteed_paths);
F
Flavio Percoco 已提交
83
        println!("paths requiring loans     : {}",
84
                 make_stat(&bccx, bccx.stats.loaned_paths_same));
F
Flavio Percoco 已提交
85
        println!("paths requiring imm loans : {}",
86
                 make_stat(&bccx, bccx.stats.loaned_paths_imm));
F
Flavio Percoco 已提交
87
        println!("stable paths              : {}",
88
                 make_stat(&bccx, bccx.stats.stable_paths));
F
Flavio Percoco 已提交
89 90
    }

91
    fn make_stat(bccx: &BorrowckCtxt, stat: uint) -> String {
92
        let total = bccx.stats.guaranteed_paths as f64;
93
        let perc = if total == 0.0 { 0.0 } else { stat as f64 * 100.0 / total };
A
Alex Crichton 已提交
94
        format!("{} ({:.0}%)", stat, perc)
F
Flavio Percoco 已提交
95
    }
B
Brian Anderson 已提交
96 97
}

F
Flavio Percoco 已提交
98 99 100 101 102 103
fn borrowck_item(this: &mut BorrowckCtxt, item: &ast::Item) {
    // Gather loans for items. Note that we don't need
    // to check loans for single expressions. The check
    // loan step is intended for things that have a data
    // flow dependent conditions.
    match item.node {
104 105
        ast::ItemStatic(_, _, ref ex) |
        ast::ItemConst(_, ref ex) => {
106
            gather_loans::gather_loans_in_static_initializer(this, &**ex);
F
Flavio Percoco 已提交
107
        }
108
        _ => { }
F
Flavio Percoco 已提交
109
    }
110 111

    visit::walk_item(this, item);
F
Flavio Percoco 已提交
112 113
}

114
/// Collection of conclusions determined via borrow checker analyses.
115
pub struct AnalysisData<'a, 'tcx: 'a> {
F
Felix S. Klock II 已提交
116
    pub all_loans: Vec<Loan<'tcx>>,
117 118
    pub loans: DataFlowContext<'a, 'tcx, LoanDataFlowOperator>,
    pub move_data: move_data::FlowedMoveData<'a, 'tcx>,
119 120
}

121
fn borrowck_fn(this: &mut BorrowckCtxt,
122
               fk: FnKind,
123
               decl: &ast::FnDecl,
E
Eduard Burtescu 已提交
124
               body: &ast::Block,
125
               sp: Span,
126
               id: ast::NodeId) {
127
    debug!("borrowck_fn(id={})", id);
128 129 130 131 132 133
    let cfg = cfg::CFG::new(this.tcx, body);
    let AnalysisData { all_loans,
                       loans: loan_dfcx,
                       move_data:flowed_moves } =
        build_borrowck_dataflow_data(this, fk, decl, &cfg, body, sp, id);

134 135 136
    move_data::fragments::instrument_move_fragments(&flowed_moves.move_data,
                                                    this.tcx, sp, id);

N
Niko Matsakis 已提交
137 138 139
    check_loans::check_loans(this,
                             &loan_dfcx,
                             flowed_moves,
140
                             &all_loans[..],
N
Niko Matsakis 已提交
141 142 143
                             id,
                             decl,
                             body);
144

145
    visit::walk_fn(this, fk, decl, body, sp);
146
}
147

148
fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
149
                                          fk: FnKind,
150 151 152 153 154
                                          decl: &ast::FnDecl,
                                          cfg: &cfg::CFG,
                                          body: &ast::Block,
                                          sp: Span,
                                          id: ast::NodeId) -> AnalysisData<'a, 'tcx> {
155
    // Check the body of fn items.
156 157
    let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
    let (all_loans, move_data) =
N
Niko Matsakis 已提交
158
        gather_loans::gather_loans_in_fn(this, id, decl, body);
159

160 161
    let mut loan_dfcx =
        DataFlowContext::new(this.tcx,
162 163
                             "borrowck",
                             Some(decl),
164
                             cfg,
165 166
                             LoanDataFlowOperator,
                             id_range,
E
Eduard Burtescu 已提交
167 168
                             all_loans.len());
    for (loan_idx, loan) in all_loans.iter().enumerate() {
169 170
        loan_dfcx.add_gen(loan.gen_scope.node_id(), loan_idx);
        loan_dfcx.add_kill(loan.kill_scope.node_id(), loan_idx);
171
    }
172 173
    loan_dfcx.add_kills_from_flow_exits(cfg);
    loan_dfcx.propagate(cfg, body);
174 175 176

    let flowed_moves = move_data::FlowedMoveData::new(move_data,
                                                      this.tcx,
177
                                                      cfg,
178
                                                      id_range,
179
                                                      decl,
180 181
                                                      body);

182 183 184 185
    AnalysisData { all_loans: all_loans,
                   loans: loan_dfcx,
                   move_data:flowed_moves }
}
N
Niko Matsakis 已提交
186

187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
/// This and a `ty::ctxt` is all you need to run the dataflow analyses
/// used in the borrow checker.
pub struct FnPartsWithCFG<'a> {
    pub fn_parts: FnParts<'a>,
    pub cfg:  &'a cfg::CFG,
}

impl<'a> FnPartsWithCFG<'a> {
    pub fn from_fn_like(f: &'a FnLikeNode,
                        g: &'a cfg::CFG) -> FnPartsWithCFG<'a> {
        FnPartsWithCFG { fn_parts: f.to_fn_parts(), cfg: g }
    }
}

/// Accessor for introspective clients inspecting `AnalysisData` and
/// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
203 204 205
pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
    tcx: &'a ty::ctxt<'tcx>,
    input: FnPartsWithCFG<'a>) -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>) {
206 207 208

    let mut bccx = BorrowckCtxt {
        tcx: tcx,
209 210 211 212 213
        stats: BorrowStats {
            loaned_paths_same: 0,
            loaned_paths_imm: 0,
            stable_paths: 0,
            guaranteed_paths: 0
214 215 216 217 218
        }
    };

    let p = input.fn_parts;

219
    let dataflow_data = build_borrowck_dataflow_data(&mut bccx,
220
                                                     p.kind,
221 222 223 224 225
                                                     &*p.decl,
                                                     input.cfg,
                                                     &*p.body,
                                                     p.span,
                                                     p.id);
226 227

    (bccx, dataflow_data)
N
Niko Matsakis 已提交
228 229
}

B
Brian Anderson 已提交
230 231 232
// ----------------------------------------------------------------------
// Type definitions

233 234
pub struct BorrowckCtxt<'a, 'tcx: 'a> {
    tcx: &'a ty::ctxt<'tcx>,
F
Flavio Percoco 已提交
235 236

    // Statistics:
237
    stats: BorrowStats
B
Brian Anderson 已提交
238 239
}

240 241 242 243 244
struct BorrowStats {
    loaned_paths_same: uint,
    loaned_paths_imm: uint,
    stable_paths: uint,
    guaranteed_paths: uint
245
}
B
Brian Anderson 已提交
246

247
pub type BckResult<'tcx, T> = Result<T, BckError<'tcx>>;
B
Brian Anderson 已提交
248

N
Niko Matsakis 已提交
249 250 251 252
///////////////////////////////////////////////////////////////////////////
// Loans and loan paths

/// Record of a loan that was issued.
F
Felix S. Klock II 已提交
253
pub struct Loan<'tcx> {
N
Niko Matsakis 已提交
254
    index: uint,
F
Felix S. Klock II 已提交
255
    loan_path: Rc<LoanPath<'tcx>>,
256
    kind: ty::BorrowKind,
F
Felix S. Klock II 已提交
257
    restricted_paths: Vec<Rc<LoanPath<'tcx>>>,
258 259 260 261 262 263 264 265 266 267 268 269 270

    /// gen_scope indicates where loan is introduced. Typically the
    /// loan is introduced at the point of the borrow, but in some
    /// cases, notably method arguments, the loan may be introduced
    /// only later, once it comes into scope.  See also
    /// `GatherLoanCtxt::compute_gen_scope`.
    gen_scope: region::CodeExtent,

    /// kill_scope indicates when the loan goes out of scope.  This is
    /// either when the lifetime expires or when the local variable
    /// which roots the loan-path goes out of scope, whichever happens
    /// faster. See also `GatherLoanCtxt::compute_kill_scope`.
    kill_scope: region::CodeExtent,
271
    span: Span,
272
    cause: euv::LoanCause,
273 274
}

F
Felix S. Klock II 已提交
275 276
impl<'tcx> Loan<'tcx> {
    pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> {
277 278 279 280
        self.loan_path.clone()
    }
}

J
Jorge Aparicio 已提交
281
#[derive(Eq, Hash, Debug)]
F
Felix S. Klock II 已提交
282 283 284
pub struct LoanPath<'tcx> {
    kind: LoanPathKind<'tcx>,
    ty: ty::Ty<'tcx>,
N
Niko Matsakis 已提交
285
}
B
Brian Anderson 已提交
286

287 288 289 290 291 292 293 294 295
impl<'tcx> PartialEq for LoanPath<'tcx> {
    fn eq(&self, that: &LoanPath<'tcx>) -> bool {
        let r = self.kind == that.kind;
        debug_assert!(self.ty == that.ty || !r,
                      "Somehow loan paths are equal though their tys are not.");
        r
    }
}

J
Jorge Aparicio 已提交
296
#[derive(PartialEq, Eq, Hash, Debug)]
F
Felix S. Klock II 已提交
297
pub enum LoanPathKind<'tcx> {
298
    LpVar(ast::NodeId),                         // `x` in README.md
F
Felix S. Klock II 已提交
299 300 301 302 303 304 305 306
    LpUpvar(ty::UpvarId),                       // `x` captured by-value into closure
    LpDowncast(Rc<LoanPath<'tcx>>, ast::DefId), // `x` downcast to particular enum variant
    LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem)
}

impl<'tcx> LoanPath<'tcx> {
    fn new(kind: LoanPathKind<'tcx>, ty: ty::Ty<'tcx>) -> LoanPath<'tcx> {
        LoanPath { kind: kind, ty: ty }
307
    }
F
Felix S. Klock II 已提交
308

309
    fn to_type(&self) -> ty::Ty<'tcx> { self.ty }
310 311
}

312 313 314 315 316
// FIXME (pnkfelix): See discussion here
// https://github.com/pnkfelix/rust/commit/
//     b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
static DOWNCAST_PRINTED_OPERATOR : &'static str = " as ";

317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
// A local, "cleaned" version of `mc::InteriorKind` that drops
// information that is not relevant to loan-path analysis. (In
// particular, the distinction between how precisely a array-element
// is tracked is irrelevant here.)
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum InteriorKind {
    InteriorField(mc::FieldName),
    InteriorElement(mc::ElementKind),
}

trait ToInteriorKind { fn cleaned(self) -> InteriorKind; }
impl ToInteriorKind for mc::InteriorKind {
    fn cleaned(self) -> InteriorKind {
        match self {
            mc::InteriorField(name) => InteriorField(name),
            mc::InteriorElement(_, elem_kind) => InteriorElement(elem_kind),
        }
    }
}

J
Jorge Aparicio 已提交
337
#[derive(Copy, PartialEq, Eq, Hash, Debug)]
N
Niko Matsakis 已提交
338
pub enum LoanPathElem {
339 340
    LpDeref(mc::PointerKind),    // `*LV` in README.md
    LpInterior(InteriorKind),    // `LV.f` in README.md
341 342
}

343
pub fn closure_to_block(closure_id: ast::NodeId,
F
Felix S. Klock II 已提交
344
                        tcx: &ty::ctxt) -> ast::NodeId {
345 346
    match tcx.map.get(closure_id) {
        ast_map::NodeExpr(expr) => match expr.node {
347
            ast::ExprClosure(_, _, ref block) => {
348 349 350 351 352
                block.id
            }
            _ => {
                panic!("encountered non-closure id: {}", closure_id)
            }
353
        },
S
Steve Klabnik 已提交
354
        _ => panic!("encountered non-expr id: {}", closure_id)
355 356 357
    }
}

F
Felix S. Klock II 已提交
358 359 360
impl<'tcx> LoanPath<'tcx> {
    pub fn kill_scope(&self, tcx: &ty::ctxt<'tcx>) -> region::CodeExtent {
        match self.kind {
361
            LpVar(local_id) => tcx.region_maps.var_scope(local_id),
362 363 364 365
            LpUpvar(upvar_id) => {
                let block_id = closure_to_block(upvar_id.closure_expr_id, tcx);
                region::CodeExtent::from_node_id(block_id)
            }
366
            LpDowncast(ref base, _) |
367
            LpExtend(ref base, _, _) => base.kill_scope(tcx),
N
Niko Matsakis 已提交
368 369
        }
    }
P
P1start 已提交
370

F
Felix S. Klock II 已提交
371 372
    fn has_fork(&self, other: &LoanPath<'tcx>) -> bool {
        match (&self.kind, &other.kind) {
P
P1start 已提交
373 374 375 376 377 378 379 380 381 382 383 384 385
            (&LpExtend(ref base, _, LpInterior(id)), &LpExtend(ref base2, _, LpInterior(id2))) =>
                if id == id2 {
                    base.has_fork(&**base2)
                } else {
                    true
                },
            (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other),
            (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&**base),
            _ => false,
        }
    }

    fn depth(&self) -> uint {
F
Felix S. Klock II 已提交
386
        match self.kind {
P
P1start 已提交
387 388 389 390 391 392
            LpExtend(ref base, _, LpDeref(_)) => base.depth(),
            LpExtend(ref base, _, LpInterior(_)) => base.depth() + 1,
            _ => 0,
        }
    }

F
Felix S. Klock II 已提交
393 394 395 396
    fn common(&self, other: &LoanPath<'tcx>) -> Option<LoanPath<'tcx>> {
        match (&self.kind, &other.kind) {
            (&LpExtend(ref base, a, LpInterior(id)),
             &LpExtend(ref base2, _, LpInterior(id2))) => {
P
P1start 已提交
397 398 399 400
                if id == id2 {
                    base.common(&**base2).map(|x| {
                        let xd = x.depth();
                        if base.depth() == xd && base2.depth() == xd {
401 402
                            assert_eq!(base.ty, base2.ty);
                            assert_eq!(self.ty, other.ty);
F
Felix S. Klock II 已提交
403 404 405 406
                            LoanPath {
                                kind: LpExtend(Rc::new(x), a, LpInterior(id)),
                                ty: self.ty,
                            }
P
P1start 已提交
407 408 409 410 411 412
                        } else {
                            x
                        }
                    })
                } else {
                    base.common(&**base2)
F
Felix S. Klock II 已提交
413 414
                }
            }
P
P1start 已提交
415 416
            (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other),
            (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&**other),
F
Felix S. Klock II 已提交
417 418
            (&LpVar(id), &LpVar(id2)) => {
                if id == id2 {
419
                    assert_eq!(self.ty, other.ty);
F
Felix S. Klock II 已提交
420 421 422 423 424 425 426
                    Some(LoanPath { kind: LpVar(id), ty: self.ty })
                } else {
                    None
                }
            }
            (&LpUpvar(id), &LpUpvar(id2)) => {
                if id == id2 {
427
                    assert_eq!(self.ty, other.ty);
F
Felix S. Klock II 已提交
428 429 430 431 432
                    Some(LoanPath { kind: LpUpvar(id), ty: self.ty })
                } else {
                    None
                }
            }
P
P1start 已提交
433 434 435
            _ => None,
        }
    }
436
}
B
Brian Anderson 已提交
437

F
Felix S. Klock II 已提交
438
pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
N
Niko Matsakis 已提交
439 440 441 442 443 444
    //! Computes the `LoanPath` (if any) for a `cmt`.
    //! Note that this logic is somewhat duplicated in
    //! the method `compute()` found in `gather_loans::restrictions`,
    //! which allows it to share common loan path pieces as it
    //! traverses the CMT.

445
    let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
F
Felix S. Klock II 已提交
446

N
Niko Matsakis 已提交
447
    match cmt.cat {
A
Alex Crichton 已提交
448
        mc::cat_rvalue(..) |
449 450 451 452
        mc::cat_static_item => {
            None
        }

453
        mc::cat_local(id) => {
F
Felix S. Klock II 已提交
454
            Some(new_lp(LpVar(id)))
N
Niko Matsakis 已提交
455 456
        }

457
        mc::cat_upvar(mc::Upvar { id, .. }) => {
F
Felix S. Klock II 已提交
458
            Some(new_lp(LpUpvar(id)))
459 460
        }

461
        mc::cat_deref(ref cmt_base, _, pk) => {
462
            opt_loan_path(cmt_base).map(|lp| {
F
Felix S. Klock II 已提交
463
                new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
464
            })
N
Niko Matsakis 已提交
465 466
        }

467
        mc::cat_interior(ref cmt_base, ik) => {
468
            opt_loan_path(cmt_base).map(|lp| {
469
                new_lp(LpExtend(lp, cmt.mutbl, LpInterior(ik.cleaned())))
470
            })
N
Niko Matsakis 已提交
471 472
        }

473
        mc::cat_downcast(ref cmt_base, variant_def_id) =>
N
Niko Matsakis 已提交
474
            opt_loan_path(cmt_base)
475
            .map(|lp| {
F
Felix S. Klock II 已提交
476
                new_lp(LpDowncast(lp, variant_def_id))
477 478
            }),

N
Niko Matsakis 已提交
479
    }
480
}
B
Brian Anderson 已提交
481

N
Niko Matsakis 已提交
482 483 484 485
///////////////////////////////////////////////////////////////////////////
// Errors

// Errors that can occur
486
#[derive(PartialEq)]
N
Niko Matsakis 已提交
487
pub enum bckerr_code {
488
    err_mutbl,
N
Niko Matsakis 已提交
489
    err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
490
    err_borrowed_pointer_too_short(ty::Region, ty::Region), // loan, ptr
N
Niko Matsakis 已提交
491 492 493 494
}

// Combination of an error code and the categorization of the expression
// that caused it
495
#[derive(PartialEq)]
496
pub struct BckError<'tcx> {
497
    span: Span,
498
    cause: euv::LoanCause,
499
    cmt: mc::cmt<'tcx>,
N
Niko Matsakis 已提交
500 501 502
    code: bckerr_code
}

503
#[derive(Copy)]
N
Niko Matsakis 已提交
504 505
pub enum AliasableViolationKind {
    MutabilityViolation,
506
    BorrowViolation(euv::LoanCause)
N
Niko Matsakis 已提交
507 508
}

J
Jorge Aparicio 已提交
509
#[derive(Copy, Debug)]
510 511 512 513 514
pub enum MovedValueUseKind {
    MovedInUse,
    MovedInCapture,
}

N
Niko Matsakis 已提交
515
///////////////////////////////////////////////////////////////////////////
B
Brian Anderson 已提交
516 517
// Misc

518
impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
519 520
    pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
                           -> bool {
521
        self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
B
Brian Anderson 已提交
522 523
    }

524
    pub fn report(&self, err: BckError<'tcx>) {
B
Brian Anderson 已提交
525
        self.span_err(
N
Niko Matsakis 已提交
526
            err.span,
527
            &self.bckerr_to_string(&err));
B
Brian Anderson 已提交
528 529 530
        self.note_and_explain_bckerr(err);
    }

531 532 533 534 535 536 537
    pub fn report_use_of_moved_value<'b>(&self,
                                         use_span: Span,
                                         use_kind: MovedValueUseKind,
                                         lp: &LoanPath<'tcx>,
                                         the_move: &move_data::Move,
                                         moved_lp: &LoanPath<'tcx>,
                                         param_env: &ty::ParameterEnvironment<'b,'tcx>) {
538 539 540 541 542
        let verb = match use_kind {
            MovedInUse => "use",
            MovedInCapture => "capture",
        };

543
        let (ol, moved_lp_msg) = match the_move.kind {
544 545 546
            move_data::Declared => {
                self.tcx.sess.span_err(
                    use_span,
J
Jorge Aparicio 已提交
547
                    &format!("{} of possibly uninitialized variable: `{}`",
548
                            verb,
549
                            self.loan_path_to_string(lp)));
550 551
                (self.loan_path_to_string(moved_lp),
                 String::new())
552 553
            }
            _ => {
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
                // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
                // normally generate a rather confusing message:
                //
                //     error: use of moved value: `x.b`
                //     note: `x.a` moved here...
                //
                // What we want to do instead is get the 'common ancestor' of the two moves and
                // use that for most of the message instead, giving is something like this:
                //
                //     error: use of moved value: `x`
                //     note: `x` moved here (through moving `x.a`)...

                let common = moved_lp.common(lp);
                let has_common = common.is_some();
                let has_fork = moved_lp.has_fork(lp);
                let (nl, ol, moved_lp_msg) =
                    if has_fork && has_common {
                        let nl = self.loan_path_to_string(&common.unwrap());
                        let ol = nl.clone();
                        let moved_lp_msg = format!(" (through moving `{}`)",
                                                   self.loan_path_to_string(moved_lp));
                        (nl, ol, moved_lp_msg)
                    } else {
                        (self.loan_path_to_string(lp),
                         self.loan_path_to_string(moved_lp),
                         String::new())
                    };

                let partial = moved_lp.depth() > lp.depth();
                let msg = if !has_fork && partial { "partially " }
                          else if has_fork && !has_common { "collaterally "}
                          else { "" };
586 587
                self.tcx.sess.span_err(
                    use_span,
J
Jorge Aparicio 已提交
588
                    &format!("{} of {}moved value: `{}`",
589
                            verb,
590
                            msg,
591
                            nl));
592
                (ol, moved_lp_msg)
593
            }
594
        };
595

596
        match the_move.kind {
597 598
            move_data::Declared => {}

E
Eduard Burtescu 已提交
599
            move_data::MoveExpr => {
600 601 602
                let (expr_ty, expr_span) = match self.tcx
                                                     .map
                                                     .find(the_move.id) {
603
                    Some(ast_map::NodeExpr(expr)) => {
604
                        (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
E
Eduard Burtescu 已提交
605
                    }
606
                    r => {
J
Jorge Aparicio 已提交
607
                        self.tcx.sess.bug(&format!("MoveExpr({}) maps to \
608
                                                   {:?}, not Expr",
609
                                                  the_move.id,
610
                                                  r))
611
                    }
E
Eduard Burtescu 已提交
612
                };
613 614
                let (suggestion, _) =
                    move_suggestion(param_env, expr_span, expr_ty, ("moved by default", ""));
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
                // If the two spans are the same, it's because the expression will be evaluated
                // multiple times. Avoid printing the same span and adjust the wording so it makes
                // more sense that it's from multiple evalutations.
                if expr_span == use_span {
                    self.tcx.sess.note(
                        &format!("`{}` was previously moved here{} because it has type `{}`, \
                                  which is {}",
                                 ol,
                                 moved_lp_msg,
                                 expr_ty.user_string(self.tcx),
                                 suggestion));
                } else {
                    self.tcx.sess.span_note(
                        expr_span,
                        &format!("`{}` moved here{} because it has type `{}`, which is {}",
                                 ol,
                                 moved_lp_msg,
                                 expr_ty.user_string(self.tcx),
                                 suggestion));
                }
635 636
            }

E
Eduard Burtescu 已提交
637
            move_data::MovePat => {
638
                let pat_ty = ty::node_id_to_type(self.tcx, the_move.id);
P
P1start 已提交
639 640
                let span = self.tcx.map.span(the_move.id);
                self.tcx.sess.span_note(span,
J
Jorge Aparicio 已提交
641
                    &format!("`{}` moved here{} because it has type `{}`, \
P
P1start 已提交
642
                             which is moved by default",
643 644
                            ol,
                            moved_lp_msg,
645
                            pat_ty.user_string(self.tcx)));
646
                self.tcx.sess.fileline_help(span,
P
P1start 已提交
647
                    "use `ref` to override");
648 649
            }

E
Eduard Burtescu 已提交
650
            move_data::Captured => {
651 652 653
                let (expr_ty, expr_span) = match self.tcx
                                                     .map
                                                     .find(the_move.id) {
654
                    Some(ast_map::NodeExpr(expr)) => {
655
                        (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
E
Eduard Burtescu 已提交
656
                    }
657
                    r => {
J
Jorge Aparicio 已提交
658
                        self.tcx.sess.bug(&format!("Captured({}) maps to \
659
                                                   {:?}, not Expr",
660
                                                  the_move.id,
661
                                                  r))
662
                    }
E
Eduard Burtescu 已提交
663
                };
664 665 666 667 668 669
                let (suggestion, help) =
                    move_suggestion(param_env,
                                    expr_span,
                                    expr_ty,
                                    ("moved by default",
                                     "make a copy and capture that instead to override"));
670
                self.tcx.sess.span_note(
E
Eduard Burtescu 已提交
671
                    expr_span,
J
Jorge Aparicio 已提交
672
                    &format!("`{}` moved into closure environment here{} because it \
673
                            has type `{}`, which is {}",
674 675
                            ol,
                            moved_lp_msg,
676
                            expr_ty.user_string(self.tcx),
677
                            suggestion));
678
                self.tcx.sess.fileline_help(expr_span, help);
679 680 681
            }
        }

682 683 684 685 686
        fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
                                    span: Span,
                                    ty: Ty<'tcx>,
                                    default_msgs: (&'static str, &'static str))
                                    -> (&'static str, &'static str) {
687
            match ty.sty {
688 689 690 691 692 693 694 695
                _ => {
                    if ty::type_moves_by_default(param_env, span, ty) {
                        ("non-copyable",
                         "perhaps you meant to use `clone()`?")
                    } else {
                        default_msgs
                    }
                }
696 697 698 699
            }
        }
    }

700 701 702 703 704 705 706 707 708 709 710 711
    pub fn report_partial_reinitialization_of_uninitialized_structure(
            &self,
            span: Span,
            lp: &LoanPath<'tcx>) {
        self.tcx
            .sess
            .span_err(span,
                      (format!("partial reinitialization of uninitialized \
                               structure `{}`",
                               self.loan_path_to_string(lp))).as_slice());
    }

712
    pub fn report_reassigned_immutable_variable(&self,
713
                                                span: Span,
F
Felix S. Klock II 已提交
714
                                                lp: &LoanPath<'tcx>,
715 716
                                                assign:
                                                &move_data::Assignment) {
717 718
        self.tcx.sess.span_err(
            span,
J
Jorge Aparicio 已提交
719
            &format!("re-assignment of immutable variable `{}`",
720
                    self.loan_path_to_string(lp)));
721
        self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
722 723
    }

724
    pub fn span_err(&self, s: Span, m: &str) {
B
Brian Anderson 已提交
725 726 727
        self.tcx.sess.span_err(s, m);
    }

728 729 730 731
    pub fn span_bug(&self, s: Span, m: &str) {
        self.tcx.sess.span_bug(s, m);
    }

732
    pub fn span_note(&self, s: Span, m: &str) {
B
Brian Anderson 已提交
733 734 735
        self.tcx.sess.span_note(s, m);
    }

736 737 738 739
    pub fn span_end_note(&self, s: Span, m: &str) {
        self.tcx.sess.span_end_note(s, m);
    }

P
P1start 已提交
740 741 742 743
    pub fn span_help(&self, s: Span, m: &str) {
        self.tcx.sess.span_help(s, m);
    }

744 745 746 747
    pub fn fileline_help(&self, s: Span, m: &str) {
        self.tcx.sess.fileline_help(s, m);
    }

748
    pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
B
Brian Anderson 已提交
749
        match err.code {
750
            err_mutbl => {
751
                let descr = match err.cmt.note {
752
                    mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
753
                        self.cmt_to_string(&*err.cmt)
754
                    }
755 756 757 758 759 760 761 762 763 764 765 766
                    _ => match opt_loan_path(&err.cmt) {
                        None => {
                            format!("{} {}",
                                    err.cmt.mutbl.to_user_str(),
                                    self.cmt_to_string(&*err.cmt))
                        }
                        Some(lp) => {
                            format!("{} {} `{}`",
                                    err.cmt.mutbl.to_user_str(),
                                    self.cmt_to_string(&*err.cmt),
                                    self.loan_path_to_string(&*lp))
                        }
767
                    }
768 769 770
                };

                match err.cause {
771
                    euv::ClosureCapture(_) => {
A
Alex Crichton 已提交
772
                        format!("closure cannot assign to {}", descr)
773
                    }
774 775 776
                    euv::OverloadedOperator |
                    euv::AddrOf |
                    euv::RefBinding |
777
                    euv::AutoRef |
778 779
                    euv::ForLoop |
                    euv::MatchDiscriminant => {
A
Alex Crichton 已提交
780
                        format!("cannot borrow {} as mutable", descr)
781
                    }
782
                    euv::ClosureInvocation => {
783 784 785
                        self.tcx.sess.span_bug(err.span,
                            "err_mutbl with a closure invocation");
                    }
786
                }
B
Brian Anderson 已提交
787
            }
A
Alex Crichton 已提交
788
            err_out_of_scope(..) => {
789
                let msg = match opt_loan_path(&err.cmt) {
790
                    None => "borrowed value".to_string(),
791
                    Some(lp) => {
792
                        format!("`{}`", self.loan_path_to_string(&*lp))
793
                    }
794
                };
A
Alex Crichton 已提交
795
                format!("{} does not live long enough", msg)
B
Brian Anderson 已提交
796
            }
N
Niko Matsakis 已提交
797
            err_borrowed_pointer_too_short(..) => {
798
                let descr = match opt_loan_path(&err.cmt) {
799
                    Some(lp) => {
800
                        format!("`{}`", self.loan_path_to_string(&*lp))
801
                    }
802
                    None => self.cmt_to_string(&*err.cmt),
803 804
                };

A
Alex Crichton 已提交
805
                format!("lifetime of {} is too short to guarantee \
806 807
                                its contents can be safely reborrowed",
                               descr)
808
            }
N
Niko Matsakis 已提交
809 810 811
        }
    }

812
    pub fn report_aliasability_violation(&self,
813
                                         span: Span,
814 815
                                         kind: AliasableViolationKind,
                                         cause: mc::AliasableReason) {
816
        let mut is_closure = false;
N
Niko Matsakis 已提交
817
        let prefix = match kind {
818 819 820
            MutabilityViolation => {
                "cannot assign to data"
            }
821
            BorrowViolation(euv::ClosureCapture(_)) |
822 823 824
            BorrowViolation(euv::OverloadedOperator) |
            BorrowViolation(euv::AddrOf) |
            BorrowViolation(euv::AutoRef) |
825 826
            BorrowViolation(euv::RefBinding) |
            BorrowViolation(euv::MatchDiscriminant) => {
827 828
                "cannot borrow data mutably"
            }
829

830
            BorrowViolation(euv::ClosureInvocation) => {
831
                is_closure = true;
832 833
                "closure invocation"
            }
834 835 836 837

            BorrowViolation(euv::ForLoop) => {
                "`for` loop"
            }
N
Niko Matsakis 已提交
838 839 840 841 842 843
        };

        match cause {
            mc::AliasableOther => {
                self.tcx.sess.span_err(
                    span,
J
Jorge Aparicio 已提交
844
                    &format!("{} in an aliasable location",
845
                             prefix));
B
Brian Anderson 已提交
846
            }
847 848
            mc::AliasableClosure(id) => {
                self.tcx.sess.span_err(span,
849 850
                                       &format!("{} in a captured outer \
                                                variable in an `Fn` closure", prefix));
851 852 853 854 855 856 857 858 859
                if let BorrowViolation(euv::ClosureCapture(_)) = kind {
                    // The aliasability violation with closure captures can
                    // happen for nested closures, so we know the enclosing
                    // closure incorrectly accepts an `Fn` while it needs to
                    // be `FnMut`.
                    span_help!(self.tcx.sess, self.tcx.map.span(id),
                           "consider changing this to accept closures that implement `FnMut`");
                } else {
                    span_help!(self.tcx.sess, self.tcx.map.span(id),
860
                           "consider changing this closure to take self by mutable reference");
861
                }
862
            }
863 864
            mc::AliasableStatic(..) |
            mc::AliasableStaticMut(..) => {
865 866
                self.tcx.sess.span_err(
                    span,
867
                    &format!("{} in a static location", prefix));
868
            }
869
            mc::AliasableBorrowed => {
N
Niko Matsakis 已提交
870 871
                self.tcx.sess.span_err(
                    span,
872
                    &format!("{} in a `&` reference", prefix));
B
Brian Anderson 已提交
873 874
            }
        }
875 876

        if is_closure {
877
            self.tcx.sess.fileline_help(
878 879 880
                span,
                "closures behind references must be called via `&mut`");
        }
B
Brian Anderson 已提交
881 882
    }

883
    pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
B
Brian Anderson 已提交
884 885
        let code = err.code;
        match code {
886 887
            err_mutbl(..) => {
                match err.cmt.note {
888 889 890 891 892 893 894 895
                    mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
                        // If this is an `Fn` closure, it simply can't mutate upvars.
                        // If it's an `FnMut` closure, the original variable was declared immutable.
                        // We need to determine which is the case here.
                        let kind = match err.cmt.upvar().unwrap().cat {
                            mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
                            _ => unreachable!()
                        };
896
                        if kind == ty::FnClosureKind {
P
P1start 已提交
897
                            self.tcx.sess.span_help(
898 899 900 901
                                self.tcx.map.span(upvar_id.closure_expr_id),
                                "consider changing this closure to take \
                                 self by mutable reference");
                        }
902 903 904 905
                    }
                    _ => {}
                }
            }
B
Brian Anderson 已提交
906 907 908 909

            err_out_of_scope(super_scope, sub_scope) => {
                note_and_explain_region(
                    self.tcx,
910
                    "reference must be valid for ",
B
Brian Anderson 已提交
911
                    sub_scope,
912
                    "...");
913
                let suggestion = if is_statement_scope(self.tcx, super_scope) {
P
P1start 已提交
914
                    Some("consider using a `let` binding to increase its lifetime")
915
                } else {
P
P1start 已提交
916
                    None
917
                };
P
P1start 已提交
918
                let span = note_and_explain_region(
B
Brian Anderson 已提交
919
                    self.tcx,
920
                    "...but borrowed value is only valid for ",
B
Brian Anderson 已提交
921
                    super_scope,
P
P1start 已提交
922 923 924 925 926 927
                    "");
                match (span, suggestion) {
                    (_, None) => {},
                    (Some(span), Some(msg)) => self.tcx.sess.span_help(span, msg),
                    (None, Some(msg)) => self.tcx.sess.help(msg),
                }
928 929
            }

930
            err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
931
                let descr = match opt_loan_path(&err.cmt) {
932
                    Some(lp) => {
933
                        format!("`{}`", self.loan_path_to_string(&*lp))
934
                    }
935
                    None => self.cmt_to_string(&*err.cmt),
936 937 938
                };
                note_and_explain_region(
                    self.tcx,
J
Jorge Aparicio 已提交
939
                    &format!("{} would have to be valid for ",
940
                            descr),
941 942 943 944
                    loan_scope,
                    "...");
                note_and_explain_region(
                    self.tcx,
945
                    &format!("...but {} is only valid for ", descr),
946 947 948
                    ptr_scope,
                    "");
            }
B
Brian Anderson 已提交
949 950 951
        }
    }

952
    pub fn append_loan_path_to_string(&self,
F
Felix S. Klock II 已提交
953 954 955
                                      loan_path: &LoanPath<'tcx>,
                                      out: &mut String) {
        match loan_path.kind {
956
            LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
N
Niko Matsakis 已提交
957
            LpVar(id) => {
958
                out.push_str(&ty::local_var_name_str(self.tcx, id));
N
Niko Matsakis 已提交
959
            }
B
Brian Anderson 已提交
960

961 962 963
            LpDowncast(ref lp_base, variant_def_id) => {
                out.push('(');
                self.append_loan_path_to_string(&**lp_base, out);
964
                out.push_str(DOWNCAST_PRINTED_OPERATOR);
965
                out.push_str(&ty::item_path_str(self.tcx, variant_def_id));
966 967 968 969
                out.push(')');
            }


970
            LpExtend(ref lp_base, _, LpInterior(InteriorField(fname))) => {
971
                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
972
                match fname {
973
                    mc::NamedField(fname) => {
974
                        out.push('.');
975
                        out.push_str(&token::get_name(fname));
976 977
                    }
                    mc::PositionalField(idx) => {
978
                        out.push('.');
979
                        out.push_str(&idx.to_string());
980 981
                    }
                }
N
Niko Matsakis 已提交
982 983
            }

984
            LpExtend(ref lp_base, _, LpInterior(InteriorElement(..))) => {
985
                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
986
                out.push_str("[..]");
N
Niko Matsakis 已提交
987 988
            }

E
Eduard Burtescu 已提交
989
            LpExtend(ref lp_base, _, LpDeref(_)) => {
990
                out.push('*');
991
                self.append_loan_path_to_string(&**lp_base, out);
N
Niko Matsakis 已提交
992 993 994 995
            }
        }
    }

996
    pub fn append_autoderefd_loan_path_to_string(&self,
F
Felix S. Klock II 已提交
997 998 999
                                                 loan_path: &LoanPath<'tcx>,
                                                 out: &mut String) {
        match loan_path.kind {
E
Eduard Burtescu 已提交
1000
            LpExtend(ref lp_base, _, LpDeref(_)) => {
1001 1002 1003
                // For a path like `(*x).f` or `(*x)[3]`, autoderef
                // rules would normally allow users to omit the `*x`.
                // So just serialize such paths to `x.f` or x[3]` respectively.
1004
                self.append_autoderefd_loan_path_to_string(&**lp_base, out)
1005 1006
            }

1007 1008 1009 1010
            LpDowncast(ref lp_base, variant_def_id) => {
                out.push('(');
                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
                out.push(':');
1011
                out.push_str(&ty::item_path_str(self.tcx, variant_def_id));
1012 1013 1014
                out.push(')');
            }

1015
            LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
1016
                self.append_loan_path_to_string(loan_path, out)
1017 1018 1019 1020
            }
        }
    }

F
Felix S. Klock II 已提交
1021
    pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
1022
        let mut result = String::new();
1023
        self.append_loan_path_to_string(loan_path, &mut result);
1024
        result
B
Brian Anderson 已提交
1025 1026
    }

1027
    pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
1028
        cmt.descriptive_string(self.tcx)
B
Brian Anderson 已提交
1029
    }
N
Niko Matsakis 已提交
1030 1031
}

1032 1033
fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool {
     match region {
1034 1035
         ty::ReScope(scope) => {
             match tcx.map.find(scope.node_id()) {
1036 1037 1038 1039 1040 1041 1042 1043
                 Some(ast_map::NodeStmt(_)) => true,
                 _ => false
             }
         }
         _ => false
     }
}

1044
impl BitwiseOperator for LoanDataFlowOperator {
1045
    #[inline]
1046 1047
    fn join(&self, succ: uint, pred: uint) -> uint {
        succ | pred // loans from both preds are in scope
N
Niko Matsakis 已提交
1048
    }
1049
}
N
Niko Matsakis 已提交
1050

1051
impl DataFlowOperator for LoanDataFlowOperator {
1052
    #[inline]
1053 1054
    fn initial_value(&self) -> bool {
        false // no loans in scope by default
N
Niko Matsakis 已提交
1055 1056
    }
}
1057

1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
impl<'tcx> Repr<'tcx> for InteriorKind {
    fn repr(&self, _tcx: &ty::ctxt<'tcx>) -> String {
        match *self {
            InteriorField(mc::NamedField(fld)) =>
                format!("{}", token::get_name(fld)),
            InteriorField(mc::PositionalField(i)) => format!("#{}", i),
            InteriorElement(..) => "[]".to_string(),
        }
    }
}

F
Felix S. Klock II 已提交
1069 1070
impl<'tcx> Repr<'tcx> for Loan<'tcx> {
    fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
1071
        format!("Loan_{}({}, {:?}, {:?}-{:?}, {})",
1072 1073 1074 1075 1076
                 self.index,
                 self.loan_path.repr(tcx),
                 self.kind,
                 self.gen_scope,
                 self.kill_scope,
1077
                 self.restricted_paths.repr(tcx))
N
Niko Matsakis 已提交
1078 1079 1080
    }
}

F
Felix S. Klock II 已提交
1081 1082 1083 1084
impl<'tcx> Repr<'tcx> for LoanPath<'tcx> {
    fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
        match self.kind {
            LpVar(id) => {
1085
                format!("$({})", tcx.map.node_to_string(id))
N
Niko Matsakis 已提交
1086 1087
            }

F
Felix S. Klock II 已提交
1088
            LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
1089
                let s = tcx.map.node_to_string(var_id);
1090
                format!("$({} captured by id={})", s, closure_expr_id)
1091 1092
            }

F
Felix S. Klock II 已提交
1093
            LpDowncast(ref lp, variant_def_id) => {
1094 1095 1096 1097 1098
                let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
                    ty::item_path_str(tcx, variant_def_id)
                } else {
                    variant_def_id.repr(tcx)
                };
1099
                format!("({}{}{})", lp.repr(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
1100 1101
            }

F
Felix S. Klock II 已提交
1102
            LpExtend(ref lp, _, LpDeref(_)) => {
1103
                format!("{}.*", lp.repr(tcx))
N
Niko Matsakis 已提交
1104 1105
            }

F
Felix S. Klock II 已提交
1106
            LpExtend(ref lp, _, LpInterior(ref interior)) => {
1107
                format!("{}.{}", lp.repr(tcx), interior.repr(tcx))
N
Niko Matsakis 已提交
1108 1109
            }
        }
B
Brian Anderson 已提交
1110 1111
    }
}
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143

impl<'tcx> UserString<'tcx> for LoanPath<'tcx> {
    fn user_string(&self, tcx: &ty::ctxt<'tcx>) -> String {
        match self.kind {
            LpVar(id) => {
                format!("$({})", tcx.map.node_to_user_string(id))
            }

            LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => {
                let s = tcx.map.node_to_user_string(var_id);
                format!("$({} captured by closure)", s)
            }

            LpDowncast(ref lp, variant_def_id) => {
                let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
                    ty::item_path_str(tcx, variant_def_id)
                } else {
                    variant_def_id.repr(tcx)
                };
                format!("({}{}{})", lp.user_string(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
            }

            LpExtend(ref lp, _, LpDeref(_)) => {
                format!("{}.*", lp.user_string(tcx))
            }

            LpExtend(ref lp, _, LpInterior(ref interior)) => {
                format!("{}.{}", lp.user_string(tcx), interior.repr(tcx))
            }
        }
    }
}