mod.rs 28.2 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.

N
Niko Matsakis 已提交
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

N
Niko Matsakis 已提交
15 16
use middle::dataflow::DataFlowContext;
use middle::dataflow::DataFlowOperator;
17
use middle::def;
18 19 20
use euv = middle::expr_use_visitor;
use mc = middle::mem_categorization;
use middle::ty;
21
use util::ppaux::{note_and_explain_region, Repr, UserString};
22

23
use std::cell::{Cell};
24
use std::ops::{BitOr, BitAnd};
E
Eduard Burtescu 已提交
25
use std::rc::Rc;
26
use std::string::String;
27
use syntax::ast;
N
Niko Matsakis 已提交
28
use syntax::ast_map;
29
use syntax::ast_util;
30
use syntax::codemap::Span;
31
use syntax::parse::token;
32
use syntax::visit;
33 34
use syntax::visit::{Visitor, FnKind};
use syntax::ast::{FnDecl, Block, NodeId};
B
Brian Anderson 已提交
35

N
Niko Matsakis 已提交
36 37 38 39 40 41 42 43 44 45 46
macro_rules! if_ok(
    ($inp: expr) => (
        match $inp {
            Ok(v) => { v }
            Err(e) => { return Err(e); }
        }
    )
)

pub mod doc;

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

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

51 52
pub mod move_data;

53
#[deriving(Clone)]
N
Niko Matsakis 已提交
54
pub struct LoanDataFlowOperator;
55

E
Eduard Burtescu 已提交
56
pub type LoanDataFlow<'a> = DataFlowContext<'a, LoanDataFlowOperator>;
B
Brian Anderson 已提交
57

E
Eduard Burtescu 已提交
58
impl<'a> Visitor<()> for BorrowckCtxt<'a> {
59
    fn visit_fn(&mut self, fk: &FnKind, fd: &FnDecl,
E
Eduard Burtescu 已提交
60
                b: &Block, s: Span, n: NodeId, _: ()) {
61
        borrowck_fn(self, fk, fd, b, s, n);
62
    }
F
Flavio Percoco 已提交
63 64 65 66

    fn visit_item(&mut self, item: &ast::Item, _: ()) {
        borrowck_item(self, item);
    }
67 68
}

E
Eduard Burtescu 已提交
69
pub fn check_crate(tcx: &ty::ctxt,
F
Flavio Percoco 已提交
70
                   krate: &ast::Crate) {
71
    let mut bccx = BorrowckCtxt {
72
        tcx: tcx,
F
Flavio Percoco 已提交
73 74 75 76 77 78
        stats: @BorrowStats {
            loaned_paths_same: Cell::new(0),
            loaned_paths_imm: Cell::new(0),
            stable_paths: Cell::new(0),
            guaranteed_paths: Cell::new(0),
        }
79
    };
B
Brian Anderson 已提交
80

E
Eduard Burtescu 已提交
81
    visit::walk_crate(&mut bccx, krate, ());
B
Brian Anderson 已提交
82

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

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

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

117
fn borrowck_fn(this: &mut BorrowckCtxt,
118 119
               fk: &FnKind,
               decl: &ast::FnDecl,
E
Eduard Burtescu 已提交
120
               body: &ast::Block,
121
               sp: Span,
122
               id: ast::NodeId) {
123 124 125
    debug!("borrowck_fn(id={})", id);

    // Check the body of fn items.
126 127
    let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
    let (all_loans, move_data) =
F
Flavio Percoco 已提交
128
        gather_loans::gather_loans_in_fn(this, decl, body);
129 130 131 132
    let mut loan_dfcx =
        DataFlowContext::new(this.tcx,
                             LoanDataFlowOperator,
                             id_range,
E
Eduard Burtescu 已提交
133 134
                             all_loans.len());
    for (loan_idx, loan) in all_loans.iter().enumerate() {
135 136 137 138 139 140 141 142 143 144 145
        loan_dfcx.add_gen(loan.gen_scope, loan_idx);
        loan_dfcx.add_kill(loan.kill_scope, loan_idx);
    }
    loan_dfcx.propagate(body);

    let flowed_moves = move_data::FlowedMoveData::new(move_data,
                                                      this.tcx,
                                                      id_range,
                                                      body);

    check_loans::check_loans(this, &loan_dfcx, flowed_moves,
146
                             all_loans.as_slice(), decl, body);
N
Niko Matsakis 已提交
147

148
    visit::walk_fn(this, fk, decl, body, sp, ());
N
Niko Matsakis 已提交
149 150
}

B
Brian Anderson 已提交
151 152 153
// ----------------------------------------------------------------------
// Type definitions

E
Eduard Burtescu 已提交
154 155
pub struct BorrowckCtxt<'a> {
    tcx: &'a ty::ctxt,
F
Flavio Percoco 已提交
156 157 158

    // Statistics:
    stats: @BorrowStats
B
Brian Anderson 已提交
159 160
}

