mod.rs 39.7 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 doc.rs for a thorough explanation of the borrow checker
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

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

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

48
pub mod gather_loans;
N
Niko Matsakis 已提交
49

50 51
pub mod move_data;

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

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

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

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

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

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

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

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

F
Flavio Percoco 已提交
100 101 102 103 104 105
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 {
106 107
        ast::ItemStatic(_, _, ref ex) |
        ast::ItemConst(_, ref ex) => {
108
            gather_loans::gather_loans_in_static_initializer(this, &**ex);
F
Flavio Percoco 已提交
109
        }
110
        _ => {
111
            visit::walk_item(this, item);
112
        }
F
Flavio Percoco 已提交
113 114 115
    }
}

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

123
fn borrowck_fn(this: &mut BorrowckCtxt,
124
               fk: FnKind,
125
               decl: &ast::FnDecl,
E
Eduard Burtescu 已提交
126
               body: &ast::Block,
127
               sp: Span,
128
               id: ast::NodeId) {
129
    debug!("borrowck_fn(id={})", id);
130 131 132 133 134 135
    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);

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

N
Niko Matsakis 已提交
139 140 141
    check_loans::check_loans(this,
                             &loan_dfcx,
                             flowed_moves,
J
Jorge Aparicio 已提交
142
                             &all_loans[],
N
Niko Matsakis 已提交
143 144 145
                             id,
                             decl,
                             body);
146

147
    visit::walk_fn(this, fk, decl, body, sp);
148
}
149

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

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

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

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

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
/// 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.
205 206 207
pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
    tcx: &'a ty::ctxt<'tcx>,
    input: FnPartsWithCFG<'a>) -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>) {
208 209 210

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

    let p = input.fn_parts;

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

    (bccx, dataflow_data)
N
Niko Matsakis 已提交
230 231
}

B
Brian Anderson 已提交
232 233 234
// ----------------------------------------------------------------------
// Type definitions

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

    // Statistics:
239
    stats: BorrowStats
B
Brian Anderson 已提交
240 241
}

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

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

N
Niko Matsakis 已提交
251 252 253 254
///////////////////////////////////////////////////////////////////////////
// Loans and loan paths

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

    /// 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,
273
    span: Span,
274
    cause: euv::LoanCause,
275 276
}

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

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

289 290 291 292 293 294 295 296 297
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 已提交
298
#[derive(PartialEq, Eq, Hash, Debug)]
F
Felix S. Klock II 已提交
299 300 301 302 303 304 305 306 307 308
pub enum LoanPathKind<'tcx> {
    LpVar(ast::NodeId),                         // `x` in doc.rs
    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 }
309
    }
F
Felix S. Klock II 已提交
310

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

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

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
// 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 已提交
339
#[derive(Copy, PartialEq, Eq, Hash, Debug)]
N
Niko Matsakis 已提交
340
pub enum LoanPathElem {
N
Niko Matsakis 已提交
341
    LpDeref(mc::PointerKind),    // `*LV` in doc.rs
342
    LpInterior(InteriorKind),    // `LV.f` in doc.rs
343 344
}

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

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

F
Felix S. Klock II 已提交
373 374
    fn has_fork(&self, other: &LoanPath<'tcx>) -> bool {
        match (&self.kind, &other.kind) {
P
P1start 已提交
375 376 377 378 379 380 381 382 383 384 385 386 387
            (&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 已提交
388
        match self.kind {
P
P1start 已提交
389 390 391 392 393 394
            LpExtend(ref base, _, LpDeref(_)) => base.depth(),
            LpExtend(ref base, _, LpInterior(_)) => base.depth() + 1,
            _ => 0,
        }
    }

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

F
Felix S. Klock II 已提交
440
pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
N
Niko Matsakis 已提交
441 442 443 444 445 446
    //! 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.

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

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

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

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

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

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

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

N
Niko Matsakis 已提交
481
    }
482
}
B
Brian Anderson 已提交
483

N
Niko Matsakis 已提交
484 485 486 487
///////////////////////////////////////////////////////////////////////////
// Errors

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

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

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

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

N
Niko Matsakis 已提交
517
///////////////////////////////////////////////////////////////////////////
B
Brian Anderson 已提交
518 519
// Misc

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

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

533 534 535 536 537 538 539
    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>) {
540 541 542 543 544
        let verb = match use_kind {
            MovedInUse => "use",
            MovedInCapture => "capture",
        };

545
        let (ol, moved_lp_msg) = match the_move.kind {
546 547 548
            move_data::Declared => {
                self.tcx.sess.span_err(
                    use_span,
J
Jorge Aparicio 已提交
549
                    &format!("{} of possibly uninitialized variable: `{}`",
550
                            verb,
J
Jorge Aparicio 已提交
551
                            self.loan_path_to_string(lp))[]);
552 553
                (self.loan_path_to_string(moved_lp),
                 String::new())
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 586 587
                // 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 { "" };
588 589
                self.tcx.sess.span_err(
                    use_span,
J
Jorge Aparicio 已提交
590
                    &format!("{} of {}moved value: `{}`",
591
                            verb,
592
                            msg,
J
Jorge Aparicio 已提交
593
                            nl)[]);
594
                (ol, moved_lp_msg)
595
            }
596
        };
597

598
        match the_move.kind {
599 600
            move_data::Declared => {}

E
Eduard Burtescu 已提交
601
            move_data::MoveExpr => {
602 603 604
                let (expr_ty, expr_span) = match self.tcx
                                                     .map
                                                     .find(the_move.id) {
605
                    Some(ast_map::NodeExpr(expr)) => {
606
                        (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
E
Eduard Burtescu 已提交
607
                    }
608
                    r => {
J
Jorge Aparicio 已提交
609
                        self.tcx.sess.bug(&format!("MoveExpr({}) maps to \
610
                                                   {:?}, not Expr",
611
                                                  the_move.id,
J
Jorge Aparicio 已提交
612
                                                  r)[])
613
                    }
E
Eduard Burtescu 已提交
614
                };
615 616
                let (suggestion, _) =
                    move_suggestion(param_env, expr_span, expr_ty, ("moved by default", ""));
617
                self.tcx.sess.span_note(
E
Eduard Burtescu 已提交
618
                    expr_span,
J
Jorge Aparicio 已提交
619
                    &format!("`{}` moved here{} because it has type `{}`, which is {}",
620 621
                            ol,
                            moved_lp_msg,
622
                            expr_ty.user_string(self.tcx),
J
Jorge Aparicio 已提交
623
                            suggestion)[]);
624 625
            }

E
Eduard Burtescu 已提交
626
            move_data::MovePat => {
627
                let pat_ty = ty::node_id_to_type(self.tcx, the_move.id);
P
P1start 已提交
628 629
                let span = self.tcx.map.span(the_move.id);
                self.tcx.sess.span_note(span,
J
Jorge Aparicio 已提交
630
                    &format!("`{}` moved here{} because it has type `{}`, \
P
P1start 已提交
631
                             which is moved by default",
632 633
                            ol,
                            moved_lp_msg,
J
Jorge Aparicio 已提交
634
                            pat_ty.user_string(self.tcx))[]);
P
P1start 已提交
635 636
                self.tcx.sess.span_help(span,
                    "use `ref` to override");
637 638
            }

E
Eduard Burtescu 已提交
639
            move_data::Captured => {
640 641 642
                let (expr_ty, expr_span) = match self.tcx
                                                     .map
                                                     .find(the_move.id) {
643
                    Some(ast_map::NodeExpr(expr)) => {
644
                        (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
E
Eduard Burtescu 已提交
645
                    }
646
                    r => {
J
Jorge Aparicio 已提交
647
                        self.tcx.sess.bug(&format!("Captured({}) maps to \
648
                                                   {:?}, not Expr",
649
                                                  the_move.id,
J
Jorge Aparicio 已提交
650
                                                  r)[])
651
                    }
E
Eduard Burtescu 已提交
652
                };
653 654 655 656 657 658
                let (suggestion, help) =
                    move_suggestion(param_env,
                                    expr_span,
                                    expr_ty,
                                    ("moved by default",
                                     "make a copy and capture that instead to override"));
659
                self.tcx.sess.span_note(
E
Eduard Burtescu 已提交
660
                    expr_span,
J
Jorge Aparicio 已提交
661
                    &format!("`{}` moved into closure environment here{} because it \
662
                            has type `{}`, which is {}",
663 664
                            ol,
                            moved_lp_msg,
665
                            expr_ty.user_string(self.tcx),
J
Jorge Aparicio 已提交
666
                            suggestion)[]);
P
P1start 已提交
667
                self.tcx.sess.span_help(expr_span, help);
668 669 670
            }
        }

671 672 673 674 675
        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) {
676
            match ty.sty {
677 678 679 680 681 682 683 684
                _ => {
                    if ty::type_moves_by_default(param_env, span, ty) {
                        ("non-copyable",
                         "perhaps you meant to use `clone()`?")
                    } else {
                        default_msgs
                    }
                }
685 686 687 688
            }
        }
    }

689
    pub fn report_reassigned_immutable_variable(&self,
690
                                                span: Span,
F
Felix S. Klock II 已提交
691
                                                lp: &LoanPath<'tcx>,
692 693
                                                assign:
                                                &move_data::Assignment) {
694 695
        self.tcx.sess.span_err(
            span,
J
Jorge Aparicio 已提交
696 697
            &format!("re-assignment of immutable variable `{}`",
                    self.loan_path_to_string(lp))[]);
698
        self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
699 700
    }

701
    pub fn span_err(&self, s: Span, m: &str) {
B
Brian Anderson 已提交
702 703 704
        self.tcx.sess.span_err(s, m);
    }

705 706 707 708
    pub fn span_bug(&self, s: Span, m: &str) {
        self.tcx.sess.span_bug(s, m);
    }

709
    pub fn span_note(&self, s: Span, m: &str) {
B
Brian Anderson 已提交
710 711 712
        self.tcx.sess.span_note(s, m);
    }

713 714 715 716
    pub fn span_end_note(&self, s: Span, m: &str) {
        self.tcx.sess.span_end_note(s, m);
    }

P
P1start 已提交
717 718 719 720
    pub fn span_help(&self, s: Span, m: &str) {
        self.tcx.sess.span_help(s, m);
    }

721
    pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
B
Brian Anderson 已提交
722
        match err.code {
723
            err_mutbl => {
724
                let descr = match err.cmt.note {
725
                    mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
726
                        self.cmt_to_string(&*err.cmt)
727
                    }
728 729 730 731 732 733 734 735 736 737 738 739
                    _ => 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))
                        }