F
Flavio Percoco 已提交
161 162 163 164 165
pub struct BorrowStats {
    loaned_paths_same: Cell<uint>,
    loaned_paths_imm: Cell<uint>,
    stable_paths: Cell<uint>,
    guaranteed_paths: Cell<uint>,
166
}
B
Brian Anderson 已提交
167

N
Niko Matsakis 已提交
168
pub type BckResult<T> = Result<T, BckError>;
B
Brian Anderson 已提交
169

170
#[deriving(PartialEq)]
N
Niko Matsakis 已提交
171 172 173
pub enum PartialTotal {
    Partial,   // Loan affects some portion
    Total      // Loan affects entire path
B
Brian Anderson 已提交
174 175
}

N
Niko Matsakis 已提交
176 177 178 179 180 181
///////////////////////////////////////////////////////////////////////////
// Loans and loan paths

/// Record of a loan that was issued.
pub struct Loan {
    index: uint,
E
Eduard Burtescu 已提交
182
    loan_path: Rc<LoanPath>,
183
    kind: ty::BorrowKind,
E
Eduard Burtescu 已提交
184
    restrictions: Vec<Restriction>,
185 186
    gen_scope: ast::NodeId,
    kill_scope: ast::NodeId,
187
    span: Span,
188
    cause: euv::LoanCause,
189 190
}

191
#[deriving(PartialEq, Eq, Hash)]
N
Niko Matsakis 已提交
192
pub enum LoanPath {
193
    LpVar(ast::NodeId),               // `x` in doc.rs
E
Eduard Burtescu 已提交
194
    LpExtend(Rc<LoanPath>, mc::MutabilityCategory, LoanPathElem)
N
Niko Matsakis 已提交
195
}
B
Brian Anderson 已提交
196

197
#[deriving(PartialEq, Eq, Hash)]
N
Niko Matsakis 已提交
198
pub enum LoanPathElem {
N
Niko Matsakis 已提交
199
    LpDeref(mc::PointerKind),    // `*LV` in doc.rs
200
    LpInterior(mc::InteriorKind) // `LV.f` in doc.rs
201 202
}

203
impl LoanPath {
204
    pub fn node_id(&self) -> ast::NodeId {
N
Niko Matsakis 已提交
205 206
        match *self {
            LpVar(local_id) => local_id,
E
Eduard Burtescu 已提交
207
            LpExtend(ref base, _, _) => base.node_id()
N
Niko Matsakis 已提交
208 209
        }
    }
210
}
B
Brian Anderson 已提交
211

212
pub fn opt_loan_path(cmt: &mc::cmt) -> Option<Rc<LoanPath>> {
N
Niko Matsakis 已提交
213 214 215 216 217 218 219
    //! 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.

    match cmt.cat {
A
Alex Crichton 已提交
220
        mc::cat_rvalue(..) |
N
Niko Matsakis 已提交
221
        mc::cat_static_item |
222
        mc::cat_copied_upvar(mc::CopiedUpvar { onceness: ast::Many, .. }) => {
N
Niko Matsakis 已提交
223 224 225
            None
        }

226 227
        mc::cat_local(id) |
        mc::cat_arg(id) |
228
        mc::cat_copied_upvar(mc::CopiedUpvar { upvar_id: id, .. }) |
229
        mc::cat_upvar(ty::UpvarId {var_id: id, ..}, _) => {
E
Eduard Burtescu 已提交
230
            Some(Rc::new(LpVar(id)))
N
Niko Matsakis 已提交
231 232
        }

233
        mc::cat_deref(ref cmt_base, _, pk) => {
234
            opt_loan_path(cmt_base).map(|lp| {
E
Eduard Burtescu 已提交
235
                Rc::new(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
236
            })
N
Niko Matsakis 已提交
237 238
        }

239
        mc::cat_interior(ref cmt_base, ik) => {
240
            opt_loan_path(cmt_base).map(|lp| {
E
Eduard Burtescu 已提交
241
                Rc::new(LpExtend(lp, cmt.mutbl, LpInterior(ik)))
242
            })
N
Niko Matsakis 已提交
243 244
        }

245 246
        mc::cat_downcast(ref cmt_base) |
        mc::cat_discr(ref cmt_base, _) => {
N
Niko Matsakis 已提交
247 248 249
            opt_loan_path(cmt_base)
        }
    }
250
}
B
Brian Anderson 已提交
251

N
Niko Matsakis 已提交
252 253 254 255 256 257
///////////////////////////////////////////////////////////////////////////
// Restrictions
//
// Borrowing an lvalue often results in *restrictions* that limit what
// can be done with this lvalue during the scope of the loan:
//
258
// - `RESTR_MUTATE`: The lvalue may not be modified or `&mut` borrowed.
259
// - `RESTR_FREEZE`: `&` borrows of the lvalue are forbidden.
N
Niko Matsakis 已提交
260 261 262 263 264 265
//
// In addition, no value which is restricted may be moved. Therefore,
// restrictions are meaningful even if the RestrictionSet is empty,
// because the restriction against moves is implied.

pub struct Restriction {
E
Eduard Burtescu 已提交
266
    loan_path: Rc<LoanPath>,
N
Niko Matsakis 已提交
267
    set: RestrictionSet
268 269
}

270
#[deriving(PartialEq)]
N
Niko Matsakis 已提交
271 272
pub struct RestrictionSet {
    bits: u32
B
Brian Anderson 已提交
273 274
}

275
#[allow(dead_code)] // potentially useful
276 277
pub static RESTR_EMPTY: RestrictionSet  = RestrictionSet {bits: 0b0000};
pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b0001};
278
pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b0010};
N
Niko Matsakis 已提交
279

280 281
impl RestrictionSet {
    pub fn intersects(&self, restr: RestrictionSet) -> bool {
N
Niko Matsakis 已提交
282
        (self.bits & restr.bits) != 0
283 284 285
    }
}

N
Niko Matsakis 已提交
286 287 288 289 290
impl BitOr<RestrictionSet,RestrictionSet> for RestrictionSet {
    fn bitor(&self, rhs: &RestrictionSet) -> RestrictionSet {
        RestrictionSet {bits: self.bits | rhs.bits}
    }
}
B
Brian Anderson 已提交
291

N
Niko Matsakis 已提交
292 293 294
impl BitAnd<RestrictionSet,RestrictionSet> for RestrictionSet {
    fn bitand(&self, rhs: &RestrictionSet) -> RestrictionSet {
        RestrictionSet {bits: self.bits & rhs.bits}
B
Brian Anderson 已提交
295 296 297
    }
}

298
impl Repr for RestrictionSet {
299
    fn repr(&self, _tcx: &ty::ctxt) -> String {
A
Alex Crichton 已提交
300
        format!("RestrictionSet(0x{:x})", self.bits as uint)
301 302 303
    }
}

N
Niko Matsakis 已提交
304 305 306 307
///////////////////////////////////////////////////////////////////////////
// Errors

// Errors that can occur
308
#[deriving(PartialEq)]
N
Niko Matsakis 已提交
309
pub enum bckerr_code {
310
    err_mutbl,
N
Niko Matsakis 已提交
311
    err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
312 313
    err_borrowed_pointer_too_short(
        ty::Region, ty::Region, RestrictionSet), // loan, ptr
N
Niko Matsakis 已提交
314 315 316 317
}

// Combination of an error code and the categorization of the expression
// that caused it
318
#[deriving(PartialEq)]
N
Niko Matsakis 已提交
319
pub struct BckError {
320
    span: Span,
321
    cause: euv::LoanCause,
N
Niko Matsakis 已提交
322 323 324 325 326 327
    cmt: mc::cmt,
    code: bckerr_code
}

pub enum AliasableViolationKind {
    MutabilityViolation,
328
    BorrowViolation(euv::LoanCause)
N
Niko Matsakis 已提交
329 330
}

331 332 333 334 335
pub enum MovedValueUseKind {
    MovedInUse,
    MovedInCapture,
}

N
Niko Matsakis 已提交
336
///////////////////////////////////////////////////////////////////////////
B
Brian Anderson 已提交
337 338
// Misc