740
                    }
741 742 743
                };

                match err.cause {
744
                    euv::ClosureCapture(_) => {
A
Alex Crichton 已提交
745
                        format!("closure cannot assign to {}", descr)
746
                    }
747 748 749
                    euv::OverloadedOperator |
                    euv::AddrOf |
                    euv::RefBinding |
750
                    euv::AutoRef |
751 752
                    euv::ForLoop |
                    euv::MatchDiscriminant => {
A
Alex Crichton 已提交
753
                        format!("cannot borrow {} as mutable", descr)
754
                    }
755
                    euv::ClosureInvocation => {
756 757 758
                        self.tcx.sess.span_bug(err.span,
                            "err_mutbl with a closure invocation");
                    }
759
                }
B
Brian Anderson 已提交
760
            }
A
Alex Crichton 已提交
761
            err_out_of_scope(..) => {
762
                let msg = match opt_loan_path(&err.cmt) {
763
                    None => "borrowed value".to_string(),
764
                    Some(lp) => {
765
                        format!("`{}`", self.loan_path_to_string(&*lp))
766
                    }
767
                };
A
Alex Crichton 已提交
768
                format!("{} does not live long enough", msg)
B
Brian Anderson 已提交
769
            }
N
Niko Matsakis 已提交
770
            err_borrowed_pointer_too_short(..) => {
771
                let descr = match opt_loan_path(&err.cmt) {
772
                    Some(lp) => {
773
                        format!("`{}`", self.loan_path_to_string(&*lp))
774
                    }
775
                    None => self.cmt_to_string(&*err.cmt),
776 777
                };

A
Alex Crichton 已提交
778
                format!("lifetime of {} is too short to guarantee \
779 780
                                its contents can be safely reborrowed",
                               descr)
781
            }