E
Eduard Burtescu 已提交
339
impl<'a> BorrowckCtxt<'a> {
340 341
    pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
                           -> bool {
342
        self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
B
Brian Anderson 已提交
343 344
    }

345
    pub fn is_subscope_of(&self, r_sub: ast::NodeId, r_sup: ast::NodeId)
346
                          -> bool {
N
Niko Matsakis 已提交
347 348 349
        self.tcx.region_maps.is_subscope_of(r_sub, r_sup)
    }

350 351
    pub fn mc(&self) -> mc::MemCategorizationContext<'a,ty::ctxt> {
        mc::MemCategorizationContext::new(self.tcx)
352 353
    }

E
Eduard Burtescu 已提交
354
    pub fn cat_expr(&self, expr: &ast::Expr) -> mc::cmt {
355 356 357 358 359 360
        match self.mc().cat_expr(expr) {
            Ok(c) => c,
            Err(()) => {
                self.tcx.sess.span_bug(expr.span, "error in mem categorization");
            }
        }
B
Brian Anderson 已提交
361 362
    }

E
Eduard Burtescu 已提交
363
    pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> mc::cmt {
364 365 366 367 368 369
        match self.mc().cat_expr_unadjusted(expr) {
            Ok(c) => c,
            Err(()) => {
                self.tcx.sess.span_bug(expr.span, "error in mem categorization");
            }
        }
B
Brian Anderson 已提交
370 371
    }

372
    pub fn cat_expr_autoderefd(&self,
E
Eduard Burtescu 已提交
373 374
                               expr: &ast::Expr,
                               adj: &ty::AutoAdjustment)
375
                               -> mc::cmt {
376
        let r = match *adj {
377
            ty::AutoAddEnv(..) | ty::AutoObject(..) => {
378
                // no autoderefs
379
                self.mc().cat_expr_unadjusted(expr)
380 381 382 383
            }

            ty::AutoDerefRef(
                ty::AutoDerefRef {
A
Alex Crichton 已提交
384
                    autoderefs: autoderefs, ..}) => {
385 386 387 388 389 390 391 392 393
                self.mc().cat_expr_autoderefd(expr, autoderefs)
            }
        };

        match r {
            Ok(c) => c,
            Err(()) => {
                self.tcx.sess.span_bug(expr.span,
                                       "error in mem categorization");
394 395
            }
        }
B
Brian Anderson 已提交
396 397
    }

398
    pub fn cat_def(&self,
399
                   id: ast::NodeId,
400
                   span: Span,
401
                   ty: ty::t,
402
                   def: def::Def)
403
                   -> mc::cmt {
404 405 406 407 408 409 410 411 412
        match self.mc().cat_def(id, span, ty, def) {
            Ok(c) => c,
            Err(()) => {
                self.tcx.sess.span_bug(span, "error in mem categorization");
            }
        }
    }

    pub fn cat_captured_var(&self,
413 414
                            closure_id: ast::NodeId,
                            closure_span: Span,
415
                            upvar_def: def::Def)
416
                            -> mc::cmt {
417 418
        // Create the cmt for the variable being borrowed, from the
        // caller's perspective
419
        let var_id = upvar_def.def_id().node;
420
        let var_ty = ty::node_id_to_type(self.tcx, var_id);
421
        self.cat_def(closure_id, closure_span, var_ty, upvar_def)
B
Brian Anderson 已提交
422 423
    }

424
    pub fn cat_discr(&self, cmt: mc::cmt, match_id: ast::NodeId) -> mc::cmt {
425 426 427 428 429
        Rc::new(mc::cmt_ {
            cat: mc::cat_discr(cmt.clone(), match_id),
            mutbl: cmt.mutbl.inherit(),
            ..*cmt
        })
B
Brian Anderson 已提交
430 431
    }

432 433
    pub fn cat_pattern(&self,
                       cmt: mc::cmt,
434
                       pat: &ast::Pat,
E
Eduard Burtescu 已提交
435
                       op: |mc::cmt, &ast::Pat|) {
436 437
        let r = self.mc().cat_pattern(cmt, pat, |_,x,y| op(x,y));
        assert!(r.is_ok());
B
Brian Anderson 已提交
438 439
    }

440
    pub fn report(&self, err: BckError) {
B
Brian Anderson 已提交
441
        self.span_err(
N
Niko Matsakis 已提交
442
            err.span,
443
            self.bckerr_to_str(&err).as_slice());
B
Brian Anderson 已提交
444 445 446
        self.note_and_explain_bckerr(err);
    }

447
    pub fn report_use_of_moved_value(&self,
448
                                     use_span: Span,
449
                                     use_kind: MovedValueUseKind,
450
                                     lp: &LoanPath,
451
                                     move: &move_data::Move,
E
Eduard Burtescu 已提交
452
                                     moved_lp: &LoanPath) {
453 454 455 456 457 458 459 460 461
        let verb = match use_kind {
            MovedInUse => "use",
            MovedInCapture => "capture",
        };

        match move.kind {
            move_data::Declared => {
                self.tcx.sess.span_err(
                    use_span,
462
                    format!("{} of possibly uninitialized variable: `{}`",
463 464
                            verb,
                            self.loan_path_to_str(lp)).as_slice());
465 466 467 468 469
            }
            _ => {
                let partially = if lp == moved_lp {""} else {"partially "};
                self.tcx.sess.span_err(
                    use_span,
A
Alex Crichton 已提交
470
                    format!("{} of {}moved value: `{}`",
471 472 473
                            verb,
                            partially,
                            self.loan_path_to_str(lp)).as_slice());
474 475 476 477 478 479
            }
        }

        match move.kind {
            move_data::Declared => {}

E
Eduard Burtescu 已提交
480
            move_data::MoveExpr => {
481
                let (expr_ty, expr_span) = match self.tcx.map.find(move.id) {
482
                    Some(ast_map::NodeExpr(expr)) => {
483
                        (ty::expr_ty_adjusted(self.tcx, expr), expr.span)
E
Eduard Burtescu 已提交
484
                    }
485 486 487 488 489 490
                    r => {
                        self.tcx.sess.bug(format!("MoveExpr({:?}) maps to \
                                                   {:?}, not Expr",
                                                  move.id,
                                                  r).as_slice())
                    }
E
Eduard Burtescu 已提交
491
                };
492 493
                let suggestion = move_suggestion(self.tcx, expr_ty,
                        "moved by default (use `copy` to override)");
494
                self.tcx.sess.span_note(
E
Eduard Burtescu 已提交
495
                    expr_span,
A
Alex Crichton 已提交
496
                    format!("`{}` moved here because it has type `{}`, which is {}",
497 498 499
                            self.loan_path_to_str(moved_lp),
                            expr_ty.user_string(self.tcx),
                            suggestion).as_slice());
500 501
            }

E
Eduard Burtescu 已提交
502 503
            move_data::MovePat => {
                let pat_ty = ty::node_id_to_type(self.tcx, move.id);
504
                self.tcx.sess.span_note(self.tcx.map.span(move.id),
A
Alex Crichton 已提交
505
                    format!("`{}` moved here because it has type `{}`, \
506 507 508 509
                             which is moved by default (use `ref` to \
                             override)",
                            self.loan_path_to_str(moved_lp),
                            pat_ty.user_string(self.tcx)).as_slice());
510 511
            }

E
Eduard Burtescu 已提交
512
            move_data::Captured => {
513
                let (expr_ty, expr_span) = match self.tcx.map.find(move.id) {
514
                    Some(ast_map::NodeExpr(expr)) => {
515
                        (ty::expr_ty_adjusted(self.tcx, expr), expr.span)
E
Eduard Burtescu 已提交
516
                    }
517 518 519 520 521 522
                    r => {
                        self.tcx.sess.bug(format!("Captured({:?}) maps to \
                                                   {:?}, not Expr",
                                                  move.id,
                                                  r).as_slice())
                    }
E
Eduard Burtescu 已提交
523
                };
524 525 526
                let suggestion = move_suggestion(self.tcx, expr_ty,
                        "moved by default (make a copy and \
                         capture that instead to override)");
527
                self.tcx.sess.span_note(
E
Eduard Burtescu 已提交
528
                    expr_span,
A
Alex Crichton 已提交
529
                    format!("`{}` moved into closure environment here because it \
530 531 532 533
                            has type `{}`, which is {}",
                            self.loan_path_to_str(moved_lp),
                            expr_ty.user_string(self.tcx),
                            suggestion).as_slice());
534 535 536
            }
        }

E
Eduard Burtescu 已提交
537
        fn move_suggestion(tcx: &ty::ctxt, ty: ty::t, default_msg: &'static str)
538 539
                          -> &'static str {
            match ty::get(ty).sty {
540 541 542 543
                ty::ty_closure(box ty::ClosureTy {
                        store: ty::RegionTraitStore(..),
                        ..
                    }) =>
544 545
                    "a non-copyable stack closure (capture it in a new closure, \
                     e.g. `|x| f(x)`, to override)",
546
                _ if ty::type_moves_by_default(tcx, ty) =>
547 548
                    "non-copyable (perhaps you meant to use clone()?)",
                _ => default_msg,
549 550 551 552
            }
        }
    }