N
Niko Matsakis 已提交
782 783 784
        }
    }

785
    pub fn report_aliasability_violation(&self,
786
                                         span: Span,
787 788
                                         kind: AliasableViolationKind,
                                         cause: mc::AliasableReason) {
789
        let mut is_closure = false;
N
Niko Matsakis 已提交
790
        let prefix = match kind {
791 792 793
            MutabilityViolation => {
                "cannot assign to data"
            }
794
            BorrowViolation(euv::ClosureCapture(_)) |
795 796 797
            BorrowViolation(euv::OverloadedOperator) |
            BorrowViolation(euv::AddrOf) |
            BorrowViolation(euv::AutoRef) |
798 799
            BorrowViolation(euv::RefBinding) |
            BorrowViolation(euv::MatchDiscriminant) => {
800 801
                "cannot borrow data mutably"
            }
802

803
            BorrowViolation(euv::ClosureInvocation) => {
804
                is_closure = true;
805 806
                "closure invocation"
            }
807 808 809 810

            BorrowViolation(euv::ForLoop) => {
                "`for` loop"
            }
N
Niko Matsakis 已提交
811 812 813 814 815 816
        };

        match cause {
            mc::AliasableOther => {
                self.tcx.sess.span_err(
                    span,
J
Jorge Aparicio 已提交
817 818
                    &format!("{} in an aliasable location",
                             prefix)[]);
B
Brian Anderson 已提交
819
            }
820 821
            mc::AliasableClosure(id) => {
                self.tcx.sess.span_err(span,
822 823
                                       &format!("{} in a captured outer \
                                                variable in an `Fn` closure", prefix));
824 825 826 827 828 829 830 831 832
                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),
833
                           "consider changing this closure to take self by mutable reference");
834
                }
835
            }
836 837
            mc::AliasableStatic(..) |
            mc::AliasableStaticMut(..) => {
838 839
                self.tcx.sess.span_err(
                    span,
J
Jorge Aparicio 已提交
840
                    &format!("{} in a static location", prefix)[]);
841
            }
842
            mc::AliasableBorrowed => {
N
Niko Matsakis 已提交
843 844
                self.tcx.sess.span_err(
                    span,
J
Jorge Aparicio 已提交
845
                    &format!("{} in a `&` reference", prefix)[]);
B
Brian Anderson 已提交
846 847
            }
        }
848 849

        if is_closure {
P
P1start 已提交
850
            self.tcx.sess.span_help(
851 852 853
                span,
                "closures behind references must be called via `&mut`");
        }
B
Brian Anderson 已提交
854 855
    }

856
    pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
B
Brian Anderson 已提交
857 858
        let code = err.code;
        match code {
859 860
            err_mutbl(..) => {
                match err.cmt.note {
861 862 863 864 865 866 867 868
                    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!()
                        };
869
                        if kind == ty::FnClosureKind {
P
P1start 已提交
870
                            self.tcx.sess.span_help(
871 872 873 874
                                self.tcx.map.span(upvar_id.closure_expr_id),
                                "consider changing this closure to take \
                                 self by mutable reference");
                        }
875 876 877 878
                    }
                    _ => {}
                }
            }
B
Brian Anderson 已提交
879 880 881 882

            err_out_of_scope(super_scope, sub_scope) => {
                note_and_explain_region(
                    self.tcx,
883
                    "reference must be valid for ",
B
Brian Anderson 已提交
884
                    sub_scope,
885
                    "...");
886
                let suggestion = if is_statement_scope(self.tcx, super_scope) {
P
P1start 已提交
887
                    Some("consider using a `let` binding to increase its lifetime")
888
                } else {
P
P1start 已提交
889
                    None
890
                };
P
P1start 已提交
891
                let span = note_and_explain_region(
B
Brian Anderson 已提交
892
                    self.tcx,
893
                    "...but borrowed value is only valid for ",
B
Brian Anderson 已提交
894
                    super_scope,
P
P1start 已提交
895 896 897 898 899 900
                    "");
                match (span, suggestion) {
                    (_, None) => {},
                    (Some(span), Some(msg)) => self.tcx.sess.span_help(span, msg),
                    (None, Some(msg)) => self.tcx.sess.help(msg),
                }
901 902
            }

903
            err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
904
                let descr = match opt_loan_path(&err.cmt) {
905
                    Some(lp) => {
906
                        format!("`{}`", self.loan_path_to_string(&*lp))
907
                    }
908
                    None => self.cmt_to_string(&*err.cmt),
909 910 911
                };
                note_and_explain_region(
                    self.tcx,
J
Jorge Aparicio 已提交
912 913
                    &format!("{} would have to be valid for ",
                            descr)[],
914 915 916 917
                    loan_scope,
                    "...");
                note_and_explain_region(
                    self.tcx,
J
Jorge Aparicio 已提交
918
                    &format!("...but {} is only valid for ", descr)[],
919 920 921
                    ptr_scope,
                    "");
            }
B
Brian Anderson 已提交
922 923 924
        }
    }

925
    pub fn append_loan_path_to_string(&self,
F
Felix S. Klock II 已提交
926 927 928
                                      loan_path: &LoanPath<'tcx>,
                                      out: &mut String) {
        match loan_path.kind {
929
            LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
N
Niko Matsakis 已提交
930
            LpVar(id) => {
931
                out.push_str(&ty::local_var_name_str(self.tcx, id));
N
Niko Matsakis 已提交
932
            }
B
Brian Anderson 已提交
933

934 935 936
            LpDowncast(ref lp_base, variant_def_id) => {
                out.push('(');
                self.append_loan_path_to_string(&**lp_base, out);
937
                out.push_str(DOWNCAST_PRINTED_OPERATOR);
J
Jorge Aparicio 已提交
938
                out.push_str(&ty::item_path_str(self.tcx, variant_def_id)[]);
939 940 941 942
                out.push(')');
            }


943
            LpExtend(ref lp_base, _, LpInterior(InteriorField(fname))) => {
944
                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
945
                match fname {
946
                    mc::NamedField(fname) => {
947
                        out.push('.');
948
                        out.push_str(&token::get_name(fname));
949 950
                    }
                    mc::PositionalField(idx) => {
951
                        out.push('.');
J
Jorge Aparicio 已提交
952
                        out.push_str(&idx.to_string()[]);
953 954
                    }
                }
N
Niko Matsakis 已提交
955 956
            }

957
            LpExtend(ref lp_base, _, LpInterior(InteriorElement(..))) => {
958
                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
959
                out.push_str("[..]");
N
Niko Matsakis 已提交
960 961
            }

E
Eduard Burtescu 已提交
962
            LpExtend(ref lp_base, _, LpDeref(_)) => {
963
                out.push('*');
964
                self.append_loan_path_to_string(&**lp_base, out);
N
Niko Matsakis 已提交
965 966 967 968
            }
        }
    }