553
    pub fn report_reassigned_immutable_variable(&self,
554
                                                span: Span,
555
                                                lp: &LoanPath,
556 557
                                                assign:
                                                &move_data::Assignment) {
558 559
        self.tcx.sess.span_err(
            span,
A
Alex Crichton 已提交
560
            format!("re-assignment of immutable variable `{}`",
561 562
                    self.loan_path_to_str(lp)).as_slice());
        self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
563 564
    }

565
    pub fn span_err(&self, s: Span, m: &str) {
B
Brian Anderson 已提交
566 567 568
        self.tcx.sess.span_err(s, m);
    }

569
    pub fn span_note(&self, s: Span, m: &str) {
B
Brian Anderson 已提交
570 571 572
        self.tcx.sess.span_note(s, m);
    }

573 574 575 576
    pub fn span_end_note(&self, s: Span, m: &str) {
        self.tcx.sess.span_end_note(s, m);
    }

577
    pub fn bckerr_to_str(&self, err: &BckError) -> String {
B
Brian Anderson 已提交
578
        match err.code {
579
            err_mutbl => {
580
                let descr = match opt_loan_path(&err.cmt) {
581
                    None => {
A
Alex Crichton 已提交
582 583 584
                        format!("{} {}",
                                err.cmt.mutbl.to_user_str(),
                                self.cmt_to_str(&*err.cmt))
585 586
                    }
                    Some(lp) => {
A
Alex Crichton 已提交
587 588 589 590
                        format!("{} {} `{}`",
                                err.cmt.mutbl.to_user_str(),
                                self.cmt_to_str(&*err.cmt),
                                self.loan_path_to_str(&*lp))
591
                    }
592 593 594
                };

                match err.cause {
595
                    euv::ClosureCapture(_) => {
A
Alex Crichton 已提交
596
                        format!("closure cannot assign to {}", descr)
597
                    }
598 599 600 601
                    euv::OverloadedOperator |
                    euv::AddrOf |
                    euv::RefBinding |
                    euv::AutoRef => {
A
Alex Crichton 已提交
602
                        format!("cannot borrow {} as mutable", descr)
603
                    }
604
                    euv::ClosureInvocation => {
605 606 607
                        self.tcx.sess.span_bug(err.span,
                            "err_mutbl with a closure invocation");
                    }
608
                }
B
Brian Anderson 已提交
609
            }
A
Alex Crichton 已提交
610
            err_out_of_scope(..) => {
611
                let msg = match opt_loan_path(&err.cmt) {
612
                    None => "borrowed value".to_string(),
613
                    Some(lp) => {
A
Alex Crichton 已提交
614
                        format!("`{}`", self.loan_path_to_str(&*lp))
615
                    }
616
                };
A
Alex Crichton 已提交
617
                format!("{} does not live long enough", msg)
B
Brian Anderson 已提交
618
            }
N
Niko Matsakis 已提交
619
            err_borrowed_pointer_too_short(..) => {
620
                let descr = match opt_loan_path(&err.cmt) {
621
                    Some(lp) => {
A
Alex Crichton 已提交
622
                        format!("`{}`", self.loan_path_to_str(&*lp))
623
                    }
624
                    None => self.cmt_to_str(&*err.cmt),
625 626
                };

A
Alex Crichton 已提交
627
                format!("lifetime of {} is too short to guarantee \
628 629
                                its contents can be safely reborrowed",
                               descr)
630
            }
N
Niko Matsakis 已提交
631 632 633
        }
    }