969
    pub fn append_autoderefd_loan_path_to_string(&self,
F
Felix S. Klock II 已提交
970 971 972
                                                 loan_path: &LoanPath<'tcx>,
                                                 out: &mut String) {
        match loan_path.kind {
E
Eduard Burtescu 已提交
973
            LpExtend(ref lp_base, _, LpDeref(_)) => {
974 975 976
                // 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.
977
                self.append_autoderefd_loan_path_to_string(&**lp_base, out)
978 979
            }

980 981 982 983
            LpDowncast(ref lp_base, variant_def_id) => {
                out.push('(');
                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
                out.push(':');
J
Jorge Aparicio 已提交
984
                out.push_str(&ty::item_path_str(self.tcx, variant_def_id)[]);
985 986 987
                out.push(')');
            }

988
            LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
989
                self.append_loan_path_to_string(loan_path, out)
990 991 992 993
            }
        }
    }

F
Felix S. Klock II 已提交
994
    pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
995
        let mut result = String::new();
996
        self.append_loan_path_to_string(loan_path, &mut result);
997
        result
B
Brian Anderson 已提交
998 999
    }

1000
    pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
1001
        cmt.descriptive_string(self.tcx)
B
Brian Anderson 已提交
1002
    }
N
Niko Matsakis 已提交
1003 1004
}

1005 1006
fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool {
     match region {
1007 1008
         ty::ReScope(scope) => {
             match tcx.map.find(scope.node_id()) {
1009 1010 1011 1012 1013 1014 1015 1016
                 Some(ast_map::NodeStmt(_)) => true,
                 _ => false
             }
         }
         _ => false
     }
}

1017
impl BitwiseOperator for LoanDataFlowOperator {
1018
    #[inline]
1019 1020
    fn join(&self, succ: uint, pred: uint) -> uint {
        succ | pred // loans from both preds are in scope
N
Niko Matsakis 已提交
1021
    }
1022
}
N
Niko Matsakis 已提交
1023

1024
impl DataFlowOperator for LoanDataFlowOperator {
1025
    #[inline]
1026 1027
    fn initial_value(&self) -> bool {
        false // no loans in scope by default
N
Niko Matsakis 已提交
1028 1029
    }
}
1030

1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
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 已提交
1042 1043
impl<'tcx> Repr<'tcx> for Loan<'tcx> {
    fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
1044
        format!("Loan_{}({}, {:?}, {:?}-{:?}, {})",
1045 1046 1047 1048 1049
                 self.index,
                 self.loan_path.repr(tcx),
                 self.kind,
                 self.gen_scope,
                 self.kill_scope,
1050
                 self.restricted_paths.repr(tcx))
N
Niko Matsakis 已提交
1051 1052 1053
    }
}

F
Felix S. Klock II 已提交
1054 1055 1056 1057
impl<'tcx> Repr<'tcx> for LoanPath<'tcx> {
    fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
        match self.kind {
            LpVar(id) => {
1058
                format!("$({})", tcx.map.node_to_string(id))
N
Niko Matsakis 已提交
1059 1060
            }

F
Felix S. Klock II 已提交
1061
            LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
1062
                let s = tcx.map.node_to_string(var_id);
1063
                format!("$({} captured by id={})", s, closure_expr_id)
1064 1065
            }

F
Felix S. Klock II 已提交
1066
            LpDowncast(ref lp, variant_def_id) => {
1067 1068 1069 1070 1071
                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)
                };
1072
                format!("({}{}{})", lp.repr(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
1073 1074
            }

F
Felix S. Klock II 已提交
1075
            LpExtend(ref lp, _, LpDeref(_)) => {
1076
                format!("{}.*", lp.repr(tcx))
N
Niko Matsakis 已提交
1077 1078
            }

F
Felix S. Klock II 已提交
1079
            LpExtend(ref lp, _, LpInterior(ref interior)) => {
1080
                format!("{}.{}", lp.repr(tcx), interior.repr(tcx))
N
Niko Matsakis 已提交
1081 1082
            }
        }
B
Brian Anderson 已提交
1083 1084
    }
}
1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116

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))
            }
        }
    }
}