634
    pub fn report_aliasability_violation(&self,
635
                                         span: Span,
636 637
                                         kind: AliasableViolationKind,
                                         cause: mc::AliasableReason) {
N
Niko Matsakis 已提交
638
        let prefix = match kind {
639 640 641
            MutabilityViolation => {
                "cannot assign to data"
            }
642
            BorrowViolation(euv::ClosureCapture(_)) => {
643 644 645 646 647 648 649 650 651
                // I don't think we can get aliasability violations
                // with closure captures, so no need to come up with a
                // good error message. The reason this cannot happen
                // is because we only capture local variables in
                // closures, and those are never aliasable.
                self.tcx.sess.span_bug(
                    span,
                    "aliasability violation with closure");
            }
652 653 654 655
            BorrowViolation(euv::OverloadedOperator) |
            BorrowViolation(euv::AddrOf) |
            BorrowViolation(euv::AutoRef) |
            BorrowViolation(euv::RefBinding) => {
656 657
                "cannot borrow data mutably"
            }
658

659
            BorrowViolation(euv::ClosureInvocation) => {
660 661
                "closure invocation"
            }
N
Niko Matsakis 已提交
662 663 664 665 666 667
        };

        match cause {
            mc::AliasableOther => {
                self.tcx.sess.span_err(
                    span,
668
                    format!("{} in an aliasable location",
669
                             prefix).as_slice());
B
Brian Anderson 已提交
670
            }
671 672
            mc::AliasableStatic(..) |
            mc::AliasableStaticMut(..) => {
673 674
                self.tcx.sess.span_err(
                    span,
675
                    format!("{} in a static location", prefix).as_slice());
676
            }
677
            mc::AliasableManaged => {
678 679
                self.tcx.sess.span_err(
                    span,
680
                    format!("{} in a `@` pointer", prefix).as_slice());
N
Niko Matsakis 已提交
681
            }
682
            mc::AliasableBorrowed => {
N
Niko Matsakis 已提交
683 684
                self.tcx.sess.span_err(
                    span,
685
                    format!("{} in a `&` reference", prefix).as_slice());
B
Brian Anderson 已提交
686 687 688 689
            }
        }
    }

690
    pub fn note_and_explain_bckerr(&self, err: BckError) {
B
Brian Anderson 已提交
691 692
        let code = err.code;
        match code {
693
            err_mutbl(..) => { }
B
Brian Anderson 已提交
694 695 696 697

            err_out_of_scope(super_scope, sub_scope) => {
                note_and_explain_region(
                    self.tcx,
698
                    "reference must be valid for ",
B
Brian Anderson 已提交
699
                    sub_scope,
700
                    "...");
701 702 703 704 705
                let suggestion = if is_statement_scope(self.tcx, super_scope) {
                    "; consider using a `let` binding to increase its lifetime"
                } else {
                    ""
                };
B
Brian Anderson 已提交
706 707
                note_and_explain_region(
                    self.tcx,
708
                    "...but borrowed value is only valid for ",
B
Brian Anderson 已提交
709
                    super_scope,
710
                    suggestion);
711 712
            }

713
            err_borrowed_pointer_too_short(loan_scope, ptr_scope, _) => {
714
                let descr = match opt_loan_path(&err.cmt) {
715
                    Some(lp) => {
A
Alex Crichton 已提交
716
                        format!("`{}`", self.loan_path_to_str(&*lp))
717
                    }
718
                    None => self.cmt_to_str(&*err.cmt),
719 720 721
                };
                note_and_explain_region(
                    self.tcx,
722 723
                    format!("{} would have to be valid for ",
                            descr).as_slice(),
724 725 726 727
                    loan_scope,
                    "...");
                note_and_explain_region(
                    self.tcx,
728
                    format!("...but {} is only valid for ", descr).as_slice(),
729 730 731
                    ptr_scope,
                    "");
            }
B
Brian Anderson 已提交
732 733 734
        }
    }

735 736
    pub fn append_loan_path_to_str(&self,
                                   loan_path: &LoanPath,
737
                                   out: &mut String) {
N
Niko Matsakis 已提交
738 739
        match *loan_path {
            LpVar(id) => {
740
                out.push_str(ty::local_var_name_str(self.tcx, id).get());
N
Niko Matsakis 已提交
741
            }
B
Brian Anderson 已提交
742

E
Eduard Burtescu 已提交
743 744
            LpExtend(ref lp_base, _, LpInterior(mc::InteriorField(fname))) => {
                self.append_autoderefd_loan_path_to_str(&**lp_base, out);
745
                match fname {
746
                    mc::NamedField(fname) => {
747
                        out.push_char('.');
748
                        out.push_str(token::get_name(fname).get());
749 750
                    }
                    mc::PositionalField(idx) => {
751
                        out.push_char('#'); // invent a notation here
752
                        out.push_str(idx.to_str().as_slice());
753 754
                    }
                }
N
Niko Matsakis 已提交
755 756
            }

E
Eduard Burtescu 已提交
757 758
            LpExtend(ref lp_base, _, LpInterior(mc::InteriorElement(_))) => {
                self.append_autoderefd_loan_path_to_str(&**lp_base, out);
759
                out.push_str("[..]");
N
Niko Matsakis 已提交
760 761
            }

E
Eduard Burtescu 已提交
762
            LpExtend(ref lp_base, _, LpDeref(_)) => {
763
                out.push_char('*');
E
Eduard Burtescu 已提交
764
                self.append_loan_path_to_str(&**lp_base, out);
N
Niko Matsakis 已提交
765 766 767 768
            }
        }
    }

769 770
    pub fn append_autoderefd_loan_path_to_str(&self,
                                              loan_path: &LoanPath,
771
                                              out: &mut String) {
772
        match *loan_path {
E
Eduard Burtescu 已提交
773
            LpExtend(ref lp_base, _, LpDeref(_)) => {
774 775 776
                // 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.
E
Eduard Burtescu 已提交
777
                self.append_autoderefd_loan_path_to_str(&**lp_base, out)
778 779 780 781 782 783 784 785
            }

            LpVar(..) | LpExtend(_, _, LpInterior(..)) => {
                self.append_loan_path_to_str(loan_path, out)
            }
        }
    }

786 787
    pub fn loan_path_to_str(&self, loan_path: &LoanPath) -> String {
        let mut result = String::new();
N
Niko Matsakis 已提交
788
        self.append_loan_path_to_str(loan_path, &mut result);
789
        result
B
Brian Anderson 已提交
790 791
    }

792
    pub fn cmt_to_str(&self, cmt: &mc::cmt_) -> String {
793
        self.mc().cmt_to_str(cmt)
B
Brian Anderson 已提交
794
    }
N
Niko Matsakis 已提交
795 796
}

797 798 799 800 801 802 803 804 805 806 807 808
fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool {
     match region {
         ty::ReScope(node_id) => {
             match tcx.map.find(node_id) {
                 Some(ast_map::NodeStmt(_)) => true,
                 _ => false
             }
         }
         _ => false
     }
}

N
Niko Matsakis 已提交
809
impl DataFlowOperator for LoanDataFlowOperator {
810
    #[inline]
N
Niko Matsakis 已提交
811 812 813 814
    fn initial_value(&self) -> bool {
        false // no loans in scope by default
    }

815
    #[inline]
N
Niko Matsakis 已提交
816 817 818 819
    fn join(&self, succ: uint, pred: uint) -> uint {
        succ | pred // loans from both preds are in scope
    }
}
820

N
Niko Matsakis 已提交
821
impl Repr for Loan {
822
    fn repr(&self, tcx: &ty::ctxt) -> String {
823 824 825 826 827 828
        (format!("Loan_{:?}({}, {:?}, {:?}-{:?}, {})",
                 self.index,
                 self.loan_path.repr(tcx),
                 self.kind,
                 self.gen_scope,
                 self.kill_scope,
829
                 self.restrictions.repr(tcx))).to_string()
B
Brian Anderson 已提交
830 831 832
    }
}

N
Niko Matsakis 已提交
833
impl Repr for Restriction {
834
    fn repr(&self, tcx: &ty::ctxt) -> String {
835 836
        (format!("Restriction({}, {:x})",
                 self.loan_path.repr(tcx),
837
                 self.set.bits as uint)).to_string()
N
Niko Matsakis 已提交
838 839 840 841
    }
}

impl Repr for LoanPath {
842
    fn repr(&self, tcx: &ty::ctxt) -> String {
N
Niko Matsakis 已提交
843 844
        match self {
            &LpVar(id) => {
845
                (format!("$({})", tcx.map.node_to_str(id))).to_string()
N
Niko Matsakis 已提交
846 847
            }

E
Eduard Burtescu 已提交
848
            &LpExtend(ref lp, _, LpDeref(_)) => {
849
                (format!("{}.*", lp.repr(tcx))).to_string()
N
Niko Matsakis 已提交
850 851
            }

E
Eduard Burtescu 已提交
852
            &LpExtend(ref lp, _, LpInterior(ref interior)) => {
853 854
                (format!("{}.{}",
                         lp.repr(tcx),
855
                         interior.repr(tcx))).to_string()
N
Niko Matsakis 已提交
856 857
            }
        }
B
Brian Anderson 已提交
858 859
    }
}